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
(Drawable)
A
{name: string, tab?: string}
object specifying which surface to render to. -
layer
(Layer)
a
tf.layers.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
(Drawable)
A
{name: string, tab?: string}
object specifying which surface to render to. - model (tf.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
(Drawable)
A
{name: string, tab?: string}
object specifying which surface to render to. - tensor (Tensor) the input 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.
model.fit(data, labels, {
epochs: 5,
batchSize: 32,
callbacks: tfvis.show.fitCallbacks(surface, ['loss', 'acc']),
});
-
container
(Drawable)
A
{name: string, tab?: string}
object specifying which surface to render to. - metrics (string[]) List of metrics to plot.
- opts (Object) Optional parameters for the line charts. See the opts parameter for render.linechart for details. Notably for 'accuracy' related plots the domain of the yAxis will always by 0-1, i.e. zoomToFit and yAxisDomain options are ignored. Optional
- callbacks (string[])
- zoomToFitAccuracy (boolean)
- xAxisDomain ([number, number])
- yAxisDomain ([number, number])
- zoomToFit (boolean)
- width (number)
- height (number)
- xLabel (string)
- yLabel (string)
- xType ('quantitative'|'ordinal'|'nominal')
- yType ('quantitative'|'ordinal'|'nominal')
- fontSize (number)
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
(Drawable)
A
{name: string, tab?: string}
object specifying which surface to render to. -
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. See the opts parameter for render.linechart for details. Notably for 'accuracy' related plots the domain of the yAxis will always by 0-1, i.e. zoomToFit and yAxisDomain options are ignored. Optional
- zoomToFitAccuracy (boolean)
- xAxisDomain ([number, number])
- yAxisDomain ([number, number])
- zoomToFit (boolean)
- width (number)
- height (number)
- xLabel (string)
- yLabel (string)
- xType ('quantitative'|'ordinal'|'nominal')
- yType ('quantitative'|'ordinal'|'nominal')
- fontSize (number)
These functions are useful for evaluating the quality of a model.
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
(Drawable)
An
HTMLElement
orSurface
in which to draw the bar chart. Note that the chart expects to have complete control over the contents of the container and can clear its contents at will. - data (Array) Data in the following format, (an array of objects) [ {index: number, value: number} ... ]
- opts (Object) optional parameters Optional
- width (number)
- height (number)
- xLabel (string)
- yLabel (string)
- xType ('quantitative'|'ordinal'|'nominal')
- yType ('quantitative'|'ordinal'|'nominal')
- fontSize (number)
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
(Drawable)
An
HTMLElement
orSurface
in which to draw the chart -
data
(Object)
Data consists of an object with a 'values' property
and a 'labels' property.
{
// a matrix of numbers representing counts for each (label, prediction)
// pair
values: number[][],
// Human readable labels for each class in the matrix. Optional tickLabels?: string[] } e.g. { values: [[80, 23], [56, 94]], tickLabels: ['dog', 'cat'], }
- values (number[][])
- tickLabels (string[])
- opts (VisOptions& {shadeDiagonal?: boolean, showTextOverlay?: boolean}) optional parameters Optional
Renders a heatmap.
const rows = 50;
const cols = 20;
const values = [];
for (let i = 0; i < rows; i++) {
const row = []
for (let j = 0; j < cols; j++) {
row.push(i * j)
}
values.push(row);
}
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
(Drawable)
An
HTMLElement
orSurface
in which to draw the chart -
data
(Object)
Data consists of an object with a 'values' property
and a 'labels' property.
{
// a matrix of numbers
values: number[][]|Tensor2D,
// Human readable labels for each class in the matrix. Optional xTickLabels?: string[] yTickLabels?: string[] } e.g. { values: [[80, 23, 50], [56, 94, 39]], xTickLabels: ['dog', 'cat'], yTickLabels: ['size', 'temperature', 'agility'], }
- values (number[][]|Tensor2D)
- xTickLabels (string[])
- yTickLabels (string[])
- opts (Object) optional parameters Optional
- colorMap (NamedColorMap)
- domain (number[])
- width (number)
- height (number)
- xLabel (string)
- yLabel (string)
- xType ('quantitative'|'ordinal'|'nominal')
- yType ('quantitative'|'ordinal'|'nominal')
- fontSize (number)
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)
An
HTMLElement
|Surface
in which to draw the histogram -
data
(Array|number[]|TypedArray)
Data in the following format:
[ {value: number}, ... ]
or[number]
or TypedArray - opts (HistogramOpts) optional parameters Optional
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 (Drawable) An HTMLElement in which to draw the chart
-
data
({values: Point2D[][]|Point2D[], series?: string[]})
Data in the following format
{
// A nested array of objects each with an x and y property,
// one per series.
// If you only have one series to render you can just pass an array
// of objects with x, y properties
values: {x: number, y: number}[][]
// An array of strings with the names of each series passed above. // Optional series: string[] }
- opts (Object) optional parameters Optional
- xAxisDomain ([number, number])
- yAxisDomain ([number, number])
- zoomToFit (boolean)
- width (number)
- height (number)
- xLabel (string)
- yLabel (string)
- xType ('quantitative'|'ordinal'|'nominal')
- yType ('quantitative'|'ordinal'|'nominal')
- fontSize (number)
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 (Drawable) An HTMLElement in which to draw the chart
-
data
({values: Point2D[][]|Point2D[], series?: string[]})
Data in the following format
{
// A nested array of objects each with an x and y property,
// one per series.
// If you only have one series to render you can just pass an array
// of objects with x, y properties
values: {x: number, y: number}[][]
// An array of strings with the names of each series passed above. // Optional series: string[] }
- opts (Object) optional parameters Optional
- xAxisDomain ([number, number])
- yAxisDomain ([number, number])
- zoomToFit (boolean)
- width (number)
- height (number)
- xLabel (string)
- yLabel (string)
- xType ('quantitative'|'ordinal'|'nominal')
- yType ('quantitative'|'ordinal'|'nominal')
- fontSize (number)
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
(Drawable)
An
HTMLElement
orSurface
in which to draw the table. Note that the chart expects to have complete control over the contents of the container and can clear its contents at will. - data ({headers: string[], values: any[][]}) Data in the following format { headers: string[], values: any[][], } data.headers are the column names data.values is an array of arrays (one for each row). The inner array length usually matches the length of data.headers. Usually the values are strings or numbers, these are inserted as html content so html strings are also supported.
- 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