tfjs-vis provides some UI helpers to make it easier to render visualizations in an unobtrusive way.
The primary interface to the visor is the visor() function.
This returns a singleton instance of the Visor class. The singleton object will be replaced if the visor is removed from the DOM for some reason.
// Show the visor
tfvis.visor();
An instance of the visor. An instance of this class is created using the
visor()
function.
Creates a surface on the visor
Most methods in tfjs-vis that take a surface also take a SurfaceInfo so you rarely need to call this method unless you want to make a custom plot.
// Create a surface on a tab
tfvis.visor().surface({name: 'My Surface', tab: 'My Tab'});
// Create a surface and specify its height
tfvis.visor().surface({name: 'Custom Height', tab: 'My Tab', styles: {
height: 500
}})
- options (Object)
- name (string) The name / label of this surface
- tab (string) The name of the tab this surface should appear on
- styles (StyleOptions) Display Styles for the surface
Returns a boolean indicating if the visor is in 'fullscreen' mode
Binds the ~ (tilde) key to toggle the visor.
This is called by default when the visor is initially created.
Sets the active tab for the visor.
- tabName (string)
This library exposes a show namespace that provides a number of higher level functions useful for rendering tfjs concepts. They lean towards being opinionated rather than flexible but are generally combinations of Renderers (see below), so one can easily roll their own.
These functions are useful for examing models, layers and tensors
Renders summary information about a layer and a histogram of parameters in that layer.
const model = tf.sequential({
layers: [
tf.layers.dense({inputShape: [784], units: 32, activation: 'relu'}),
tf.layers.dense({units: 10, activation: 'softmax'}),
]
});
const surface = { name: 'Layer Summary', tab: 'Model Inspection'};
tfvis.show.layer(surface, model.getLayer(undefined, 1));
- container (HTMLElement|{name: string, tab?: string}|Surface|{drawArea: HTMLElement})
- layer (Layer)
Renders a summary of a tf.Model. Displays a table with layer information.
const model = tf.sequential({
layers: [
tf.layers.dense({inputShape: [784], units: 32, activation: 'relu'}),
tf.layers.dense({units: 10, activation: 'softmax'}),
]
});
const surface = { name: 'Model Summary', tab: 'Model Inspection'};
tfvis.show.modelSummary(surface, model);
- container (HTMLElement|{name: string, tab?: string}|Surface|{drawArea: HTMLElement})
- model (LayersModel)
Shows a histogram with the distribution of all values in a given tensor.
const tensor = tf.tensor1d([0, 0, 0, 0, 2, 3, 4]);
const surface = {name: 'Values Distribution', tab: 'Model Inspection'};
await tfvis.show.valuesDistribution(surface, tensor);
- container (HTMLElement|{name: string, tab?: string}|Surface|{drawArea: HTMLElement})
- tensor (Tensor)
These functions are useful for monitoring training.
Returns a collection of callbacks to pass to tf.Model.fit. Callbacks are
returned for the following events, onBatchEnd
& onEpochEnd
.
const model = tf.sequential({
layers: [
tf.layers.dense({inputShape: [784], units: 32, activation: 'relu'}),
tf.layers.dense({units: 10, activation: 'softmax'}),
]
});
model.compile({
optimizer: 'sgd',
loss: 'categoricalCrossentropy',
metrics: ['accuracy']
});
const data = tf.randomNormal([100, 784]);
const labels = tf.randomUniform([100, 10]);
function onBatchEnd(batch, logs) {
console.log('Accuracy', logs.acc);
}
const surface = { name: 'show.fitCallbacks', tab: 'Training' };
// Train for 5 epochs with batch size of 32.
await model.fit(data, labels, {
epochs: 5,
batchSize: 32,
callbacks: tfvis.show.fitCallbacks(surface, ['loss', 'acc']),
});
- container (HTMLElement|{name: string, tab?: string}|Surface|{drawArea: HTMLElement})
- metrics (string[]) List of metrics to plot.
- opts (Object) Optional parameters Optional
- callbacks (string[]) Array of callback names. Valid options are 'onEpochEnd' and 'onBatchEnd'. Defaults to ['onEpochEnd', 'onBatchEnd'].
- zoomToFitAccuracy (boolean)
- xAxisDomain ([number, number]) domain of the x axis. Overriden by zoomToFit
- yAxisDomain ([number, number]) domain of the y axis. Overriden by zoomToFit
- zoomToFit (boolean) Set the chart bounds to just fit the data. This may modify the axis scales but allows fitting more data into view.
- seriesColors (string[]) Colors to for each series plotted. An array of valid CSS color strings.
- width (number) Width of chart in px
- height (number) Height of chart in px
- xLabel (string) Label for xAxis
- yLabel (string) Label for yAxis
- fontSize (number) Fontsize in px
- xType ('quantitative'|'ordinal'|'nominal') Will be set automatically
- yType ('quantitative'|'ordinal'|'nominal') Will be set automatically
Renders a tf.Model training 'History'.
const model = tf.sequential({
layers: [
tf.layers.dense({inputShape: [784], units: 32, activation: 'relu'}),
tf.layers.dense({units: 10, activation: 'softmax'}),
]
});
model.compile({
optimizer: 'sgd',
loss: 'categoricalCrossentropy',
metrics: ['accuracy']
});
const data = tf.randomNormal([100, 784]);
const labels = tf.randomUniform([100, 10]);
function onBatchEnd(batch, logs) {
console.log('Accuracy', logs.acc);
}
const surface = { name: 'show.history', tab: 'Training' };
// Train for 5 epochs with batch size of 32.
const history = await model.fit(data, labels, {
epochs: 5,
batchSize: 32
});
tfvis.show.history(surface, history, ['loss', 'acc']);
const model = tf.sequential({
layers: [
tf.layers.dense({inputShape: [784], units: 32, activation: 'relu'}),
tf.layers.dense({units: 10, activation: 'softmax'}),
]
});
model.compile({
optimizer: 'sgd',
loss: 'categoricalCrossentropy',
metrics: ['accuracy']
});
const data = tf.randomNormal([100, 784]);
const labels = tf.randomUniform([100, 10]);
function onBatchEnd(batch, logs) {
console.log('Accuracy', logs.acc);
}
const surface = { name: 'show.history live', tab: 'Training' };
// Train for 5 epochs with batch size of 32.
const history = [];
await model.fit(data, labels, {
epochs: 5,
batchSize: 32,
callbacks: {
onEpochEnd: (epoch, log) => {
history.push(log);
tfvis.show.history(surface, history, ['loss', 'acc']);
}
}
});
- container (HTMLElement|{name: string, tab?: string}|Surface|{drawArea: HTMLElement})
-
history
(HistoryLike)
A history like object. Either a tfjs-layers
History
object or an array of tfjs-layersLogs
objects. - metrics (string[]) An array of strings for each metric to plot from the history object. Using this allows you to control which metrics appear on the same plot.
- opts (Object) Optional parameters for the line charts. Optional
- zoomToFitAccuracy (boolean)
- xAxisDomain ([number, number]) domain of the x axis. Overriden by zoomToFit
- yAxisDomain ([number, number]) domain of the y axis. Overriden by zoomToFit
- zoomToFit (boolean) Set the chart bounds to just fit the data. This may modify the axis scales but allows fitting more data into view.
- seriesColors (string[]) Colors to for each series plotted. An array of valid CSS color strings.
- width (number) Width of chart in px
- height (number) Height of chart in px
- xLabel (string) Label for xAxis
- yLabel (string) Label for yAxis
- fontSize (number) Fontsize in px
- xType ('quantitative'|'ordinal'|'nominal') Will be set automatically
- yType ('quantitative'|'ordinal'|'nominal') Will be set automatically
These functions are useful for evaluating the quality of a model.
Renders a per class accuracy table for classification task evaluation
const labels = tf.tensor1d([0, 0, 1, 2, 2, 2]);
const predictions = tf.tensor1d([0, 0, 0, 2, 1, 1]);
const result = await tfvis.metrics.perClassAccuracy(labels, predictions);
console.log(result)
const container = {name: 'Per Class Accuracy', tab: 'Evaluation'};
const categories = ['cat', 'dog', 'mouse'];
await tfvis.show.perClassAccuracy(container, result, categories);
-
container
(HTMLElement|{name: string, tab?: string}|Surface|{drawArea:
HTMLElement})
A
{name: string, tab?: string}
object specifying which surface to render to. -
classAccuracy
(Array)
An
Array<{accuracy: number, count: number}>
array with the accuracy data. See metrics.perClassAccuracy for details on how to generate this object. -
classLabels
(string[])
An array of string labels for the classes in
classAccuracy
. Optional. Optional
The library exposes a render namespace that provides a number of functions that plot particular visualizations. Most of these functions take regular JavaScript objects
Renders a barchart.
const data = [
{ index: 0, value: 50 },
{ index: 1, value: 100 },
{ index: 2, value: 150 },
];
// Render to visor
const surface = { name: 'Bar chart', tab: 'Charts' };
tfvis.render.barchart(surface, data);
- container (HTMLElement|{name: string, tab?: string}|Surface|{drawArea: HTMLElement})
-
data
(Array)
Data in the following format, (an array of objects)
[ {index: number, value: number} ... ]
- opts (Object) Optional
- color (string|string[]) Fill color for bars. Should be a valid CSS color string
- width (number) Width of chart in px
- height (number) Height of chart in px
- xLabel (string) Label for xAxis
- yLabel (string) Label for yAxis
- fontSize (number) Fontsize in px
- xType ('quantitative'|'ordinal'|'nominal') Will be set automatically
- yType ('quantitative'|'ordinal'|'nominal') Will be set automatically
Renders a confusion matrix.
Can optionally exclude the diagonal from being shaded if one wants the visual focus to be on the incorrect classifications. Note that if the classification is perfect (i.e. only the diagonal has values) then the diagonal will always be shaded.
const rows = 5;
const cols = 5;
const values = [];
for (let i = 0; i < rows; i++) {
const row = []
for (let j = 0; j < cols; j++) {
row.push(Math.round(Math.random() * 50));
}
values.push(row);
}
const data = { values };
// Render to visor
const surface = { name: 'Confusion Matrix', tab: 'Charts' };
tfvis.render.confusionMatrix(surface, data);
// The diagonal can be excluded from shading.
const data = {
values: [[4, 2, 8], [1, 7, 2], [3, 3, 20]],
}
// Render to visor
const surface = {
name: 'Confusion Matrix with Excluded Diagonal', tab: 'Charts'
};
tfvis.render.confusionMatrix(surface, data, {
shadeDiagonal: false
});
- container (HTMLElement|{name: string, tab?: string}|Surface|{drawArea: HTMLElement})
- data (Object)
- values (number[][]) a square matrix of numbers representing counts for each (label, prediction) pair
- tickLabels (string[]) Human readable labels for each class in the matrix. Optional
- opts (Object) Optional
- shadeDiagonal (boolean) Color cells on the diagonal. Defaults to true
- showTextOverlay (boolean) render the values of each cell as text. Defaults to true
- colorMap ('greyscale'|'viridis'|'blues'|[string, string]) Output range of color scale. Either a 2 element array of valid css color or one of 'greyscale'|'viridis'|'blues'
- width (number) Width of chart in px
- height (number) Height of chart in px
- xLabel (string) Label for xAxis
- yLabel (string) Label for yAxis
- fontSize (number) Fontsize in px
- xType ('quantitative'|'ordinal'|'nominal') Will be set automatically
- yType ('quantitative'|'ordinal'|'nominal') Will be set automatically
Renders a heatmap.
const cols = 50;
const rows = 20;
const values = [];
for (let i = 0; i < cols; i++) {
const col = []
for (let j = 0; j < rows; j++) {
col.push(i * j)
}
values.push(col);
}
const data = { values };
// Render to visor
const surface = { name: 'Heatmap', tab: 'Charts' };
tfvis.render.heatmap(surface, data);
const data = {
values: [[4, 2, 8, 20], [1, 7, 2, 10], [3, 3, 20, 13]],
xTickLabels: ['cheese', 'pig', 'font'],
yTickLabels: ['speed', 'smoothness', 'dexterity', 'mana'],
}
// Render to visor
const surface = { name: 'Heatmap w Custom Labels', tab: 'Charts' };
tfvis.render.heatmap(surface, data);
- container (HTMLElement|{name: string, tab?: string}|Surface|{drawArea: HTMLElement})
- data (Object)
-
values
(number[][]|Tensor2D)
Matrix of values in column-major order.
Row major order is supported by setting a boolean in options.
- xTickLabels (string[]) x axis tick labels
- yTickLabels (string[]) y axis tick labels
- opts (Object) Optional
- colorMap ('greyscale'|'viridis'|'blues') Defaults to viridis
- domain (number[]) Custom input domain for the color scale. Useful if you want to plot multiple heatmaps using the same scale.
-
rowMajor
(boolean)
Pass in data values in row-major order.
Internally this will transpose the data values before rendering.
- width (number) Width of chart in px
- height (number) Height of chart in px
- xLabel (string) Label for xAxis
- yLabel (string) Label for yAxis
- fontSize (number) Fontsize in px
- xType ('quantitative'|'ordinal'|'nominal') Will be set automatically
- yType ('quantitative'|'ordinal'|'nominal') Will be set automatically
Renders a histogram of values
const data = Array(100).fill(0)
.map(x => Math.random() * 100 - (Math.random() * 50))
// Push some special values for the stats table.
data.push(Infinity);
data.push(NaN);
data.push(0);
const surface = { name: 'Histogram', tab: 'Charts' };
tfvis.render.histogram(surface, data);
- container (HTMLElement|{name: string, tab?: string}|Surface|{drawArea: HTMLElement})
- data (Array|number[]|TypedArray)
- opts (Object) Optional
-
stats
(HistogramStats|false)
By default a histogram will also compute and display summary statistics.
If stats is set to false then summary statistics will not be displayed.
Pre computed stats can also be passed in and should have the following format: { numVals?: number, min?: number, max?: number, numNans?: number, numZeros?: number, numInfs?: number, }
- maxBins (number) Maximum number of bins in histogram.
- color (string) Fill color for bars. Should be a valid CSS color string
- width (number) Width of chart in px
- height (number) Height of chart in px
- xLabel (string) Label for xAxis
- yLabel (string) Label for yAxis
- fontSize (number) Fontsize in px
- xType ('quantitative'|'ordinal'|'nominal') Will be set automatically
- yType ('quantitative'|'ordinal'|'nominal') Will be set automatically
Renders a line chart
const series1 = Array(100).fill(0)
.map(y => Math.random() * 100 - (Math.random() * 50))
.map((y, x) => ({ x, y, }));
const series2 = Array(100).fill(0)
.map(y => Math.random() * 100 - (Math.random() * 150))
.map((y, x) => ({ x, y, }));
const series = ['First', 'Second'];
const data = { values: [series1, series2], series }
const surface = { name: 'Line chart', tab: 'Charts' };
tfvis.render.linechart(surface, data);
const series1 = Array(100).fill(0)
.map(y => Math.random() * 100 + 50)
.map((y, x) => ({ x, y, }));
const data = { values: [series1] }
// Render to visor
const surface = { name: 'Zoomed Line Chart', tab: 'Charts' };
tfvis.render.linechart(surface, data, { zoomToFit: true });
- container (HTMLElement|{name: string, tab?: string}|Surface|{drawArea: HTMLElement})
- data (Object)
- values (Point2D[][]|Point2D[]) An array (or nested array) of {x, y} tuples.
- series (string[]) Series names/labels
- opts (Object) Optional
- xAxisDomain ([number, number]) domain of the x axis. Overriden by zoomToFit
- yAxisDomain ([number, number]) domain of the y axis. Overriden by zoomToFit
- zoomToFit (boolean) Set the chart bounds to just fit the data. This may modify the axis scales but allows fitting more data into view.
- seriesColors (string[]) Colors to for each series plotted. An array of valid CSS color strings.
- width (number) Width of chart in px
- height (number) Height of chart in px
- xLabel (string) Label for xAxis
- yLabel (string) Label for yAxis
- fontSize (number) Fontsize in px
- xType ('quantitative'|'ordinal'|'nominal') Will be set automatically
- yType ('quantitative'|'ordinal'|'nominal') Will be set automatically
Renders a scatter plot
const series1 = Array(100).fill(0)
.map(y => Math.random() * 100 - (Math.random() * 50))
.map((y, x) => ({ x, y, }));
const series2 = Array(100).fill(0)
.map(y => Math.random() * 100 - (Math.random() * 150))
.map((y, x) => ({ x, y, }));
const series = ['First', 'Second'];
const data = { values: [series1, series2], series }
const surface = { name: 'Scatterplot', tab: 'Charts' };
tfvis.render.scatterplot(surface, data);
- container (HTMLElement|{name: string, tab?: string}|Surface|{drawArea: HTMLElement})
- data (Object)
- values (Point2D[][]|Point2D[]) An array (or nested array) of {x, y} tuples.
- series (string[]) Series names/labels
- opts (Object) Optional
- xAxisDomain ([number, number]) domain of the x axis. Overriden by zoomToFit
- yAxisDomain ([number, number]) domain of the y axis. Overriden by zoomToFit
- zoomToFit (boolean) Set the chart bounds to just fit the data. This may modify the axis scales but allows fitting more data into view.
- seriesColors (string[]) Colors to for each series plotted. An array of valid CSS color strings.
- width (number) Width of chart in px
- height (number) Height of chart in px
- xLabel (string) Label for xAxis
- yLabel (string) Label for yAxis
- fontSize (number) Fontsize in px
- xType ('quantitative'|'ordinal'|'nominal') Will be set automatically
- yType ('quantitative'|'ordinal'|'nominal') Will be set automatically
Renders a table
const headers = [
'Col 1',
'Col 2',
'Col 3',
];
const values = [
[1, 2, 3],
['4', '5', '6'],
['strong>7</strong>', true, false],
];
const surface = { name: 'Table', tab: 'Charts' };
tfvis.render.table(surface, { headers, values });
- container (HTMLElement|{name: string, tab?: string}|Surface|{drawArea: HTMLElement})
- data (Object)
- headers (string[]) Column names
-
values
(any[][])
An array of arrays (one for each row). The inner
array length usually matches the length of data.headers.
Typically the values are numbers or strings.
- opts ({fontSize?: number}) Optional
The metrics namespace contains a few utility functions for computing quality metrics like accuracy or creating confusion matrices.
Computes how often predictions matches labels
const labels = tf.tensor1d([0, 0, 1, 2, 2, 2]);
const predictions = tf.tensor1d([0, 0, 0, 2, 1, 1]);
const result = await tfvis.metrics.accuracy(labels, predictions);
console.log(result)
- labels (Tensor) tensor of true values
- predictions (Tensor) tensor of predicted values
Computes a confusion matrix from predictions and labels. Each value in labels and predictions should correspond to some output class. It is assumed that these values go from 0 to numClasses - 1.
const labels = tf.tensor1d([1, 2, 4]);
const predictions = tf.tensor1d([2, 2, 4]);
const result = await tfvis.metrics.confusionMatrix(labels, predictions);
console.log(JSON.stringify(result, null, 2))
- labels (Tensor1D) 1D tensor of true values
- predictions (Tensor1D) 1D tensor of predicted values
- numClasses (number) Number of distinct classes. Optional. If not passed in numClasses will equal the highest number in either labels or predictions plus 1 Optional
- weights (Tensor1D) 1d tensor that is the same size as predictions. If weights is passed in then each prediction contributes its corresponding weight to the total value of the confusion matrix cell. Optional
Computes per class accuracy between prediction and labels. Each value in labels and predictions should correspond to some output class. It is assumed that these values go from 0 to numClasses - 1.
const labels = tf.tensor1d([0, 0, 1, 2, 2, 2]);
const predictions = tf.tensor1d([0, 0, 0, 2, 1, 1]);
const result = await tfvis.metrics.perClassAccuracy(labels, predictions);
console.log(JSON.stringify(result, null, 2))
Returns an array of objects that each have an an accuracy
and a count
property for each class.
- labels (Tensor1D) 1D tensor of true values
- predictions (Tensor1D) 1D tensor of predicted values
- numClasses (number) Number of distinct classes. Optional. If not passed in numClasses will equal the highest number in either labels or predictions plus 1 Optional