aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--WORKSPACE2
-rw-r--r--tensorflow/tensorboard/TAG2
-rw-r--r--tensorflow/tensorboard/dist/tf-tensorboard.html817
3 files changed, 520 insertions, 301 deletions
diff --git a/WORKSPACE b/WORKSPACE
index fe10db0289..bbc2e27356 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -418,5 +418,5 @@ new_git_repository(
name = "webcomponentsjs",
build_file = "bower.BUILD",
remote = "https://github.com/polymer/webcomponentsjs.git",
- tag = "v0.7.21",
+ tag = "v0.7.22",
)
diff --git a/tensorflow/tensorboard/TAG b/tensorflow/tensorboard/TAG
index b6a7d89c68..98d9bcb75a 100644
--- a/tensorflow/tensorboard/TAG
+++ b/tensorflow/tensorboard/TAG
@@ -1 +1 @@
-16
+17
diff --git a/tensorflow/tensorboard/dist/tf-tensorboard.html b/tensorflow/tensorboard/dist/tf-tensorboard.html
index a9cc4e419e..a253b800cb 100644
--- a/tensorflow/tensorboard/dist/tf-tensorboard.html
+++ b/tensorflow/tensorboard/dist/tf-tensorboard.html
@@ -18,6 +18,88 @@ Instead, use `gulp regenerate` to create a new version with your changes.
-->
<html><head><meta charset="UTF-8">
+<script>/* Copyright 2015 Google Inc. 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.
+==============================================================================*/
+var TF;
+(function (TF) {
+ var TensorBoard;
+ (function (TensorBoard) {
+ TensorBoard.TABS = ['events', 'images', 'graphs', 'histograms'];
+ })(TensorBoard = TF.TensorBoard || (TF.TensorBoard = {}));
+})(TF || (TF = {}));
+</script>
+<script>/* Copyright 2015 Google Inc. 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.
+==============================================================================*/
+var TF;
+(function (TF) {
+ var TensorBoard;
+ (function (TensorBoard) {
+ TensorBoard.AUTORELOAD_LOCALSTORAGE_KEY = 'TF.TensorBoard.autoReloadEnabled';
+ var getAutoReloadFromLocalStorage = function () {
+ var val = window.localStorage.getItem(TensorBoard.AUTORELOAD_LOCALSTORAGE_KEY);
+ return val === 'true' || val == null; // defaults to true
+ };
+ TensorBoard.AutoReloadBehavior = {
+ properties: {
+ autoReloadEnabled: {
+ type: Boolean,
+ observer: '_autoReloadObserver',
+ value: getAutoReloadFromLocalStorage,
+ },
+ _autoReloadId: {
+ type: Number,
+ },
+ autoReloadIntervalSecs: {
+ type: Number,
+ value: 120,
+ },
+ },
+ detached: function () { window.clearTimeout(this._autoReloadId); },
+ _autoReloadObserver: function (autoReload) {
+ window.localStorage.setItem(TensorBoard.AUTORELOAD_LOCALSTORAGE_KEY, autoReload);
+ if (autoReload) {
+ var _this = this;
+ this._autoReloadId = window.setTimeout(this._doAutoReload.bind(this), this.autoReloadIntervalSecs * 1000);
+ }
+ else {
+ window.clearTimeout(this._autoReloadId);
+ }
+ },
+ _doAutoReload: function () {
+ if (this.reload == null) {
+ throw new Error('AutoReloadBehavior requires a reload method');
+ }
+ this.reload();
+ this._autoReloadId = window.setTimeout(this._doAutoReload.bind(this), this.autoReloadIntervalSecs * 1000);
+ }
+ };
+ })(TensorBoard = TF.TensorBoard || (TF.TensorBoard = {}));
+})(TF || (TF = {}));
+</script>
</head><body><div hidden="" by-vulcanize="">
<dom-module id="tf-tooltip-coordinator" assetpath="../tf-event-dashboard/">
<script>
@@ -550,222 +632,6 @@ Instead, use `gulp regenerate` to create a new version with your changes.
</script>
</dom-module>
-<dom-module id="tf-dashboard-layout" assetpath="../tf-dashboard-common/">
- <template>
- <div id="sidebar">
- <content select=".sidebar"></content>
- </div>
-
- <div id="center" class="scrollbar">
- <content select=".center"></content>
- </div>
- <style include="scrollbar-style"></style>
- <style>
- #sidebar {
- width: inherit;
- height: 100%;
- overflow: ellipsis;
- flex-grow: 0;
- flex-shrink: 0;
- }
-
- #center {
- height: 100%;
- overflow-y: scroll;
- flex-grow: 1;
- flex-shrink: 1;
- }
-
- .tf-graph-dashboard #center {
- background: white;
- }
-
- :host {
- display: flex;
- flex-direction: row;
- height: 100%;
- }
- </style>
- </template>
- <script>
- Polymer({
- is: "tf-dashboard-layout",
- });
- </script>
-</dom-module>
-<dom-module id="dashboard-style" assetpath="../tf-dashboard-common/">
- <template>
- <style>
- .card {
- height: 200px;
- width: 300px;
- display: flex;
- flex-direction: column;
- margin: 5px;
- padding: 0 30px 30px 0;
- -webkit-user-select: none;
- -moz-user-select: none;
- position: relative;
- }
-
- .card .card-title {
- flex-grow: 0;
- flex-shrink: 0;
- margin-bottom: 10px;
- font-size: 14px;
- text-overflow: ellipsis;
- overflow: hidden;
- }
-
- .card .card-content {
- flex-grow: 1;
- flex-shrink: 1;
- display: flex;
- }
- .card .card-bottom-row {
- flex-grow: 0;
- flex-shrink: 0;
- padding-left: 10px;
- padding-right: 10px;
- }
-
- .card.selected {
- height: 400px;
- width: 100%;
- }
-
- [shift] {
- bottom: 20px !important;
- }
-
- .expand-button {
- position: absolute;
- left: 0px;
- bottom: 20px;
- color: #2196F3;
- display: block;
- }
-
- #content-container{
- display: block;
- }
-
- .sidebar {
- display: flex;
- flex-direction: column;
- height: 100%;
- margin-right: 20px;
- }
-
- #categorizer {
- flex-shrink: 0;
- }
-
- #xTypeSelector {
- flex-shrink: 0;
- margin: 20px 0;
- }
-
- #runSelector {
- flex-shrink: 1;
- flex-grow: 1;
- }
-
- .sidebar-section {
- border-top: solid 1px rgba(0, 0, 0, 0.12);
- padding: 20px 0px 20px 30px;
- }
-
- .sidebar-section:first-child {
- border: none;
- }
-
- .sidebar-section:last-child {
- flex-grow: 1;
- display: flex;
- }
-
- paper-checkbox {
- --paper-checkbox-checked-color: var(--tb-ui-dark-accent);
- --paper-checkbox-unchecked-color: var(--tb-ui-dark-accent);
- font-size: 14px;
- }
-
- </style>
- </template>
-</dom-module>
-
-<dom-module id="tf-downloader" assetpath="../tf-dashboard-common/">
- <template>
- <paper-dropdown-menu no-label-float="true" label="run to download" selected-item-label="{{_run}}">
- <paper-menu class="dropdown-content">
- <template is="dom-repeat" items="[[_runs]]">
- <paper-item no-label-float="true">[[item]]</paper-item>
- </template>
- </paper-menu>
- </paper-dropdown-menu>
- <a download="[[_csvName(_run)]]" href="[[_csvUrl(_run, urlFn)]]">CSV</a>
- <a download="[[_jsonName(_run)]]" href="[[_jsonUrl(_run, urlFn)]]">JSON</a>
- <style>
- :host {
- display: block;
- }
- paper-dropdown-menu {
- width: 220px;
- --paper-input-container-label: {
- font-size: 10px;
- }
- --paper-input-container-input: {
- font-size: 10px;
- }
- }
- a {
- font-size: 10px;
- border-radius: 3px;
- border: 1px solid #EEE;
- }
- paper-input {
- font-size: 22px;
- }
- </style>
- </template>
- <script>
- Polymer({
- is: "tf-downloader",
- properties: {
- _run: String,
- _runs: {
- type: Array,
- computed: "_computeRuns(runToTag.*, selectedRuns.*)",
- },
- selectedRuns: Array,
- runToTag: Object,
- tag: String,
- urlFn: Function,
- },
- _computeRuns: function(runToTagChange, selectedRunsChange) {
- var runToTag = this.runToTag;
- var tag = this.tag;
- return this.selectedRuns.filter(function(x) {
- return runToTag[x].indexOf(tag) !== -1;
- })
- },
- _csvUrl: function(_run, urlFn) {
- return urlFn(this.tag, _run) + "&format=csv";
- },
- _jsonUrl: function(_run, urlFn) {
- return urlFn(this.tag, _run);
- },
- _csvName: function(_run) {
- return "run_" + _run + ",tag_" + this.tag + ".csv";
- },
- _jsonName: function(_run) {
- return "run-" + _run + "-tag-" + this.tag + ".json";
- },
- });
- </script>
-</dom-module>
-
<dom-module id="tf-regex-group" assetpath="../tf-regex-group/">
<template>
@@ -1268,15 +1134,32 @@ var TF;
this.tooltipUpdater = tooltipUpdater;
this.buildChart(xType);
}
+ /**
+ * Change the runs on the chart. The work of actually setting the dataset
+ * on the plot is deferred to the subclass because it is impl-specific.
+ * Changing runs automatically triggers a reload; this ensures that the
+ * newly selected run will have data, and that all the runs will be current
+ * (it would be weird if one run was ahead of the others, and the display
+ * depended on the order in which runs were added)
+ */
BaseChart.prototype.changeRuns = function (runs) {
- throw new Error("Abstract method not implemented");
+ this.runs = runs;
+ this.reload();
};
- BaseChart.prototype.getDataset = function (run) {
+ /**
+ * Reload data for each run in view.
+ */
+ BaseChart.prototype.reload = function () {
var _this = this;
+ this.runs.forEach(function (run) {
+ var dataset = _this.getDataset(run);
+ _this.dataFn(_this.tag, run).then(function (x) { return dataset.data(x); });
+ });
+ };
+ BaseChart.prototype.getDataset = function (run) {
if (this.datasets[run] === undefined) {
this.datasets[run] = new Plottable.Dataset([], { run: run, tag: this.tag });
}
- this.dataFn(this.tag, run).then(function (x) { return _this.datasets[run].data(x); });
return this.datasets[run];
};
BaseChart.prototype.addCrosshairs = function (plot, yAccessor) {
@@ -1399,6 +1282,7 @@ var TF;
};
LineChart.prototype.changeRuns = function (runs) {
var _this = this;
+ _super.prototype.changeRuns.call(this, runs);
var datasets = runs.map(function (r) { return _this.getDataset(r); });
this.plot.datasets(datasets);
};
@@ -1412,6 +1296,7 @@ var TF;
}
HistogramChart.prototype.changeRuns = function (runs) {
var _this = this;
+ _super.prototype.changeRuns.call(this, runs);
var datasets = runs.map(function (r) { return _this.getDataset(r); });
this.plots.forEach(function (p) { return p.datasets(datasets); });
};
@@ -1569,6 +1454,9 @@ var TF;
redraw: function() {
this._chart.redraw();
},
+ reload: function() {
+ this._chart.reload();
+ },
_constructor: function(type) {
if (type === "scalar") {
return TF.LineChart;
@@ -1701,6 +1589,222 @@ var TF;
</dom-module>
+<dom-module id="tf-dashboard-layout" assetpath="../tf-dashboard-common/">
+ <template>
+ <div id="sidebar">
+ <content select=".sidebar"></content>
+ </div>
+
+ <div id="center" class="scrollbar">
+ <content select=".center"></content>
+ </div>
+ <style include="scrollbar-style"></style>
+ <style>
+ #sidebar {
+ width: inherit;
+ height: 100%;
+ overflow: ellipsis;
+ flex-grow: 0;
+ flex-shrink: 0;
+ }
+
+ #center {
+ height: 100%;
+ overflow-y: scroll;
+ flex-grow: 1;
+ flex-shrink: 1;
+ }
+
+ .tf-graph-dashboard #center {
+ background: white;
+ }
+
+ :host {
+ display: flex;
+ flex-direction: row;
+ height: 100%;
+ }
+ </style>
+ </template>
+ <script>
+ Polymer({
+ is: "tf-dashboard-layout",
+ });
+ </script>
+</dom-module>
+<dom-module id="dashboard-style" assetpath="../tf-dashboard-common/">
+ <template>
+ <style>
+ .card {
+ height: 200px;
+ width: 300px;
+ display: flex;
+ flex-direction: column;
+ margin: 5px;
+ padding: 0 30px 30px 0;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ position: relative;
+ }
+
+ .card .card-title {
+ flex-grow: 0;
+ flex-shrink: 0;
+ margin-bottom: 10px;
+ font-size: 14px;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ }
+
+ .card .card-content {
+ flex-grow: 1;
+ flex-shrink: 1;
+ display: flex;
+ }
+ .card .card-bottom-row {
+ flex-grow: 0;
+ flex-shrink: 0;
+ padding-left: 10px;
+ padding-right: 10px;
+ }
+
+ .card.selected {
+ height: 400px;
+ width: 100%;
+ }
+
+ [shift] {
+ bottom: 20px !important;
+ }
+
+ .expand-button {
+ position: absolute;
+ left: 0px;
+ bottom: 20px;
+ color: #2196F3;
+ display: block;
+ }
+
+ #content-container{
+ display: block;
+ }
+
+ .sidebar {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+ margin-right: 20px;
+ }
+
+ #categorizer {
+ flex-shrink: 0;
+ }
+
+ #xTypeSelector {
+ flex-shrink: 0;
+ margin: 20px 0;
+ }
+
+ #runSelector {
+ flex-shrink: 1;
+ flex-grow: 1;
+ }
+
+ .sidebar-section {
+ border-top: solid 1px rgba(0, 0, 0, 0.12);
+ padding: 20px 0px 20px 30px;
+ }
+
+ .sidebar-section:first-child {
+ border: none;
+ }
+
+ .sidebar-section:last-child {
+ flex-grow: 1;
+ display: flex;
+ }
+
+ paper-checkbox {
+ --paper-checkbox-checked-color: var(--tb-ui-dark-accent);
+ --paper-checkbox-unchecked-color: var(--tb-ui-dark-accent);
+ font-size: 14px;
+ }
+
+ </style>
+ </template>
+</dom-module>
+
+<dom-module id="tf-downloader" assetpath="../tf-dashboard-common/">
+ <template>
+ <paper-dropdown-menu no-label-float="true" label="run to download" selected-item-label="{{_run}}">
+ <paper-menu class="dropdown-content">
+ <template is="dom-repeat" items="[[_runs]]">
+ <paper-item no-label-float="true">[[item]]</paper-item>
+ </template>
+ </paper-menu>
+ </paper-dropdown-menu>
+ <a download="[[_csvName(_run)]]" href="[[_csvUrl(_run, urlFn)]]">CSV</a>
+ <a download="[[_jsonName(_run)]]" href="[[_jsonUrl(_run, urlFn)]]">JSON</a>
+ <style>
+ :host {
+ display: block;
+ }
+ paper-dropdown-menu {
+ width: 220px;
+ --paper-input-container-label: {
+ font-size: 10px;
+ }
+ --paper-input-container-input: {
+ font-size: 10px;
+ }
+ }
+ a {
+ font-size: 10px;
+ border-radius: 3px;
+ border: 1px solid #EEE;
+ }
+ paper-input {
+ font-size: 22px;
+ }
+ </style>
+ </template>
+ <script>
+ Polymer({
+ is: "tf-downloader",
+ properties: {
+ _run: String,
+ _runs: {
+ type: Array,
+ computed: "_computeRuns(runToTag.*, selectedRuns.*)",
+ },
+ selectedRuns: Array,
+ runToTag: Object,
+ tag: String,
+ urlFn: Function,
+ },
+ _computeRuns: function(runToTagChange, selectedRunsChange) {
+ var runToTag = this.runToTag;
+ var tag = this.tag;
+ return this.selectedRuns.filter(function(x) {
+ return runToTag[x].indexOf(tag) !== -1;
+ })
+ },
+ _csvUrl: function(_run, urlFn) {
+ return urlFn(this.tag, _run) + "&format=csv";
+ },
+ _jsonUrl: function(_run, urlFn) {
+ return urlFn(this.tag, _run);
+ },
+ _csvName: function(_run) {
+ return "run_" + _run + ",tag_" + this.tag + ".csv";
+ },
+ _jsonName: function(_run) {
+ return "run-" + _run + "-tag-" + this.tag + ".json";
+ },
+ });
+ </script>
+</dom-module>
+
<dom-module id="tf-no-data-warning" assetpath="../tf-dashboard-common/">
<template>
<template is="dom-if" if="[[showWarning]]">
@@ -1782,6 +1886,36 @@ var TF;
});
</script>
</dom-module>
+<script>var TF;
+(function (TF) {
+ var Dashboard;
+ (function (Dashboard) {
+ /**
+ * ReloadBehavior: A simple behavior for dashboards where the
+ * frontendReload() function should find every child element with a
+ * given tag name (e.g. "tf-chart" or "tf-image-loader")
+ * and call a `reload` method on that child.
+ * May later extend it so it has more sophisticated logic, e.g. reloading
+ * only tags that are in view.
+ */
+ function ReloadBehavior(tagName) {
+ return {
+ properties: {
+ reloadTag: {
+ type: String,
+ value: tagName,
+ },
+ },
+ frontendReload: function () {
+ var elements = this.getElementsByTagName(this.reloadTag);
+ Array.prototype.forEach.call(elements, function (x) { x.reload(); });
+ },
+ };
+ }
+ Dashboard.ReloadBehavior = ReloadBehavior;
+ })(Dashboard = TF.Dashboard || (TF.Dashboard = {}));
+})(TF || (TF = {}));
+</script>
<script>/* Copyright 2015 Google Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
@@ -2081,7 +2215,10 @@ var TF;
(function (TF) {
var Backend;
(function (Backend_1) {
- Backend_1.TYPES = ["scalar", "histogram", "compressedHistogram", "graph", "image", "runMetadata"];
+ Backend_1.TYPES = [
+ 'scalar', 'histogram', 'compressedHistogram', 'graph', 'image',
+ 'runMetadata'
+ ];
/**
* The Backend class provides a convenient and typed interface to the backend.
*
@@ -2116,7 +2253,7 @@ var TF;
* available.
*/
Backend.prototype.scalarRuns = function () {
- return this.runs().then(function (x) { return _.mapValues(x, "scalars"); });
+ return this.runs().then(function (x) { return _.mapValues(x, 'scalars'); });
};
/**
* Return a promise showing the Run-to-Tag mapping for histogram data.
@@ -2124,7 +2261,7 @@ var TF;
* available.
*/
Backend.prototype.histogramRuns = function () {
- return this.runs().then(function (x) { return _.mapValues(x, "histograms"); });
+ return this.runs().then(function (x) { return _.mapValues(x, 'histograms'); });
};
/**
* Return a promise showing the Run-to-Tag mapping for image data.
@@ -2132,7 +2269,7 @@ var TF;
* available.
*/
Backend.prototype.imageRuns = function () {
- return this.runs().then(function (x) { return _.mapValues(x, "images"); });
+ return this.runs().then(function (x) { return _.mapValues(x, 'images'); });
};
/**
* Return a promise showing the Run-to-Tag mapping for compressedHistogram
@@ -2141,7 +2278,7 @@ var TF;
* available.
*/
Backend.prototype.compressedHistogramRuns = function () {
- return this.runs().then(function (x) { return _.mapValues(x, "compressedHistograms"); });
+ return this.runs().then(function (x) { return _.mapValues(x, 'compressedHistograms'); });
};
/**
* Return a promise showing list of runs that contain graphs.
@@ -2149,9 +2286,7 @@ var TF;
* available.
*/
Backend.prototype.graphRuns = function () {
- return this.runs().then(function (x) {
- return _.keys(x).filter(function (k) { return x[k].graph; });
- });
+ return this.runs().then(function (x) { return _.keys(x).filter(function (k) { return x[k].graph; }); });
};
/**
* Return a promise showing the Run-to-Tag mapping for run_metadata objects.
@@ -2159,7 +2294,7 @@ var TF;
* available.
*/
Backend.prototype.runMetadataRuns = function () {
- return this.runs().then(function (x) { return _.mapValues(x, "run_metadata"); });
+ return this.runs().then(function (x) { return _.mapValues(x, 'run_metadata'); });
};
/**
* Return a promise of a graph string from the backend.
@@ -2184,8 +2319,7 @@ var TF;
var p;
var url = this.router.histograms(tag, run);
p = this.requestManager.request(url);
- return p.then(map(detupler(createHistogram)))
- .then(function (histos) {
+ return p.then(map(detupler(createHistogram))).then(function (histos) {
return histos.map(function (histo, i) {
return {
wall_time: histo.wall_time,
@@ -2235,9 +2369,7 @@ var TF;
}());
Backend_1.Backend = Backend;
/** Given a RunToTag, return sorted array of all runs */
- function getRuns(r) {
- return _.keys(r).sort();
- }
+ function getRuns(r) { return _.keys(r).sort(); }
Backend_1.getRuns = getRuns;
/** Given a RunToTag, return array of all tags (sorted + dedup'd) */
function getTags(r) {
@@ -2278,9 +2410,7 @@ var TF;
};
}
;
- function createScalar(x) {
- return { scalar: x };
- }
+ function createScalar(x) { return { scalar: x }; }
;
function createHistogram(x) {
return {
@@ -2296,9 +2426,9 @@ var TF;
;
/**
* Takes histogram data as stored by tensorboard backend and converts it to
- * the standard d3 histogram data format to make it more compatible and easier to
- * visualize. When visualizing histograms, having the left edge and width makes
- * things quite a bit easier.
+ * the standard d3 histogram data format to make it more compatible and easier
+ * to visualize. When visualizing histograms, having the left edge and width
+ * makes things quite a bit easier.
*
* @param {histogram} Histogram - A histogram from tensorboard backend.
* @return {HistogramBin[]} - Each bin has an x (left edge), a dx (width), and a y (count).
@@ -2307,7 +2437,7 @@ var TF;
*/
function convertBins(histogram) {
if (histogram.bucketRightEdges.length !== histogram.bucketCounts.length) {
- throw (new Error("Edges and counts are of different lengths."));
+ throw (new Error('Edges and counts are of different lengths.'));
}
var previousRightEdge = histogram.min;
return histogram.bucketRightEdges.map(function (rightEdge, i) {
@@ -2318,11 +2448,7 @@ var TF;
var right = Math.min(histogram.max, rightEdge);
// Store rightEdgeValue for next iteration
previousRightEdge = rightEdge;
- return {
- x: left,
- dx: right - left,
- y: histogram.bucketCounts[i]
- };
+ return { x: left, dx: right - left, y: histogram.bucketCounts[i] };
});
}
Backend_1.convertBins = convertBins;
@@ -2339,7 +2465,7 @@ var TF;
/** Data type. One of TF.Backend.TYPES */
dataType: {
type: String,
- observer: "_throwErrorOnUnrecognizedType",
+ observer: '_throwErrorOnUnrecognizedType',
},
/** TF.Backend.Backend for data loading. */
backend: {
@@ -2370,14 +2496,11 @@ var TF;
notify: true,
},
/** Promise provider for the data. Useful for passing to subcomponents */
- dataProvider: {
- type: Function,
- computed: "_getDataProvider(dataType, backend)"
- },
+ dataProvider: { type: Function, computed: '_getDataProvider(dataType, backend)' },
/** Has the dashboard loaded yet? */
loadState: {
type: String,
- value: "noload",
+ value: 'noload',
readOnly: true,
},
/**
@@ -2392,12 +2515,26 @@ var TF;
readOnly: true,
}
},
- observers: ["_do_autoLoad(dataType, backend, autoLoad)"],
+ observers: ['_do_autoLoad(dataType, backend, autoLoad)'],
+ /**
+ * Reloading works in two steps:
+ * Backend reload, which gets metadata on available runs, tags, etc from
+ * the backend.
+ * Frontend reload, which loads new data for each chart or visual display.
+ * Backend reload logic is provided by this behaivor. The frontend reload
+ * logic should be provided elsewhere, since it is component-specific.
+ * To keep things simple and consistent, we do the backend reload first,
+ * and the frontend reload afterwards.
+ */
+ reload: function () {
+ var _this = this;
+ return this.backendReload().then(function (x) { return _this.frontendReload(); });
+ },
/**
* Load data from backend and then set run2tag, tags, runs, and loadState.
* Returns a promise that resolves/rejects when data is loaded.
*/
- reload: function () {
+ backendReload: function () {
var _this = this;
if (this.dataType == null) {
throw new Error("TF.Backend.Behavior: Need a dataType to reload.");
@@ -2409,6 +2546,11 @@ var TF;
this._setLoadState("pending");
return runsRoute().then(function (x) {
_this._setLoadState("loaded");
+ if (_.isEqual(x, _this.run2tag)) {
+ // If x and run2tag are equal, let's avoid updating everything
+ // since that can needlessly trigger run changes, reloads, etc
+ return x;
+ }
_this._setRun2tag(x);
var tags = TF.Backend.getTags(x);
_this._setDataNotFound(tags.length === 0);
@@ -2494,7 +2636,10 @@ var TF;
<script>
Polymer({
is: "tf-event-dashboard",
- behaviors: [TF.Backend.Behavior],
+ behaviors: [
+ TF.Dashboard.ReloadBehavior("tf-chart"),
+ TF.Backend.Behavior,
+ ],
properties: {
dataType: {value: "scalar"},
router: Object,
@@ -2599,7 +2744,10 @@ var TF;
<script>
Polymer({
is: "tf-histogram-dashboard",
- behaviors: [TF.Backend.Behavior],
+ behaviors: [
+ TF.Dashboard.ReloadBehavior("tf-chart"),
+ TF.Backend.Behavior,
+ ],
properties: {
_visibleTags: {
type: Array,
@@ -2661,11 +2809,12 @@ var TF;
width: 100%;
height: 100%;
image-rendering: pixelated;
+ border: 1px solid #555;
}
</style>
<template>
<template is="dom-if" if="[[imageUrl]]">
- <img src="[[imageUrl]]" on-error="retry">
+ <img src="[[imageUrl]]" on-error="reload">
</template>
</template>
<script>
@@ -2679,17 +2828,18 @@ var TF;
},
reload: function() {
var _this = this;
+ this.imageUrl = ""; // force reload
this.imagesGenerator(this.tag, this.run).then(function(metadatas) {
var last_metadata = _.last(metadatas);
_this.imageUrl = last_metadata.url;
- })
+ });
},
ready: function() {
- this.reload();
- },
- retry: function() {
- this.imageUrl = ""; // force reload
- this.reload();
+ // Need to test so that it will not error if it is constructed w/o
+ // all properties (so that it's possible to use stub to mock it out)
+ if (this.run != null && this.tag != null && this.imagesGenerator != null) {
+ this.reload();
+ }
},
});
</script>
@@ -2700,9 +2850,9 @@ var TF;
<style include="scrollbar-style"></style>
<div id="fullContainer" class="container scrollbar">
<div id="topRow" class="container">
- <div class="noshrink" id="paddingCell"></div>
+ <div class="noshrink cell" id="paddingCell"></div>
<template is="dom-repeat" items="[[runs]]" as="run">
- <div class="run-name-cell noshrink">
+ <div class="run-name-cell cell noshrink">
<span>[[run]]</span>
</div>
</template>
@@ -2710,11 +2860,11 @@ var TF;
<div id="bottomContainer" class="container">
<template is="dom-repeat" items="[[tags]]" as="tag">
<div class="image-row container noshrink">
- <div class="tag-name-cell noshrink">
+ <div class="tag-name-cell cell noshrink">
<span class="tag-name">[[tag]]</span>
</div>
<template is="dom-repeat" items="[[runs]]" as="run">
- <div class="image-cell noshrink">
+ <div class="image-cell cell noshrink">
<template is="dom-if" if="[[_exists(run, tag, runToImages.*)]]">
<tf-image-loader id="loader" run="[[run]]" tag="[[tag]]" images-generator="[[imagesGenerator]]">
</tf-image-loader>
@@ -2752,14 +2902,16 @@ var TF;
height: 100%;
width: 100%;
}
+ .cell {
+ margin-right: 10px;
+ }
.image-row {
flex-direction: row;
- padding-top: 5px;
+ padding-top: 10px;
}
.image-cell {
width: 300px;
height: 300px;
- border: 1px solid black;
}
.tag-name-cell {
height: 300px;
@@ -2807,7 +2959,7 @@ var TF;
<template>
<div class="center">
<tf-no-data-warning data-type="image" show-warning="[[dataNotFound]]"></tf-no-data-warning>
- <tf-image-grid id="imageGrid" run-to-images="[[run2tag]]" images-generator="[[dataProvider]]" tags="[[tags]]" runs="[[runs]]"></tf-image-grid>
+ <tf-image-grid id="imagegrid" run-to-images="[[run2tag]]" images-generator="[[dataProvider]]" tags="[[tags]]" runs="[[runs]]"></tf-image-grid>
</div>
<style>
@@ -2833,7 +2985,10 @@ var TF;
properties: {
dataType: {value: "image"},
},
- behaviors: [TF.Backend.Behavior],
+ behaviors: [
+ TF.Dashboard.ReloadBehavior("tf-image-loader"),
+ TF.Backend.Behavior
+ ],
attached: function() {
this.async(function() {
this.fire("rendered");
@@ -10664,7 +10819,7 @@ Polymer({
(<span>[[_totalPredecessors]]</span>)
<iron-list class="sub-list" id="inputsList" items="[[_predecessors.regular]]">
<template>
- <tf-node-list-item class="non-control-list-item" card-node="[[_node]]" item-node="[[_getNode(item, graphHierarchy)]]" edge-label="[[_getPredEdgeLabel(item)]]" item-render-info="[[_getRenderInfo(item, renderHierarchy)]]" name="[[item]]" item-type="predecessors" color-by="[[colorBy]]" template-index="[[_templateIndex]]">
+ <tf-node-list-item class="non-control-list-item" card-node="[[_node]]" item-node="[[item.node]]" edge-label="[[item.edgeLabel]]" item-render-info="[[item.renderInfo]]" name="[[item.name]]" item-type="predecessors" color-by="[[colorBy]]" template-index="[[_templateIndex]]">
</tf-node-list-item>
</template>
</iron-list>
@@ -10679,7 +10834,7 @@ Polymer({
<template is="dom-if" if="{{_openedControlPred}}" restamp="true">
<iron-list class="sub-list" items="[[_predecessors.control]]">
<template>
- <tf-node-list-item card-node="[[_node]]" item-node="[[_getNode(item, graphHierarchy)]]" item-render-info="[[_getRenderInfo(item, renderHierarchy)]]" name="[[item]]" item-type="predecessors" color-by="[[colorBy]]" template-index="[[_templateIndex]]">
+ <tf-node-list-item card-node="[[_node]]" item-node="[[item.node]]" item-render-info="[[item.renderInfo]]" name="[[item.name]]" item-type="predecessors" color-by="[[colorBy]]" template-index="[[_templateIndex]]">
</tf-node-list-item>
</template>
</iron-list>
@@ -10694,7 +10849,7 @@ Polymer({
(<span>[[_totalSuccessors]]</span>)
<iron-list class="sub-list" id="outputsList" items="[[_successors.regular]]">
<template>
- <tf-node-list-item class="non-control-list-item" card-node="[[_node]]" item-node="[[_getNode(item, graphHierarchy)]]" edge-label="[[_getSuccEdgeLabel(item)]]" item-render-info="[[_getRenderInfo(item, renderHierarchy)]]" name="[[item]]" item-type="successor" color-by="[[colorBy]]" template-index="[[_templateIndex]]">
+ <tf-node-list-item class="non-control-list-item" card-node="[[_node]]" item-node="[[item.node]]" edge-label="[[item.edgeLabel]]" item-render-info="[[item.renderInfo]]" name="[[item.name]]" item-type="successor" color-by="[[colorBy]]" template-index="[[_templateIndex]]">
</tf-node-list-item>
</template>
</iron-list>
@@ -10709,7 +10864,7 @@ Polymer({
<template is="dom-if" if="{{_openedControlSucc}}" restamp="true">
<iron-list class="sub-list" items="[[_successors.control]]">
<template>
- <tf-node-list-item card-node="[[_node]]" item-node="[[_getNode(item, graphHierarchy)]]" item-render-info="[[_getRenderInfo(item, renderHierarchy)]]" name="[[item]]" item-type="successors" color-by="[[colorBy]]" template-index="[[_templateIndex]]">
+ <tf-node-list-item card-node="[[_node]]" item-node="[[item.node]]" item-render-info="[[item.renderInfo]]" name="[[item.name]]" item-type="successors" color-by="[[colorBy]]" template-index="[[_templateIndex]]">
</tf-node-list-item>
</template>
</iron-list>
@@ -10872,11 +11027,40 @@ Polymer({
},
_getSuccessors: function(node, hierarchy) {
this.async(this._resizeList.bind(this, "#inputsList"));
- return node ? hierarchy.getSuccessors(node.name) : [[], []];
+ if (!node) {
+ return {regular: [], control: []}
+ }
+ return this._convertEdgeListToEdgeInfoList(
+ hierarchy.getSuccessors(node.name), false);
},
_getPredecessors: function(node, hierarchy) {
this.async(this._resizeList.bind(this, "#outputsList"));
- return node ? hierarchy.getPredecessors(node.name) : [[], []];
+ if (!node) {
+ return {regular: [], control: []}
+ }
+ return this._convertEdgeListToEdgeInfoList(
+ hierarchy.getPredecessors(node.name), true);
+ },
+ _convertEdgeListToEdgeInfoList: function(list, isPredecessor) {
+ return {
+ regular: list.regular.map(function(name) {
+ return {
+ name: name,
+ node: this._getNode(name, this.graphHierarchy),
+ edgeLabel: isPredecessor
+ ? this._getPredEdgeLabel(name)
+ : this._getSuccEdgeLabel(name),
+ renderInfo: this._getRenderInfo(name, this.renderHierarchy)
+ }
+ }, this),
+ control: list.control.map(function(name) {
+ return {
+ name: name,
+ node: this._getNode(name, this.graphHierarchy),
+ renderInfo: this._getRenderInfo(name, this.renderHierarchy)
+ }
+ }, this)
+ };
},
_getSubnodes: function(node) {
return node && node.metagraph ? node.metagraph.nodes() : null;
@@ -11767,41 +11951,50 @@ Polymer({
</script>
</div><dom-module id="tf-tensorboard">
<template>
+ <paper-dialog with-backdrop="" id="settings">
+ <h2>Settings</h2>
+ <paper-checkbox id="auto-reload-checkbox" checked="{{autoReloadEnabled}}">
+ Reload data every <span>[[autoReloadIntervalSecs]]</span>s.
+ </paper-checkbox>
+ </paper-dialog>
<paper-header-panel>
<paper-toolbar id="toolbar">
<div id="toolbar-content">
<div class="toolbar-title">TensorBoard</div>
<paper-tabs selected="{{modeIndex}}" noink="" class="tabs" id="tabs">
- <paper-tab data-mode="events">Events</paper-tab>
- <paper-tab data-mode="images">Images</paper-tab>
- <paper-tab data-mode="graphs">Graph</paper-tab>
- <paper-tab data-mode="histograms">Histograms</paper-tab>
+ <template is="dom-repeat" items="[[tabs]]">
+ <paper-tab data-mode="[[item]]">[[item]]</paper-tab>
+ </template>
</paper-tabs>
<div class="global-actions">
+ <paper-icon-button icon="refresh" on-tap="reload" disabled$="[[_modeIsGraphs(mode)]]" id="reload-button"></paper-icon-button>
+ <paper-icon-button icon="settings" on-tap="openSettings" id="settings-button"></paper-icon-button>
<a href="https://github.com/tensorflow/tensorflow/blob/master/tensorflow/tensorboard/README.md" tabindex="-1">
<paper-icon-button icon="help-outline"></paper-icon-button>
</a>
</div>
</div>
</paper-toolbar>
+
<div id="content" class="fit">
- <template is="dom-if" if="[[eventDashboard(mode)]]">
- <tf-event-dashboard id="eventDash" backend="[[_backend]]" router="[[router]]"></tf-event-dashboard>
+ <template is="dom-if" if="[[_modeIsEvents(mode)]]">
+ <tf-event-dashboard id="events" backend="[[_backend]]" router="[[router]]"></tf-event-dashboard>
</template>
- <template is="dom-if" if="[[imageDashboard(mode)]]">
- <tf-image-dashboard id="imageDash" backend="[[_backend]]"></tf-image-dashboard>
+ <template is="dom-if" if="[[_modeIsImages(mode)]]">
+ <tf-image-dashboard id="images" backend="[[_backend]]"></tf-image-dashboard>
</template>
- <template is="dom-if" if="[[graphDashboard(mode)]]">
- <tf-graph-dashboard id="graphDash" backend="[[_backend]]" router="[[router]]"></tf-graph-dashboard>
+ <template is="dom-if" if="[[_modeIsGraphs(mode)]]">
+ <tf-graph-dashboard id="graphs" backend="[[_backend]]" router="[[router]]"></tf-graph-dashboard>
</template>
- <template is="dom-if" if="[[histogramDashboard(mode)]]">
- <tf-histogram-dashboard id="histogramDash" backend="[[_backend]]"></tf-histogram-dashboard>
+ <template is="dom-if" if="[[_modeIsHistograms(mode)]]">
+ <tf-histogram-dashboard id="histograms" backend="[[_backend]]"></tf-histogram-dashboard>
</template>
</div>
</paper-header-panel>
+
<style>
:host {
height: 100%;
@@ -11820,11 +12013,11 @@ Polymer({
text-rendering: optimizeLegibility;
letter-spacing: -0.025em;
font-weight: 500;
- width: 340px;
+ flex-grow: 2;
}
.tabs {
- width: 400px;
+ flex-grow: 1;
text-transform: uppercase;
height: 100%;
}
@@ -11835,6 +12028,8 @@ Polymer({
.global-actions {
flex-grow: 2;
+ display: inline-flex; /* Ensure that icons stay aligned */
+ justify-content: flex-end;
text-align: right;
color: white;
}
@@ -11855,12 +12050,17 @@ Polymer({
#content {
height: 100%;
}
+ [disabled] {
+ opacity: 0.2;
+ color: white;
+ }
</style>
</template>
<script>
Polymer({
is: "tf-tensorboard",
+ behaviors: [TF.TensorBoard.AutoReloadBehavior],
properties: {
router: {
type: Object,
@@ -11873,14 +12073,20 @@ Polymer({
// Which tab is selected (events, graph, images etc).
mode: {
type: String,
- computed: '_getModeFromIndex(modeIndex)'
+ computed: '_getModeFromIndex(modeIndex)',
+ notify: true,
},
// If true, tab switching in TensorBoard will not update
// location hash. Hash update interferes with selenium tests.
noHash: {
type: Boolean,
value: false
- }
+ },
+ tabs: {
+ type: Array,
+ readOnly: true,
+ value: TF.TensorBoard.TABS,
+ },
},
_getModeFromIndex: function(modeIndex) {
var mode = this.tabs[modeIndex];
@@ -11892,22 +12098,26 @@ Polymer({
_makeBackend: function(router) {
return new TF.Backend.Backend(router);
},
- eventDashboard: function(mode) {
+ _modeIsEvents: function(mode) {
return mode === "events";
},
- imageDashboard: function(mode) {
+ _modeIsImages: function(mode) {
return mode === "images";
},
- graphDashboard: function(mode) {
+ _modeIsGraphs: function(mode) {
return mode === "graphs";
},
- histogramDashboard: function(mode) {
+ _modeIsHistograms: function(mode) {
return mode === "histograms";
},
+ selectedDashboard: function() {
+ var dashboard = this.$$("#" + this.mode);
+ if (dashboard == null) {
+ throw new Error(`Unable to find dashboard for mode: ${this.mode}`);
+ }
+ return dashboard;
+ },
ready: function() {
- this.tabs = [].slice.call(this.querySelectorAll('paper-tab')).map(function(a) {
- return a.dataset.mode;
- });
this._getModeFromHash();
window.addEventListener('hashchange', function() {
this._getModeFromHash();
@@ -11925,6 +12135,15 @@ Polymer({
this.set('modeIndex', modeIndex);
}
},
+ reload: function() {
+ if (this.mode === "graphs") {
+ return;
+ }
+ this.selectedDashboard().reload();
+ },
+ openSettings: function() {
+ this.$.settings.open();
+ },
});
</script>
</dom-module>