Extract the distributions dashboard to a plugin
This continues the great plugin migration. The distributions plugin was similar to the histograms plugin, but it also purported to allow CSV download like the scalars plugin. However, the existing implementation of this was flawed, and would always yield a 500 on current prod [1] (unless there were actually no data). This indicates that no one is actually using it---probably because there isn't a relevant button on the frontend, anyway!---so I just removed it. This also changes most frontend occurrences of "compressedHistograms" to "distributions" while we're at it. [1]: Due to the reference `value.rank_in_bps` in the handler `_serve_compressed_histograms`; this field does not exist and throws an `AttributeError`. PiperOrigin-RevId: 157787156
This commit is contained in:
parent
23cdf96b85
commit
7d7a403096
tensorflow
BUILD
contrib/cmake
tensorboard
@ -380,6 +380,7 @@ filegroup(
|
||||
"//tensorflow/tensorboard/java/org/tensorflow/tensorboard/vulcanize:all_files",
|
||||
"//tensorflow/tensorboard/plugins:all_files",
|
||||
"//tensorflow/tensorboard/plugins/audio:all_files",
|
||||
"//tensorflow/tensorboard/plugins/distributions:all_files",
|
||||
"//tensorflow/tensorboard/plugins/histograms:all_files",
|
||||
"//tensorflow/tensorboard/plugins/images:all_files",
|
||||
"//tensorflow/tensorboard/plugins/projector:all_files",
|
||||
|
@ -230,6 +230,7 @@ add_python_module("tensorflow/tensorboard/backend")
|
||||
add_python_module("tensorflow/tensorboard/backend/event_processing")
|
||||
add_python_module("tensorflow/tensorboard/plugins")
|
||||
add_python_module("tensorflow/tensorboard/plugins/audio")
|
||||
add_python_module("tensorflow/tensorboard/plugins/distributions")
|
||||
add_python_module("tensorflow/tensorboard/plugins/histograms")
|
||||
add_python_module("tensorflow/tensorboard/plugins/images")
|
||||
add_python_module("tensorflow/tensorboard/plugins/projector")
|
||||
|
@ -14,6 +14,7 @@ py_binary(
|
||||
"//tensorflow/tensorboard/backend:application",
|
||||
"//tensorflow/tensorboard/backend/event_processing:event_file_inspector",
|
||||
"//tensorflow/tensorboard/plugins/audio:audio_plugin",
|
||||
"//tensorflow/tensorboard/plugins/distributions:distributions_plugin",
|
||||
"//tensorflow/tensorboard/plugins/histograms:histograms_plugin",
|
||||
"//tensorflow/tensorboard/plugins/images:images_plugin",
|
||||
"//tensorflow/tensorboard/plugins/projector:projector_plugin",
|
||||
|
@ -22,15 +22,12 @@ from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
|
||||
import csv
|
||||
import os
|
||||
import re
|
||||
import threading
|
||||
import time
|
||||
|
||||
import six
|
||||
from six import StringIO
|
||||
from six.moves import xrange # pylint: disable=redefined-builtin
|
||||
from six.moves.urllib import parse as urlparse
|
||||
import tensorflow as tf
|
||||
from werkzeug import wrappers
|
||||
@ -59,6 +56,7 @@ DEFAULT_SIZE_GUIDANCE = {
|
||||
# /data/runs entirely.
|
||||
_MIGRATED_DATA_KEYS = frozenset((
|
||||
'audio',
|
||||
'distributions',
|
||||
'histograms',
|
||||
'images',
|
||||
'scalars',
|
||||
@ -69,7 +67,6 @@ LOGDIR_ROUTE = '/logdir'
|
||||
RUNS_ROUTE = '/runs'
|
||||
PLUGIN_PREFIX = '/plugin'
|
||||
PLUGINS_LISTING_ROUTE = '/plugins_listing'
|
||||
COMPRESSED_HISTOGRAMS_ROUTE = '/' + event_accumulator.COMPRESSED_HISTOGRAMS
|
||||
GRAPH_ROUTE = '/' + event_accumulator.GRAPH
|
||||
RUN_METADATA_ROUTE = '/' + event_accumulator.RUN_METADATA
|
||||
TAB_ROUTES = ['', '/events', '/images', '/audio', '/graphs', '/histograms']
|
||||
@ -80,16 +77,6 @@ TAB_ROUTES = ['', '/events', '/images', '/audio', '/graphs', '/histograms']
|
||||
_VALID_PLUGIN_RE = re.compile(r'^[A-Za-z0-9_.-]+$')
|
||||
|
||||
|
||||
class _OutputFormat(object):
|
||||
"""An enum used to list the valid output formats for API calls.
|
||||
|
||||
Not all API calls support all formats (for example, only scalars and
|
||||
compressed histograms support CSV).
|
||||
"""
|
||||
JSON = 'json'
|
||||
CSV = 'csv'
|
||||
|
||||
|
||||
def standard_tensorboard_wsgi(
|
||||
logdir,
|
||||
purge_orphaned_data,
|
||||
@ -159,8 +146,6 @@ class TensorBoardWSGIApp(object):
|
||||
reload_multiplexer(self._multiplexer, path_to_run)
|
||||
|
||||
self.data_applications = {
|
||||
DATA_PREFIX + COMPRESSED_HISTOGRAMS_ROUTE:
|
||||
self._serve_compressed_histograms,
|
||||
DATA_PREFIX + GRAPH_ROUTE:
|
||||
self._serve_graph,
|
||||
DATA_PREFIX + LOGDIR_ROUTE:
|
||||
@ -278,35 +263,6 @@ class TensorBoardWSGIApp(object):
|
||||
return http_util.Respond(
|
||||
request, str(run_metadata), 'text/x-protobuf') # pbtxt
|
||||
|
||||
@wrappers.Request.application
|
||||
def _serve_compressed_histograms(self, request):
|
||||
"""Given a tag and single run, return an array of compressed histograms."""
|
||||
tag = request.args.get('tag')
|
||||
run = request.args.get('run')
|
||||
compressed_histograms = self._multiplexer.CompressedHistograms(run, tag)
|
||||
if request.args.get('format') == _OutputFormat.CSV:
|
||||
string_io = StringIO()
|
||||
writer = csv.writer(string_io)
|
||||
|
||||
# Build the headers; we have two columns for timing and two columns for
|
||||
# each compressed histogram bucket.
|
||||
headers = ['Wall time', 'Step']
|
||||
if compressed_histograms:
|
||||
bucket_count = len(compressed_histograms[0].compressed_histogram_values)
|
||||
for i in xrange(bucket_count):
|
||||
headers += ['Edge %d basis points' % i, 'Edge %d value' % i]
|
||||
writer.writerow(headers)
|
||||
|
||||
for compressed_histogram in compressed_histograms:
|
||||
row = [compressed_histogram.wall_time, compressed_histogram.step]
|
||||
for value in compressed_histogram.compressed_histogram_values:
|
||||
row += [value.rank_in_bps, value.value]
|
||||
writer.writerow(row)
|
||||
return http_util.Respond(request, string_io.getvalue(), 'text/csv')
|
||||
else:
|
||||
return http_util.Respond(
|
||||
request, compressed_histograms, 'application/json')
|
||||
|
||||
@wrappers.Request.application
|
||||
def _serve_plugins_listing(self, request):
|
||||
"""Serves an object mapping plugin name to whether it is enabled.
|
||||
|
@ -167,7 +167,6 @@ class TensorboardServerTest(tf.test.TestCase):
|
||||
run_json,
|
||||
{
|
||||
'run1': {
|
||||
'compressedHistograms': ['histogram'],
|
||||
# if only_use_meta_graph, the graph is from the metagraph
|
||||
'graph': True,
|
||||
'meta_graph': self._only_use_meta_graph,
|
||||
@ -265,13 +264,8 @@ class TensorboardServerTest(tf.test.TestCase):
|
||||
"""Generates the test data directory.
|
||||
|
||||
The test data has a single run named run1 which contains:
|
||||
- a histogram [1]
|
||||
- a graph definition
|
||||
|
||||
[1]: Histograms no longer appear in `/runs`, but compressed
|
||||
histograms do, and they use the same test data. Thus, histograms are
|
||||
still here for now.
|
||||
|
||||
Returns:
|
||||
temp_dir: The directory the test data is generated under.
|
||||
"""
|
||||
@ -281,14 +275,6 @@ class TensorboardServerTest(tf.test.TestCase):
|
||||
os.makedirs(run1_path)
|
||||
writer = tf.summary.FileWriter(run1_path)
|
||||
|
||||
histogram_value = tf.HistogramProto(
|
||||
min=0,
|
||||
max=2,
|
||||
num=3,
|
||||
sum=6,
|
||||
sum_squares=5,
|
||||
bucket_limit=[0, 1, 2],
|
||||
bucket=[1, 1, 1])
|
||||
# Add a simple graph event.
|
||||
graph_def = tf.GraphDef()
|
||||
node1 = graph_def.node.add()
|
||||
@ -309,13 +295,6 @@ class TensorboardServerTest(tf.test.TestCase):
|
||||
device_stats = run_metadata.step_stats.dev_stats.add()
|
||||
device_stats.device = 'test device'
|
||||
writer.add_run_metadata(run_metadata, 'test run')
|
||||
writer.add_event(
|
||||
tf.Event(
|
||||
wall_time=0,
|
||||
step=0,
|
||||
summary=tf.Summary(value=[
|
||||
tf.Summary.Value(tag='histogram', histo=histogram_value),
|
||||
])))
|
||||
|
||||
writer.flush()
|
||||
writer.close()
|
||||
|
@ -72,7 +72,7 @@ SUMMARY_TYPES = {
|
||||
|
||||
## The tagTypes below are just arbitrary strings chosen to pass the type
|
||||
## information of the tag from the backend to the frontend
|
||||
COMPRESSED_HISTOGRAMS = 'compressedHistograms'
|
||||
COMPRESSED_HISTOGRAMS = 'distributions'
|
||||
HISTOGRAMS = 'histograms'
|
||||
IMAGES = 'images'
|
||||
AUDIO = 'audio'
|
||||
|
@ -193,8 +193,9 @@ export class Backend {
|
||||
* Return a promise showing the Run-to-Tag mapping for compressedHistogram
|
||||
* data.
|
||||
*/
|
||||
public compressedHistogramRuns(): Promise<RunToTag> {
|
||||
return this.runs().then((x) => _.mapValues(x, 'compressedHistograms'));
|
||||
public compressedHistogramTags(): Promise<RunToTag> {
|
||||
return this.requestManager.request(
|
||||
this.router.pluginRoute('distributions', '/tags'));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -343,7 +344,8 @@ export class Backend {
|
||||
*/
|
||||
private compressedHistogram(tag: string, run: string):
|
||||
Promise<Array<Datum&CompressedHistogramTuple>> {
|
||||
const url = this.router.compressedHistograms(tag, run);
|
||||
const url = (this.router.pluginRunTagRoute(
|
||||
'distributions', '/distributions')(tag, run));
|
||||
let p: Promise<TupleData<CompressedHistogramTuple>[]>;
|
||||
p = this.requestManager.request(url);
|
||||
return p.then(map(detupler((x) => x)));
|
||||
|
@ -21,7 +21,6 @@ export interface Router {
|
||||
logdir: () => string;
|
||||
runs: () => string;
|
||||
isDemoMode: () => boolean;
|
||||
compressedHistograms: RunTagUrlFn;
|
||||
graph:
|
||||
(run: string, limit_attr_size?: number,
|
||||
large_attrs_key?: string) => string;
|
||||
@ -88,7 +87,6 @@ export function router(dataDir = 'data', demoMode = false): Router {
|
||||
runs: () => dataDir + '/runs' + (demoMode ? '.json' : ''),
|
||||
isDemoMode: () => demoMode,
|
||||
graph: graphUrl,
|
||||
compressedHistograms: standardRoute('compressedHistograms'),
|
||||
runMetadata: standardRoute('run_metadata', '.pbtxt'),
|
||||
healthPills: () => dataDir + '/plugin/debugger/health_pills',
|
||||
textRuns: () => dataDir + '/plugin/text/runs' + (demoMode ? '.json' : ''),
|
||||
|
@ -55,13 +55,11 @@ all of the data available from the TensorBoard server. Here is an example:
|
||||
|
||||
{
|
||||
"train_run": {
|
||||
"compressedHistograms": ["foo_histogram", "bar_histogram"],
|
||||
"graph": true,
|
||||
"firstEventTimestamp": 123456.789
|
||||
"run_metadata": ["forward prop", "inference"]
|
||||
},
|
||||
"eval": {
|
||||
"compressedHistograms": ["foo_histogram", "bar_histogram"],
|
||||
"graph": false,
|
||||
"run_metadata": []
|
||||
}
|
||||
@ -81,6 +79,7 @@ and will not appear in the output from this route:
|
||||
- `audio`
|
||||
- `images`
|
||||
- `scalars`
|
||||
- `compressedHistograms`, moved to `distributions`
|
||||
- `histograms`
|
||||
|
||||
## `/data/plugin/scalars/tags`
|
||||
@ -160,7 +159,21 @@ Annotated Example: (note - real data is higher precision)
|
||||
]
|
||||
]
|
||||
|
||||
## '/data/compressedHistograms?run=foo&tag=bar'
|
||||
## `/data/plugin/distributions/tags`
|
||||
|
||||
Returns a dictionary mapping from `run_name` (quoted string) to arrays of
|
||||
`tag_name` (quoted string), where each array contains the names of all
|
||||
distribution tags present in the corresponding run. Here is an example:
|
||||
|
||||
{
|
||||
"train_run": ["foo_histogram", "bar_histogram"],
|
||||
"eval": ["foo_histogram", "bar_histogram"]
|
||||
}
|
||||
|
||||
Note that runs without any distribution tags are included as keys with
|
||||
value the empty array.
|
||||
|
||||
## `/data/plugin/distributions/distributions?run=foo&tag=bar`
|
||||
|
||||
Returns an array of event_accumulator.CompressedHistogramEvents ([wall_time,
|
||||
step, CompressedHistogramValues]) for the given run and tag.
|
||||
@ -180,8 +193,8 @@ Annotated Example: (note - real data is higher precision)
|
||||
[
|
||||
1441154832.580509, # wall_time
|
||||
5, # step
|
||||
[ [0, -3.67], # CompressedHistogramValue for 0th percentile
|
||||
[2500, -4.19], # CompressedHistogramValue for 25th percentile
|
||||
[ [0, -3.67], # CompressedHistogramValue for 0th percentile
|
||||
[2500, -4.19], # CompressedHistogramValue for 25th percentile
|
||||
[5000, 6.29],
|
||||
[7500, 1.64],
|
||||
[10000, 3.67]
|
||||
|
50
tensorflow/tensorboard/plugins/distributions/BUILD
Normal file
50
tensorflow/tensorboard/plugins/distributions/BUILD
Normal file
@ -0,0 +1,50 @@
|
||||
# Description:
|
||||
# TensorBoard plugin for distributions
|
||||
|
||||
package(default_visibility = ["//tensorflow:internal"])
|
||||
|
||||
licenses(["notice"]) # Apache 2.0
|
||||
|
||||
exports_files(["LICENSE"])
|
||||
|
||||
load("//tensorflow:tensorflow.bzl", "py_test")
|
||||
|
||||
## Distributions Plugin ##
|
||||
py_library(
|
||||
name = "distributions_plugin",
|
||||
srcs = ["distributions_plugin.py"],
|
||||
srcs_version = "PY2AND3",
|
||||
visibility = [
|
||||
"//tensorflow:internal",
|
||||
],
|
||||
deps = [
|
||||
"//tensorflow/tensorboard/backend:http_util",
|
||||
"//tensorflow/tensorboard/backend/event_processing:event_accumulator",
|
||||
"//tensorflow/tensorboard/plugins:base_plugin",
|
||||
"@org_pocoo_werkzeug//:werkzeug",
|
||||
"@six_archive//:six",
|
||||
],
|
||||
)
|
||||
|
||||
py_test(
|
||||
name = "distributions_plugin_test",
|
||||
size = "small",
|
||||
srcs = ["distributions_plugin_test.py"],
|
||||
main = "distributions_plugin_test.py",
|
||||
srcs_version = "PY2AND3",
|
||||
deps = [
|
||||
":distributions_plugin",
|
||||
"//tensorflow:tensorflow_py",
|
||||
"//tensorflow/tensorboard/backend:application",
|
||||
"//tensorflow/tensorboard/backend/event_processing:event_accumulator",
|
||||
"//tensorflow/tensorboard/backend/event_processing:event_multiplexer",
|
||||
"@org_pocoo_werkzeug//:werkzeug",
|
||||
"@six_archive//:six",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all_files",
|
||||
srcs = glob(["**"]),
|
||||
visibility = ["//tensorflow:__pkg__"],
|
||||
)
|
@ -0,0 +1,69 @@
|
||||
# Copyright 2017 The TensorFlow Authors. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# ==============================================================================
|
||||
"""The TensorBoard Distributions (a.k.a. compressed histograms) plugin."""
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
|
||||
from werkzeug import wrappers
|
||||
|
||||
from tensorflow.tensorboard.backend import http_util
|
||||
from tensorflow.tensorboard.backend.event_processing import event_accumulator
|
||||
from tensorflow.tensorboard.plugins import base_plugin
|
||||
|
||||
_PLUGIN_PREFIX_ROUTE = event_accumulator.COMPRESSED_HISTOGRAMS
|
||||
|
||||
|
||||
class DistributionsPlugin(base_plugin.TBPlugin):
|
||||
"""Distributions Plugin for TensorBoard."""
|
||||
|
||||
plugin_name = _PLUGIN_PREFIX_ROUTE
|
||||
|
||||
def get_plugin_apps(self, multiplexer, unused_logdir):
|
||||
self._multiplexer = multiplexer
|
||||
return {
|
||||
'/distributions': self.distributions_route,
|
||||
'/tags': self.tags_route,
|
||||
}
|
||||
|
||||
def is_active(self):
|
||||
"""This plugin is active iff any run has at least one relevant tag."""
|
||||
return any(self.index_impl().values())
|
||||
|
||||
def index_impl(self):
|
||||
return {
|
||||
run_name: run_data[event_accumulator.COMPRESSED_HISTOGRAMS]
|
||||
for (run_name, run_data) in self._multiplexer.Runs().items()
|
||||
if event_accumulator.COMPRESSED_HISTOGRAMS in run_data
|
||||
}
|
||||
|
||||
def distributions_impl(self, tag, run):
|
||||
"""Result of the form `(body, mime_type)`."""
|
||||
values = self._multiplexer.CompressedHistograms(run, tag)
|
||||
return (values, 'application/json')
|
||||
|
||||
@wrappers.Request.application
|
||||
def tags_route(self, request):
|
||||
index = self.index_impl()
|
||||
return http_util.Respond(request, index, 'application/json')
|
||||
|
||||
@wrappers.Request.application
|
||||
def distributions_route(self, request):
|
||||
"""Given a tag and single run, return array of compressed histograms."""
|
||||
tag = request.args.get('tag')
|
||||
run = request.args.get('run')
|
||||
(body, mime_type) = self.distributions_impl(tag, run)
|
||||
return http_util.Respond(request, body, mime_type)
|
@ -0,0 +1,125 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2017 The TensorFlow Authors. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# ==============================================================================
|
||||
"""Integration tests for the Distributions Plugin."""
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
|
||||
import os.path
|
||||
|
||||
from six.moves import xrange # pylint: disable=redefined-builtin
|
||||
import tensorflow as tf
|
||||
|
||||
from tensorflow.tensorboard.backend.event_processing import event_accumulator
|
||||
from tensorflow.tensorboard.backend.event_processing import event_multiplexer
|
||||
from tensorflow.tensorboard.plugins.distributions import distributions_plugin
|
||||
|
||||
|
||||
class DistributionsPluginTest(tf.test.TestCase):
|
||||
|
||||
_STEPS = 99
|
||||
|
||||
_DISTRIBUTION_TAG = 'my-favorite-distribution'
|
||||
_SCALAR_TAG = 'my-boring-scalars'
|
||||
|
||||
_RUN_WITH_DISTRIBUTION = '_RUN_WITH_DISTRIBUTION'
|
||||
_RUN_WITH_SCALARS = '_RUN_WITH_SCALARS'
|
||||
|
||||
def set_up_with_runs(self, run_names):
|
||||
self.logdir = self.get_temp_dir()
|
||||
for run_name in run_names:
|
||||
self.generate_run(run_name)
|
||||
multiplexer = event_multiplexer.EventMultiplexer(size_guidance={
|
||||
# don't truncate my test data, please
|
||||
event_accumulator.COMPRESSED_HISTOGRAMS:
|
||||
self._STEPS,
|
||||
})
|
||||
multiplexer.AddRunsFromDirectory(self.logdir)
|
||||
multiplexer.Reload()
|
||||
self.plugin = distributions_plugin.DistributionsPlugin()
|
||||
self.apps = self.plugin.get_plugin_apps(multiplexer, None)
|
||||
|
||||
def generate_run(self, run_name):
|
||||
if run_name == self._RUN_WITH_DISTRIBUTION:
|
||||
(use_distributions, use_scalars) = (True, False)
|
||||
elif run_name == self._RUN_WITH_SCALARS:
|
||||
(use_distributions, use_scalars) = (False, True)
|
||||
else:
|
||||
assert False, 'Invalid run name: %r' % run_name
|
||||
tf.reset_default_graph()
|
||||
sess = tf.Session()
|
||||
placeholder = tf.placeholder(tf.float32, shape=[3])
|
||||
if use_distributions:
|
||||
tf.summary.histogram(self._DISTRIBUTION_TAG, placeholder)
|
||||
if use_scalars:
|
||||
tf.summary.scalar(self._SCALAR_TAG, tf.reduce_mean(placeholder))
|
||||
summ = tf.summary.merge_all()
|
||||
|
||||
subdir = os.path.join(self.logdir, run_name)
|
||||
writer = tf.summary.FileWriter(subdir)
|
||||
writer.add_graph(sess.graph)
|
||||
for step in xrange(self._STEPS):
|
||||
feed_dict = {placeholder: [1 + step, 2 + step, 3 + step]}
|
||||
s = sess.run(summ, feed_dict=feed_dict)
|
||||
writer.add_summary(s, global_step=step)
|
||||
writer.close()
|
||||
|
||||
def test_index(self):
|
||||
self.set_up_with_runs([self._RUN_WITH_DISTRIBUTION,
|
||||
self._RUN_WITH_SCALARS])
|
||||
self.assertEqual({
|
||||
self._RUN_WITH_DISTRIBUTION: [self._DISTRIBUTION_TAG],
|
||||
self._RUN_WITH_SCALARS: [],
|
||||
}, self.plugin.index_impl())
|
||||
|
||||
def _test_distributions_json(self, run_name, should_have_distributions):
|
||||
self.set_up_with_runs([self._RUN_WITH_DISTRIBUTION,
|
||||
self._RUN_WITH_SCALARS])
|
||||
if should_have_distributions:
|
||||
(data, mime_type) = self.plugin.distributions_impl(
|
||||
self._DISTRIBUTION_TAG, run_name)
|
||||
self.assertEqual('application/json', mime_type)
|
||||
self.assertEqual(len(data), self._STEPS)
|
||||
for i in xrange(self._STEPS):
|
||||
self.assertEqual(i, data[i].step)
|
||||
else:
|
||||
with self.assertRaises(KeyError):
|
||||
self.plugin.distributions_impl(
|
||||
self._DISTRIBUTION_TAG, run_name)
|
||||
|
||||
def test_distributions_json_with_scalars(self):
|
||||
self._test_distributions_json(self._RUN_WITH_DISTRIBUTION, True)
|
||||
|
||||
def test_distributions_json_with_histogram(self):
|
||||
self._test_distributions_json(self._RUN_WITH_SCALARS, False)
|
||||
|
||||
def test_active_with_distribution(self):
|
||||
self.set_up_with_runs([self._RUN_WITH_DISTRIBUTION])
|
||||
self.assertTrue(self.plugin.is_active())
|
||||
|
||||
def test_active_with_scalars(self):
|
||||
self.set_up_with_runs([self._RUN_WITH_SCALARS])
|
||||
self.assertFalse(self.plugin.is_active())
|
||||
|
||||
def test_active_with_both(self):
|
||||
self.set_up_with_runs([self._RUN_WITH_DISTRIBUTION,
|
||||
self._RUN_WITH_SCALARS])
|
||||
self.assertTrue(self.plugin.is_active())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
tf.test.main()
|
@ -33,6 +33,7 @@ from werkzeug import serving
|
||||
from tensorflow.tensorboard.backend import application
|
||||
from tensorflow.tensorboard.backend.event_processing import event_file_inspector as efi
|
||||
from tensorflow.tensorboard.plugins.audio import audio_plugin
|
||||
from tensorflow.tensorboard.plugins.distributions import distributions_plugin
|
||||
from tensorflow.tensorboard.plugins.histograms import histograms_plugin
|
||||
from tensorflow.tensorboard.plugins.images import images_plugin
|
||||
from tensorflow.tensorboard.plugins.projector import projector_plugin
|
||||
@ -204,10 +205,11 @@ def main(unused_argv=None):
|
||||
return 0
|
||||
else:
|
||||
plugins = [
|
||||
audio_plugin.AudioPlugin(),
|
||||
histograms_plugin.HistogramsPlugin(),
|
||||
images_plugin.ImagesPlugin(),
|
||||
scalars_plugin.ScalarsPlugin(),
|
||||
images_plugin.ImagesPlugin(),
|
||||
audio_plugin.AudioPlugin(),
|
||||
distributions_plugin.DistributionsPlugin(),
|
||||
histograms_plugin.HistogramsPlugin(),
|
||||
projector_plugin.ProjectorPlugin(),
|
||||
text_plugin.TextPlugin(),
|
||||
]
|
||||
|
Loading…
Reference in New Issue
Block a user