def render_vis(model, objective_f, param_f=None, optimizer=None,
               transforms=None, thresholds=(512,), print_objectives=None,
               verbose=True, relu_gradient_override=True, use_fixed_seed=False):
  """Flexible optimization-base feature vis.

  There's a lot of ways one might wish to customize otpimization-based
  feature visualization. It's hard to create an abstraction that stands up
  to all the things one might wish to try.

  This function probably can't do *everything* you want, but it's much more
  flexible than a naive attempt. The basic abstraction is to split the problem
  into several parts. Consider the rguments:

  Args:
    model: The model to be visualized, from Alex' modelzoo.
    objective_f: The objective our visualization maximizes.
      See the objectives module for more details.
    param_f: Paramaterization of the image we're optimizing.
      See the paramaterization module for more details.
      Defaults to a naively paramaterized [1, 128, 128, 3] image.
    optimizer: Optimizer to optimize with. Either tf.train.Optimizer instance,
      or a function from (graph, sess) to such an instance.
      Defaults to Adam with lr .05.
    transforms: A list of stochastic transformations that get composed,
      which our visualization should robustly activate the network against.
      See the transform module for more details.
      Defaults to [transform.jitter(8)].
    thresholds: A list of numbers of optimization steps, at which we should
      save (and display if verbose=True) the visualization.
    print_objectives: A list of objectives separate from those being optimized,
      whose values get logged during the optimization.
    verbose: Should we display the visualization when we hit a threshold?
      This should only be used in IPython.
    relu_gradient_override: Whether to use the gradient override scheme
      described in lucid/misc/redirected_relu_grad.py. On by default!
    use_fixed_seed: Seed the RNG with a fixed value so results are reproducible.
      Off by default. As of tf 1.8 this does not work as intended, see:
      https://github.com/tensorflow/tensorflow/issues/9171
  Returns:
    2D array of optimization results containing of evaluations of supplied
    param_f snapshotted at specified thresholds. Usually that will mean one or
    multiple channel visualizations stacked on top of each other.
  """

  with tf.Graph().as_default() as graph, tf.Session() as sess:

    if use_fixed_seed:  # does not mean results are reproducible, see Args doc
      tf.set_random_seed(0)

    T = make_vis_T(model, objective_f, param_f, optimizer, transforms,
                   relu_gradient_override)
    print_objective_func = make_print_objective_func(print_objectives, T)
    loss, vis_op, t_image = T("loss"), T("vis_op"), T("input")
    tf.global_variables_initializer().run()

    images = []
    try:
      for i in range(max(thresholds)+1):
        loss_, _ = sess.run([loss, vis_op])
        if i in thresholds:
          vis = t_image.eval()
          images.append(vis)
          if verbose:
            print(i, loss_)
            print_objective_func(sess)
            show(np.hstack(vis))
    except KeyboardInterrupt:
      log.warning("Interrupted optimization at step {:d}.".format(i+1))
      vis = t_image.eval()
      show(np.hstack(vis))

    return images
Flexible optimization-base feature vis.

  There's a lot of ways one might wish to customize otpimization-based
  feature visualization. It's hard to create an abstraction that stands up
  to all the things one might wish to try.

  This function probably can't do *everything* you want, but it's much more
  flexible than a naive attempt. The basic abstraction is to split the problem
  into several parts. Consider the rguments:

  Args:
    model: The model to be visualized, from Alex' modelzoo.
    objective_f: The objective our visualization maximizes.
      See the objectives module for more details.
    param_f: Paramaterization of the image we're optimizing.
      See the paramaterization module for more details.
      Defaults to a naively paramaterized [1, 128, 128, 3] image.
    optimizer: Optimizer to optimize with. Either tf.train.Optimizer instance,
      or a function from (graph, sess) to such an instance.
      Defaults to Adam with lr .05.
    transforms: A list of stochastic transformations that get composed,
      which our visualization should robustly activate the network against.
      See the transform module for more details.
      Defaults to [transform.jitter(8)].
    thresholds: A list of numbers of optimization steps, at which we should
      save (and display if verbose=True) the visualization.
    print_objectives: A list of objectives separate from those being optimized,
      whose values get logged during the optimization.
    verbose: Should we display the visualization when we hit a threshold?
      This should only be used in IPython.
    relu_gradient_override: Whether to use the gradient override scheme
      described in lucid/misc/redirected_relu_grad.py. On by default!
    use_fixed_seed: Seed the RNG with a fixed value so results are reproducible.
      Off by default. As of tf 1.8 this does not work as intended, see:
      https://github.com/tensorflow/tensorflow/issues/9171
  Returns:
    2D array of optimization results containing of evaluations of supplied
    param_f snapshotted at specified thresholds. Usually that will mean one or
    multiple channel visualizations stacked on top of each other.