aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--WORKSPACE78
-rw-r--r--tensorflow/tensorboard/TAG2
-rw-r--r--tensorflow/tensorboard/bower.json8
-rw-r--r--tensorflow/tensorboard/dist/tf-tensorboard.html693
4 files changed, 502 insertions, 279 deletions
diff --git a/WORKSPACE b/WORKSPACE
index d3e01b7637..4ec77c790b 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -46,7 +46,7 @@ new_git_repository(
new_git_repository(
name = "font_roboto",
build_file = "bower.BUILD",
- remote = "https://github.com/PolymerElements/font-roboto.git",
+ remote = "https://github.com/polymerelements/font-roboto.git",
tag = "v1.0.1",
)
@@ -60,49 +60,49 @@ new_git_repository(
new_git_repository(
name = "iron_a11y_announcer",
build_file = "bower.BUILD",
- remote = "https://github.com/PolymerElements/iron-a11y-announcer.git",
+ remote = "https://github.com/polymerelements/iron-a11y-announcer.git",
tag = "v1.0.4",
)
new_git_repository(
name = "iron_a11y_keys_behavior",
build_file = "bower.BUILD",
- remote = "https://github.com/PolymerElements/iron-a11y-keys-behavior.git",
+ remote = "https://github.com/polymerelements/iron-a11y-keys-behavior.git",
tag = "v1.1.2",
)
new_git_repository(
name = "iron_ajax",
build_file = "bower.BUILD",
- remote = "https://github.com/PolymerElements/iron-ajax.git",
+ remote = "https://github.com/polymerelements/iron-ajax.git",
tag = "v1.1.1",
)
new_git_repository(
name = "iron_autogrow_textarea",
build_file = "bower.BUILD",
- remote = "https://github.com/PolymerElements/iron-autogrow-textarea.git",
+ remote = "https://github.com/polymerelements/iron-autogrow-textarea.git",
tag = "v1.0.12",
)
new_git_repository(
name = "iron_behaviors",
build_file = "bower.BUILD",
- remote = "https://github.com/PolymerElements/iron-behaviors.git",
+ remote = "https://github.com/polymerelements/iron-behaviors.git",
tag = "v1.0.13",
)
new_git_repository(
name = "iron_checked_element_behavior",
build_file = "bower.BUILD",
- remote = "https://github.com/PolymerElements/iron-checked-element-behavior.git",
+ remote = "https://github.com/polymerelements/iron-checked-element-behavior.git",
tag = "v1.0.4",
)
new_git_repository(
name = "iron_collapse",
build_file = "bower.BUILD",
- remote = "https://github.com/PolymerElements/iron-collapse.git",
+ remote = "https://github.com/polymerelements/iron-collapse.git",
tag = "v1.0.6",
)
@@ -116,7 +116,7 @@ new_git_repository(
new_git_repository(
name = "iron_fit_behavior",
build_file = "bower.BUILD",
- remote = "https://github.com/PolymerElements/iron-fit-behavior.git",
+ remote = "https://github.com/polymerelements/iron-fit-behavior.git",
tag = "v1.0.6",
)
@@ -130,7 +130,7 @@ new_git_repository(
new_git_repository(
name = "iron_form_element_behavior",
build_file = "bower.BUILD",
- remote = "https://github.com/PolymerElements/iron-form-element-behavior.git",
+ remote = "https://github.com/polymerelements/iron-form-element-behavior.git",
tag = "v1.0.6",
)
@@ -151,28 +151,28 @@ new_git_repository(
new_git_repository(
name = "iron_iconset_svg",
build_file = "bower.BUILD",
- remote = "https://github.com/PolymerElements/iron-iconset-svg.git",
+ remote = "https://github.com/polymerelements/iron-iconset-svg.git",
tag = "v1.0.9",
)
new_git_repository(
name = "iron_input",
build_file = "bower.BUILD",
- remote = "https://github.com/PolymerElements/iron-input.git",
+ remote = "https://github.com/polymerelements/iron-input.git",
tag = "v1.0.9",
)
new_git_repository(
name = "iron_list",
build_file = "bower.BUILD",
- remote = "https://github.com/PolymerElements/iron-list.git",
+ remote = "https://github.com/polymerelements/iron-list.git",
tag = "v1.1.7",
)
new_git_repository(
name = "iron_menu_behavior",
build_file = "bower.BUILD",
- remote = "https://github.com/PolymerElements/iron-menu-behavior.git",
+ remote = "https://github.com/polymerelements/iron-menu-behavior.git",
tag = "v1.1.5",
)
@@ -187,13 +187,13 @@ new_git_repository(
name = "iron_overlay_behavior",
build_file = "bower.BUILD",
remote = "https://github.com/polymerelements/iron-overlay-behavior.git",
- tag = "v1.6.1",
+ tag = "v1.6.2",
)
new_git_repository(
name = "iron_range_behavior",
build_file = "bower.BUILD",
- remote = "https://github.com/PolymerElements/iron-range-behavior.git",
+ remote = "https://github.com/polymerelements/iron-range-behavior.git",
tag = "v1.0.4",
)
@@ -207,14 +207,14 @@ new_git_repository(
new_git_repository(
name = "iron_selector",
build_file = "bower.BUILD",
- remote = "https://github.com/PolymerElements/iron-selector.git",
+ remote = "https://github.com/polymerelements/iron-selector.git",
tag = "v1.2.4",
)
new_git_repository(
name = "iron_validatable_behavior",
build_file = "bower.BUILD",
- remote = "https://github.com/PolymerElements/iron-validatable-behavior.git",
+ remote = "https://github.com/polymerelements/iron-validatable-behavior.git",
tag = "v1.0.5",
)
@@ -235,56 +235,56 @@ new_git_repository(
new_git_repository(
name = "paper_behaviors",
build_file = "bower.BUILD",
- remote = "https://github.com/PolymerElements/paper-behaviors.git",
+ remote = "https://github.com/polymerelements/paper-behaviors.git",
tag = "v1.0.11",
)
new_git_repository(
name = "paper_button",
build_file = "bower.BUILD",
- remote = "https://github.com/PolymerElements/paper-button.git",
+ remote = "https://github.com/polymerelements/paper-button.git",
tag = "v1.0.11",
)
new_git_repository(
name = "paper_checkbox",
build_file = "bower.BUILD",
- remote = "https://github.com/PolymerElements/paper-checkbox.git",
+ remote = "https://github.com/polymerelements/paper-checkbox.git",
tag = "v1.1.3",
)
new_git_repository(
name = "paper_dropdown_menu",
build_file = "bower.BUILD",
- remote = "https://github.com/PolymerElements/paper-dropdown-menu.git",
+ remote = "https://github.com/polymerelements/paper-dropdown-menu.git",
tag = "v1.1.3",
)
new_git_repository(
name = "paper_header_panel",
build_file = "bower.BUILD",
- remote = "https://github.com/PolymerElements/paper-header-panel.git",
+ remote = "https://github.com/polymerelements/paper-header-panel.git",
tag = "v1.1.4",
)
new_git_repository(
name = "paper_icon_button",
build_file = "bower.BUILD",
- remote = "https://github.com/PolymerElements/paper-icon-button.git",
+ remote = "https://github.com/polymerelements/paper-icon-button.git",
tag = "v1.0.6",
)
new_git_repository(
name = "paper_input",
build_file = "bower.BUILD",
- remote = "https://github.com/PolymerElements/paper-input.git",
+ remote = "https://github.com/polymerelements/paper-input.git",
tag = "v1.1.5",
)
new_git_repository(
name = "paper_item",
build_file = "bower.BUILD",
- remote = "https://github.com/PolymerElements/paper-item.git",
+ remote = "https://github.com/polymerelements/paper-item.git",
tag = "v1.1.4",
)
@@ -298,7 +298,7 @@ new_git_repository(
new_git_repository(
name = "paper_menu",
build_file = "bower.BUILD",
- remote = "https://github.com/PolymerElements/paper-menu.git",
+ remote = "https://github.com/polymerelements/paper-menu.git",
tag = "v1.2.2",
)
@@ -306,27 +306,27 @@ new_git_repository(
name = "paper_menu_button",
build_file = "bower.BUILD",
remote = "https://github.com/polymerelements/paper-menu-button.git",
- tag = "v1.0.4",
+ tag = "v1.1.0",
)
new_git_repository(
name = "paper_progress",
build_file = "bower.BUILD",
- remote = "https://github.com/PolymerElements/paper-progress.git",
- tag = "v1.0.8",
+ remote = "https://github.com/polymerelements/paper-progress.git",
+ tag = "v1.0.9",
)
new_git_repository(
name = "paper_radio_button",
build_file = "bower.BUILD",
- remote = "https://github.com/PolymerElements/paper-radio-button.git",
+ remote = "https://github.com/polymerelements/paper-radio-button.git",
tag = "v1.1.1",
)
new_git_repository(
name = "paper_radio_group",
build_file = "bower.BUILD",
- remote = "https://github.com/PolymerElements/paper-radio-group.git",
+ remote = "https://github.com/polymerelements/paper-radio-group.git",
tag = "v1.0.9",
)
@@ -340,35 +340,35 @@ new_git_repository(
new_git_repository(
name = "paper_slider",
build_file = "bower.BUILD",
- remote = "https://github.com/PolymerElements/paper-slider.git",
+ remote = "https://github.com/polymerelements/paper-slider.git",
tag = "v1.0.8",
)
new_git_repository(
name = "paper_styles",
build_file = "bower.BUILD",
- remote = "https://github.com/PolymerElements/paper-styles.git",
+ remote = "https://github.com/polymerelements/paper-styles.git",
tag = "v1.1.1",
)
new_git_repository(
name = "paper_tabs",
build_file = "bower.BUILD",
- remote = "https://github.com/PolymerElements/paper-tabs.git",
+ remote = "https://github.com/polymerelements/paper-tabs.git",
tag = "v1.2.4",
)
new_git_repository(
name = "paper_toggle_button",
build_file = "bower.BUILD",
- remote = "https://github.com/PolymerElements/paper-toggle-button.git",
+ remote = "https://github.com/polymerelements/paper-toggle-button.git",
tag = "v1.0.12",
)
new_git_repository(
name = "paper_toolbar",
build_file = "bower.BUILD",
- remote = "https://github.com/PolymerElements/paper-toolbar.git",
+ remote = "https://github.com/polymerelements/paper-toolbar.git",
tag = "v1.1.2",
)
@@ -382,7 +382,7 @@ new_git_repository(
new_git_repository(
name = "polymer",
build_file = "bower.BUILD",
- remote = "https://github.com/Polymer/polymer.git",
+ remote = "https://github.com/polymer/polymer.git",
tag = "v1.4.0",
)
@@ -403,6 +403,6 @@ new_git_repository(
new_git_repository(
name = "webcomponentsjs",
build_file = "bower.BUILD",
- remote = "https://github.com/Polymer/webcomponentsjs.git",
+ remote = "https://github.com/polymer/webcomponentsjs.git",
tag = "v0.7.21",
)
diff --git a/tensorflow/tensorboard/TAG b/tensorflow/tensorboard/TAG
index 60d3b2f4a4..b6a7d89c68 100644
--- a/tensorflow/tensorboard/TAG
+++ b/tensorflow/tensorboard/TAG
@@ -1 +1 @@
-15
+16
diff --git a/tensorflow/tensorboard/bower.json b/tensorflow/tensorboard/bower.json
index 39b50cc2aa..503fd25e4f 100644
--- a/tensorflow/tensorboard/bower.json
+++ b/tensorflow/tensorboard/bower.json
@@ -46,7 +46,7 @@
"iron-collapse": "PolymerElements/iron-collapse#1.0.6",
"iron-dropdown": "PolymerElements/iron-dropdown#1.3.0",
"iron-fit-behavior": "PolymerElements/iron-fit-behavior#1.0.6",
- "iron-flex-layout": "PolymerElements/iron-flex-layout#1.2.3",
+ "iron-flex-layout": "PolymerElements/iron-flex-layout#1.3.0",
"iron-form-element-behavior": "PolymerElements/iron-form-element-behavior#1.0.6",
"iron-icon": "PolymerElements/iron-icon#1.0.8",
"iron-icons": "PolymerElements/iron-icons#1.1.3",
@@ -73,7 +73,7 @@
"paper-material": "PolymerElements/paper-material#1.0.6",
"paper-menu": "PolymerElements/paper-menu#1.2.2",
"paper-menu-button": "PolymerElements/paper-menu-button#1.0.4",
- "paper-progress": "PolymerElements/paper-progress#1.0.8",
+ "paper-progress": "PolymerElements/paper-progress#1.0.9",
"paper-radio-button": "PolymerElements/paper-radio-button#1.1.1",
"paper-radio-group": "PolymerElements/paper-radio-group#1.0.9",
"paper-ripple": "PolymerElements/paper-ripple#1.0.5",
@@ -117,7 +117,7 @@
"iron-collapse": "1.0.6",
"iron-dropdown": "1.3.0",
"iron-fit-behavior": "1.0.6",
- "iron-flex-layout": "1.2.3",
+ "iron-flex-layout": "1.3.0",
"iron-form-element-behavior": "1.0.6",
"iron-icon": "1.0.8",
"iron-icons": "1.1.3",
@@ -144,7 +144,7 @@
"paper-material": "1.0.6",
"paper-menu": "1.2.2",
"paper-menu-button": "1.0.4",
- "paper-progress": "1.0.8",
+ "paper-progress": "1.0.9",
"paper-radio-button": "1.1.1",
"paper-radio-group": "1.0.9",
"paper-ripple": "1.0.5",
diff --git a/tensorflow/tensorboard/dist/tf-tensorboard.html b/tensorflow/tensorboard/dist/tf-tensorboard.html
index 15ede2b755..a9cc4e419e 100644
--- a/tensorflow/tensorboard/dist/tf-tensorboard.html
+++ b/tensorflow/tensorboard/dist/tf-tensorboard.html
@@ -1,4 +1,22 @@
-// AUTOGENERATED FILE - DO NOT MODIFY
+<!-- 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.
+============================================================================
+
+This file is generated by `gulp` & `vulcanize`. Do not directly change it.
+Instead, use `gulp regenerate` to create a new version with your changes.
+-->
+
<html><head><meta charset="UTF-8">
</head><body><div hidden="" by-vulcanize="">
<dom-module id="tf-tooltip-coordinator" assetpath="../tf-event-dashboard/">
@@ -405,8 +423,8 @@
<style is="custom-style">
:root {
- --tb-orange-weak: #fcb938;
- --tb-orange-strong: #f3913e;
+ --tb-orange-weak: #ffa726;
+ --tb-orange-strong: #f57c00;
--tb-grey-darker: #e2e2e2;
--tb-grey-lighter: #f3f3f3;
--tb-ui-dark-accent: #757575;
@@ -1797,19 +1815,33 @@ var TF;
*/
var RequestCancellationError = (function (_super) {
__extends(RequestCancellationError, _super);
- function RequestCancellationError(message) {
- _super.call(this, message);
+ function RequestCancellationError() {
+ _super.apply(this, arguments);
this.name = "RequestCancellationError";
}
return RequestCancellationError;
}(Error));
Backend.RequestCancellationError = RequestCancellationError;
+ var RequestNetworkError = (function (_super) {
+ __extends(RequestNetworkError, _super);
+ function RequestNetworkError(req, url) {
+ _super.call(this);
+ this.message = "RequestNetworkError: " + req.status + " at " + url;
+ this.name = "RequestNetworkError";
+ this.req = req;
+ this.url = url;
+ }
+ return RequestNetworkError;
+ }(Error));
+ Backend.RequestNetworkError = RequestNetworkError;
var RequestManager = (function () {
- function RequestManager(nSimultaneousRequests) {
+ function RequestManager(nSimultaneousRequests, maxRetries) {
if (nSimultaneousRequests === void 0) { nSimultaneousRequests = 10; }
+ if (maxRetries === void 0) { maxRetries = 3; }
this._queue = [];
this._nActiveRequests = 0;
this._nSimultaneousRequests = nSimultaneousRequests;
+ this._maxRetries = maxRetries;
}
/* Gives a promise that loads assets from given url (respects queuing) */
RequestManager.prototype.request = function (url) {
@@ -1819,11 +1851,20 @@ var TF;
_this._queue.push(resolver);
_this.launchRequests();
}).then(function () {
- return _this._promiseFromUrl(url);
+ return _this.promiseWithRetries(url, _this._maxRetries);
}).then(function (response) {
+ // Success - Let's free space for another active reqest, and launch it
_this._nActiveRequests--;
- _this.launchRequests(); // since we may have queued responses to launch
+ _this.launchRequests();
return response;
+ }, function (rejection) {
+ if (rejection.name === "RequestNetworkError") {
+ // If we failed due to network error, we should decrement
+ // _nActiveRequests because this request was active
+ _this._nActiveRequests--;
+ _this.launchRequests();
+ }
+ return Promise.reject(rejection);
});
return promise;
};
@@ -1846,6 +1887,29 @@ var TF;
this._queue.pop().resolve();
}
};
+ /**
+ * Try to request a given URL using overwritable _promiseFromUrl method.
+ * If the request fails for any reason, we will retry up to maxRetries
+ * times. In practice, this will help us paper over transient network issues
+ * like "502 Bad Gateway".
+ * By default, Chrome displays network errors in console, so
+ * the user will be able to tell when the requests are failing. I think this
+ * is a feature, if the request failures and retries are causing any
+ * pain to users, they can see it and file issues.
+ */
+ RequestManager.prototype.promiseWithRetries = function (url, maxRetries) {
+ var _this = this;
+ var success = function (x) { return x; };
+ var failure = function (x) {
+ if (maxRetries > 0) {
+ return _this.promiseWithRetries(url, maxRetries - 1);
+ }
+ else {
+ return Promise.reject(x);
+ }
+ };
+ return this._promiseFromUrl(url).then(success, failure);
+ };
/* Actually get promise from url using XMLHttpRequest */
RequestManager.prototype._promiseFromUrl = function (url) {
return new Promise(function (resolve, reject) {
@@ -1856,11 +1920,11 @@ var TF;
resolve(JSON.parse(req.responseText));
}
else {
- reject(Error("Status: " + req.status + ":" + req.statusText + " at url: " + url));
+ reject(new RequestNetworkError(req, url));
}
};
req.onerror = function () {
- reject(Error("Network error"));
+ reject(new RequestNetworkError(req, url));
};
req.send();
});
@@ -1952,11 +2016,19 @@ var TF;
}
function standardRoute(route) {
return function (tag, run) {
- return dataDir + "/" + route + clean(Backend.queryEncoder({ tag: tag, run: run }));
+ var url = dataDir + "/" + route + clean(Backend.queryEncoder({ tag: tag, run: run }));
+ if (demoMode) {
+ url += ".json";
+ }
+ return url;
};
}
function individualImageUrl(query) {
- return dataDir + "/" + clean("individualImage?" + query);
+ var url = dataDir + "/" + clean("individualImage?" + query);
+ if (demoMode) {
+ url += ".png";
+ }
+ return url;
}
function graphUrl(run, limit_attr_size, large_attrs_key) {
var query_params = [["run", clean(run)]];
@@ -1969,10 +2041,14 @@ var TF;
var query = query_params.map(function (param) {
return param[0] + "=" + encodeURIComponent(param[1]);
}).join("&");
- return dataDir + "/graph" + clean("?" + query);
+ var url = dataDir + "/graph" + clean("?" + query);
+ if (demoMode) {
+ url += ".pbtxt";
+ }
+ return url;
}
return {
- runs: function () { return dataDir + "/runs"; },
+ runs: function () { return dataDir + "/runs" + (demoMode ? ".json" : ""); },
individualImage: individualImageUrl,
graph: graphUrl,
scalars: standardRoute("scalars"),
@@ -2108,7 +2184,16 @@ var TF;
var p;
var url = this.router.histograms(tag, run);
p = this.requestManager.request(url);
- return p.then(map(detupler(createHistogram)));
+ return p.then(map(detupler(createHistogram)))
+ .then(function (histos) {
+ return histos.map(function (histo, i) {
+ return {
+ wall_time: histo.wall_time,
+ step: histo.step,
+ bins: convertBins(histo)
+ };
+ });
+ });
};
/**
* Return a promise containing ImageDatums for given run and tag.
@@ -2209,6 +2294,38 @@ 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.
+ *
+ * @param {histogram} Histogram - A histogram from tensorboard backend.
+ * @return {HistogramBin[]} - Each bin has an x (left edge), a dx (width), and a y (count).
+ *
+ * If given rightedges are inclusive, then these left edges (x) are exclusive.
+ */
+ function convertBins(histogram) {
+ if (histogram.bucketRightEdges.length !== histogram.bucketCounts.length) {
+ throw (new Error("Edges and counts are of different lengths."));
+ }
+ var previousRightEdge = histogram.min;
+ return histogram.bucketRightEdges.map(function (rightEdge, i) {
+ // Use the previous bin's rightEdge as the new leftEdge
+ var left = previousRightEdge;
+ // We need to clip the rightEdge because right-most edge can be
+ // infinite-sized
+ var right = Math.min(histogram.max, rightEdge);
+ // Store rightEdgeValue for next iteration
+ previousRightEdge = rightEdge;
+ return {
+ x: left,
+ dx: right - left,
+ y: histogram.bucketCounts[i]
+ };
+ });
+ }
+ Backend_1.convertBins = convertBins;
})(Backend = TF.Backend || (TF.Backend = {}));
})(TF || (TF = {}));
</script>
@@ -2548,7 +2665,7 @@ var TF;
</style>
<template>
<template is="dom-if" if="[[imageUrl]]">
- <img src="[[imageUrl]]">
+ <img src="[[imageUrl]]" on-error="retry">
</template>
</template>
<script>
@@ -2569,7 +2686,11 @@ var TF;
},
ready: function() {
this.reload();
- }
+ },
+ retry: function() {
+ this.imageUrl = ""; // force reload
+ this.reload();
+ },
});
</script>
</dom-module>
@@ -2747,11 +2868,6 @@ Polymer({
notify: true,
},
datasets: Array,
- hasStats: {
- type: Boolean,
- readOnly: true, // This property produces data.
- notify: true
- },
selectedDataset: Number,
selectedFile: {
type: Object,
@@ -2767,21 +2883,41 @@ Polymer({
readOnly: true, //readonly so outsider can't change this via binding
notify: true
},
- outGraphName: {
- type: String,
- readOnly: true,
- notify: true
- },
outHierarchyParams: {
type: Object,
readOnly: true,
notify: true
},
+ outStats: {
+ type: Object,
+ readOnly: true, // This property produces data.
+ notify: true
+ }
},
observers: [
- '_selectedDatasetChanged(selectedDataset, datasets)'
+ '_selectedDatasetChanged(selectedDataset, datasets)',
+ '_readAndParseMetadata(selectedDataset, selectedMetadataTag, datasets)'
],
- _parseAndConstructHierarchicalGraph: function(dataset, pbTxtFile) {
+ _readAndParseMetadata: function(datasetIndex, metadataIndex, datasets) {
+ if (metadataIndex == -1 || datasets[datasetIndex] == null ||
+ datasets[datasetIndex].runMetadata == null ||
+ datasets[datasetIndex].runMetadata[metadataIndex] == null) {
+ this._setOutStats(null);
+ return;
+ }
+ var path = datasets[datasetIndex].runMetadata[metadataIndex].path;
+ // Reset the progress bar to 0.
+ this.set('progress', {
+ value: 0,
+ msg: ''
+ });
+ var tracker = tf.getTracker(this);
+ tf.graph.parser.fetchAndParseMetadata(path, tracker)
+ .then(function(stats) {
+ this._setOutStats(stats);
+ }.bind(this));
+ },
+ _parseAndConstructHierarchicalGraph: function(path, pbTxtFile) {
// Reset the progress bar to 0.
this.set('progress', {
value: 0,
@@ -2800,13 +2936,10 @@ Polymer({
seriesMap: {},
};
this._setOutHierarchyParams(hierarchyParams);
- var statsJson;
var dataTracker = tf.getSubtaskTracker(tracker, 30, 'Data');
- tf.graph.parser.readAndParseData(dataset, pbTxtFile, dataTracker)
- .then(function(result) {
+ tf.graph.parser.fetchAndParseGraphData(path, pbTxtFile, dataTracker)
+ .then(function(graph) {
// Build the flat graph (consists only of Op nodes).
- var nodes = result.nodes;
- statsJson = result.statsJson;
// This is the whitelist of inputs on op types that are considered
// reference edges. "Assign 0" indicates that the first input to
@@ -2831,18 +2964,11 @@ Polymer({
outEmbeddingTypes: ['^[a-zA-Z]+Summary$'],
refEdges: refEdges
};
- var graphTracker = tf.getSubtaskTracker(tracker, 20,
- 'Graph');
- return tf.graph.build(nodes, buildParams, graphTracker);
+ var graphTracker = tf.getSubtaskTracker(tracker, 20, 'Graph');
+ return tf.graph.build(graph, buildParams, graphTracker);
})
.then(function(graph) {
this._setOutGraph(graph);
- if (statsJson) {
- // If there are associated stats, join them with the graph.
- tf.time('Joining stats info with graph...', function() {
- tf.graph.joinStatsInfoWithGraph(graph, statsJson);
- });
- }
var hierarchyTracker = tf.getSubtaskTracker(tracker, 50,
'Namespace hierarchy');
return tf.graph.hierarchy.build(graph, hierarchyParams, hierarchyTracker);
@@ -2850,7 +2976,6 @@ Polymer({
.then(function(graphHierarchy) {
// Update the properties which notify the parent with the
// graph hierarchy and whether the data has live stats or not.
- this._setHasStats(statsJson != null);
this._setOutGraphHierarchy(graphHierarchy);
}.bind(this))
.catch(function(e) {
@@ -2860,9 +2985,7 @@ Polymer({
});
},
_selectedDatasetChanged: function(datasetIndex, datasets) {
- var dataset = datasets[datasetIndex];
- this._parseAndConstructHierarchicalGraph(dataset);
- this._setOutGraphName(dataset.name);
+ this._parseAndConstructHierarchicalGraph(datasets[datasetIndex].path);
},
_selectedFileChanged: function(e) {
if (!e) {
@@ -3177,33 +3300,36 @@ var tf;
* Joins the information from the stats file (memory, compute time) with the
* graph information.
*/
- function joinStatsInfoWithGraph(graph, statsJson) {
- _.each(statsJson.devStats, function (stats) {
- _.each(stats.nodeStats, function (nodeStats) {
+ function joinStatsInfoWithGraph(graph, stats) {
+ _.each(stats.dev_stats, function (devStats) {
+ _.each(devStats.node_stats, function (nodeStats) {
// Lookup the node in the graph by its original name, e.g. A. If not
// found, lookup by the rewritten name A/(A) in case the name is both
// a namespace and a node name.
- var nodeName = nodeStats.nodeName in graph.nodes ?
- nodeStats.nodeName :
- nodeStats.nodeName + graph_1.NAMESPACE_DELIM + "(" + nodeStats.nodeName + ")";
- if (nodeName in graph.nodes) {
- // Compute the total bytes used.
- var totalBytes_1 = 0;
- if (nodeStats.memory) {
- _.each(nodeStats.memory, function (alloc) {
- if (alloc.totalBytes) {
- totalBytes_1 += Number(alloc.totalBytes);
- }
- });
- }
- var outputSize = null;
- if (nodeStats.output) {
- outputSize = _.map(nodeStats.output, function (output) {
- return _.map(output.tensorDescription.shape.dim, function (dim) { return Number(dim.size); });
- });
- }
- graph.nodes[nodeName].stats = new NodeStats(totalBytes_1, Number(nodeStats.allEndRelMicros), outputSize);
+ var nodeName = nodeStats.node_name in graph.nodes ?
+ nodeStats.node_name :
+ nodeStats.node_name + graph_1.NAMESPACE_DELIM + "(" + nodeStats.node_name + ")";
+ // Couldn't find a matching node.
+ if (!(nodeName in graph.nodes)) {
+ return;
+ }
+ // Compute the total bytes used.
+ var totalBytes = 0;
+ if (nodeStats.memory) {
+ _.each(nodeStats.memory, function (alloc) {
+ if (alloc.total_bytes) {
+ totalBytes += Number(alloc.total_bytes);
+ }
+ });
+ }
+ var outputSize = null;
+ if (nodeStats.output) {
+ outputSize = _.map(nodeStats.output, function (output) {
+ return _.map(output.tensor_description.shape.dim, function (dim) { return Number(dim.size); });
+ });
}
+ graph.nodes[nodeName].device = devStats.device;
+ graph.nodes[nodeName].stats = new NodeStats(totalBytes, Number(nodeStats.all_end_rel_micros), outputSize);
});
});
}
@@ -3261,7 +3387,6 @@ var tf;
this.templateId = null;
/** Metanode which contains this node, if any */
this.parentNode = null;
- this.stats = new NodeStats(0, 0, null);
this.hasNonControlEdges = false;
this.include = InclusionType.UNSPECIFIED;
}
@@ -3335,6 +3460,7 @@ var tf;
// Compute the size of the tensor flowing through this
// base edge.
this.totalSize += MetaedgeImpl.computeSizeOfEdge(edge, h);
+ h.maxMetaEdgeSize = Math.max(h.maxMetaEdgeSize, this.totalSize);
};
MetaedgeImpl.computeSizeOfEdge = function (edge, h) {
var opNode = h.node(edge.v);
@@ -3343,6 +3469,7 @@ var tf;
// a lower bound for the total size.
return 1;
}
+ h.hasShapeInfo = true;
// Sum the sizes of all output tensors.
return _(opNode.outputShapes).map(function (shape) {
// If the shape is unknown, treat it as 1 when computing
@@ -3394,7 +3521,6 @@ var tf;
this.parentNode = null;
this.deviceHistogram = {};
this.hasNonControlEdges = false;
- this.stats = new NodeStats(0, 0, null);
this.include = InclusionType.UNSPECIFIED;
}
return SeriesNodeImpl;
@@ -3439,7 +3565,7 @@ var tf;
}
// We didn't find OUTPUT_SHAPES_KEY in attributes, so we don't know anything
// about the output tensors.
- return result;
+ return null;
}
/**
* Normalizes the inputs and extracts associated metadata:
@@ -3828,7 +3954,7 @@ var tf;
/**
* Fetches a text file and returns a promise of the result.
*/
- function readPbTxt(filepath) {
+ function fetchPbTxt(filepath) {
return new Promise(function (resolve, reject) {
d3.text(filepath, function (error, text) {
if (error) {
@@ -3839,55 +3965,40 @@ var tf;
});
});
}
- parser.readPbTxt = readPbTxt;
+ parser.fetchPbTxt = fetchPbTxt;
/**
- * Fetches and parses a json file and returns a promise of the result.
+ * Fetches the metadata file, parses it and returns a promise of the result.
*/
- function readJson(filepath) {
- return new Promise(function (resolve, reject) {
- d3.json(filepath, function (error, text) {
- if (error) {
- reject(error);
- return;
- }
- resolve(text);
- });
+ function fetchAndParseMetadata(path, tracker) {
+ return tf.runTask("Reading metadata pbtxt", 40, function () {
+ if (path == null) {
+ return Promise.resolve(null);
+ }
+ return fetchPbTxt(path).then(function (text) { return new Blob([text]); });
+ }, tracker)
+ .then(function (blob) {
+ return tf.runTask("Parsing metadata.pbtxt", 60, function () {
+ return blob != null ? parseStatsPbTxt(blob) : null;
+ }, tracker);
});
}
- parser.readJson = readJson;
+ parser.fetchAndParseMetadata = fetchAndParseMetadata;
/**
- * Reads the graph and stats file (if available), parses them and returns a
- * promise of the result.
+ * Fetches the graph file, parses it and returns a promise of the result.
*/
- function readAndParseData(dataset, pbTxtFile, tracker) {
- var graphPbTxt;
- var statsJson;
- return tf.runTask("Reading graph.pbtxt", 20, function () {
+ function fetchAndParseGraphData(path, pbTxtFile, tracker) {
+ return tf.runTask("Reading graph pbtxt", 40, function () {
return pbTxtFile ?
Promise.resolve(pbTxtFile) :
- readPbTxt(dataset.path).then(function (text) { return new Blob([text]); });
+ fetchPbTxt(path).then(function (text) { return new Blob([text]); });
}, tracker)
.then(function (blob) {
- graphPbTxt = blob;
- return tf.runTask("Reading stats.pbtxt", 20, function () {
- return (dataset != null && dataset.statsPath != null) ?
- readJson(dataset.statsPath) : null;
- }, tracker);
- })
- .then(function (json) {
- statsJson = json;
return tf.runTask("Parsing graph.pbtxt", 60, function () {
- return parsePbtxtFile(graphPbTxt);
+ return parseGraphPbTxt(blob);
}, tracker);
- })
- .then(function (nodes) {
- return {
- nodes: nodes,
- statsJson: statsJson
- };
});
}
- parser.readAndParseData = readAndParseData;
+ parser.fetchAndParseGraphData = fetchAndParseGraphData;
/**
* Parse a file object in a streaming fashion line by line (or custom delim).
* Can handle very large files.
@@ -3946,13 +4057,55 @@ var tf;
}
parser.streamParse = streamParse;
/**
- * Parses a proto txt file or blob into javascript object.
+ * Since proto-txt doesn't explicitly say whether an attribute is repeated
+ * (an array) or not, we keep a hard-coded list of attributes that are known
+ * to be repeated. This list is used in parsing time to convert repeated
+ * attributes into arrays even when the attribute only shows up once in the
+ * object.
+ */
+ var GRAPH_REPEATED_FIELDS = {
+ "node": true,
+ "node.input": true,
+ "node.attr": true,
+ "node.attr.value.list.type": true,
+ "node.attr.value.shape.dim": true,
+ "node.attr.value.tensor.string_val": true,
+ "node.attr.value.tensor.tensor_shape.dim": true,
+ "node.attr.value.list.shape": true,
+ "node.attr.value.list.shape.dim": true,
+ "node.attr.value.list.s": true
+ };
+ var METADATA_REPEATED_FIELDS = {
+ "step_stats.dev_stats": true,
+ "step_stats.dev_stats.node_stats": true,
+ "step_stats.dev_stats.node_stats.output": true,
+ "step_stats.dev_stats.node_stats.memory": true,
+ "step_stats.dev_stats.node_stats.output.tensor_description.shape.dim": true
+ };
+ /**
+ * Parses a blob of proto txt file into a raw Graph object.
+ */
+ function parseGraphPbTxt(input) {
+ return parsePbtxtFile(input, GRAPH_REPEATED_FIELDS).then(function (obj) { return obj["node"]; });
+ }
+ parser.parseGraphPbTxt = parseGraphPbTxt;
+ /**
+ * Parses a blob of proto txt file into a StepStats object.
+ */
+ function parseStatsPbTxt(input) {
+ return parsePbtxtFile(input, METADATA_REPEATED_FIELDS)
+ .then(function (obj) { return obj["step_stats"]; });
+ }
+ /**
+ * Parses a blob of proto txt file into javascript object.
*
* @param input The Blob or file object implementing slice.
+ * @param repeatedFields Map (Set) of all the repeated fields, since you can't
+ * tell directly from the pbtxt if a field is repeated or not.
* @returns The parsed object.
*/
- function parsePbtxtFile(input) {
- var output = { node: [] };
+ function parsePbtxtFile(input, repeatedFields) {
+ var output = {};
var stack = [];
var path = [];
var current = output;
@@ -3966,25 +4119,6 @@ var tf;
};
}
/**
- * Since proto-txt doesn't explicitly say whether an attribute is repeated
- * (an array) or not, we keep a hard-coded list of attributes that are known
- * to be repeated. This list is used in parsing time to convert repeated
- * attributes into arrays even when the attribute only shows up once in the
- * object.
- */
- var ARRAY_ATTRIBUTES = {
- "node": true,
- "node.input": true,
- "node.attr": true,
- "node.attr.value.list.type": true,
- "node.attr.value.shape.dim": true,
- "node.attr.value.tensor.string_val": true,
- "node.attr.value.tensor.tensor_shape.dim": true,
- "node.attr.value.list.shape": true,
- "node.attr.value.list.shape.dim": true,
- "node.attr.value.list.s": true
- };
- /**
* Adds a value, given the attribute name and the host object. If the
* attribute already exists, but is not an array, it will convert it to an
* array of values.
@@ -3999,7 +4133,7 @@ var tf;
// We treat "node" specially since it is done so often.
var existingValue = obj[name];
if (existingValue == null) {
- obj[name] = path.join(".") in ARRAY_ATTRIBUTES ? [value] : value;
+ obj[name] = path.join(".") in repeatedFields ? [value] : value;
}
else if (Array.isArray(existingValue)) {
existingValue.push(value);
@@ -4032,21 +4166,9 @@ var tf;
break;
}
}).then(function () {
- return output["node"];
+ return output;
});
}
- parser.parsePbtxtFile = parsePbtxtFile;
- /**
- * Parses a proto txt file into a javascript object.
- *
- * @param input The string contents of the proto txt file.
- * @return The parsed object.
- */
- function parsePbtxt(input) {
- var blob = new Blob([input]);
- return parsePbtxtFile(blob);
- }
- parser.parsePbtxt = parsePbtxt;
})(parser = graph.parser || (graph.parser = {}));
})(graph = tf.graph || (tf.graph = {}));
})(tf || (tf = {})); // Close module tf.graph.parser.
@@ -4079,6 +4201,8 @@ var tf;
*/
var HierarchyImpl = (function () {
function HierarchyImpl() {
+ this.hasShapeInfo = false;
+ this.maxMetaEdgeSize = 1;
this.root = graph_1.createMetanode(graph_1.ROOT_NAME, { compound: true });
this.templates = null;
this.devices = null;
@@ -4415,6 +4539,40 @@ var tf;
}
hierarchy_1.build = build;
;
+ function joinAndAggregateStats(h, stats) {
+ // Get all the possible device names.
+ var deviceNames = {};
+ _.each(h.root.leaves(), function (nodeName) {
+ var leaf = h.node(nodeName);
+ if (leaf.device != null) {
+ deviceNames[leaf.device] = true;
+ }
+ });
+ h.devices = _.keys(deviceNames);
+ // Reset stats for each group node.
+ _.each(h.getNodeMap(), function (node, nodeName) {
+ if (node.isGroupNode) {
+ node.stats = new graph_1.NodeStats(0, 0, null);
+ node.deviceHistogram = {};
+ }
+ });
+ // Bubble-up the stats and device distribution from leaves to parents.
+ _.each(h.root.leaves(), function (nodeName) {
+ var leaf = h.node(nodeName);
+ var node = leaf;
+ while (node.parentNode != null) {
+ if (leaf.device != null) {
+ var deviceHistogram = node.parentNode.deviceHistogram;
+ deviceHistogram[leaf.device] = (deviceHistogram[leaf.device] || 0) + 1;
+ }
+ if (leaf.stats != null) {
+ node.parentNode.stats.combine(leaf.stats);
+ }
+ node = node.parentNode;
+ }
+ });
+ }
+ hierarchy_1.joinAndAggregateStats = joinAndAggregateStats;
/**
* Creates the metanodes in the hierarchical graph and assigns parent-child
* relationship between them.
@@ -4431,9 +4589,6 @@ var tf;
parent.depth = Math.max(parent.depth, path.length - i);
parent.cardinality += node.cardinality;
parent.opHistogram[node.op] = (parent.opHistogram[node.op] || 0) + 1;
- if (node.stats) {
- parent.stats.combine(node.stats);
- }
if (node.device != null) {
parent.deviceHistogram[node.device] =
(parent.deviceHistogram[node.device] || 0) + 1;
@@ -4593,9 +4748,6 @@ var tf;
}
child.parentNode = seriesNode;
seriesNames[n] = seriesName;
- if (child.stats) {
- seriesNode.stats.combine(child.stats);
- }
// Remove now-grouped node from its original parent's metagraph.
metagraph.removeNode(n);
});
@@ -4870,10 +5022,20 @@ var tf;
function RenderGraphInfo(hierarchy) {
this.hierarchy = hierarchy;
this.index = {};
+ this.computeScales();
+ // Maps node name to whether the rendering hierarchy was already
+ // constructed.
+ this.hasSubhierarchy = {};
+ this.root = new RenderGroupNodeInfo(hierarchy.root);
+ this.index[hierarchy.root.name] = this.root;
+ this.buildSubhierarchy(hierarchy.root.name);
+ this.root.expanded = true;
+ }
+ RenderGraphInfo.prototype.computeScales = function () {
this.deviceColorMap = d3.scale.ordinal()
- .domain(hierarchy.devices)
- .range(_.map(d3.range(hierarchy.devices.length), render.MetanodeColors.DEVICE_PALETTE));
- var topLevelGraph = hierarchy.root.metagraph;
+ .domain(this.hierarchy.devices)
+ .range(_.map(d3.range(this.hierarchy.devices.length), render.MetanodeColors.DEVICE_PALETTE));
+ var topLevelGraph = this.hierarchy.root.metagraph;
// Find the maximum and minimum memory usage.
var memoryExtent = d3.extent(topLevelGraph.nodes(), function (nodeName, index) {
var node = topLevelGraph.node(nodeName);
@@ -4896,14 +5058,12 @@ var tf;
this.computeTimeScale = d3.scale.linear()
.domain(computeTimeExtent)
.range(PARAMS.minMaxColors);
- // Maps node name to whether the rendering hierarchy was already
- // constructed.
- this.hasSubhierarchy = {};
- this.root = new RenderGroupNodeInfo(hierarchy.root);
- this.index[hierarchy.root.name] = this.root;
- this.buildSubhierarchy(hierarchy.root.name);
- this.root.expanded = true;
- }
+ this.edgeWidthScale = this.hierarchy.hasShapeInfo ?
+ graph_1.scene.edge.EDGE_WIDTH_SCALE :
+ d3.scale.linear()
+ .domain([1, this.hierarchy.maxMetaEdgeSize])
+ .range([graph_1.scene.edge.MIN_EDGE_WIDTH, graph_1.scene.edge.MAX_EDGE_WIDTH]);
+ };
/**
* Get a previously created RenderNodeInfo by its node name.
*/
@@ -6818,20 +6978,20 @@ var tf;
/** Delimiter between dimensions when showing sizes of tensors. */
var TENSOR_SHAPE_DELIM = "×";
/** The minimum stroke width of an edge. */
- var MIN_EDGE_WIDTH = 0.75;
+ edge.MIN_EDGE_WIDTH = 0.75;
/** The maximum stroke width of an edge. */
- var MAX_EDGE_WIDTH = 12;
+ edge.MAX_EDGE_WIDTH = 12;
/** The exponent used in the power scale for edge thickness. */
var EDGE_WIDTH_SCALE_EXPONENT = 0.3;
/** The domain (min and max value) for the edge width. */
var DOMAIN_EDGE_WIDTH_SCALE = [1, 5E6];
- var edgeWidthScale = d3.scale.pow()
+ edge.EDGE_WIDTH_SCALE = d3.scale.pow()
.exponent(EDGE_WIDTH_SCALE_EXPONENT)
.domain(DOMAIN_EDGE_WIDTH_SCALE)
- .range([MIN_EDGE_WIDTH, MAX_EDGE_WIDTH])
+ .range([edge.MIN_EDGE_WIDTH, edge.MAX_EDGE_WIDTH])
.clamp(true);
var arrowheadMap = d3.scale.quantize()
- .domain([MIN_EDGE_WIDTH, MAX_EDGE_WIDTH])
+ .domain([edge.MIN_EDGE_WIDTH, edge.MAX_EDGE_WIDTH])
.range(["small", "medium", "large", "xlarge"]);
/** Minimum stroke width to put edge labels in the middle of edges */
var CENTER_EDGE_LABEL_MIN_STROKE_WIDTH = 2.5;
@@ -7005,7 +7165,7 @@ var tf;
// Give the path a unique id, which will be used to link
// the textPath (edge label) to this path.
var pathId = "path_" + getEdgeKey(d);
- var strokeWidth = edgeWidthScale(size);
+ var strokeWidth = sceneElement.renderHierarchy.edgeWidthScale(size);
var path = edgeGroup.append("path")
.attr({
"id": pathId,
@@ -9614,12 +9774,14 @@ Polymer({
* UI controls.
*/
_colorByChanged: function() {
- // We iterate through each svg node and update its state.
- _.each(this._nodeGroupIndex, function(nodeGroup, nodeName) {
- this._updateNodeState(nodeName);
- }, this);
- // Notify also the minimap.
- this.minimap.update();
+ if (this.renderHierarchy != null) {
+ // We iterate through each svg node and update its state.
+ _.each(this._nodeGroupIndex, function(nodeGroup, nodeName) {
+ this._updateNodeState(nodeName);
+ }, this);
+ // Notify also the minimap.
+ this.minimap.update();
+ }
},
fit: function() {
tf.graph.scene.fit(this.$.svg, this.$.root, this._zoom, function() {
@@ -9779,7 +9941,7 @@ paper-button {
<div class="container">
<div class="vertical">
<h2>[[title]]</h2>
- <tf-graph-scene id="scene" class="auto" render-hierarchy="[[renderHierarchy]]" highlighted-node="[[_getVisible(highlightedNode)]]" selected-node="[[selectedNode]]" color-by="[[colorBy]]" name="[[graphName]]" progress="[[progress]]"></tf-graph-scene>
+ <tf-graph-scene id="scene" class="auto" render-hierarchy="[[renderHierarchy]]" highlighted-node="[[_getVisible(highlightedNode)]]" selected-node="[[selectedNode]]" color-by="[[colorBy]]" progress="[[progress]]"></tf-graph-scene>
</div>
</div>
</template>
@@ -9797,6 +9959,10 @@ Polymer({
observer: '_graphChanged'
},
basicGraph: Object,
+ stats: {
+ type: Object,
+ observer: '_statsChanged'
+ },
hierarchyParams: Object,
progress: {
type: Object,
@@ -9835,6 +10001,14 @@ Polymer({
observers: [
'_buildRenderHierarchy(graphHierarchy)'
],
+ _statsChanged: function(stats) {
+ if (stats != null) {
+ tf.graph.joinStatsInfoWithGraph(this.basicGraph, stats);
+ tf.graph.hierarchy.joinAndAggregateStats(this.graphHierarchy, stats);
+ // Recompute the rendering information.
+ this._buildRenderHierarchy(this.graphHierarchy);
+ }
+ },
_buildRenderHierarchy: function(graphHierarchy) {
tf.time('new tf.graph.render.Hierarchy', function() {
if (graphHierarchy.root.type !== tf.graph.NodeType.META) {
@@ -10936,7 +11110,7 @@ paper-progress {
</template>
<div class$="[[_getContainerClass(progress)]]">
<div id="main">
- <tf-graph id="graph" graph-hierarchy="{{graphHierarchy}}" basic-graph="[[graph]]" hierarchy-params="[[hierarchyParams]]" render-hierarchy="{{_renderHierarchy}}" selected-node="{{_selectedNode}}" highlighted-node="{{_highlightedNode}}" color-by="[[colorBy]]" color-by-params="{{colorByParams}}" graph-name="[[graphName]]" progress="{{progress}}"></tf-graph>
+ <tf-graph id="graph" graph-hierarchy="{{graphHierarchy}}" basic-graph="[[graph]]" hierarchy-params="[[hierarchyParams]]" render-hierarchy="{{_renderHierarchy}}" stats="[[stats]]" selected-node="{{_selectedNode}}" highlighted-node="{{_highlightedNode}}" color-by="[[colorBy]]" color-by-params="{{colorByParams}}" progress="{{progress}}"></tf-graph>
</div>
<div id="info">
<tf-graph-info id="graph-info" title="selected" graph-hierarchy="[[graphHierarchy]]" render-hierarchy="[[_renderHierarchy]]" graph="[[graph]]" selected-node="{{_selectedNode}}" selected-node-include="{{_selectedNodeInclude}}" highlighted-node="{{_highlightedNode}}" color-by="[[colorBy]]" color-by-params="[[colorByParams]]"></tf-graph-info>
@@ -10953,9 +11127,7 @@ Polymer({
// Public API.
graphHierarchy: Object,
graph: Object,
- graphName: String,
- // True if the graph data has also run-time stats.
- hasStats: Boolean,
+ stats: Object,
/**
* @type {value: number, msg: string}
*
@@ -11005,6 +11177,7 @@ Polymer({
}
});
</script>
+
<dom-module id="tf-graph-controls" assetpath="../tf-graph/">
<template>
<style>
@@ -11055,6 +11228,7 @@ table td {
}
.allcontrols {
+ width: 188px;
padding: 30px;
}
@@ -11065,6 +11239,7 @@ table td {
}
paper-radio-button {
+ display: block;
padding: 5px;
}
svg.icon {
@@ -11128,7 +11303,7 @@ svg.icon {
}
.color-text {
- padding: 0 0 0 55px;
+ padding: 0 0 0 49px;
}
.button-text {
@@ -11161,6 +11336,15 @@ svg.icon {
display: flex;
clear: both;
}
+
+.allcontrols .control-holder paper-radio-group {
+ margin-top: 5px;
+}
+
+span.counter {
+ font-size: 13px;
+ color: gray;
+}
</style>
<div class="allcontrols">
<div class="control-holder">
@@ -11178,7 +11362,7 @@ svg.icon {
</a>
</div>
<div class="control-holder">
- <div class="title">Run</div>
+ <div class="title">Run <span class="counter">([[datasets.length]])</span></div>
<paper-dropdown-menu no-label-float="" no-animations="" noink="" class="run-dropdown">
<paper-menu id="select" class="dropdown-content" selected="{{selectedDataset}}">
<template is="dom-repeat" items="[[datasets]]">
@@ -11188,6 +11372,17 @@ svg.icon {
</paper-dropdown-menu>
</div>
<div class="control-holder">
+ <div class="title">Session runs <span class="counter">([[_numSessionRuns(metadataTags)]])</span></div>
+ <paper-dropdown-menu no-label-float="" no-animations="" noink="" class="run-dropdown">
+ <paper-menu id="select" class="dropdown-content" selected="{{selectedMetadataTag}}">
+ <template is="dom-repeat" items="[[metadataTags]]">
+ <paper-item>[[item.tag]]</paper-item>
+ </template>
+ <paper-item>None</paper-item>
+ </paper-menu>
+ </paper-dropdown-menu>
+ </div>
+ <div class="control-holder">
<div class="title">Upload</div>
<paper-button raised="" class="text-button upload-button" on-click="_getFile">Choose File</paper-button>
<div class="hidden-input">
@@ -11196,27 +11391,25 @@ svg.icon {
</div>
<div class="control-holder">
<div class="title">Color</div>
- <paper-dropdown-menu no-label-float="" no-animations="" noink="" class="color-dropdown">
- <paper-menu class="dropdown-content" selected="{{_colorByIndex}}">
- <paper-item>Structure</paper-item>
- <paper-item>Device</paper-item>
- <template is="dom-if" if="[[hasStats]]">
- <paper-item>Compute time</paper-item>
- <paper-item>Memory</paper-item>
- </template>
- </paper-menu>
- </paper-dropdown-menu>
+ <paper-radio-group selected="{{colorBy}}">
+ <paper-radio-button name="structure">Structure</paper-radio-button>
+ <paper-radio-button name="device">Device</paper-radio-button>
+ <template is="dom-if" if="[[_statsNotNull(stats)]]">
+ <paper-radio-button name="compute_time">Compute time</paper-radio-button>
+ <paper-radio-button name="memory">Memory</paper-radio-button>
+ </template>
+ </paper-radio-group>
</div>
<div>
<template is="dom-if" if="[[_isGradientColoring(colorBy)]]">
- <svg width="160" height="20" style="margin: 0 5px" class="color-text">
+ <svg width="140" height="20" style="margin: 0 5px" class="color-text">
<defs>
<linearGradient id="linearGradient" x1="0%" y1="0%" x2="100%" y2="0%">
<stop class="start" offset="0%" stop-color$="[[_currentGradientParams.startColor]]"></stop>
<stop class="end" offset="100%" stop-color$="[[_currentGradientParams.endColor]]"></stop>
</linearGradient>
</defs>
- <rect x="0" y="0" width="160" height="20" fill="url(#linearGradient)" stroke="black"></rect>
+ <rect x="0" y="0" width="135" height="20" fill="url(#linearGradient)" stroke="black"></rect>
</svg>
<div class="domainValues color-text">
<div class="domainStart">[[_currentGradientParams.minValue]]</div>
@@ -11343,19 +11536,22 @@ Polymer({
is: 'tf-graph-controls',
properties: {
// Public API.
- hasStats: {
- type: Boolean
- },
+ stats: Object,
colorBy: {
type: String,
+ value: 'structure',
notify: true,
- computed: '_getColorBy(_colorByIndex)'
+ readonly: true
},
colorByParams: Object,
datasets: {
type: Array,
observer: '_datasetsChanged'
},
+ metadataTags: {
+ type: Array,
+ computed: '_getMetadataTags(selectedDataset, datasets)'
+ },
selectedDataset: {
type: Number,
notify: true,
@@ -11366,18 +11562,21 @@ Polymer({
type: Object,
notify: true
},
- // Private API.
- _colorByIndex: {
+ selectedMetadataTag: {
type: Number,
- value: 0 // Defaults to 'structure'.
+ notify: true,
+ value: -1
},
_currentGradientParams: {
type: Object,
computed: '_getCurrentGradientParams(colorByParams, colorBy)'
}
},
- _getColorBy: function(colorByIndex) {
- return ["structure", "device", "compute_time", "memory"][colorByIndex];
+ _statsNotNull: function(stats) {
+ return stats != null;
+ },
+ _numSessionRuns: function(metadataTags) {
+ return metadataTags != null ? metadataTags.length : 0;
},
_getBackgroundColor: function(color) {
return 'background-color:' + color;
@@ -11430,8 +11629,13 @@ Polymer({
this._setDownloadFilename(this.datasets[this.selectedDataset].path);
}
},
+ _getMetadataTags: function(selectedDataset, datasets) {
+ return this.datasets[selectedDataset].runMetadata;
+ },
_selectedDatasetChanged: function(newDataset, oldDataset) {
if (this.datasets) {
+ this.set('selectedMetadataTag', -1);
+ this.set('colorBy', 'structure');
this._setDownloadFilename(this.datasets[newDataset].path);
}
},
@@ -11500,11 +11704,11 @@ function convertToHumanReadable(value, units, unitIndex) {
<template is="dom-if" if="[[!_datasetsEmpty(_datasets)]]">
<tf-dashboard-layout>
<div class="sidebar">
- <tf-graph-controls id="controls" color-by-params="[[_colorByParams]]" has-stats="[[_hasStats]]" color-by="{{_colorBy}}" ,="" datasets="[[_datasets]]" selected-dataset="{{_selectedDataset}}" selected-file="{{_selectedFile}}"></tf-graph-controls>
- <tf-graph-loader id="loader" datasets="[[_datasets]]" ,="" selected-dataset="[[_selectedDataset]]" selected-file="[[_selectedFile]]" out-graph-hierarchy="{{_graphHierarchy}}" out-graph="{{_graph}}" out-graph-name="{{_graphName}}" has-stats="{{_hasStats}}" progress="{{_progress}}" out-hierarchy-params="{{_hierarchyParams}}"></tf-graph-loader>
+ <tf-graph-controls id="controls" color-by-params="[[_colorByParams]]" stats="[[_stats]]" color-by="{{_colorBy}}" ,="" datasets="[[_datasets]]" selected-dataset="{{_selectedDataset}}" selected-file="{{_selectedFile}}" selected-metadata-tag="{{_selectedMetadataTag}}"></tf-graph-controls>
+ <tf-graph-loader id="loader" datasets="[[_datasets]]" ,="" selected-dataset="[[_selectedDataset]]" selected-metadata-tag="[[_selectedMetadataTag]]" selected-file="[[_selectedFile]]" out-graph-hierarchy="{{_graphHierarchy}}" out-graph="{{_graph}}" out-stats="{{_stats}}" progress="{{_progress}}" out-hierarchy-params="{{_hierarchyParams}}"></tf-graph-loader>
</div>
<div class="center">
- <tf-graph-board id="graphboard" graph-hierarchy="[[_graphHierarchy]]" graph="[[_graph]]" has-stats="[[_hasStats]]" graph-name="[[_graphName]]" progress="[[_progress]]" color-by="[[_colorBy]]" color-by-params="{{_colorByParams}}" hierarchy-params="[[_hierarchyParams]]">
+ <tf-graph-board id="graphboard" graph-hierarchy="[[_graphHierarchy]]" graph="[[_graph]]" stats="[[_stats]]" progress="[[_progress]]" color-by="[[_colorBy]]" color-by-params="{{_colorByParams}}" hierarchy-params="[[_hierarchyParams]]">
</tf-graph-board>
</div>
</tf-dashboard-layout>
@@ -11529,28 +11733,31 @@ function convertToHumanReadable(value, units, unitIndex) {
Polymer({
is: 'tf-graph-dashboard',
properties: {
- _datasets: {
- type: Object,
- computed: '_getDatasets(runs.*, router)'
- },
+ _datasets: Object,
backend: {type: Object, observer: 'reload'},
router: {type: Object},
runs: Array,
},
reload: function() {
- var _this = this;
- this.backend.graphRuns().then(function(x) {
- _this.runs = x;
- });
- },
- _getDatasets: function(runs, router) {
- return _.map(this.runs, function(runName) {
- return {
- name: runName,
- path: router.graph(runName, tf.graph.LIMIT_ATTR_SIZE,
- tf.graph.LARGE_ATTRS_KEY)
- };
- });
+ Promise.all([this.backend.graphRuns(), this.backend.runMetadataRuns()])
+ .then(function(result) {
+ var runsWithGraph = result[0];
+ var runToMetadata = result[1];
+ var datasets = _.map(runsWithGraph, function(runName) {
+ return {
+ name: runName,
+ path: this.router.graph(runName, tf.graph.LIMIT_ATTR_SIZE,
+ tf.graph.LARGE_ATTRS_KEY),
+ runMetadata: _.map(runToMetadata[runName], function(tag) {
+ return {
+ tag: tag,
+ path: this.router.runMetadata(tag, runName)
+ };
+ }, this)
+ };
+ }, this);
+ this.set('_datasets', datasets);
+ }.bind(this));
},
_datasetsEmpty: function(datasets) {
return !datasets || !datasets.length;
@@ -11570,6 +11777,11 @@ Polymer({
<paper-tab data-mode="graphs">Graph</paper-tab>
<paper-tab data-mode="histograms">Histograms</paper-tab>
</paper-tabs>
+ <div class="global-actions">
+ <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">
@@ -11602,25 +11814,13 @@ Polymer({
-webkit-font-smoothing: antialiased;
}
- #toolbar-content {
- width: 100%;
- height: 100%;
- display: flex;
- flex-direction: row;
- justify-content: space-between;
- align-items: center;
- }
-
.toolbar-title {
font-size: 20px;
margin-left: 10px;
text-rendering: optimizeLegibility;
letter-spacing: -0.025em;
font-weight: 500;
- }
-
- #content {
- height: 100%;
+ width: 340px;
}
.tabs {
@@ -11633,6 +11833,29 @@ Polymer({
--paper-tabs-selection-bar-color: white;
}
+ .global-actions {
+ flex-grow: 2;
+ text-align: right;
+ color: white;
+ }
+
+ .global-actions a {
+ color: white;
+ }
+
+ #toolbar-content {
+ width: 100%;
+ height: 100%;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+ }
+
+ #content {
+ height: 100%;
+ }
+
</style>
</template>
<script>