orb-tensorflowjs is a collection of APIs to facilitate concise model designs. They allow efficient interaction between inputs and the layers. See some examples...
This library is organized into the following components:
- Input: Input component transforms an input array to another form. Some example APIs are: split(), flat(), repeat(). It does not operate on tensors. Input APIs are consumed, exclusively, by pseudo layers. Jump to the docs...
- Layer: Tensorflow layer. Jump to the docs...
- Pseudo Layer: A Pseudo Layer defines the arrangement of inputs, layers and pseudo layers. The input, layer and pseudo layer APIs, all expose an apply() method which glues them together. Jump to the docs...
- Tensor: APIs to create and manipulate tensors. Jump to the docs...
These APIs work together to simplfy the model design. We will go through a few examples later on.
Browser Installation. The module is exported as orbtfjs global variable.
<script src="https://cdn.jsdelivr.net/npm/orb-tensorflowjs@latest/dist/index.js"></script>
Node Installation
npm install orb-tensorflowjs
serial pseudo layer constructs a sequential flow of its arg layers. The input is applied with a call to apply() method. The output of an arg layer is fed as input to the next arg layer. The output of the last arg layer is returned.
const {serial } = require('orb-tensorflowjs')
const input = tf.input({shape: [4, 4]})
const output = serial(tf.layers.dense({units: 10, activation: 'softmax'}))
.apply(input)
const m = tf.model({inputs: input, outputs: output})
m.summary()
_________________________________________________________________ Layer (type) Output shape Param # ================================================================= input1 (InputLayer) [null,4,4] 0 _________________________________________________________________ dense_Dense1 (Dense) [null,4,10] 50 ================================================================= Total params: 50 Trainable params: 50 Non-trainable params: 0 _________________________________________________________________### Feed an input to multiple layers in parallel *parallel* pseudo layer feeds its input to each arg layer in parallel. The input is applied with a call to *apply()* method. It outputs an array containing the output from each arg layer.
const {serial, parallel } = require('orb-tensorflowjs')
const input = tf.input({shape: [4, 4]})
const output = serial(
parallel(
tf.layers.dense({units: 10}),
tf.layers.dense({units: 10})
),
tf.layers.concatenate(),
tf.layers.dense({units: 10, activation: 'softmax'})
).apply(input)
const m = tf.model({inputs: input, outputs: output})
m.summary()
__________________________________________________________________________________________________ Layer (type) Output shape Param # Receives inputs ================================================================================================== input1 (InputLayer) [null,4,4] 0 __________________________________________________________________________________________________ dense_Dense1 (Dense) [null,4,10] 50 input1[0][0] __________________________________________________________________________________________________ dense_Dense2 (Dense) [null,4,10] 50 input1[0][0] __________________________________________________________________________________________________ concatenate_Concatenate1 (Conca [null,4,20] 0 dense_Dense1[0][0] dense_Dense2[0][0] __________________________________________________________________________________________________ dense_Dense3 (Dense) [null,4,10] 210 concatenate_Concatenate1[0][0] ================================================================================================== Total params: 310 Trainable params: 310 Non-trainable params: 0 __________________________________________________________________________________________________
split pseudo layer splits the input. It supports configurations to tweak the splits.
const {serial, split } = require('orb-tensorflowjs')
const input = tf.input({shape: [4, 4]})
const output = serial(
split(),
tf.layers.concatenate(),
tf.layers.dense({units: 10, activation: 'softmax'})
).apply(input)
const m = tf.model({inputs: input, outputs: output})
m.summary()
__________________________________________________________________________________________________ Layer (type) Output shape Param # Receives inputs ================================================================================================== input1 (InputLayer) [null,4,4] 0 __________________________________________________________________________________________________ split_Split1 (Split) [[null,1,4],[null,1, 0 input1[0][0] __________________________________________________________________________________________________ concatenate_Concatenate1 (Conca [null,1,16] 0 split_Split1[0][0] split_Split1[0][1] split_Split1[0][2] split_Split1[0][3] __________________________________________________________________________________________________ dense_Dense1 (Dense) [null,1,10] 170 concatenate_Concatenate1[0][0] ================================================================================================== Total params: 170 Trainable params: 170 Non-trainable params: 0 __________________________________________________________________________________________________
mapTo feeds the individual elements of an input array to the same layer.
const {serial, split, mapTo } = require('orb-tensorflowjs')
const input = tf.input({shape: [4, 4]})
const ml = tf.layers.dense({units: 5})
const output = serial(
split(),
mapTo(ml),
tf.layers.concatenate(),
tf.layers.dense({units: 10, activation: 'softmax'})
).apply(input)
const m = tf.model({inputs: input, outputs: output})
m.summary()
__________________________________________________________________________________________________ Layer (type) Output shape Param # Receives inputs ================================================================================================== input1 (InputLayer) [null,4,4] 0 __________________________________________________________________________________________________ split_Split1 (Split) [[null,1,4],[null,1, 0 input1[0][0] __________________________________________________________________________________________________ dense_Dense1 (Dense) [null,1,5] 25 split_Split1[0][0] split_Split1[0][1] split_Split1[0][2] split_Split1[0][3] __________________________________________________________________________________________________ concatenate_Concatenate1 (Conca [null,1,20] 0 dense_Dense1[0][0] dense_Dense1[1][0] dense_Dense1[2][0] dense_Dense1[3][0] __________________________________________________________________________________________________ dense_Dense2 (Dense) [null,1,10] 210 concatenate_Concatenate1[0][0] ================================================================================================== Total params: 235 Trainable params: 235 Non-trainable params: 0 __________________________________________________________________________________________________
split() pseudo layer splits the input into 2 components. map() pseudo layer maps each split to a dense layer.
const {serial, split, map } = require('orb-tensorflowjs')
const input = tf.input({shape: [4, 4]})
const output = serial(
split({factor: 2}),
map(
tf.layers.dense({units: 10}),
tf.layers.dense({units: 10}),
),
tf.layers.concatenate(),
tf.layers.dense({units: 10, activation: 'softmax'})
).apply(input)
const m = tf.model({inputs: input, outputs: output})
m.summary()
__________________________________________________________________________________________________ Layer (type) Output shape Param # Receives inputs ================================================================================================== input1 (InputLayer) [null,4,4] 0 __________________________________________________________________________________________________ split_Split1 (Split) [[null,2,4],[null,2, 0 input1[0][0] __________________________________________________________________________________________________ dense_Dense1 (Dense) [null,2,10] 50 split_Split1[0][0] __________________________________________________________________________________________________ dense_Dense2 (Dense) [null,2,10] 50 split_Split1[0][1] __________________________________________________________________________________________________ concatenate_Concatenate1 (Conca [null,2,20] 0 dense_Dense1[0][0] dense_Dense2[0][0] __________________________________________________________________________________________________ dense_Dense3 (Dense) [null,2,10] 210 concatenate_Concatenate1[0][0] ================================================================================================== Total params: 310 Trainable params: 310 Non-trainable params: 0 __________________________________________________________________________________________________
expandDims expands a dimension.
const {serial, expandDims } = require('orb-tensorflowjs')
const input = tf.input({shape: [4, 4]})
const output = serial(
expandDims(),
tf.layers.dense({units: 10, activation: 'softmax'})
).apply(input)
const m = tf.model({inputs: input, outputs: output})
m.summary()
_________________________________________________________________ Layer (type) Output shape Param # ================================================================= input1 (InputLayer) [null,4,4] 0 _________________________________________________________________ expand_dims_ExpandDims1 (Exp [null,1,4,4] 0 _________________________________________________________________ dense_Dense1 (Dense) [null,1,4,10] 50 ================================================================= Total params: 50 Trainable params: 50 Non-trainable params: 0 _________________________________________________________________
In the below example, the split() pseudo layers splits the input into a 2x2 grid. flat() Input API arranges transforms it into an array of grids. Each element is mapped to the dense layer using mapTo(). The output of dense is forwarded to the downstream layers.
const {serial, parallel, split, mapTo, expandDims, input: orbinput } = require('orb-tensorflowjs')
const input = tf.input({shape: [4, 4]})
const dl = tf.layers.dense({units: 4})
const sl = split({factor: 2, axis: 2})
const output = serial(
split({factor: 2, axis: 1}),
mapTo(sl),
orbinput.flat(),
mapTo(dl),
tf.layers.concatenate(),
tf.layers.flatten(),
tf.layers.dense({units: 10, activation: 'softmax'})
).apply(input)
const m = tf.model({inputs: input, outputs: output})
m.summary()
__________________________________________________________________________________________________ Layer (type) Output shape Param # Receives inputs ================================================================================================== input1 (InputLayer) [null,4,4] 0 __________________________________________________________________________________________________ split_Split2 (Split) [[null,2,4],[null,2, 0 input1[0][0] __________________________________________________________________________________________________ split_Split1 (Split) [[null,2,2],[null,2, 0 split_Split2[0][0] split_Split2[0][1] __________________________________________________________________________________________________ dense_Dense1 (Dense) [null,2,4] 12 split_Split1[0][0] split_Split1[0][1] split_Split1[1][0] split_Split1[1][1] __________________________________________________________________________________________________ concatenate_Concatenate1 (Conca [null,2,16] 0 dense_Dense1[0][0] dense_Dense1[1][0] dense_Dense1[2][0] dense_Dense1[3][0] __________________________________________________________________________________________________ flatten_Flatten1 (Flatten) [null,32] 0 concatenate_Concatenate1[0][0] __________________________________________________________________________________________________ dense_Dense2 (Dense) [null,10] 330 flatten_Flatten1[0][0] ================================================================================================== Total params: 342 Trainable params: 342 Non-trainable params: 0 __________________________________________________________________________________________________
Here, the split() input API splits the input array. map() pseudo layer applies 1:1 mapping of inputs to the arg layers.
const {serial, map, input: orbinput } = require('orb-tensorflowjs')
const input1 = tf.input({shape: [4, 4]})
const input2 = tf.input({shape: [4, 4]})
const output = serial(
orbinput.split(),
map(
tf.layers.dense({units: 10}),
tf.layers.dense({units: 10}),
),
tf.layers.concatenate(),
tf.layers.flatten(),
tf.layers.dense({units: 10, activation: 'softmax'})
).apply([input1, input2])
const m = tf.model({inputs: [input1, input2], outputs: output})
m.summary()
__________________________________________________________________________________________________ Layer (type) Output shape Param # Receives inputs ================================================================================================== input1 (InputLayer) [null,4,4] 0 __________________________________________________________________________________________________ input2 (InputLayer) [null,4,4] 0 __________________________________________________________________________________________________ dense_Dense1 (Dense) [null,4,10] 50 input1[0][0] __________________________________________________________________________________________________ dense_Dense2 (Dense) [null,4,10] 50 input2[0][0] __________________________________________________________________________________________________ concatenate_Concatenate1 (Conca [null,4,20] 0 dense_Dense1[0][0] dense_Dense2[0][0] __________________________________________________________________________________________________ flatten_Flatten1 (Flatten) [null,80] 0 concatenate_Concatenate1[0][0] __________________________________________________________________________________________________ dense_Dense3 (Dense) [null,10] 810 flatten_Flatten1[0][0] ================================================================================================== Total params: 910 Trainable params: 910 Non-trainable params: 0 __________________________________________________________________________________________________
repeat() input API repeats the input and forwards the result. map() pseudo layer performs one-to-one mapping between input and layer args.
const {serial, map, input: orbinput } = require('orb-tensorflowjs')
const input = tf.input({shape: [4, 4]})
const output = serial(
orbinput.repeat(),
map(
tf.layers.dense({units: 10}),
tf.layers.dense({units: 10}),
),
tf.layers.concatenate(),
tf.layers.dense({units: 10, activation: 'softmax'})
).apply(input)
const m = tf.model({inputs: input, outputs: output})
m.summary()
__________________________________________________________________________________________________ Layer (type) Output shape Param # Receives inputs ================================================================================================== input1 (InputLayer) [null,4,4] 0 __________________________________________________________________________________________________ dense_Dense1 (Dense) [null,4,10] 50 input1[0][0] __________________________________________________________________________________________________ dense_Dense2 (Dense) [null,4,10] 50 input1[0][0] __________________________________________________________________________________________________ concatenate_Concatenate1 (Conca [null,4,20] 0 dense_Dense1[0][0] dense_Dense2[0][0] __________________________________________________________________________________________________ dense_Dense3 (Dense) [null,4,10] 210 concatenate_Concatenate1[0][0] ================================================================================================== Total params: 310 Trainable params: 310 Non-trainable params: 0 __________________________________________________________________________________________________
An input API operates on input arrays. They do not modify tensors. We explain them below with a few examples.
It flattens a nested input array. A depth parameter is supported to control the flattening behavior. The default depth is 1.
// flat() without the depth.
const {input: orbinput } = require('orb-tensorflowjs')
const inputs = [t1, [t2, [t3]]] // t1, t2 and t3 are tensors.
const fi = orbinput.flat()
const output = fi.apply(inputs)
// Output: [t1, t2, [t3]]
// flat() with the depth parameter.
const {input: orbinput } = require('orb-tensorflowjs')
const inputs = [t1, [t2, [t3]]] // t1, t2 and t3 are tensors.
const fi = orbinput.flat(depth = 2)
const output = fi.apply(inputs)
// Output: [t1, t2, t3]
It splits an input. A factor parameter controls the number of splits. The default factor is 1.
// Split into 2 pieces
const {input: orbinput } = require('orb-tensorflowjs')
const inputs = [t1, t2, t3] // t1, t2 and t3 are tensors.
const si = orbinput.split()
const output = si.apply(inputs)
// Output: [[t1, t2], [t3]]
// Split into 3 pieces
const {input: orbinput } = require('orb-tensorflowjs')
const inputs = [t1, t2, t3] // t1, t2 and t3 are tensors.
const si = orbinput.split(3)
const output = si.apply(inputs)
// Output: [[t1], [t2], [t3]]
It repeats an input. The result is placed in an array. A count parameter controls the number of repeasts. The default count is 1.
// Repeat one more time
const {input: orbinput } = require('orb-tensorflowjs')
const input = t // A tensor
const ri = orbinput.repeat()
const output = ri.apply(input)
// Output: [t, t]
// Repeat 3 more times
const {input: orbinput } = require('orb-tensorflowjs')
const input = t // A tensor
const ri = orbinput.repeat(3)
const output = ri.apply(input)
// Output: [t, t, t, t]
func() allows custom manipulation of an input. It accepts a function argument.
// Create three pieces of size: 2, 3, 2
const {input: orbinput } = require('orb-tensorflowjs')
const inputs = [t1, t2, t3, t4, t5, t6, t7] // A list of tensors
const fn = ts => [[t1, t2], [t3, t4, t5], [t6, t7]]
const fi = orbinput.func(fn)
const output = fi.apply(inputs)
// Output: [[t1, t2], [t3, t4, t5], [t6, t7]]
tap() is useful for diagnostic purposes during the model design process. It outputs the shape of incoming inputs. The incoming inputs are assumed to be a tensor or a list of tensors. For complex inputs or customized diagnostic information, it supports a fn parameter.
// A single tensor example
const {input: orbinput} = require('orb-tensorflowjs')
const input = t // A tensor
const ti = orbinput.tap()
const output = ti.apply(input)
// Prints the shape of t
// Output: t
// An array of tensors example
const {input: orbinput} = require('orb-tensorflowjs')
const inputs = [t1, t2] // t1 and t2 are tensors
const ti = orbinput.tap()
const output = ti.apply(inputs)
// Prints an array of shapes of tensors
// Output: [t1, t2]
These are a set of useful tensorflow layers. They operate on tensors.
The split() layer splits a tensor into an array of tensors along an axis. An input with shape [4, 4, 4], when split along axis-0, results in 4 tensors. Each tensor has a shape: [1, 4, 4]. The factor and axis configurations control the split behavior. The default axis is 1 and the default factor is the size of the dimension represented by the axis.
const {split} = require('orb-tensorflowjs')
const input = tf.input({shape: [4, 2]})
const sl = split()
const output = sl.apply(input)
// Outputs an array of 4 tensors, each with shape [null, 1, 2]
// Output: [t1, t2, t3, t4]
const {split} = require('orb-tensorflowjs')
const input = tf.input({shape: [4, 2]})
const sl = split({axis: 2})
const output = sl.apply(input)
// Outputs an array of 2 tensors, each with shape [null, 4, 1]
// Output: [t1, t2]
The expandDims layer expands input dimensions. An axis parameter allows to configure the axis of new dimension. The default axis is 1.
const {expandDims} = require('orb-tensorflowjs')
const input = tf.input({shape: [4, 2]})
const ei = expandDims()
const output = ei.apply(input)
// Outputs a tensor with shape: [null, 1, 4, 2]
// Output: t
The pseudo layers glue the model structure together. The operate on both, arrays and tensors. With the help of input and layer APIs, they enable concise and flexible model designs. We have posted several examples here
serial() connects the argument layers sequentially. It can handle an arbitrary number of arg layers.
const {serial} = require('orb-tensorflowjs')
const input = tf.input({shape: [4, 1]})
const sl = serial(
tf.layers.reshape({targetShape: [2, 2]}),
tf.layers.reshape({targetShape: [4, 1, 1]})
)
const output = sl.apply(input)
// Outputs a tensor with shape: [null, 4, 1, 1]
parallel() connects the input with each arg layer. The output is an array containing the output of arg layers. It can handle an arbitrary number of arg layers.
const {parallel} = require('orb-tensorflowjs')
const input = tf.input({shape: [4, 1]})
const pl = parallel(
tf.layers.reshape({targetShape: [2, 2]}),
tf.layers.reshape({targetShape: [4, 1, 1]})
)
const output = pl.apply(input)
// Outputs an array of tensors with shapes: [[null, 2, 2], [null, 4, 1, 1]]
map() performs a one-to-one mapping between the inputs and the layers.
const {map} = require('orb-tensorflowjs')
const inputs = [
tf.input({shape: [4, 1]}),
tf.input({shape: [1, 6]}),
]
const ml = map(
tf.layers.reshape({targetShape: [2, 2]}),
tf.layers.reshape({targetShape: [2, 3]}),
)
const output = ml.apply(inputs)
// Outputs an array of tensors with shapes: [[null, 2, 2], [null, 2, 3]]
mapTo() maps input elements to the same layer.
const {mapTo} = require('orb-tensorflowjs')
const inputs = [
tf.input({shape: [4, 1]}),
tf.input({shape: [1, 4]}),
]
const ml = mapTo(tf.layers.reshape({targetShape: [2, 2]}))
const output = ml.apply(inputs)
// Outputs an array of tensors with shapes: [[null, 2, 2], [null, 2, 2]]
A set of APIs to create and manipulate tensors.
generate supports several features.
generate.lowerTriangular generates a 2D lower triangular tensor. The size, the lower values and the upper values are configurable. The default size is 2, lower value is 1 and the upper value is 0
const {tensor} = require('orb-tensorflowjs')
const t = tensor.generate.lowerTriangular()
// Output shape: [2, 2]
//
// +---+---+
// | 1 | 0 |
// +---+---+
// | 1 | 1 |
// +---+---+
//
const {tensor} = require('orb-tensorflowjs')
const t = tensor.generate.lowerTriangular(4, {lower: 2})
// Output shape: [2, 2]
//
// +---+---+---+---+
// | 2 | 0 | 0 | 0 |
// +---+---+---+---+
// | 2 | 2 | 0 | 0 |
// +---+---+---+---+
// | 2 | 2 | 2 | 0 |
// +---+---+---+---+
// | 2 | 2 | 2 | 2 |
// +---+---+---+---+
//
random supports several APIs. They were primarily designed for test purposes. However, if need arises, they can be used in production.
random.oneHot generates one hot tensors. The size and the one hot dimension are configurable. The default values for size is 1 and dims is 10.
// A tensor with default one hot dimensions (10)
const {tensor} = require('orb-tensorflowjs')
const size = 4
const t = tensor.random.oneHot(size)
// Outputs a one hot tensor with shape: [4, 10]
//
// +---+---+---+---+---+---+---+---+---+---+
// | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
// +---+---+---+---+---+---+---+---+---+---+
// | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
// +---+---+---+---+---+---+---+---+---+---+
// | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
// +---+---+---+---+---+---+---+---+---+---+
// | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 |
// +---+---+---+---+---+---+---+---+---+---+
//
const {tensor} = require('orb-tensorflowjs')
const size = 4
const t = tensor.random.oneHot(size, {dims: 4})
// Outputs a one hot tensor with shape: [4, 4]
//
// +---+---+---+---+
// | 0 | 1 | 0 | 0 |
// +---+---+---+---+
// | 1 | 0 | 0 | 0 |
// +---+---+---+---+
// | 0 | 1 | 0 | 0 |
// +---+---+---+---+
// | 0 | 0 | 0 | 1 |
// +---+---+---+---+
//
random.normalizedSample creates a sample of values between 0 and 1. The size and the shape of the output is configurable. The default sample size is 1 and the default shape is [1]. This API is designed specifically for test purposes.
const {tensor} = require('orb-tensorflowjs')
const size = 5
const t = tensor.random.normalizedSample(size)
// Output shape: [5, 1]
const {tensor} = require('orb-tensorflowjs')
const size = 5
const shape = [3, 4]
const t = tensor.random.normalizedSample(size, {shape: [3, 4]})
// Output shape: [5, 3, 4]