# TensorBoard Histogram Dashboard The TensorBoard Histogram Dashboard displays how the distribution of some `Tensor` in your TensorFlow graph has changed over time. It does this by showing many histograms visualizations of your tensor at different points in time. ## A Basic Example Let's start with a simple case: a normally-distributed variable, where the mean shifts over time. TensorFlow has an op [`tf.random_normal`](https://www.tensorflow.org/api_docs/python/tf/random_normal) which is perfect for this purpose. As is usually the case with TensorBoard, we will ingest data using a summary op; in this case, ['tf.summary.histogram'](https://www.tensorflow.org/api_docs/python/tf/summary/histogram). For a primer on how summaries work, please see the [TensorBoard guide](./summaries_and_tensorboard.md). Here is a code snippet that will generate some histogram summaries containing normally distributed data, where the mean of the distribution increases over time. ```python import tensorflow as tf k = tf.placeholder(tf.float32) # Make a normal distribution, with a shifting mean mean_moving_normal = tf.random_normal(shape=[1000], mean=(5*k), stddev=1) # Record that distribution into a histogram summary tf.summary.histogram("normal/moving_mean", mean_moving_normal) # Setup a session and summary writer sess = tf.Session() writer = tf.summary.FileWriter("/tmp/histogram_example") summaries = tf.summary.merge_all() # Setup a loop and write the summaries to disk N = 400 for step in range(N): k_val = step/float(N) summ = sess.run(summaries, feed_dict={k: k_val}) writer.add_summary(summ, global_step=step) ``` Once that code runs, we can load the data into TensorBoard via the command line: ```sh tensorboard --logdir=/tmp/histogram_example ``` Once TensorBoard is running, load it in Chrome or Firefox and navigate to the Histogram Dashboard. Then we can see a histogram visualization for our normally distributed data. ![](https://www.tensorflow.org/images/tensorboard/histogram_dashboard/1_moving_mean.png) `tf.summary.histogram` takes an arbitrarily sized and shaped Tensor, and compresses it into a histogram data structure consisting of many bins with widths and counts. For example, let's say we want to organize the numbers `[0.5, 1.1, 1.3, 2.2, 2.9, 2.99]` into bins. We could make three bins: * a bin containing everything from 0 to 1 (it would contain one element, 0.5), * a bin containing everything from 1-2 (it would contain two elements, 1.1 and 1.3), * a bin containing everything from 2-3 (it would contain three elements: 2.2, 2.9 and 2.99). TensorFlow uses a similar approach to create bins, but unlike in our example, it doesn't create integer bins. For large, sparse datasets, that might result in many thousands of bins. Instead, [the bins are exponentially distributed, with many bins close to 0 and comparatively few bins for very large numbers.](https://github.com/tensorflow/tensorflow/blob/c8b59c046895fa5b6d79f73e0b5817330fcfbfc1/tensorflow/core/lib/histogram/histogram.cc#L28) However, visualizing exponentially-distributed bins is tricky; if height is used to encode count, then wider bins take more space, even if they have the same number of elements. Conversely, encoding count in the area makes height comparisons impossible. Instead, the histograms [resample the data](https://github.com/tensorflow/tensorflow/blob/17c47804b86e340203d451125a721310033710f1/tensorflow/tensorboard/components/tf_backend/backend.ts#L400) into uniform bins. This can lead to unfortunate artifacts in some cases. Each slice in the histogram visualizer displays a single histogram. The slices are organized by step; older slices (e.g. step 0) are further "back" and darker, while newer slices (e.g. step 400) are close to the foreground, and lighter in color. The y-axis on the right shows the step number. You can mouse over the histogram to see tooltips with some more detailed information. For example, in the following image we can see that the histogram at timestep 176 has a bin centered at 2.25 with 177 elements in that bin. ![](https://www.tensorflow.org/images/tensorboard/histogram_dashboard/2_moving_mean_tooltip.png) Also, you may note that the histogram slices are not always evenly spaced in step count or time. This is because TensorBoard uses [reservoir sampling](https://en.wikipedia.org/wiki/Reservoir_sampling) to keep a subset of all the histograms, to save on memory. Reservoir sampling guarantees that every sample has an equal likelihood of being included, but because it is a randomized algorithm, the samples chosen don't occur at even steps. ## Overlay Mode There is a control on the left of the dashboard that allows you to toggle the histogram mode from "offset" to "overlay": ![](https://www.tensorflow.org/images/tensorboard/histogram_dashboard/3_overlay_offset.png) In "offset" mode, the visualization rotates 45 degrees, so that the individual histogram slices are no longer spread out in time, but instead are all plotted on the same y-axis. ![](https://www.tensorflow.org/images/tensorboard/histogram_dashboard/4_overlay.png) Now, each slice is a separate line on the chart, and the y-axis shows the item count within each bucket. Darker lines are older, earlier steps, and lighter lines are more recent, later steps. Once again, you can mouse over the chart to see some additional information. ![](https://www.tensorflow.org/images/tensorboard/histogram_dashboard/5_overlay_tooltips.png) In general, the overlay visualization is useful if you want to directly compare the counts of different histograms. ## Multimodal Distributions The Histogram Dashboard is great for visualizing multimodal distributions. Let's construct a simple bimodal distribution by concatenating the outputs from two different normal distributions. The code will look like this: ```python import tensorflow as tf k = tf.placeholder(tf.float32) # Make a normal distribution, with a shifting mean mean_moving_normal = tf.random_normal(shape=[1000], mean=(5*k), stddev=1) # Record that distribution into a histogram summary tf.summary.histogram("normal/moving_mean", mean_moving_normal) # Make a normal distribution with shrinking variance variance_shrinking_normal = tf.random_normal(shape=[1000], mean=0, stddev=1-(k)) # Record that distribution too tf.summary.histogram("normal/shrinking_variance", variance_shrinking_normal) # Let's combine both of those distributions into one dataset normal_combined = tf.concat([mean_moving_normal, variance_shrinking_normal], 0) # We add another histogram summary to record the combined distribution tf.summary.histogram("normal/bimodal", normal_combined) summaries = tf.summary.merge_all() # Setup a session and summary writer sess = tf.Session() writer = tf.summary.FileWriter("/tmp/histogram_example") # Setup a loop and write the summaries to disk N = 400 for step in range(N): k_val = step/float(N) summ = sess.run(summaries, feed_dict={k: k_val}) writer.add_summary(summ, global_step=step) ``` You already remember our "moving mean" normal distribution from the example above. Now we also have a "shrinking variance" distribution. Side-by-side, they look like this: ![](https://www.tensorflow.org/images/tensorboard/histogram_dashboard/6_two_distributions.png) When we concatenate them, we get a chart that clearly reveals the divergent, bimodal structure: ![](https://www.tensorflow.org/images/tensorboard/histogram_dashboard/7_bimodal.png) ## Some more distributions Just for fun, let's generate and visualize a few more distributions, and then combine them all into one chart. Here's the code we'll use: ```python import tensorflow as tf k = tf.placeholder(tf.float32) # Make a normal distribution, with a shifting mean mean_moving_normal = tf.random_normal(shape=[1000], mean=(5*k), stddev=1) # Record that distribution into a histogram summary tf.summary.histogram("normal/moving_mean", mean_moving_normal) # Make a normal distribution with shrinking variance variance_shrinking_normal = tf.random_normal(shape=[1000], mean=0, stddev=1-(k)) # Record that distribution too tf.summary.histogram("normal/shrinking_variance", variance_shrinking_normal) # Let's combine both of those distributions into one dataset normal_combined = tf.concat([mean_moving_normal, variance_shrinking_normal], 0) # We add another histogram summary to record the combined distribution tf.summary.histogram("normal/bimodal", normal_combined) # Add a gamma distribution gamma = tf.random_gamma(shape=[1000], alpha=k) tf.summary.histogram("gamma", gamma) # And a poisson distribution poisson = tf.random_poisson(shape=[1000], lam=k) tf.summary.histogram("poisson", poisson) # And a uniform distribution uniform = tf.random_uniform(shape=[1000], maxval=k*10) tf.summary.histogram("uniform", uniform) # Finally, combine everything together! all_distributions = [mean_moving_normal, variance_shrinking_normal, gamma, poisson, uniform] all_combined = tf.concat(all_distributions, 0) tf.summary.histogram("all_combined", all_combined) summaries = tf.summary.merge_all() # Setup a session and summary writer sess = tf.Session() writer = tf.summary.FileWriter("/tmp/histogram_example") # Setup a loop and write the summaries to disk N = 400 for step in range(N): k_val = step/float(N) summ = sess.run(summaries, feed_dict={k: k_val}) writer.add_summary(summ, global_step=step) ``` ### Gamma Distribution ![](https://www.tensorflow.org/images/tensorboard/histogram_dashboard/8_gamma.png) ### Uniform Distribution ![](https://www.tensorflow.org/images/tensorboard/histogram_dashboard/9_uniform.png) ### Poisson Distribution ![](https://www.tensorflow.org/images/tensorboard/histogram_dashboard/10_poisson.png) The poisson distribution is defined over the integers. So, all of the values being generated are perfect integers. The histogram compression moves the data into floating-point bins, causing the visualization to show little bumps over the integer values rather than perfect spikes. ### All Together Now Finally, we can concatenate all of the data into one funny-looking curve. ![](https://www.tensorflow.org/images/tensorboard/histogram_dashboard/11_all_combined.png)