UNPKG

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
"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