recurrent-js-gpu
Version:
GPU-accelerated Deep Recurrent Neural Networks and LSTMs in Typescript. Ported, object-oriented and refactored version of Andrej Karpathy's recurrent-js (https://github.com/karpathy/recurrentjs)
316 lines • 10.7 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const Assertable_1 = require("./utils/Assertable");
const R_1 = require("./R");
const GPU = require("gpu.js");
class Mat extends Assertable_1.Assertable {
constructor(rows, cols) {
super();
this.rows = rows;
this.cols = cols;
this.length = rows * cols;
this.w = R_1.R.zeros(this.length);
this.dw = R_1.R.zeros(this.length);
}
static isGPUSupported() { return GPU.utils.isWebGlSupported() || false; }
static isGPUAccelerated() {
if (Mat.tryGPU) {
return true;
}
else {
return false;
}
}
;
static switchOnGPUSupport() {
Mat.tryGPU = true;
}
static switchOffGPUSupport() {
Mat.tryGPU = false;
}
static getKernel(kernel, length, constants = {}) {
const lengthKey = length.toString();
if (!this.gpuKernelRegister[kernel] || !this.gpuKernelRegister[kernel][lengthKey]) {
this.registerKernel(kernel, length, constants);
return this.gpuKernelRegister[kernel][lengthKey];
}
else {
return this.gpuKernelRegister[kernel][lengthKey];
}
}
static registerKernel(kernel, length, constants = {}) {
let lengthKey = length.toString();
if (!this.gpuKernelRegister[kernel]) {
this.gpuKernelRegister[kernel] = {};
}
if (!this.gpuKernelRegister[kernel][lengthKey]) {
this.gpuKernelRegister[kernel][lengthKey] = function () { };
}
let newKernel;
switch (kernel) {
case 'setFrom':
newKernel = this.gpu.createKernel(function (arr0) {
return arr0[this.thread.x];
});
break;
case 'add':
newKernel = this.gpu.createKernel(function (arr0, arr1) {
return arr0[this.thread.x] + arr1[this.thread.x];
});
break;
case 'mul':
newKernel = this.gpu.createKernel(function (arr0, arr1) {
let cellValue = 0;
const rowA = Math.floor((this.thread.x) / (this.constants.size / this.constants.rows0));
const colB = this.thread.x % this.constants.cols1;
for (let k = 0; k < this.constants.rows1; k++) {
cellValue += arr0[rowA * this.constants.cols0 + k] * arr1[k * this.constants.cols1 + colB];
}
return cellValue;
}, {
'constants': constants
});
break;
case 'eltmul':
newKernel = this.gpu.createKernel(function (arr0, arr1) {
return arr0[this.thread.x] * arr1[this.thread.x];
});
break;
case 'tanh':
newKernel = this.gpu.createKernel(function (arr0) {
return (1 - Math.exp(-2 * arr0[this.thread.x])) / (1 + Math.exp(-2 * arr0[this.thread.x]));
});
break;
case 'sig':
newKernel = this.gpu.createKernel(function (arr0) {
return 1.0 / (1 + Math.exp(-arr0[this.thread.x]));
});
break;
case 'relu':
newKernel = this.gpu.createKernel(function (arr0) {
return Math.max(0, arr0[this.thread.x]);
});
break;
default:
throw Error('[' + new Date().toISOString() + '] Kernel: ' + kernel + ' not supported');
}
this.gpuKernelRegister[kernel][lengthKey] = newKernel.setOutput([length]);
}
get(row, col) {
const ix = this.getIndexBy(row, col);
Mat.assert(ix >= 0 && ix < this.w.length);
return this.w[ix];
}
set(row, col, v) {
const ix = this.getIndexBy(row, col);
Mat.assert(ix >= 0 && ix < this.w.length);
this.w[ix] = v;
}
getIndexBy(row, col) {
return (row * this.cols) + col;
}
setFrom(arr) {
Mat.assert(arr.length === this.length, 'Mat.setFrom dimensions misaligned');
let setFrom;
if (Mat.isGPUAccelerated()) {
setFrom = Mat.getKernel('setFrom', arr.length);
}
else {
setFrom = (arr) => {
const result = new Array(arr.length);
for (let i = 0; i < arr.length; i++) {
result[i] = arr[i];
}
return result;
};
}
this.w = setFrom(arr);
}
setColumn(m, i) {
for (let q = 0; q < m.w.length; q++) {
this.w[(this.cols * q) + i] = m.w[q];
}
}
update(alpha) {
for (let i = 0; i < this.length; i++) {
if (this.dw[i] !== 0) {
this.w[i] += -alpha * this.dw[i];
this.dw[i] = 0;
}
}
}
static toJSON(m) {
const json = {};
json['rows'] = m.rows || m.n;
json['cols'] = m.cols || m.d;
json['w'] = m.w;
return json;
}
static fromJSON(json) {
const mat = new Mat(json.n, json.d);
for (let i = 0; i < mat.length; i++) {
mat.w[i] = json.w[i];
}
return mat;
}
static add(m1, m2) {
Mat.assert(m1.w.length === m2.w.length, 'Mat.add dimensions misaligned');
const out = new Mat(m1.rows, m1.cols);
let _add;
if (Mat.isGPUAccelerated()) {
_add = Mat.getKernel('add', out.length);
}
else {
_add = (arr0, arr1) => {
const result = new Array(arr0.length);
for (let i = 0; i < result.length; i++) {
result[i] = arr0[i] + arr1[i];
}
return result;
};
}
out.w = _add(m1.w, m2.w);
return out;
}
static sig(m) {
const out = new Mat(m.rows, m.cols);
let _sig;
if (Mat.isGPUAccelerated) {
_sig = Mat.getKernel('sig', out.length);
}
else {
_sig = (arr0) => {
const result = new Array(arr0.length);
for (let i = 0; i < result.length; i++) {
result[i] = Mat.sigmoid(arr0[i]);
}
return result;
};
}
out.w = _sig(m.w);
return out;
}
static sigmoid(x) {
return 1.0 / (1 + Math.exp(-x));
}
static relu(m) {
const out = new Mat(m.rows, m.cols);
let _relu;
if (Mat.isGPUAccelerated) {
_relu = Mat.getKernel('relu', out.length);
}
else {
_relu = (arr0) => {
const result = new Array(arr0.length);
for (let i = 0; i < result.length; i++) {
result[i] = Math.max(0, arr0[i]);
}
return result;
};
}
out.w = _relu(m.w);
return out;
}
static mul(m1, m2) {
Mat.assert(m1.cols === m2.rows, 'Mat.mul dimensions misaligned');
const out = new Mat(m1.rows, m2.cols);
if (Mat.isGPUAccelerated()) {
const constants = { size: out.length, rows0: m1.rows, cols0: m1.cols, rows1: m2.rows, cols1: m2.cols };
const _mul = this.getKernel('mul', out.length, constants);
out.w = _mul(m1.w, m2.w);
}
else {
const _mul = (arr0, arr1, rows0, cols0, rows1, cols1) => {
const result = new Array(rows0 * cols1);
for (let x = 0; x < result.length; x++) {
result[x] = Mat.mul1d(x, arr0, arr1, rows0, cols0, rows1, cols1);
}
return result;
};
out.w = _mul(m1.w, m2.w, m1.rows, m1.cols, m2.rows, m2.cols);
}
return out;
}
static mul1d(x, arr0, arr1, rows0, cols0, rows1, cols1) {
let cellValue = 0;
const rowA = Math.floor((x + 1) / (arr0.length / rows0));
const colB = x % cols1;
for (let k = 0; k < rows1; k++) {
cellValue += arr0[rowA * cols0 + k] * arr1[k * cols1 + colB];
}
return cellValue;
}
static tanh(m) {
const out = new Mat(m.rows, m.cols);
let _tanh;
if (Mat.isGPUAccelerated()) {
_tanh = this.getKernel('tanh', out.length);
}
else {
_tanh = function (arr0) {
const result = new Array(arr0.length);
for (let i = 0; i < result.length; i++) {
result[i] = Math.tanh(arr0[i]);
}
return result;
};
}
out.w = _tanh(m.w);
return out;
}
static dot(m1, m2) {
Mat.assert(m1.w.length === m2.w.length, 'Mat.dot dimensions misaligned');
const out = new Mat(1, 1);
let dot = 0.0;
for (let i = 0; i < m1.w.length; i++) {
dot += m1.w[i] * m2.w[i];
}
out.w[0] = dot;
return out;
}
static eltmul(m1, m2) {
Mat.assert(m1.w.length === m2.w.length, 'Mat.eltmul dimensions misaligned');
const out = new Mat(m1.rows, m1.cols);
let _eltmult;
if (Mat.isGPUAccelerated()) {
_eltmult = Mat.getKernel('eltmul', out.length);
}
else {
_eltmult = (arr0, arr1) => {
const result = new Array(arr0.length);
for (let i = 0; i < result.length; i++) {
result[i] = arr0[i] * arr1[i];
}
return result;
};
}
out.w = _eltmult(m1.w, m2.w);
return out;
}
static rowPluck(m, rowIndex) {
Mat.assert(rowIndex >= 0 && rowIndex < m.rows, 'Mat.rowPluck dimensions misaligned');
const out = new Mat(m.cols, 1);
for (let i = 0; i < m.cols; i++) {
out.w[i] = m.w[m.cols * rowIndex + i];
}
return out;
}
}
Mat.gpu = (() => {
const _warn = console.warn;
console.warn = function () { };
const gpu = new GPU();
console.warn = _warn;
if (Mat.isGPUSupported()) {
console.log('GPU-Acceleration active!');
}
else {
console.log('No GPU-Acceleration available. Falling back to CPU-Functions.');
}
return gpu;
})();
Mat.tryGPU = true;
Mat.cpuLimit = 50;
Mat.gpuKernelRegister = {};
exports.Mat = Mat;
//# sourceMappingURL=Mat.js.map