Repository files navigation

TensorJS

TestVersion

This is a JS/TS library for accelerated tensor computation intended to be run in the browser. It contains an implementation for numpy-style multidimensional arrays and their operators.

It also allows executing Onnx models. For examples check the examples folder.

There are three execution backends available:

  • CPU: This is implemented in plain javascript and thus not very fast. It is intended to be a reference implementation. Big optimizations are avoided for simplicity.
  • Web Assembly: This is implemented in Rust. It is optimized for faster execution (although right now there is a lot of work to be done).
  • GPU: This uses WebGL to enable very fast execution and should be used whenever a GPU is available. It is typically ~10-100 times faster than the WASM backend (except for a few operators). Most of the development focus was spent here so this is by far the fastest backend.

How to use

Install with

$ npm install @hoff97/tensor-js

and then import

import*astjsfrom'@hoff97/tensor-js';

or import the stuff you need directly.

Tensors

You can create tensors of the respective backend like this:

  • CPU:
    consttensor=newtjs.tensor.cpu.CPUTensor([2,2],[1,2,3,4]);
  • WASM:
    consttensor=newtjs.tensor.wasm.WASMTensor(newFloat32Array([1,2,3,4]),[2,2]);
  • GPU:
    consttensor=newtjs.tensor.gpu.GPUTensor(newFloat32Array([1,2,3,4]),[2,2]);
    or directly from an image/video element:
    constvideo: HTMLVideoElement=document.querySelector("#videoElement");consttensor=tjs.tensor.gpu.GPUTensor.fromData(video);
    which will be a tensor with shape [height,width,4] and data type float32. Creating a GPU tensor from a video element will usually be pretty fast. Creation from an image not necessarily, since here the image data first has to be transferred to the GPU.

Tensor operations

Once you have created a tensor, you can do operations on it, for example:

  • Add two tensors
    constres=a.add(b);
  • Matrix multiplication
    constres=a.matMul(b);
  • Find the maximum
    constres=a.max(1);

For a list of all operators, see the docs. Most operators will behave like their numpy/pytorch counterparts.

Reading values

When you want to read data from a tensor:

constvalues=awaittensor.getValues();

which will give you the values as a array of the values. For CPU tensors you can also get the value at an index:

constvalue=tensor.get([1,2,3,4]);

Data types

Tensors are created with float values (using 32 bits) by default. You can specify another data type on creation:

consttensor=newtjs.tensor.cpu.CPUTensor([2,2],[1,2,3,4],'float16');

or cast to another data type with:

constcasted=tensor.cast('float16');

The available data types are float64, float32, float16, int32, int16, int8, uint32, uint16, uint8. Note that not all backends support all data types:

  • CPU: Supports all data types, but float16 will be represented as float32 internally
  • WASM: Supports all except float16
  • GPU: Supports all except float64. Note that except for float16, all other data types will be represented by float32 internally, since WebGL1 does not allow writing anything else than floats to frame buffers. This means that for int32 and uint32, not the whole range of values of the respective data type is available.

The data type of a tensor can be accessed via tensor.dtype. Additionally, each tensor has a generic type argument, which will carry its data type:

consttensor: Tensor<'float16'>=newtjs.tensor.cpu.CPUTensor([2,2],[1,2,3,4],'float16');

This allows type checking tensor operations, which means that only tensor operations with the same data type compile when using typescript. The generic type defaults to float32. If you want to represent the data type of a tensor with an unknown data type, write for example

consttensor: Tensor<any>=a.add(b);

or alternatively

consttensor: Tensor<DType>=a.add(b);

Converting between backends

You can conver a tensor to a different backend like so:

constcpuTensor=awaittjs.util.convert.toCPU(tensor);constwasmTensor=awaittjs.util.convert.toWASM(tensor);constgpuTensor=awaittjs.util.convert.toGPU(tensor);

Note that converting between backends (especially from/to WebGL) is an expensive operation and should be prevented if possible!

Onnx model support

You can load an onnx model like this:

constrespones=awaitfetch(`model.onnx`);constbuffer=awaitres.arrayBuffer();constmodel=newtjs.onnx.model.OnnxModel(buffer);

To see all supported operators, check the supported operator list.

You will very likely want to run this model on the GPU. To do this:

awaitmodel.toGPU();

Optimizations

There are a few optimization passes that can be done on an Onnx model to get faster execution. To do this, run

model.optimize()

Running with half precision

By default full precision floats (32-bits) are used for model execution. On the GPU backend, you can try executing with half precision, but be aware that this might not work for all models. To use half precision, specify this when loading the model:

constmodel=newtjs.onnx.model.OnnxModel(buffer,{precision: 16})model.toGPU();

For the best performance you should also create your GPU tensors with half precision

consttensor=newtjs.tensor.gpu.GPUTensor(newFloat32Array([1,2,3,4]),[2,2],'float16');

The outputs of the model will be half-precision tensors as well. To read the values of a half precision gpu tensor, you have to convert it to full precision first, which can be done with:

constvalues=awaittensor.cast('float32').getValues();

Other performance considerations

Try to run your models with static input sizes. TensorJS will compile specialized versions of all operations after enough forward passes. For this the input shapes of the tensors have to be constant though.

Autograd functionality

Automatic differentiation is supported. For this create variables from all your tensors:

consta=newtjs.tensor.cpu.CPUTensor([2,2],[1,2,3,4]);constb=newtjs.tensor.cpu.CPUTensor([2,2],[5,6,7,8]);constvarA=newtjs.autograd.Variable(a);constvarB=newtjs.autograd.Variable(b);

Or use the utility methods:

constvarA=tjs.autograd.Variable.create([2,2],[1,2,3,4],'GPU');constvideoElement=document.querySelector("#videoElement");constvarB=tjs.autograd.Variable.fromData(videoElement);

Afterwards you can perform normal tensor operations:

constmul=varA.matMul(varB);constsum=mul.sum();

To perform a backward pass, call backward on a scalar tensor (a tensor with shape [1]). All variables will have an attribute .grad, which is the gradient

sum.backward();console.log(varA.grad);

Multiple backward passes will add up the gradients. After you are done with the variable, delete the computation graph by calling delete().

Sparse tensors

Sparse tensors are tensors where most entries are zero, for example the following one:

consta=newCPUTensor([3,3],[1,0,0,0,2,0,0,3,4]);

TensorJS supports sparse tensors in coordinate format, where we store the coordinates and values of the nonzero entries in two tensors:

constindices=[0,0,// Corresponds to value 11,1,// Corresponds to value 22,1,// Corresponds to value 32,2// Corresponds to value 4];constindiceTensor=newCPUTensor([4,2],indices,'uint32');constvalues=[1,2,3,4];constvalueTensor=newCPUTensor([4],values);constsparseTensor=newSparseTensor(valueTensor,indiceTensor,[3,3]);

The implementations of the operators for sparse tensors only consider the nonzero entries and are thus faster than their dense counterparts.

Note that some operators make specific assumptions on the sparse tensor, for details check the corresponding documentation here.

Backend support for sparse tensors

As of now, most operators are only supported on the CPU and WASM backend. If an operation is not supported, this is noted in the docs.

Documentation

You can find the documentation here.

Contributing

See Contributing.md

Development

See Development.md

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all \u003cpre\u003e\u003ccode\u003e blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks"); } } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); } })(); (function(){ try { var __m = "github.com"; var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

TensorJS

TestVersion

This is a JS/TS library for accelerated tensor computation intended to be run in the browser. It contains an implementation for numpy-style multidimensional arrays and their operators.

It also allows executing Onnx models. For examples check the examples folder.

There are three execution backends available:

  • CPU: This is implemented in plain javascript and thus not very fast. It is intended to be a reference implementation. Big optimizations are avoided for simplicity.
  • Web Assembly: This is implemented in Rust. It is optimized for faster execution (although right now there is a lot of work to be done).
  • GPU: This uses WebGL to enable very fast execution and should be used whenever a GPU is available. It is typically ~10-100 times faster than the WASM backend (except for a few operators). Most of the development focus was spent here so this is by far the fastest backend.

How to use

Install with

$ npm install @hoff97/tensor-js

and then import

import*astjsfrom'@hoff97/tensor-js';

or import the stuff you need directly.

Tensors

You can create tensors of the respective backend like this:

  • CPU:
    consttensor=newtjs.tensor.cpu.CPUTensor([2,2],[1,2,3,4]);
  • WASM:
    consttensor=newtjs.tensor.wasm.WASMTensor(newFloat32Array([1,2,3,4]),[2,2]);
  • GPU:
    consttensor=newtjs.tensor.gpu.GPUTensor(newFloat32Array([1,2,3,4]),[2,2]);
    or directly from an image/video element:
    constvideo: HTMLVideoElement=document.querySelector("#videoElement");consttensor=tjs.tensor.gpu.GPUTensor.fromData(video);
    which will be a tensor with shape [height,width,4] and data type float32. Creating a GPU tensor from a video element will usually be pretty fast. Creation from an image not necessarily, since here the image data first has to be transferred to the GPU.

Tensor operations

Once you have created a tensor, you can do operations on it, for example:

  • Add two tensors
    constres=a.add(b);
  • Matrix multiplication
    constres=a.matMul(b);
  • Find the maximum
    constres=a.max(1);

For a list of all operators, see the docs. Most operators will behave like their numpy/pytorch counterparts.

Reading values

When you want to read data from a tensor:

constvalues=awaittensor.getValues();

which will give you the values as a array of the values. For CPU tensors you can also get the value at an index:

constvalue=tensor.get([1,2,3,4]);

Data types

Tensors are created with float values (using 32 bits) by default. You can specify another data type on creation:

consttensor=newtjs.tensor.cpu.CPUTensor([2,2],[1,2,3,4],'float16');

or cast to another data type with:

constcasted=tensor.cast('float16');

The available data types are float64, float32, float16, int32, int16, int8, uint32, uint16, uint8. Note that not all backends support all data types:

  • CPU: Supports all data types, but float16 will be represented as float32 internally
  • WASM: Supports all except float16
  • GPU: Supports all except float64. Note that except for float16, all other data types will be represented by float32 internally, since WebGL1 does not allow writing anything else than floats to frame buffers. This means that for int32 and uint32, not the whole range of values of the respective data type is available.

The data type of a tensor can be accessed via tensor.dtype. Additionally, each tensor has a generic type argument, which will carry its data type:

consttensor: Tensor<'float16'>=newtjs.tensor.cpu.CPUTensor([2,2],[1,2,3,4],'float16');

This allows type checking tensor operations, which means that only tensor operations with the same data type compile when using typescript. The generic type defaults to float32. If you want to represent the data type of a tensor with an unknown data type, write for example

consttensor: Tensor<any>=a.add(b);

or alternatively

consttensor: Tensor<DType>=a.add(b);

Converting between backends

You can conver a tensor to a different backend like so:

constcpuTensor=awaittjs.util.convert.toCPU(tensor);constwasmTensor=awaittjs.util.convert.toWASM(tensor);constgpuTensor=awaittjs.util.convert.toGPU(tensor);

Note that converting between backends (especially from/to WebGL) is an expensive operation and should be prevented if possible!

Onnx model support

You can load an onnx model like this:

constrespones=awaitfetch(`model.onnx`);constbuffer=awaitres.arrayBuffer();constmodel=newtjs.onnx.model.OnnxModel(buffer);

To see all supported operators, check the supported operator list.

You will very likely want to run this model on the GPU. To do this:

awaitmodel.toGPU();

Optimizations

There are a few optimization passes that can be done on an Onnx model to get faster execution. To do this, run

model.optimize()

Running with half precision

By default full precision floats (32-bits) are used for model execution. On the GPU backend, you can try executing with half precision, but be aware that this might not work for all models. To use half precision, specify this when loading the model:

constmodel=newtjs.onnx.model.OnnxModel(buffer,{precision: 16})model.toGPU();

For the best performance you should also create your GPU tensors with half precision

consttensor=newtjs.tensor.gpu.GPUTensor(newFloat32Array([1,2,3,4]),[2,2],'float16');

The outputs of the model will be half-precision tensors as well. To read the values of a half precision gpu tensor, you have to convert it to full precision first, which can be done with:

constvalues=awaittensor.cast('float32').getValues();

Other performance considerations

Try to run your models with static input sizes. TensorJS will compile specialized versions of all operations after enough forward passes. For this the input shapes of the tensors have to be constant though.

Autograd functionality

Automatic differentiation is supported. For this create variables from all your tensors:

consta=newtjs.tensor.cpu.CPUTensor([2,2],[1,2,3,4]);constb=newtjs.tensor.cpu.CPUTensor([2,2],[5,6,7,8]);constvarA=newtjs.autograd.Variable(a);constvarB=newtjs.autograd.Variable(b);

Or use the utility methods:

constvarA=tjs.autograd.Variable.create([2,2],[1,2,3,4],'GPU');constvideoElement=document.querySelector("#videoElement");constvarB=tjs.autograd.Variable.fromData(videoElement);

Afterwards you can perform normal tensor operations:

constmul=varA.matMul(varB);constsum=mul.sum();

To perform a backward pass, call backward on a scalar tensor (a tensor with shape [1]). All variables will have an attribute .grad, which is the gradient

sum.backward();console.log(varA.grad);

Multiple backward passes will add up the gradients. After you are done with the variable, delete the computation graph by calling delete().

Sparse tensors

Sparse tensors are tensors where most entries are zero, for example the following one:

consta=newCPUTensor([3,3],[1,0,0,0,2,0,0,3,4]);

TensorJS supports sparse tensors in coordinate format, where we store the coordinates and values of the nonzero entries in two tensors:

constindices=[0,0,// Corresponds to value 11,1,// Corresponds to value 22,1,// Corresponds to value 32,2// Corresponds to value 4];constindiceTensor=newCPUTensor([4,2],indices,'uint32');constvalues=[1,2,3,4];constvalueTensor=newCPUTensor([4],values);constsparseTensor=newSparseTensor(valueTensor,indiceTensor,[3,3]);

The implementations of the operators for sparse tensors only consider the nonzero entries and are thus faster than their dense counterparts.

Note that some operators make specific assumptions on the sparse tensor, for details check the corresponding documentation here.

Backend support for sparse tensors

As of now, most operators are only supported on the CPU and WASM backend. If an operation is not supported, this is noted in the docs.

Documentation

You can find the documentation here.

Contributing

See Contributing.md

Development

See Development.md

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

TensorJS

TestVersion

This is a JS/TS library for accelerated tensor computation intended to be run in the browser. It contains an implementation for numpy-style multidimensional arrays and their operators.

It also allows executing Onnx models. For examples check the examples folder.

There are three execution backends available:

  • CPU: This is implemented in plain javascript and thus not very fast. It is intended to be a reference implementation. Big optimizations are avoided for simplicity.
  • Web Assembly: This is implemented in Rust. It is optimized for faster execution (although right now there is a lot of work to be done).
  • GPU: This uses WebGL to enable very fast execution and should be used whenever a GPU is available. It is typically ~10-100 times faster than the WASM backend (except for a few operators). Most of the development focus was spent here so this is by far the fastest backend.

How to use

Install with

$ npm install @hoff97/tensor-js

and then import

import*astjsfrom'@hoff97/tensor-js';

or import the stuff you need directly.

Tensors

You can create tensors of the respective backend like this:

  • CPU:
    consttensor=newtjs.tensor.cpu.CPUTensor([2,2],[1,2,3,4]);
  • WASM:
    consttensor=newtjs.tensor.wasm.WASMTensor(newFloat32Array([1,2,3,4]),[2,2]);
  • GPU:
    consttensor=newtjs.tensor.gpu.GPUTensor(newFloat32Array([1,2,3,4]),[2,2]);
    or directly from an image/video element:
    constvideo: HTMLVideoElement=document.querySelector("#videoElement");consttensor=tjs.tensor.gpu.GPUTensor.fromData(video);
    which will be a tensor with shape [height,width,4] and data type float32. Creating a GPU tensor from a video element will usually be pretty fast. Creation from an image not necessarily, since here the image data first has to be transferred to the GPU.

Tensor operations

Once you have created a tensor, you can do operations on it, for example:

  • Add two tensors
    constres=a.add(b);
  • Matrix multiplication
    constres=a.matMul(b);
  • Find the maximum
    constres=a.max(1);

For a list of all operators, see the docs. Most operators will behave like their numpy/pytorch counterparts.

Reading values

When you want to read data from a tensor:

constvalues=awaittensor.getValues();

which will give you the values as a array of the values. For CPU tensors you can also get the value at an index:

constvalue=tensor.get([1,2,3,4]);

Data types

Tensors are created with float values (using 32 bits) by default. You can specify another data type on creation:

consttensor=newtjs.tensor.cpu.CPUTensor([2,2],[1,2,3,4],'float16');

or cast to another data type with:

constcasted=tensor.cast('float16');

The available data types are float64, float32, float16, int32, int16, int8, uint32, uint16, uint8. Note that not all backends support all data types:

  • CPU: Supports all data types, but float16 will be represented as float32 internally
  • WASM: Supports all except float16
  • GPU: Supports all except float64. Note that except for float16, all other data types will be represented by float32 internally, since WebGL1 does not allow writing anything else than floats to frame buffers. This means that for int32 and uint32, not the whole range of values of the respective data type is available.

The data type of a tensor can be accessed via tensor.dtype. Additionally, each tensor has a generic type argument, which will carry its data type:

consttensor: Tensor<'float16'>=newtjs.tensor.cpu.CPUTensor([2,2],[1,2,3,4],'float16');

This allows type checking tensor operations, which means that only tensor operations with the same data type compile when using typescript. The generic type defaults to float32. If you want to represent the data type of a tensor with an unknown data type, write for example

consttensor: Tensor<any>=a.add(b);

or alternatively

consttensor: Tensor<DType>=a.add(b);

Converting between backends

You can conver a tensor to a different backend like so:

constcpuTensor=awaittjs.util.convert.toCPU(tensor);constwasmTensor=awaittjs.util.convert.toWASM(tensor);constgpuTensor=awaittjs.util.convert.toGPU(tensor);

Note that converting between backends (especially from/to WebGL) is an expensive operation and should be prevented if possible!

Onnx model support

You can load an onnx model like this:

constrespones=awaitfetch(`model.onnx`);constbuffer=awaitres.arrayBuffer();constmodel=newtjs.onnx.model.OnnxModel(buffer);

To see all supported operators, check the supported operator list.

You will very likely want to run this model on the GPU. To do this:

awaitmodel.toGPU();

Optimizations

There are a few optimization passes that can be done on an Onnx model to get faster execution. To do this, run

model.optimize()

Running with half precision

By default full precision floats (32-bits) are used for model execution. On the GPU backend, you can try executing with half precision, but be aware that this might not work for all models. To use half precision, specify this when loading the model:

constmodel=newtjs.onnx.model.OnnxModel(buffer,{precision: 16})model.toGPU();

For the best performance you should also create your GPU tensors with half precision

consttensor=newtjs.tensor.gpu.GPUTensor(newFloat32Array([1,2,3,4]),[2,2],'float16');

The outputs of the model will be half-precision tensors as well. To read the values of a half precision gpu tensor, you have to convert it to full precision first, which can be done with:

constvalues=awaittensor.cast('float32').getValues();

Other performance considerations

Try to run your models with static input sizes. TensorJS will compile specialized versions of all operations after enough forward passes. For this the input shapes of the tensors have to be constant though.

Autograd functionality

Automatic differentiation is supported. For this create variables from all your tensors:

consta=newtjs.tensor.cpu.CPUTensor([2,2],[1,2,3,4]);constb=newtjs.tensor.cpu.CPUTensor([2,2],[5,6,7,8]);constvarA=newtjs.autograd.Variable(a);constvarB=newtjs.autograd.Variable(b);

Or use the utility methods:

constvarA=tjs.autograd.Variable.create([2,2],[1,2,3,4],'GPU');constvideoElement=document.querySelector("#videoElement");constvarB=tjs.autograd.Variable.fromData(videoElement);

Afterwards you can perform normal tensor operations:

constmul=varA.matMul(varB);constsum=mul.sum();

To perform a backward pass, call backward on a scalar tensor (a tensor with shape [1]). All variables will have an attribute .grad, which is the gradient

sum.backward();console.log(varA.grad);

Multiple backward passes will add up the gradients. After you are done with the variable, delete the computation graph by calling delete().

Sparse tensors

Sparse tensors are tensors where most entries are zero, for example the following one:

consta=newCPUTensor([3,3],[1,0,0,0,2,0,0,3,4]);

TensorJS supports sparse tensors in coordinate format, where we store the coordinates and values of the nonzero entries in two tensors:

constindices=[0,0,// Corresponds to value 11,1,// Corresponds to value 22,1,// Corresponds to value 32,2// Corresponds to value 4];constindiceTensor=newCPUTensor([4,2],indices,'uint32');constvalues=[1,2,3,4];constvalueTensor=newCPUTensor([4],values);constsparseTensor=newSparseTensor(valueTensor,indiceTensor,[3,3]);

The implementations of the operators for sparse tensors only consider the nonzero entries and are thus faster than their dense counterparts.

Note that some operators make specific assumptions on the sparse tensor, for details check the corresponding documentation here.

Backend support for sparse tensors

As of now, most operators are only supported on the CPU and WASM backend. If an operation is not supported, this is noted in the docs.

Documentation

You can find the documentation here.

Contributing

See Contributing.md

Development

See Development.md

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length \u003e 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

TensorJS

TestVersion

This is a JS/TS library for accelerated tensor computation intended to be run in the browser. It contains an implementation for numpy-style multidimensional arrays and their operators.

It also allows executing Onnx models. For examples check the examples folder.

There are three execution backends available:

  • CPU: This is implemented in plain javascript and thus not very fast. It is intended to be a reference implementation. Big optimizations are avoided for simplicity.
  • Web Assembly: This is implemented in Rust. It is optimized for faster execution (although right now there is a lot of work to be done).
  • GPU: This uses WebGL to enable very fast execution and should be used whenever a GPU is available. It is typically ~10-100 times faster than the WASM backend (except for a few operators). Most of the development focus was spent here so this is by far the fastest backend.

How to use

Install with

$ npm install @hoff97/tensor-js

and then import

import*astjsfrom'@hoff97/tensor-js';

or import the stuff you need directly.

Tensors

You can create tensors of the respective backend like this:

  • CPU:
    consttensor=newtjs.tensor.cpu.CPUTensor([2,2],[1,2,3,4]);
  • WASM:
    consttensor=newtjs.tensor.wasm.WASMTensor(newFloat32Array([1,2,3,4]),[2,2]);
  • GPU:
    consttensor=newtjs.tensor.gpu.GPUTensor(newFloat32Array([1,2,3,4]),[2,2]);
    or directly from an image/video element:
    constvideo: HTMLVideoElement=document.querySelector("#videoElement");consttensor=tjs.tensor.gpu.GPUTensor.fromData(video);
    which will be a tensor with shape [height,width,4] and data type float32. Creating a GPU tensor from a video element will usually be pretty fast. Creation from an image not necessarily, since here the image data first has to be transferred to the GPU.

Tensor operations

Once you have created a tensor, you can do operations on it, for example:

  • Add two tensors
    constres=a.add(b);
  • Matrix multiplication
    constres=a.matMul(b);
  • Find the maximum
    constres=a.max(1);

For a list of all operators, see the docs. Most operators will behave like their numpy/pytorch counterparts.

Reading values

When you want to read data from a tensor:

constvalues=awaittensor.getValues();

which will give you the values as a array of the values. For CPU tensors you can also get the value at an index:

constvalue=tensor.get([1,2,3,4]);

Data types

Tensors are created with float values (using 32 bits) by default. You can specify another data type on creation:

consttensor=newtjs.tensor.cpu.CPUTensor([2,2],[1,2,3,4],'float16');

or cast to another data type with:

constcasted=tensor.cast('float16');

The available data types are float64, float32, float16, int32, int16, int8, uint32, uint16, uint8. Note that not all backends support all data types:

  • CPU: Supports all data types, but float16 will be represented as float32 internally
  • WASM: Supports all except float16
  • GPU: Supports all except float64. Note that except for float16, all other data types will be represented by float32 internally, since WebGL1 does not allow writing anything else than floats to frame buffers. This means that for int32 and uint32, not the whole range of values of the respective data type is available.

The data type of a tensor can be accessed via tensor.dtype. Additionally, each tensor has a generic type argument, which will carry its data type:

consttensor: Tensor<'float16'>=newtjs.tensor.cpu.CPUTensor([2,2],[1,2,3,4],'float16');

This allows type checking tensor operations, which means that only tensor operations with the same data type compile when using typescript. The generic type defaults to float32. If you want to represent the data type of a tensor with an unknown data type, write for example

consttensor: Tensor<any>=a.add(b);

or alternatively

consttensor: Tensor<DType>=a.add(b);

Converting between backends

You can conver a tensor to a different backend like so:

constcpuTensor=awaittjs.util.convert.toCPU(tensor);constwasmTensor=awaittjs.util.convert.toWASM(tensor);constgpuTensor=awaittjs.util.convert.toGPU(tensor);

Note that converting between backends (especially from/to WebGL) is an expensive operation and should be prevented if possible!

Onnx model support

You can load an onnx model like this:

constrespones=awaitfetch(`model.onnx`);constbuffer=awaitres.arrayBuffer();constmodel=newtjs.onnx.model.OnnxModel(buffer);

To see all supported operators, check the supported operator list.

You will very likely want to run this model on the GPU. To do this:

awaitmodel.toGPU();

Optimizations

There are a few optimization passes that can be done on an Onnx model to get faster execution. To do this, run

model.optimize()

Running with half precision

By default full precision floats (32-bits) are used for model execution. On the GPU backend, you can try executing with half precision, but be aware that this might not work for all models. To use half precision, specify this when loading the model:

constmodel=newtjs.onnx.model.OnnxModel(buffer,{precision: 16})model.toGPU();

For the best performance you should also create your GPU tensors with half precision

consttensor=newtjs.tensor.gpu.GPUTensor(newFloat32Array([1,2,3,4]),[2,2],'float16');

The outputs of the model will be half-precision tensors as well. To read the values of a half precision gpu tensor, you have to convert it to full precision first, which can be done with:

constvalues=awaittensor.cast('float32').getValues();

Other performance considerations

Try to run your models with static input sizes. TensorJS will compile specialized versions of all operations after enough forward passes. For this the input shapes of the tensors have to be constant though.

Autograd functionality

Automatic differentiation is supported. For this create variables from all your tensors:

consta=newtjs.tensor.cpu.CPUTensor([2,2],[1,2,3,4]);constb=newtjs.tensor.cpu.CPUTensor([2,2],[5,6,7,8]);constvarA=newtjs.autograd.Variable(a);constvarB=newtjs.autograd.Variable(b);

Or use the utility methods:

constvarA=tjs.autograd.Variable.create([2,2],[1,2,3,4],'GPU');constvideoElement=document.querySelector("#videoElement");constvarB=tjs.autograd.Variable.fromData(videoElement);

Afterwards you can perform normal tensor operations:

constmul=varA.matMul(varB);constsum=mul.sum();

To perform a backward pass, call backward on a scalar tensor (a tensor with shape [1]). All variables will have an attribute .grad, which is the gradient

sum.backward();console.log(varA.grad);

Multiple backward passes will add up the gradients. After you are done with the variable, delete the computation graph by calling delete().

Sparse tensors

Sparse tensors are tensors where most entries are zero, for example the following one:

consta=newCPUTensor([3,3],[1,0,0,0,2,0,0,3,4]);

TensorJS supports sparse tensors in coordinate format, where we store the coordinates and values of the nonzero entries in two tensors:

constindices=[0,0,// Corresponds to value 11,1,// Corresponds to value 22,1,// Corresponds to value 32,2// Corresponds to value 4];constindiceTensor=newCPUTensor([4,2],indices,'uint32');constvalues=[1,2,3,4];constvalueTensor=newCPUTensor([4],values);constsparseTensor=newSparseTensor(valueTensor,indiceTensor,[3,3]);

The implementations of the operators for sparse tensors only consider the nonzero entries and are thus faster than their dense counterparts.

Note that some operators make specific assumptions on the sparse tensor, for details check the corresponding documentation here.

Backend support for sparse tensors

As of now, most operators are only supported on the CPU and WASM backend. If an operation is not supported, this is noted in the docs.

Documentation

You can find the documentation here.

Contributing

See Contributing.md

Development

See Development.md

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

TensorJS

TestVersion

This is a JS/TS library for accelerated tensor computation intended to be run in the browser. It contains an implementation for numpy-style multidimensional arrays and their operators.

It also allows executing Onnx models. For examples check the examples folder.

There are three execution backends available:

  • CPU: This is implemented in plain javascript and thus not very fast. It is intended to be a reference implementation. Big optimizations are avoided for simplicity.
  • Web Assembly: This is implemented in Rust. It is optimized for faster execution (although right now there is a lot of work to be done).
  • GPU: This uses WebGL to enable very fast execution and should be used whenever a GPU is available. It is typically ~10-100 times faster than the WASM backend (except for a few operators). Most of the development focus was spent here so this is by far the fastest backend.

How to use

Install with

$ npm install @hoff97/tensor-js

and then import

import*astjsfrom'@hoff97/tensor-js';

or import the stuff you need directly.

Tensors

You can create tensors of the respective backend like this:

  • CPU:
    consttensor=newtjs.tensor.cpu.CPUTensor([2,2],[1,2,3,4]);
  • WASM:
    consttensor=newtjs.tensor.wasm.WASMTensor(newFloat32Array([1,2,3,4]),[2,2]);
  • GPU:
    consttensor=newtjs.tensor.gpu.GPUTensor(newFloat32Array([1,2,3,4]),[2,2]);
    or directly from an image/video element:
    constvideo: HTMLVideoElement=document.querySelector("#videoElement");consttensor=tjs.tensor.gpu.GPUTensor.fromData(video);
    which will be a tensor with shape [height,width,4] and data type float32. Creating a GPU tensor from a video element will usually be pretty fast. Creation from an image not necessarily, since here the image data first has to be transferred to the GPU.

Tensor operations

Once you have created a tensor, you can do operations on it, for example:

  • Add two tensors
    constres=a.add(b);
  • Matrix multiplication
    constres=a.matMul(b);
  • Find the maximum
    constres=a.max(1);

For a list of all operators, see the docs. Most operators will behave like their numpy/pytorch counterparts.

Reading values

When you want to read data from a tensor:

constvalues=awaittensor.getValues();

which will give you the values as a array of the values. For CPU tensors you can also get the value at an index:

constvalue=tensor.get([1,2,3,4]);

Data types

Tensors are created with float values (using 32 bits) by default. You can specify another data type on creation:

consttensor=newtjs.tensor.cpu.CPUTensor([2,2],[1,2,3,4],'float16');

or cast to another data type with:

constcasted=tensor.cast('float16');

The available data types are float64, float32, float16, int32, int16, int8, uint32, uint16, uint8. Note that not all backends support all data types:

  • CPU: Supports all data types, but float16 will be represented as float32 internally
  • WASM: Supports all except float16
  • GPU: Supports all except float64. Note that except for float16, all other data types will be represented by float32 internally, since WebGL1 does not allow writing anything else than floats to frame buffers. This means that for int32 and uint32, not the whole range of values of the respective data type is available.

The data type of a tensor can be accessed via tensor.dtype. Additionally, each tensor has a generic type argument, which will carry its data type:

consttensor: Tensor<'float16'>=newtjs.tensor.cpu.CPUTensor([2,2],[1,2,3,4],'float16');

This allows type checking tensor operations, which means that only tensor operations with the same data type compile when using typescript. The generic type defaults to float32. If you want to represent the data type of a tensor with an unknown data type, write for example

consttensor: Tensor<any>=a.add(b);

or alternatively

consttensor: Tensor<DType>=a.add(b);

Converting between backends

You can conver a tensor to a different backend like so:

constcpuTensor=awaittjs.util.convert.toCPU(tensor);constwasmTensor=awaittjs.util.convert.toWASM(tensor);constgpuTensor=awaittjs.util.convert.toGPU(tensor);

Note that converting between backends (especially from/to WebGL) is an expensive operation and should be prevented if possible!

Onnx model support

You can load an onnx model like this:

constrespones=awaitfetch(`model.onnx`);constbuffer=awaitres.arrayBuffer();constmodel=newtjs.onnx.model.OnnxModel(buffer);

To see all supported operators, check the supported operator list.

You will very likely want to run this model on the GPU. To do this:

awaitmodel.toGPU();

Optimizations

There are a few optimization passes that can be done on an Onnx model to get faster execution. To do this, run

model.optimize()

Running with half precision

By default full precision floats (32-bits) are used for model execution. On the GPU backend, you can try executing with half precision, but be aware that this might not work for all models. To use half precision, specify this when loading the model:

constmodel=newtjs.onnx.model.OnnxModel(buffer,{precision: 16})model.toGPU();

For the best performance you should also create your GPU tensors with half precision

consttensor=newtjs.tensor.gpu.GPUTensor(newFloat32Array([1,2,3,4]),[2,2],'float16');

The outputs of the model will be half-precision tensors as well. To read the values of a half precision gpu tensor, you have to convert it to full precision first, which can be done with:

constvalues=awaittensor.cast('float32').getValues();

Other performance considerations

Try to run your models with static input sizes. TensorJS will compile specialized versions of all operations after enough forward passes. For this the input shapes of the tensors have to be constant though.

Autograd functionality

Automatic differentiation is supported. For this create variables from all your tensors:

consta=newtjs.tensor.cpu.CPUTensor([2,2],[1,2,3,4]);constb=newtjs.tensor.cpu.CPUTensor([2,2],[5,6,7,8]);constvarA=newtjs.autograd.Variable(a);constvarB=newtjs.autograd.Variable(b);

Or use the utility methods:

constvarA=tjs.autograd.Variable.create([2,2],[1,2,3,4],'GPU');constvideoElement=document.querySelector("#videoElement");constvarB=tjs.autograd.Variable.fromData(videoElement);

Afterwards you can perform normal tensor operations:

constmul=varA.matMul(varB);constsum=mul.sum();

To perform a backward pass, call backward on a scalar tensor (a tensor with shape [1]). All variables will have an attribute .grad, which is the gradient

sum.backward();console.log(varA.grad);

Multiple backward passes will add up the gradients. After you are done with the variable, delete the computation graph by calling delete().

Sparse tensors

Sparse tensors are tensors where most entries are zero, for example the following one:

consta=newCPUTensor([3,3],[1,0,0,0,2,0,0,3,4]);

TensorJS supports sparse tensors in coordinate format, where we store the coordinates and values of the nonzero entries in two tensors:

constindices=[0,0,// Corresponds to value 11,1,// Corresponds to value 22,1,// Corresponds to value 32,2// Corresponds to value 4];constindiceTensor=newCPUTensor([4,2],indices,'uint32');constvalues=[1,2,3,4];constvalueTensor=newCPUTensor([4],values);constsparseTensor=newSparseTensor(valueTensor,indiceTensor,[3,3]);

The implementations of the operators for sparse tensors only consider the nonzero entries and are thus faster than their dense counterparts.

Note that some operators make specific assumptions on the sparse tensor, for details check the corresponding documentation here.

Backend support for sparse tensors

As of now, most operators are only supported on the CPU and WASM backend. If an operation is not supported, this is noted in the docs.

Documentation

You can find the documentation here.

Contributing

See Contributing.md

Development

See Development.md

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

TensorJS

TestVersion

This is a JS/TS library for accelerated tensor computation intended to be run in the browser. It contains an implementation for numpy-style multidimensional arrays and their operators.

It also allows executing Onnx models. For examples check the examples folder.

There are three execution backends available:

  • CPU: This is implemented in plain javascript and thus not very fast. It is intended to be a reference implementation. Big optimizations are avoided for simplicity.
  • Web Assembly: This is implemented in Rust. It is optimized for faster execution (although right now there is a lot of work to be done).
  • GPU: This uses WebGL to enable very fast execution and should be used whenever a GPU is available. It is typically ~10-100 times faster than the WASM backend (except for a few operators). Most of the development focus was spent here so this is by far the fastest backend.

How to use

Install with

$ npm install @hoff97/tensor-js

and then import

import*astjsfrom'@hoff97/tensor-js';

or import the stuff you need directly.

Tensors

You can create tensors of the respective backend like this:

  • CPU:
    consttensor=newtjs.tensor.cpu.CPUTensor([2,2],[1,2,3,4]);
  • WASM:
    consttensor=newtjs.tensor.wasm.WASMTensor(newFloat32Array([1,2,3,4]),[2,2]);
  • GPU:
    consttensor=newtjs.tensor.gpu.GPUTensor(newFloat32Array([1,2,3,4]),[2,2]);
    or directly from an image/video element:
    constvideo: HTMLVideoElement=document.querySelector("#videoElement");consttensor=tjs.tensor.gpu.GPUTensor.fromData(video);
    which will be a tensor with shape [height,width,4] and data type float32. Creating a GPU tensor from a video element will usually be pretty fast. Creation from an image not necessarily, since here the image data first has to be transferred to the GPU.

Tensor operations

Once you have created a tensor, you can do operations on it, for example:

  • Add two tensors
    constres=a.add(b);
  • Matrix multiplication
    constres=a.matMul(b);
  • Find the maximum
    constres=a.max(1);

For a list of all operators, see the docs. Most operators will behave like their numpy/pytorch counterparts.

Reading values

When you want to read data from a tensor:

constvalues=awaittensor.getValues();

which will give you the values as a array of the values. For CPU tensors you can also get the value at an index:

constvalue=tensor.get([1,2,3,4]);

Data types

Tensors are created with float values (using 32 bits) by default. You can specify another data type on creation:

consttensor=newtjs.tensor.cpu.CPUTensor([2,2],[1,2,3,4],'float16');

or cast to another data type with:

constcasted=tensor.cast('float16');

The available data types are float64, float32, float16, int32, int16, int8, uint32, uint16, uint8. Note that not all backends support all data types:

  • CPU: Supports all data types, but float16 will be represented as float32 internally
  • WASM: Supports all except float16
  • GPU: Supports all except float64. Note that except for float16, all other data types will be represented by float32 internally, since WebGL1 does not allow writing anything else than floats to frame buffers. This means that for int32 and uint32, not the whole range of values of the respective data type is available.

The data type of a tensor can be accessed via tensor.dtype. Additionally, each tensor has a generic type argument, which will carry its data type:

consttensor: Tensor<'float16'>=newtjs.tensor.cpu.CPUTensor([2,2],[1,2,3,4],'float16');

This allows type checking tensor operations, which means that only tensor operations with the same data type compile when using typescript. The generic type defaults to float32. If you want to represent the data type of a tensor with an unknown data type, write for example

consttensor: Tensor<any>=a.add(b);

or alternatively

consttensor: Tensor<DType>=a.add(b);

Converting between backends

You can conver a tensor to a different backend like so:

constcpuTensor=awaittjs.util.convert.toCPU(tensor);constwasmTensor=awaittjs.util.convert.toWASM(tensor);constgpuTensor=awaittjs.util.convert.toGPU(tensor);

Note that converting between backends (especially from/to WebGL) is an expensive operation and should be prevented if possible!

Onnx model support

You can load an onnx model like this:

constrespones=awaitfetch(`model.onnx`);constbuffer=awaitres.arrayBuffer();constmodel=newtjs.onnx.model.OnnxModel(buffer);

To see all supported operators, check the supported operator list.

You will very likely want to run this model on the GPU. To do this:

awaitmodel.toGPU();

Optimizations

There are a few optimization passes that can be done on an Onnx model to get faster execution. To do this, run

model.optimize()

Running with half precision

By default full precision floats (32-bits) are used for model execution. On the GPU backend, you can try executing with half precision, but be aware that this might not work for all models. To use half precision, specify this when loading the model:

constmodel=newtjs.onnx.model.OnnxModel(buffer,{precision: 16})model.toGPU();

For the best performance you should also create your GPU tensors with half precision

consttensor=newtjs.tensor.gpu.GPUTensor(newFloat32Array([1,2,3,4]),[2,2],'float16');

The outputs of the model will be half-precision tensors as well. To read the values of a half precision gpu tensor, you have to convert it to full precision first, which can be done with:

constvalues=awaittensor.cast('float32').getValues();

Other performance considerations

Try to run your models with static input sizes. TensorJS will compile specialized versions of all operations after enough forward passes. For this the input shapes of the tensors have to be constant though.

Autograd functionality

Automatic differentiation is supported. For this create variables from all your tensors:

consta=newtjs.tensor.cpu.CPUTensor([2,2],[1,2,3,4]);constb=newtjs.tensor.cpu.CPUTensor([2,2],[5,6,7,8]);constvarA=newtjs.autograd.Variable(a);constvarB=newtjs.autograd.Variable(b);

Or use the utility methods:

constvarA=tjs.autograd.Variable.create([2,2],[1,2,3,4],'GPU');constvideoElement=document.querySelector("#videoElement");constvarB=tjs.autograd.Variable.fromData(videoElement);

Afterwards you can perform normal tensor operations:

constmul=varA.matMul(varB);constsum=mul.sum();

To perform a backward pass, call backward on a scalar tensor (a tensor with shape [1]). All variables will have an attribute .grad, which is the gradient

sum.backward();console.log(varA.grad);

Multiple backward passes will add up the gradients. After you are done with the variable, delete the computation graph by calling delete().

Sparse tensors

Sparse tensors are tensors where most entries are zero, for example the following one:

consta=newCPUTensor([3,3],[1,0,0,0,2,0,0,3,4]);

TensorJS supports sparse tensors in coordinate format, where we store the coordinates and values of the nonzero entries in two tensors:

constindices=[0,0,// Corresponds to value 11,1,// Corresponds to value 22,1,// Corresponds to value 32,2// Corresponds to value 4];constindiceTensor=newCPUTensor([4,2],indices,'uint32');constvalues=[1,2,3,4];constvalueTensor=newCPUTensor([4],values);constsparseTensor=newSparseTensor(valueTensor,indiceTensor,[3,3]);

The implementations of the operators for sparse tensors only consider the nonzero entries and are thus faster than their dense counterparts.

Note that some operators make specific assumptions on the sparse tensor, for details check the corresponding documentation here.

Backend support for sparse tensors

As of now, most operators are only supported on the CPU and WASM backend. If an operation is not supported, this is noted in the docs.

Documentation

You can find the documentation here.

Contributing

See Contributing.md

Development

See Development.md

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

TensorJS

TestVersion

This is a JS/TS library for accelerated tensor computation intended to be run in the browser. It contains an implementation for numpy-style multidimensional arrays and their operators.

It also allows executing Onnx models. For examples check the examples folder.

There are three execution backends available:

  • CPU: This is implemented in plain javascript and thus not very fast. It is intended to be a reference implementation. Big optimizations are avoided for simplicity.
  • Web Assembly: This is implemented in Rust. It is optimized for faster execution (although right now there is a lot of work to be done).
  • GPU: This uses WebGL to enable very fast execution and should be used whenever a GPU is available. It is typically ~10-100 times faster than the WASM backend (except for a few operators). Most of the development focus was spent here so this is by far the fastest backend.

How to use

Install with

$ npm install @hoff97/tensor-js

and then import

import*astjsfrom'@hoff97/tensor-js';

or import the stuff you need directly.

Tensors

You can create tensors of the respective backend like this:

  • CPU:
    consttensor=newtjs.tensor.cpu.CPUTensor([2,2],[1,2,3,4]);
  • WASM:
    consttensor=newtjs.tensor.wasm.WASMTensor(newFloat32Array([1,2,3,4]),[2,2]);
  • GPU:
    consttensor=newtjs.tensor.gpu.GPUTensor(newFloat32Array([1,2,3,4]),[2,2]);
    or directly from an image/video element:
    constvideo: HTMLVideoElement=document.querySelector("#videoElement");consttensor=tjs.tensor.gpu.GPUTensor.fromData(video);
    which will be a tensor with shape [height,width,4] and data type float32. Creating a GPU tensor from a video element will usually be pretty fast. Creation from an image not necessarily, since here the image data first has to be transferred to the GPU.

Tensor operations

Once you have created a tensor, you can do operations on it, for example:

  • Add two tensors
    constres=a.add(b);
  • Matrix multiplication
    constres=a.matMul(b);
  • Find the maximum
    constres=a.max(1);

For a list of all operators, see the docs. Most operators will behave like their numpy/pytorch counterparts.

Reading values

When you want to read data from a tensor:

constvalues=awaittensor.getValues();

which will give you the values as a array of the values. For CPU tensors you can also get the value at an index:

constvalue=tensor.get([1,2,3,4]);

Data types

Tensors are created with float values (using 32 bits) by default. You can specify another data type on creation:

consttensor=newtjs.tensor.cpu.CPUTensor([2,2],[1,2,3,4],'float16');

or cast to another data type with:

constcasted=tensor.cast('float16');

The available data types are float64, float32, float16, int32, int16, int8, uint32, uint16, uint8. Note that not all backends support all data types:

  • CPU: Supports all data types, but float16 will be represented as float32 internally
  • WASM: Supports all except float16
  • GPU: Supports all except float64. Note that except for float16, all other data types will be represented by float32 internally, since WebGL1 does not allow writing anything else than floats to frame buffers. This means that for int32 and uint32, not the whole range of values of the respective data type is available.

The data type of a tensor can be accessed via tensor.dtype. Additionally, each tensor has a generic type argument, which will carry its data type:

consttensor: Tensor<'float16'>=newtjs.tensor.cpu.CPUTensor([2,2],[1,2,3,4],'float16');

This allows type checking tensor operations, which means that only tensor operations with the same data type compile when using typescript. The generic type defaults to float32. If you want to represent the data type of a tensor with an unknown data type, write for example

consttensor: Tensor<any>=a.add(b);

or alternatively

consttensor: Tensor<DType>=a.add(b);

Converting between backends

You can conver a tensor to a different backend like so:

constcpuTensor=awaittjs.util.convert.toCPU(tensor);constwasmTensor=awaittjs.util.convert.toWASM(tensor);constgpuTensor=awaittjs.util.convert.toGPU(tensor);

Note that converting between backends (especially from/to WebGL) is an expensive operation and should be prevented if possible!

Onnx model support

You can load an onnx model like this:

constrespones=awaitfetch(`model.onnx`);constbuffer=awaitres.arrayBuffer();constmodel=newtjs.onnx.model.OnnxModel(buffer);

To see all supported operators, check the supported operator list.

You will very likely want to run this model on the GPU. To do this:

awaitmodel.toGPU();

Optimizations

There are a few optimization passes that can be done on an Onnx model to get faster execution. To do this, run

model.optimize()

Running with half precision

By default full precision floats (32-bits) are used for model execution. On the GPU backend, you can try executing with half precision, but be aware that this might not work for all models. To use half precision, specify this when loading the model:

constmodel=newtjs.onnx.model.OnnxModel(buffer,{precision: 16})model.toGPU();

For the best performance you should also create your GPU tensors with half precision

consttensor=newtjs.tensor.gpu.GPUTensor(newFloat32Array([1,2,3,4]),[2,2],'float16');

The outputs of the model will be half-precision tensors as well. To read the values of a half precision gpu tensor, you have to convert it to full precision first, which can be done with:

constvalues=awaittensor.cast('float32').getValues();

Other performance considerations

Try to run your models with static input sizes. TensorJS will compile specialized versions of all operations after enough forward passes. For this the input shapes of the tensors have to be constant though.

Autograd functionality

Automatic differentiation is supported. For this create variables from all your tensors:

consta=newtjs.tensor.cpu.CPUTensor([2,2],[1,2,3,4]);constb=newtjs.tensor.cpu.CPUTensor([2,2],[5,6,7,8]);constvarA=newtjs.autograd.Variable(a);constvarB=newtjs.autograd.Variable(b);

Or use the utility methods:

constvarA=tjs.autograd.Variable.create([2,2],[1,2,3,4],'GPU');constvideoElement=document.querySelector("#videoElement");constvarB=tjs.autograd.Variable.fromData(videoElement);

Afterwards you can perform normal tensor operations:

constmul=varA.matMul(varB);constsum=mul.sum();

To perform a backward pass, call backward on a scalar tensor (a tensor with shape [1]). All variables will have an attribute .grad, which is the gradient

sum.backward();console.log(varA.grad);

Multiple backward passes will add up the gradients. After you are done with the variable, delete the computation graph by calling delete().

Sparse tensors

Sparse tensors are tensors where most entries are zero, for example the following one:

consta=newCPUTensor([3,3],[1,0,0,0,2,0,0,3,4]);

TensorJS supports sparse tensors in coordinate format, where we store the coordinates and values of the nonzero entries in two tensors:

constindices=[0,0,// Corresponds to value 11,1,// Corresponds to value 22,1,// Corresponds to value 32,2// Corresponds to value 4];constindiceTensor=newCPUTensor([4,2],indices,'uint32');constvalues=[1,2,3,4];constvalueTensor=newCPUTensor([4],values);constsparseTensor=newSparseTensor(valueTensor,indiceTensor,[3,3]);

The implementations of the operators for sparse tensors only consider the nonzero entries and are thus faster than their dense counterparts.

Note that some operators make specific assumptions on the sparse tensor, for details check the corresponding documentation here.

Backend support for sparse tensors

As of now, most operators are only supported on the CPU and WASM backend. If an operation is not supported, this is noted in the docs.

Documentation

You can find the documentation here.

Contributing

See Contributing.md

Development

See Development.md

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

TensorJS

TestVersion

This is a JS/TS library for accelerated tensor computation intended to be run in the browser. It contains an implementation for numpy-style multidimensional arrays and their operators.

It also allows executing Onnx models. For examples check the examples folder.

There are three execution backends available:

  • CPU: This is implemented in plain javascript and thus not very fast. It is intended to be a reference implementation. Big optimizations are avoided for simplicity.
  • Web Assembly: This is implemented in Rust. It is optimized for faster execution (although right now there is a lot of work to be done).
  • GPU: This uses WebGL to enable very fast execution and should be used whenever a GPU is available. It is typically ~10-100 times faster than the WASM backend (except for a few operators). Most of the development focus was spent here so this is by far the fastest backend.

How to use

Install with

$ npm install @hoff97/tensor-js

and then import

import*astjsfrom'@hoff97/tensor-js';

or import the stuff you need directly.

Tensors

You can create tensors of the respective backend like this:

  • CPU:
    consttensor=newtjs.tensor.cpu.CPUTensor([2,2],[1,2,3,4]);
  • WASM:
    consttensor=newtjs.tensor.wasm.WASMTensor(newFloat32Array([1,2,3,4]),[2,2]);
  • GPU:
    consttensor=newtjs.tensor.gpu.GPUTensor(newFloat32Array([1,2,3,4]),[2,2]);
    or directly from an image/video element:
    constvideo: HTMLVideoElement=document.querySelector("#videoElement");consttensor=tjs.tensor.gpu.GPUTensor.fromData(video);
    which will be a tensor with shape [height,width,4] and data type float32. Creating a GPU tensor from a video element will usually be pretty fast. Creation from an image not necessarily, since here the image data first has to be transferred to the GPU.

Tensor operations

Once you have created a tensor, you can do operations on it, for example:

  • Add two tensors
    constres=a.add(b);
  • Matrix multiplication
    constres=a.matMul(b);
  • Find the maximum
    constres=a.max(1);

For a list of all operators, see the docs. Most operators will behave like their numpy/pytorch counterparts.

Reading values

When you want to read data from a tensor:

constvalues=awaittensor.getValues();

which will give you the values as a array of the values. For CPU tensors you can also get the value at an index:

constvalue=tensor.get([1,2,3,4]);

Data types

Tensors are created with float values (using 32 bits) by default. You can specify another data type on creation:

consttensor=newtjs.tensor.cpu.CPUTensor([2,2],[1,2,3,4],'float16');

or cast to another data type with:

constcasted=tensor.cast('float16');

The available data types are float64, float32, float16, int32, int16, int8, uint32, uint16, uint8. Note that not all backends support all data types:

  • CPU: Supports all data types, but float16 will be represented as float32 internally
  • WASM: Supports all except float16
  • GPU: Supports all except float64. Note that except for float16, all other data types will be represented by float32 internally, since WebGL1 does not allow writing anything else than floats to frame buffers. This means that for int32 and uint32, not the whole range of values of the respective data type is available.

The data type of a tensor can be accessed via tensor.dtype. Additionally, each tensor has a generic type argument, which will carry its data type:

consttensor: Tensor<'float16'>=newtjs.tensor.cpu.CPUTensor([2,2],[1,2,3,4],'float16');

This allows type checking tensor operations, which means that only tensor operations with the same data type compile when using typescript. The generic type defaults to float32. If you want to represent the data type of a tensor with an unknown data type, write for example

consttensor: Tensor<any>=a.add(b);

or alternatively

consttensor: Tensor<DType>=a.add(b);

Converting between backends

You can conver a tensor to a different backend like so:

constcpuTensor=awaittjs.util.convert.toCPU(tensor);constwasmTensor=awaittjs.util.convert.toWASM(tensor);constgpuTensor=awaittjs.util.convert.toGPU(tensor);

Note that converting between backends (especially from/to WebGL) is an expensive operation and should be prevented if possible!

Onnx model support

You can load an onnx model like this:

constrespones=awaitfetch(`model.onnx`);constbuffer=awaitres.arrayBuffer();constmodel=newtjs.onnx.model.OnnxModel(buffer);

To see all supported operators, check the supported operator list.

You will very likely want to run this model on the GPU. To do this:

awaitmodel.toGPU();

Optimizations

There are a few optimization passes that can be done on an Onnx model to get faster execution. To do this, run

model.optimize()

Running with half precision

By default full precision floats (32-bits) are used for model execution. On the GPU backend, you can try executing with half precision, but be aware that this might not work for all models. To use half precision, specify this when loading the model:

constmodel=newtjs.onnx.model.OnnxModel(buffer,{precision: 16})model.toGPU();

For the best performance you should also create your GPU tensors with half precision

consttensor=newtjs.tensor.gpu.GPUTensor(newFloat32Array([1,2,3,4]),[2,2],'float16');

The outputs of the model will be half-precision tensors as well. To read the values of a half precision gpu tensor, you have to convert it to full precision first, which can be done with:

constvalues=awaittensor.cast('float32').getValues();

Other performance considerations

Try to run your models with static input sizes. TensorJS will compile specialized versions of all operations after enough forward passes. For this the input shapes of the tensors have to be constant though.

Autograd functionality

Automatic differentiation is supported. For this create variables from all your tensors:

consta=newtjs.tensor.cpu.CPUTensor([2,2],[1,2,3,4]);constb=newtjs.tensor.cpu.CPUTensor([2,2],[5,6,7,8]);constvarA=newtjs.autograd.Variable(a);constvarB=newtjs.autograd.Variable(b);

Or use the utility methods:

constvarA=tjs.autograd.Variable.create([2,2],[1,2,3,4],'GPU');constvideoElement=document.querySelector("#videoElement");constvarB=tjs.autograd.Variable.fromData(videoElement);

Afterwards you can perform normal tensor operations:

constmul=varA.matMul(varB);constsum=mul.sum();

To perform a backward pass, call backward on a scalar tensor (a tensor with shape [1]). All variables will have an attribute .grad, which is the gradient

sum.backward();console.log(varA.grad);

Multiple backward passes will add up the gradients. After you are done with the variable, delete the computation graph by calling delete().

Sparse tensors

Sparse tensors are tensors where most entries are zero, for example the following one:

consta=newCPUTensor([3,3],[1,0,0,0,2,0,0,3,4]);

TensorJS supports sparse tensors in coordinate format, where we store the coordinates and values of the nonzero entries in two tensors:

constindices=[0,0,// Corresponds to value 11,1,// Corresponds to value 22,1,// Corresponds to value 32,2// Corresponds to value 4];constindiceTensor=newCPUTensor([4,2],indices,'uint32');constvalues=[1,2,3,4];constvalueTensor=newCPUTensor([4],values);constsparseTensor=newSparseTensor(valueTensor,indiceTensor,[3,3]);

The implementations of the operators for sparse tensors only consider the nonzero entries and are thus faster than their dense counterparts.

Note that some operators make specific assumptions on the sparse tensor, for details check the corresponding documentation here.

Backend support for sparse tensors

As of now, most operators are only supported on the CPU and WASM backend. If an operation is not supported, this is noted in the docs.

Documentation

You can find the documentation here.

Contributing

See Contributing.md

Development

See Development.md

Releases

Packages

Used by

Contributors

Languages