Update the TensorBoard backend to use a new binning algorithm.

Update the TensorBoard backend size guidelines for the new histograms.
Change: 129806300
This commit is contained in:
Renato Utsch 2016-08-09 15:14:03 -08:00 committed by TensorFlower Gardener
parent cf6857d476
commit bfcbc262af
5 changed files with 91 additions and 47 deletions

View File

@ -41,7 +41,7 @@ TENSORBOARD_SIZE_GUIDANCE = {
event_accumulator.IMAGES: 4, event_accumulator.IMAGES: 4,
event_accumulator.AUDIO: 4, event_accumulator.AUDIO: 4,
event_accumulator.SCALARS: 1000, event_accumulator.SCALARS: 1000,
event_accumulator.HISTOGRAMS: 1, event_accumulator.HISTOGRAMS: 50,
} }

View File

@ -182,11 +182,16 @@ module TF.Backend {
let url = this.router.histograms(tag, run); let url = this.router.histograms(tag, run);
p = this.requestManager.request(url); p = this.requestManager.request(url);
return p.then(map(detupler(createHistogram))).then(function(histos) { return p.then(map(detupler(createHistogram))).then(function(histos) {
// Get the minimum and maximum values across all histograms so that the
// visualization is aligned for all timesteps.
let min = d3.min(histos, d => d.min);
let max = d3.max(histos, d => d.max);
return histos.map(function(histo, i) { return histos.map(function(histo, i) {
return { return {
wall_time: histo.wall_time, wall_time: histo.wall_time,
step: histo.step, step: histo.step,
bins: convertBins(histo) bins: convertBins(histo, min, max)
}; };
}); });
}); });
@ -367,34 +372,59 @@ module TF.Backend {
* Takes histogram data as stored by tensorboard backend and converts it to * 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 * the standard d3 histogram data format to make it more compatible and easier
* to visualize. When visualizing histograms, having the left edge and width * to visualize. When visualizing histograms, having the left edge and width
* makes things quite a bit easier. * makes things quite a bit easier. The bins are also converted to have an
* uniform width, what makes the visualization easier to understand.
* *
* @param histogram A histogram from tensorboard backend. * @param histogram A histogram from tensorboard backend.
* @param min The leftmost edge. The binning will start on it.
* @param max The rightmost edge. The binning will end on it.
* @param numBins The number of bins of the converted data. The default of 30
* is a sensible default, using more starts to get artifacts because the event
* data is stored in buckets, and you start being able to see the aliased
* borders between each bucket.
* @return A histogram bin. Each bin has an x (left edge), a dx (width), * @return A histogram bin. Each bin has an x (left edge), a dx (width),
* and a y (count). * and a y (count).
* *
* If given rightedges are inclusive, then these left edges (x) are exclusive. * If given rightedges are inclusive, then these left edges (x) are exclusive.
*/ */
export function convertBins(histogram: Histogram) { export function convertBins(
histogram: Histogram, min: number, max: number, numBins = 30) {
if (histogram.bucketRightEdges.length !== histogram.bucketCounts.length) { 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; let binWidth = (max - min) / numBins;
return histogram.bucketRightEdges.map(function( let bucketLeft = min; // Use the min as the starting point for the bins.
rightEdge: number, i: number) { let bucketPos = 0;
return d3.range(min, max, binWidth).map(function(binLeft) {
let binRight = binLeft + binWidth;
// Use the previous bin's rightEdge as the new leftEdge // Take the count of each existing bucket, multiply it by the proportion
var left = previousRightEdge; // of overlap with the new bin, then sum and store as the count for the
// new bin. If no overlap, will add to zero, if 100% overlap, will include
// the full count into new bin.
let binY = 0;
while (bucketPos < histogram.bucketRightEdges.length) {
// Clip the right edge because right-most edge can be infinite-sized.
let bucketRight = Math.min(max, histogram.bucketRightEdges[bucketPos]);
// We need to clip the rightEdge because right-most edge can be let intersect =
// infinite-sized Math.min(bucketRight, binRight) - Math.max(bucketLeft, binLeft);
var right = Math.min(histogram.max, rightEdge); let count = (intersect / (bucketRight - bucketLeft)) *
histogram.bucketCounts[bucketPos];
// Store rightEdgeValue for next iteration binY += intersect > 0 ? count : 0;
previousRightEdge = rightEdge;
return {x: left, dx: right - left, y: histogram.bucketCounts[i]}; // If bucketRight is bigger than binRight, than this bin is finished and
// there is data for the next bin, so don't increment bucketPos.
if (bucketRight > binRight) {
break;
}
bucketLeft = Math.max(min, bucketRight);
bucketPos++;
};
return {x: binLeft, dx: binWidth, y: binY};
}); });
} }

View File

@ -191,13 +191,16 @@ module TF.Backend {
it('Throws and error if the inputs are of different lengths', function() { it('Throws and error if the inputs are of different lengths', function() {
assert.throws(function() { assert.throws(function() {
convertBins( convertBins(
{bucketRightEdges: [0], bucketCounts: [1, 2], min: 1, max: 2}); {bucketRightEdges: [0], bucketCounts: [1, 2], min: 1, max: 2}, 1, 2,
2);
}, 'Edges and counts are of different lengths.'); }, 'Edges and counts are of different lengths.');
}); });
it('Handles data with no bins', function() { it('Handles data with no bins', function() {
assert.deepEqual( assert.deepEqual(
convertBins({bucketRightEdges: [], bucketCounts: [], min: 0, max: 0}), convertBins(
{bucketRightEdges: [], bucketCounts: [], min: 0, max: 0}, 0, 0,
0),
[]); []);
}); });
@ -205,12 +208,14 @@ module TF.Backend {
let counts = [1]; let counts = [1];
let rightEdges = [1.21e-12]; let rightEdges = [1.21e-12];
let histogram = [{x: 1.1e-12, dx: 1.21e-12 - 1.1e-12, y: 1}]; let histogram = [{x: 1.1e-12, dx: 1.21e-12 - 1.1e-12, y: 1}];
let newHistogram = convertBins({ let newHistogram = convertBins(
bucketRightEdges: rightEdges, {
bucketCounts: counts, bucketRightEdges: rightEdges,
min: 1.1e-12, bucketCounts: counts,
max: 1.21e-12 min: 1.1e-12,
}); max: 1.21e-12
},
1.1e-12, 1.21e-12, 1);
assertHistogramEquality(newHistogram, histogram); assertHistogramEquality(newHistogram, histogram);
}); });
@ -218,15 +223,17 @@ module TF.Backend {
let counts = [1, 2]; let counts = [1, 2];
let rightEdges = [1.1e-12, 1.21e-12]; let rightEdges = [1.1e-12, 1.21e-12];
let histogram = [ let histogram = [
{x: 1.0e-12, dx: 1.1e-12 - 1.0e-12, y: 1}, {x: 1.0e-12, dx: 1.05e-13, y: 1.09090909090909},
{x: 1.1e-12, dx: 1.21e-12 - 1.1e-12, y: 2} {x: 1.105e-12, dx: 1.05e-13, y: 1.9090909090909}
]; ];
let newHistogram = convertBins({ let newHistogram = convertBins(
bucketRightEdges: rightEdges, {
bucketCounts: counts, bucketRightEdges: rightEdges,
min: 1.0e-12, bucketCounts: counts,
max: 1.21e-12 min: 1.0e-12,
}); max: 1.21e-12
},
1.0e-12, 1.21e-12, 2);
assertHistogramEquality(newHistogram, histogram); assertHistogramEquality(newHistogram, histogram);
}); });
@ -236,15 +243,17 @@ module TF.Backend {
let counts = [1, 2]; let counts = [1, 2];
let rightEdges = [-1.0e-12, 1.0e-12]; let rightEdges = [-1.0e-12, 1.0e-12];
let histogram = [ let histogram = [
{x: -1.1e-12, dx: 1.1e-12 - 1.0e-12, y: 1}, {x: -1.1e-12, dx: 1.05e-12, y: 1.95},
{x: -1.0e-12, dx: 2.0e-12, y: 2} {x: -0.5e-13, dx: 1.05e-12, y: 1.05}
]; ];
let newHistogram = convertBins({ let newHistogram = convertBins(
bucketRightEdges: rightEdges, {
bucketCounts: counts, bucketRightEdges: rightEdges,
min: -1.1e-12, bucketCounts: counts,
max: 1.0e-12 min: -1.1e-12,
}); max: 1.0e-12
},
-1.1e-12, 1.0e-12, 2);
assertHistogramEquality(newHistogram, histogram); assertHistogramEquality(newHistogram, histogram);
}); });
@ -253,15 +262,18 @@ module TF.Backend {
let counts = [1, 2, 3]; let counts = [1, 2, 3];
let rightEdges = [0, 1.0e-12, 1.0e14]; let rightEdges = [0, 1.0e-12, 1.0e14];
let histogram = [ let histogram = [
{x: -1.0e-12, dx: 1.0e-12, y: 1}, {x: 0, dx: 1.0e-12, y: 2}, {x: -1.0e-12, dx: 0.7e-12, y: 0.7},
{x: 1.0e-12, dx: 1.1e-12 - 1.0e-12, y: 3} {x: -0.3e-12, dx: 0.7e-12, y: 1.1},
{x: 0.4e-12, dx: 0.7e-12, y: 4.2}
]; ];
let newHistogram = convertBins({ let newHistogram = convertBins(
bucketRightEdges: rightEdges, {
bucketCounts: counts, bucketRightEdges: rightEdges,
min: -1.0e-12, bucketCounts: counts,
max: 1.1e-12 min: -1.0e-12,
}); max: 1.1e-12
},
-1.0e-12, 1.1e-12, 3);
assertHistogramEquality(newHistogram, histogram); assertHistogramEquality(newHistogram, histogram);
}); });
}); });

View File

@ -19,6 +19,7 @@ limitations under the License.
<script src="../../webcomponentsjs/webcomponents-lite.min.js"></script> <script src="../../webcomponentsjs/webcomponents-lite.min.js"></script>
<script src="../../web-component-tester/browser.js"></script> <script src="../../web-component-tester/browser.js"></script>
<link rel="import" href="../../polymer/polymer.html"> <link rel="import" href="../../polymer/polymer.html">
<link rel="import" href="../../tf-imports/d3.html">
</head> </head>
<body> <body>
<test-fixture id="testElementFixture"> <test-fixture id="testElementFixture">

View File

@ -1,5 +1,6 @@
<link rel="import" href="../polymer/polymer.html"> <link rel="import" href="../polymer/polymer.html">
<link rel="import" href="../tf-imports/lodash.html"> <link rel="import" href="../tf-imports/lodash.html">
<link rel="import" href="../tf-imports/d3.html">
<script src="requestManager.js"></script> <script src="requestManager.js"></script>
<script src="urlPathHelpers.js"></script> <script src="urlPathHelpers.js"></script>