Autogenerated Change: Release TensorBoard at TAG: 18

Change: 122095172
This commit is contained in:
Dan Mané 2016-05-11 13:41:07 -08:00 committed by TensorFlower Gardener
parent 136a89a7df
commit d269d53c05
4 changed files with 466 additions and 126 deletions

View File

@ -229,7 +229,7 @@ new_git_repository(
name = "neon_animation",
build_file = "bower.BUILD",
remote = "https://github.com/polymerelements/neon-animation.git",
tag = "v1.2.2",
tag = "v1.2.3",
)
new_git_repository(

View File

@ -1 +1 @@
18
19

View File

@ -44,8 +44,8 @@
"iron-behaviors": "PolymerElements/iron-behaviors#1.0.13",
"iron-checked-element-behavior": "PolymerElements/iron-checked-element-behavior#1.0.4",
"iron-collapse": "PolymerElements/iron-collapse#1.0.8",
"iron-dropdown": "PolymerElements/iron-dropdown#1.3.0",
"iron-fit-behavior": "PolymerElements/iron-fit-behavior#1.0.6",
"iron-dropdown": "PolymerElements/iron-dropdown#1.4.0",
"iron-fit-behavior": "PolymerElements/iron-fit-behavior#1.2.0",
"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",
@ -118,8 +118,8 @@
"iron-behaviors": "1.0.13",
"iron-checked-element-behavior": "1.0.4",
"iron-collapse": "1.0.8",
"iron-dropdown": "1.3.0",
"iron-fit-behavior": "1.0.6",
"iron-dropdown": "1.4.0",
"iron-fit-behavior": "1.2.0",
"iron-flex-layout": "1.3.0",
"iron-form-element-behavior": "1.0.6",
"iron-icon": "1.0.8",

View File

@ -196,7 +196,7 @@ var TF;
<template>
<div id="outer-container" class="scrollbar">
<template is="dom-repeat" items="[[names]]">
<div class="run-row" color-class$="[[_applyColorClass(item, classScale)]]">
<div class="run-row">
<div class="checkbox-container vertical-align-container">
<paper-checkbox class="checkbox vertical-align-center" name="[[item]]" checked$="[[_isChecked(item,outSelected.*)]]" on-change="_checkboxChange"></paper-checkbox>
</div>
@ -273,7 +273,10 @@ var TF;
return [];
},
},
classScale: Function, // map from run name to css class
colorScale: Object, // map from run name to css class
},
listeners: {
'dom-change': 'onDomChange',
},
observers: [
"_initializeOutSelected(names.*)",
@ -281,6 +284,18 @@ var TF;
_initializeOutSelected: function(change) {
this.outSelected = change.base.slice();
},
onDomChange: function(e) {
var checkboxes = Array.prototype.slice.call(this.querySelectorAll("paper-checkbox"));
var scale = this.colorScale;
checkboxes.forEach(function(p) {
var color = scale.scale(p.name);
p.customStyle['--paper-checkbox-checked-color'] = color;
p.customStyle['--paper-checkbox-checked-ink-color'] = color;
p.customStyle['--paper-checkbox-unchecked-color'] = color;
p.customStyle['--paper-checkbox-unchecked-ink-color'] = color;
});
this.updateStyles();
},
_checkboxChange: function(e) {
var name = e.srcElement.name;
var idx = this.outSelected.indexOf(name);
@ -318,7 +333,7 @@ var TF;
Runs
</h3>
</div>
<tf-multi-checkbox names="[[runs]]" out-selected="{{outSelected}}" class-scale="[[classScale]]"></tf-multi-checkbox>
<tf-multi-checkbox id="multiCheckbox" names="[[runs]]" out-selected="{{outSelected}}" color-scale="[[colorScale]]"></tf-multi-checkbox>
<paper-button class="x-button" id="toggle-all" on-tap="_toggleAll">
Toggle All Runs
</paper-button>
@ -368,7 +383,7 @@ var TF;
outSelected: {type: Array, notify: true},
// runs: an array of strings, representing the run names that may be chosen
runs: Array,
classScale: Object, // map from run name to color class (css)
colorScale: Object, // TF.ColorScale
},
_toggleAll: function() {
if (this.outSelected.length > 0) {
@ -452,58 +467,210 @@ var TF;
</script>
</dom-module>
<dom-module id="tf-color-scale" assetpath="../tf-event-dashboard/">
<dom-module id="tf-color-scale" assetpath="../tf-color-scale/">
<script>var TF;
(function (TF) {
TF.palettes = {
googleStandard: [
'#db4437',
'#ff7043',
'#f4b400',
'#0f9d58',
'#00796b',
'#00acc1',
'#4285f4',
'#5c6bc0',
'#ab47bc' // purple 400
],
googleCool: [
'#9e9d24',
'#0f9d58',
'#00796b',
'#00acc1',
'#4285f4',
'#5c6bc0',
'#607d8b' // blue gray 500
],
googleWarm: [
'#795548',
'#ab47bc',
'#f06292',
'#c2185b',
'#db4437',
'#ff7043',
'#f4b400' // google yellow 700
],
googleColorBlind: [
'#c53929',
'#ff7043',
'#f7cb4d',
'#0b8043',
'#80deea',
'#4285f4',
'#5e35b1' // deep purple 600
],
// This rainbow palette attempts to keep a constant brightness across hues.
constantValue: [
'#f44336', '#ffa216', '#c2d22d', '#51b455', '#1ca091', '#505ec4',
'#a633ba'
]
};
})(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.
==============================================================================*/
// Each color scale is initialized with a configurable number of base hues.
// There are also several palettes available.
// TF.palettes.googleStandard, TF.palettes.googleColorBlind,
// TF.palettes.googleCool, TF.palettes.googleWarm, TF.palettes.constantValue
// Each string is hashed to an integer,
// then mapped to one of the base hues above.
// If there is a collision, the color that is later in an alphabetical sort
// gets nudged a little darker or lighter to disambiguate.
// I would call it mostly stable, in that the same array of strings will
// always return the same colors, but the same individual string may
// shift a little depending on its peers.
//
// runs = ["train", "test", "test1", "test2"]
// ccs = new TF.ColorScale(12, "googleStandard");
// ccs.domain(runs);
// ccs.getColor("train");
// ccs.getColor("test1");
var TF;
(function (TF) {
var ColorScale = (function () {
/**
* The palette you provide defines your spectrum. The colorscale will
* always use the full spectrum you provide. When you define "numColors"
* it resamples at regular intervals along the full extent of the spectrum.
* Thus you get the maximum distance between hues for the "numColors"
* given. This allows the programmer to tweak the algorithm depending on
* how big your expected domain is. If you generally think you're going to
* have a small number of elements in the domain, then a small numColors
* will be serviceable. With large domains, a small numColors would produce
* too many hash collisions, so you'd want to bump it up to the threshold
* of human perception (probably around 14 or 18).
*
* @param {number} [numColors=12] - The number of base colors you want
* in the palette. The more colors, the smaller the number
* the more hash collisions you will have, but the more
* differentiable the base colors will be.
*
* @param {string[]} [palette=TF.palettes.googleColorBlind] - The color
* palette you want as an Array of hex strings. Note, the
* length of the array in this palette is independent of the
* param numColors above. The scale will interpolate to
* create the proper "numColors" given in the first param.
*
*/
function ColorScale(numColors, palette) {
if (numColors === void 0) { numColors = 12; }
if (palette === void 0) { palette = TF.palettes.googleColorBlind; }
this.numColors = numColors;
this.domain([]);
if (palette.length < 2) {
throw new Error('Not enough colors in palette. Must be more than one.');
}
var k = (this.numColors - 1) / (palette.length - 1);
this.internalColorScale =
d3.scale.linear()
.domain(d3.range(palette.length).map(function (i) { return i * k; }))
.range(palette);
}
ColorScale.prototype.hash = function (s) {
function h(hash, str) {
hash = (hash << 5) - hash + str.charCodeAt(0);
return hash & hash;
}
return Math.abs(Array.prototype.reduce.call(s, h, 0)) % this.numColors;
};
/**
* Set the domain of strings so we can calculate collisions preemptively.
* Can be reset at any point.
*
* @param {string[]} strings - An array of strings to use as the domain
* for your scale.
*/
ColorScale.prototype.domain = function (strings) {
var _this = this;
this.buckets = d3.range(this.numColors).map(function () { return []; });
var sortedUniqueKeys = d3.set(strings).values().sort(function (a, b) {
return a.localeCompare(b);
});
sortedUniqueKeys.forEach(function (s) { return _this.addToDomain(s); });
return this;
};
ColorScale.prototype.getBucketForString = function (s) {
var bucketIdx = this.hash(s);
return this.buckets[bucketIdx];
};
ColorScale.prototype.addToDomain = function (s) {
var bucketIdx = this.hash(s);
var bucket = this.buckets[bucketIdx];
if (bucket.indexOf(s) === -1) {
bucket.push(s);
}
};
ColorScale.prototype.nudge = function (color, amount) {
// If amount is zero, just give back same color
if (amount === 0) {
return color;
}
else if (amount === 1) {
return d3.hcl(color).brighter(0.6);
}
else {
return d3.hcl(color).darker((amount - 1) / 2);
}
};
/**
* Use the color scale to transform an element in the domain into a color.
* If there was a hash conflict, the color will be "nudged" darker or
* lighter so that it is unique.
* @param {string} The input string to map to a color.
* @return {string} The color corresponding to that input string.
* @throws Will error if input string is not in the scale's domain.
*/
ColorScale.prototype.scale = function (s) {
var bucket = this.getBucketForString(s);
var idx = bucket.indexOf(s);
if (idx === -1) {
throw new Error('String was not in the domain.');
}
var color = this.internalColorScale(this.hash(s));
return this.nudge(color, idx).toString();
};
return ColorScale;
}());
TF.ColorScale = ColorScale;
})(TF || (TF = {}));
</script>
<script>
(function() {
// TODO(danmane) - get Plottable team to make an API point for this
Plottable.Scales.Color._LOOP_LIGHTEN_FACTOR = 0;
var classColorPairs = [
["light-blue", "#03A9F4"],
["red" , "#f44366"],
["green" , "#4CAF50"],
["purple" , "#9c27b0"],
["teal" , "#009688"],
["pink" , "#e91e63"],
["orange" , "#ff9800"],
["brown" , "#795548"],
["indigo" , "#3f51b5"],
];
var classes = _.pluck(classColorPairs, 0);
var colors = _.pluck(classColorPairs, 1);
Polymer({
is: "tf-color-scale",
properties: {
runs: Array,
outClassScale: {
type: Object,
notify: true,
readOnly: true,
value: function() {
return new d3.scale.ordinal().range(classes);
},
// TODO(danmane): the class scale will not update if the domain changes.
// this behavior is inconsistent with the ColorScale.
// in practice we don't change runs after initial load so it's not currently an issue
},
outColorScale: {
type: Object,
computed: "makeColorScale(runs.*)",
notify: true,
readOnly: true,
value: function() {
var scale = new Plottable.Scales.Color().range(colors);
scale.onUpdate(this._notifyColorScaleDomainChange.bind(this));
return scale;
},
},
},
observers: ["_changeRuns(runs.*)"],
_changeRuns: function(runs) {
this.outClassScale.domain(this.runs);
this.outColorScale.domain(this.runs);
},
_notifyColorScaleDomainChange: function() {
this.notifyPath("outColorScale.domain_path", this.outColorScale.domain());
this.outColorScale.domain_path = null;
makeColorScale: function(runs) {
return new TF.ColorScale().domain(this.runs);
},
});
})();
@ -1073,12 +1240,12 @@ var TF;
var Y_AXIS_FORMATTER_PRECISION = 3;
var TOOLTIP_Y_PIXEL_OFFSET = 15;
var TOOLTIP_X_PIXEL_OFFSET = 0;
var TOOLTIP_CIRCLE_SIZE = 3;
var TOOLTIP_CIRCLE_SIZE = 4;
var TOOLTIP_CLOSEST_CIRCLE_SIZE = 6;
var BaseChart = (function () {
function BaseChart(tag, dataFn, xType, colorScale, tooltip) {
this.dataFn = dataFn;
this.datasets = {};
this.run2datasets = {};
this.tag = tag;
this.colorScale = colorScale;
this.tooltip = tooltip;
@ -1107,11 +1274,11 @@ var TF;
});
};
BaseChart.prototype.getDataset = function (run) {
if (this.datasets[run] === undefined) {
this.datasets[run] =
if (this.run2datasets[run] === undefined) {
this.run2datasets[run] =
new Plottable.Dataset([], { run: run, tag: this.tag });
}
return this.datasets[run];
return this.run2datasets[run];
};
BaseChart.prototype.buildChart = function (xType) {
if (this.outer) {
@ -1156,20 +1323,58 @@ var TF;
TF.BaseChart = BaseChart;
var LineChart = (function (_super) {
__extends(LineChart, _super);
function LineChart() {
_super.apply(this, arguments);
function LineChart(tag, dataFn, xType, colorScale, tooltip) {
this.datasets = [];
// lastPointDataset is a dataset that contains just the last point of
// every dataset we're currently drawing.
this.lastPointsDataset = new Plottable.Dataset();
// need to do a single bind, so we can deregister the callback from
// old Plottable.Datasets. (Deregistration is done by identity checks.)
this.updateLastPointDataset = this._updateLastPointDataset.bind(this);
_super.call(this, tag, dataFn, xType, colorScale, tooltip);
}
LineChart.prototype.buildPlot = function (xAccessor, xScale, yScale) {
var _this = this;
this.yAccessor = function (d) { return d.scalar; };
var plot = new Plottable.Plots.Line();
plot.x(xAccessor, xScale);
plot.y(this.yAccessor, yScale);
plot.attr('stroke', function (d, i, dataset) {
return dataset.metadata().run;
}, this.colorScale);
this.plot = plot;
var group = this.setupTooltips(plot);
return group;
var linePlot = new Plottable.Plots.Line();
linePlot.x(xAccessor, xScale);
linePlot.y(this.yAccessor, yScale);
linePlot.attr('stroke', function (d, i, dataset) {
return _this.colorScale.scale(dataset.metadata().run);
});
this.linePlot = linePlot;
var group = this.setupTooltips(linePlot);
// The scatterPlot will display the last point for each dataset.
// This way, if there is only one datum for the series, it is still
// visible. We hide it when tooltips are active to keep things clean.
var scatterPlot = new Plottable.Plots.Scatter();
scatterPlot.x(xAccessor, xScale);
scatterPlot.y(this.yAccessor, yScale);
scatterPlot.attr('fill', function (d) { return _this.colorScale.scale(d.run); });
scatterPlot.attr('opacity', 1);
scatterPlot.size(TOOLTIP_CIRCLE_SIZE * 2);
scatterPlot.datasets([this.lastPointsDataset]);
this.scatterPlot = scatterPlot;
return new Plottable.Components.Group([scatterPlot, group]);
};
/** Iterates over every dataset, takes the last point, and puts all these
* points in the lastPointsDataset.
*/
LineChart.prototype._updateLastPointDataset = function () {
var relativeAccessor = relativeX().accessor;
var data = this.datasets
.map(function (d) {
var datum = null;
if (d.data().length > 0) {
var idx = d.data().length - 1;
datum = d.data()[idx];
datum.run = d.metadata().run;
datum.relative = relativeAccessor(datum, idx, d);
}
return datum;
})
.filter(function (x) { return x != null; });
this.lastPointsDataset.data(data);
};
LineChart.prototype.setupTooltips = function (plot) {
var _this = this;
@ -1181,6 +1386,7 @@ var TF;
var group = new Plottable.Components.Group([plot, pointsComponent]);
var hideTooltips = function () {
_this.tooltip.style('opacity', 0);
_this.scatterPlot.attr('opacity', 1);
pointsComponent.content().selectAll('.point').remove();
};
var enabled = true;
@ -1233,6 +1439,7 @@ var TF;
LineChart.prototype.drawTooltips = function (closestPoint) {
var _this = this;
// Formatters for value, step, and wall_time
this.scatterPlot.attr('opacity', 0);
var valueFormatter = multiscaleFormatter(Y_TOOLTIP_FORMATTER_PRECISION);
var stepFormatter = stepX().tooltipFormatter;
var wall_timeFormatter = wallX().tooltipFormatter;
@ -1282,9 +1489,11 @@ 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); });
datasets.reverse(); // draw first run on top
this.plot.datasets(datasets);
runs.reverse(); // draw first run on top
this.datasets.forEach(function (d) { return d.offUpdate(_this.updateLastPointDataset); });
this.datasets = runs.map(function (r) { return _this.getDataset(r); });
this.datasets.forEach(function (d) { return d.onUpdate(_this.updateLastPointDataset); });
this.linePlot.datasets(this.datasets);
};
return LineChart;
}(BaseChart));
@ -1316,11 +1525,11 @@ var TF;
p.y(y, yScale);
p.y0(y0);
p.attr('fill', function (d, i, dataset) {
return dataset.metadata().run;
}, _this.colorScale);
return _this.colorScale.scale(dataset.metadata().run);
});
p.attr('stroke', function (d, i, dataset) {
return dataset.metadata().run;
}, _this.colorScale);
return _this.colorScale.scale(dataset.metadata().run);
});
p.attr('stroke-weight', function (d, i, m) { return '0.5px'; });
p.attr('stroke-opacity', function () { return opacities[i]; });
p.attr('fill-opacity', function () { return opacities[i]; });
@ -1329,7 +1538,7 @@ var TF;
var medianPlot = new Plottable.Plots.Line();
medianPlot.x(xAccessor, xScale);
medianPlot.y(medianAccessor, yScale);
medianPlot.attr('stroke', function (d, i, m) { return m.run; }, this.colorScale);
medianPlot.attr('stroke', function (d, i, m) { return _this.colorScale.scale(m.run); });
this.plots = plots;
return new Plottable.Components.Group(plots);
};
@ -1403,11 +1612,15 @@ var TF;
scale: scale,
axis: new Plottable.Axes.Numeric(scale, 'bottom'),
accessor: function (d, index, dataset) {
// We may be rendering the final-point datum for scatterplot.
// If so, we will have already provided the 'relative' property
if (d.relative != null) {
return d.relative;
}
var data = dataset.data();
// I can't imagine how this function would be called when the data
// is empty
// (after all, it iterates over the data), but lets guard just to be
// safe.
// I can't imagine how this function would be called when the data is
// empty (after all, it iterates over the data), but lets guard just
// to be safe.
var first = data.length > 0 ? +data[0].wall_time : 0;
return (+d.wall_time - first) / (60 * 60 * 1000); // ms to hours
},
@ -2633,7 +2846,7 @@ var TF;
<dom-module id="tf-event-dashboard" assetpath="../tf-event-dashboard/">
<template>
<div id="plumbing">
<tf-color-scale id="colorScale" runs="[[runs]]" out-color-scale="{{colorScale}}" out-class-scale="{{classScale}}"></tf-color-scale>
<tf-color-scale id="colorScale" runs="[[runs]]" out-color-scale="{{colorScale}}"></tf-color-scale>
</div>
<tf-dashboard-layout>
@ -2646,7 +2859,7 @@ var TF;
<tf-x-type-selector id="xTypeSelector" out-x-type="{{xType}}"></tf-x-type-selector>
</div>
<div class="sidebar-section">
<tf-run-selector id="runSelector" runs="[[runs]]" class-scale="[[classScale]]" out-selected="{{selectedRuns}}"></tf-run-selector>
<tf-run-selector id="runSelector" runs="[[runs]]" color-scale="[[colorScale]]" out-selected="{{selectedRuns}}"></tf-run-selector>
</div>
</div>
<div class="center">
@ -2698,6 +2911,10 @@ var TF;
computed: "_getVisibleTags(selectedRuns.*, run2tag.*)"
},
_show_download_links: Boolean,
colorScale: {
type: Object,
notify: true,
},
},
attached: function() {
this.async(function() {
@ -2742,7 +2959,7 @@ var TF;
<dom-module id="tf-histogram-dashboard" assetpath="../tf-histogram-dashboard/">
<template>
<div id="plumbing">
<tf-color-scale id="colorScale" runs="[[runs]]" out-color-scale="{{colorScale}}" out-class-scale="{{classScale}}"></tf-color-scale>
<tf-color-scale id="colorScale" runs="[[runs]]" out-color-scale="{{colorScale}}"></tf-color-scale>
</div>
<tf-dashboard-layout>
@ -2754,7 +2971,7 @@ var TF;
<tf-x-type-selector id="xTypeSelector" out-x-type="{{xType}}"></tf-x-type-selector>
</div>
<div class="sidebar-section">
<tf-run-selector id="runSelector" runs="[[runs]]" class-scale="[[classScale]]" out-selected="{{selectedRuns}}"></tf-run-selector>
<tf-run-selector id="runSelector" runs="[[runs]]" color-scale="[[colorScale]]" out-selected="{{selectedRuns}}"></tf-run-selector>
</div>
</div>
@ -6028,6 +6245,21 @@ var tf;
})(graph = tf.graph || (tf.graph = {}));
})(tf || (tf = {})); // Close module tf.graph.parser.
</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.
==============================================================================*/
</script>
<script>var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
@ -9241,6 +9473,38 @@ var tf;
return querySelector.replace(/([:.\[\],/\\\(\)])/g, '\\$1');
}
util.escapeQuerySelector = escapeQuerySelector;
// For unit conversion.
util.MEMORY_UNITS = [
// Atomic unit.
{ symbol: 'B' },
// numUnits specifies how many previous units this unit contains.
{ symbol: 'KB', numUnits: 1024 }, { symbol: 'MB', numUnits: 1024 },
{ symbol: 'GB', numUnits: 1024 }, { symbol: 'TB', numUnits: 1024 },
{ symbol: 'PB', numUnits: 1024 }
];
util.TIME_UNITS = [
// Atomic unit. Finest granularity in TensorFlow stat collection.
{ symbol: 'µs' },
// numUnits specifies how many previous units this unit contains.
{ symbol: 'ms', numUnits: 1000 }, { symbol: 's', numUnits: 1000 },
{ symbol: 'min', numUnits: 60 }, { symbol: 'hr', numUnits: 60 },
{ symbol: 'days', numUnits: 24 }
];
/**
* Returns the human readable version of the unit.
* (e.g. 1.35 GB, 23 MB, 34 ms, 6.53 min etc).
*/
function convertUnitsToHumanReadable(value, units, unitIndex) {
unitIndex = unitIndex == null ? 0 : unitIndex;
if (unitIndex + 1 < units.length &&
value >= units[unitIndex + 1].numUnits) {
return tf.graph.util.convertUnitsToHumanReadable(value / units[unitIndex + 1].numUnits, units, unitIndex + 1);
}
// toPrecision() has the tendency to return a number in scientific
// notation and (number - 0) brings it back to normal notation.
return (value.toPrecision(3) - 0) + ' ' + units[unitIndex].symbol;
}
util.convertUnitsToHumanReadable = convertUnitsToHumanReadable;
})(util = graph.util || (graph.util = {}));
})(graph = tf.graph || (tf.graph = {}));
})(tf || (tf = {}));
@ -10981,9 +11245,10 @@ Polymer({
<dom-module id="tf-node-info" assetpath="../tf-graph-info/">
<style>
.sub-list-group {
padding: 8px 12px 0px;
font-weight: 500;
font-size: 12pt;
padding-bottom: 8px;
width: 100%;
}
.sub-list {
@ -11007,6 +11272,24 @@ Polymer({
font-weight: 400;
}
.sub-list-table {
display: table;
width: 100%;
}
.sub-list-table-row {
display: table-row;
}
.sub-list-table-cell {
color: #565656;
display: table-cell;
font-size: 11pt;
font-weight: 400;
max-width: 200px;
padding: 0 4px;
}
paper-item {
padding: 0;
background: #e9e9e9;
@ -11018,7 +11301,7 @@ Polymer({
}
.expandedInfo {
padding: 0 0 8px;
padding: 8px 12px;
}
.controlDeps {
@ -11188,6 +11471,31 @@ Polymer({
</div>
</template>
</div>
<template is="dom-if" if="{{_hasDisplayableNodeStats}}">
<div class="sub-list-group node-stats">
Node Stats
<div class="sub-list-table">
<template is="dom-if" if="{{_nodeStats.totalBytes}}">
<div class="sub-list-table-row">
<div class="sub-list-table-cell">Memory</div>
<div class="sub-list-table-cell">[[_nodeStatsFormattedBytes]]</div>
</div>
</template>
<template is="dom-if" if="{{_nodeStats.totalMicros}}">
<div class="sub-list-table-row">
<div class="sub-list-table-cell">Compute Time</div>
<div class="sub-list-table-cell">[[_nodeStatsFormattedComputeTime]]</div>
</div>
</template>
<template is="dom-if" if="{{_nodeStats.outputSize}}">
<div class="sub-list-table-row">
<div class="sub-list-table-cell">Tensor Output Size</div>
<div class="sub-list-table-cell">[[_nodeStatsFormattedOutputSize]]</div>
</div>
</template>
</div>
</div>
</template>
<div class="toggle-include-group">
<paper-button raised="" class="toggle-include" on-click="_toggleInclude">
<span>[[_auxButtonText]]</span>
@ -11225,6 +11533,27 @@ Polymer({
computed: '_getNode(nodeName, graphHierarchy)',
observer: '_resetState'
},
_nodeStats: {
type: Object,
computed: '_getNodeStats(nodeName, graphHierarchy)',
observer: '_resetState'
},
_hasDisplayableNodeStats: {
type: Object,
computed: '_getHasDisplayableNodeStats(_nodeStats)',
},
_nodeStatsFormattedBytes: {
type: String,
computed: '_getNodeStatsFormattedBytes(_nodeStats)',
},
_nodeStatsFormattedComputeTime: {
type: String,
computed: '_getNodeStatsFormattedComputeTime(_nodeStats)',
},
_nodeStatsFormattedOutputSize: {
type: String,
computed: '_getNodeStatsFormattedOutputSize(_nodeStats)',
},
// The enum value of the include property of the selected node.
nodeInclude: {
type: Number,
@ -11282,6 +11611,50 @@ Polymer({
_getNode: function(nodeName, graphHierarchy) {
return graphHierarchy.node(nodeName);
},
_getNodeStats: function(nodeName, graphHierarchy) {
var node = this._getNode(nodeName, graphHierarchy);
if (node) {
return node.stats;
}
return null;
},
_getHasDisplayableNodeStats: function(stats) {
if (stats &&
(stats.totalBytes > 0 ||
stats.totalBytes > 0 ||
stats.outputSize)) {
return true;
}
return false;
},
_getNodeStatsFormattedBytes(stats) {
if (!stats || !stats.totalBytes) {
return;
}
return tf.graph.util.convertUnitsToHumanReadable(
stats.totalBytes, tf.graph.util.MEMORY_UNITS);
},
_getNodeStatsFormattedComputeTime(stats) {
if (!stats || !stats.totalMicros) {
return;
}
return tf.graph.util.convertUnitsToHumanReadable(
stats.totalMicros, tf.graph.util.TIME_UNITS);
},
_getNodeStatsFormattedOutputSize(stats) {
if (!stats || !stats.outputSize || !stats.outputSize.length) {
return;
}
// TODO(nsthorat): Display more than just the first tensor shape.
if (stats.outputSize[0].length === 0) {
return "scalar";
}
return "[" + stats.outputSize[0].join(", ") + "]";
},
_getPrintableHTMLNodeName: function(nodeName) {
// Insert an optional line break before each slash so that
// long node names wrap cleanly at path boundaries.
@ -12097,11 +12470,15 @@ Polymer({
var minValue = params.minValue;
var maxValue = params.maxValue;
if (colorBy === 'memory') {
minValue = convertToHumanReadable(minValue, MEMORY_UNITS);
maxValue = convertToHumanReadable(maxValue, MEMORY_UNITS);
minValue = tf.graph.util.convertUnitsToHumanReadable(
minValue, tf.graph.util.MEMORY_UNITS);
maxValue = tf.graph.util.convertUnitsToHumanReadable(
maxValue, tf.graph.util.MEMORY_UNITS);
} else if (colorBy === 'compute_time') {
minValue = convertToHumanReadable(minValue, TIME_UNITS);
maxValue = convertToHumanReadable(maxValue, TIME_UNITS);
minValue = tf.graph.util.convertUnitsToHumanReadable(
minValue, tf.graph.util.TIME_UNITS);
maxValue = tf.graph.util.convertUnitsToHumanReadable(
maxValue, tf.graph.util.TIME_UNITS);
}
return {
minValue: minValue,
@ -12155,43 +12532,6 @@ Polymer({
this.$.graphdownload.setAttribute('download', graphPath + '.png');
}
});
// Private methods.
var MEMORY_UNITS = [
// Atomic unit.
{symbol: 'B'},
// numUnits specifies how many previous units this unit contains.
{symbol: 'KB', numUnits: 1024},
{symbol: 'MB', numUnits: 1024},
{symbol: 'GB', numUnits: 1024},
{symbol: 'TB', numUnits: 1024},
{symbol: 'PB', numUnits: 1024}
];
var TIME_UNITS = [
// Atomic unit. Finest granularity in TensorFlow stat collection.
{symbol: 'µs'},
// numUnits specifies how many previous units this unit contains.
{symbol: 'ms', numUnits: 1000},
{symbol: 's', numUnits: 1000},
{symbol: 'min', numUnits: 60},
{symbol: 'hr', numUnits: 60},
{symbol: 'days', numUnits: 24}
];
/**
* Returns the human readable version of the unit.
* (e.g. 1.35 GB, 23 MB, 34 ms, 6.53 min etc).
*/
function convertToHumanReadable(value, units, unitIndex) {
unitIndex = unitIndex == null ? 0 : unitIndex;
if (unitIndex + 1 < units.length && value >= units[unitIndex + 1].numUnits) {
return convertToHumanReadable(value / units[unitIndex + 1].numUnits,
units, unitIndex + 1);
}
// toPrecision() has the tendency to return a number in scientific
// notation and (number - 0) brings it back to normal notation.
return (value.toPrecision(3) - 0) + ' ' + units[unitIndex].symbol;
}
})(); // Closing private scope.
</script>
</dom-module>