UNPKG

matrixlib-js

Version:

MatrixLib.js is a JavaScript simple matrix calculation library.

374 lines (334 loc) 10.9 kB
/** * # deeplearning.js - v0.2.1 * * Copyright (c) 2018 Kohei Katada. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is furnished to do * so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ module.exports = () => { 'use strict'; return new function () { const mt = require('./matrixlib-multithread.js')({ libUrl: 'https://webryone.github.io/matrixlib.js/js/matrixlib.js' // Please change as appropriate. }); /* # perceptron */ this.perceptron = { AND: async (x1, x2) => { const x = [x1, x2]; const w = [0.5, 0.5]; const b = -0.7; const tmp = await mt.math.plus(await mt.math.sum(await mt.math.multi(w, x)), b); if (tmp <= 0) { return 0; } else { return 1; } }, NAND: async (x1, x2) => { const x = [x1, x2]; const w = [-0.5, -0.5]; // 重みとバイアスだけが AND と違う const b = 0.7; const tmp = await mt.math.plus(await mt.math.sum(await mt.math.multi(w, x)), b); if (tmp <= 0) { return 0; } else { return 1; } }, OR: async (x1, x2) => { const x = [x1, x2]; const w = [0.5, 0.5]; // 重みとバイアスだけが AND と違う const b = -0.2; const tmp = await mt.math.plus(await mt.math.sum(await mt.math.multi(w, x)), b); if (tmp <= 0) { return 0; } else { return 1; } }, XOR: async (x1, x2) => { const s1 = await this.perceptron.NAND(x1, x2); const s2 = await this.perceptron.OR(x1, x2); return await this.perceptron.AND(s1, s2); } }; /* # fn */ this.fn = { step: async (x) => { return await mt.math.boolToInt(await mt.math.more(x, 0)); }, sigmoid: async (x) => { return await mt.math.div(1, await mt.math.plus(1, await mt.math.exp(await mt.math.multi(x, -1)))); }, relu: async (x) => { return await mt.math.maximum(0, x); }, identity: async (x) => { return x; }, softmax: async (a) => { if (mt.math.util.shape(a)[0] === 2) { a = mt.math.matrix.util.colToRow(a); a = await mt.math.minus(a, await mt.math.max(a, 0)); const y = await mt.math.div(await mt.math.exp(a), await mt.math.sum(await mt.math.exp(a), 0)); return mt.math.matrix.util.colToRow(y); } const c = await mt.math.max(a); const expA = await mt.math.exp(await mt.math.minus(a, c)); // オーバーフロー対策 const sumExpA = await mt.math.sum(expA); return await mt.math.div(expA, sumExpA); }, /** * # 二乗和誤差 * @param y * @param t * @returns {Promise<*>} */ meanSquaredError: async (y, t) => { return await mt.math.multi(0.5, await mt.math.sum(await mt.math.pow(await mt.math.minus(y, t), 2))); }, /** * # バッチ対応版の交差エントロピー誤差 * @param y * @param t * @returns {Promise<*>} */ crossEntropyError: async (y, t) => { if (mt.math.util.shape(y)[1] === 0) { t = [t]; y = [y]; } // 教師データがone-hot-vectorの場合、正解ラベルのインデックスに変換 if (t.length === y.length) { t = await mt.math.maxIdx(t, 1); } const batchSize = mt.math.util.shape(y)[0]; const range = mt.math.arr.util.arange(0, batchSize, 1); let r = []; for (let idx = 0, l = range.length; idx < l; idx += 1) { r[idx] = await mt.math.div(await mt.math.multi(-1, await mt.math.sum(await mt.math.plus(await mt.math.log(y[range[idx]][t[idx]]), 1e-7))), batchSize); } return r; }, /** * # 数値微分 */ numericalDiff: async (f, x) => { const h = 1e-4; // 0.0001 return await mt.math.div(await mt.math.minus(await f(await mt.math.plus(x, h)), await f(await mt.math.minus(x, h))), await mt.math.multi(2, h)); }, /** * # 勾配 */ numericalGradient: async (f, x) => { if (!mt.util.truthy(mt.util.isArray(x[0]))) { // 1次元配列だった場合 const h = 1e-4; // 0.001 let grad = await mt.math.multi(0, x); for (let idx = 0, l = mt.math.arr.util.arange(0, mt.math.util.shape(x)[0], 1).length; idx < l; idx += 1) { let tmpVal = x[idx]; // f(x+h) x[idx] = await mt.math.plus(tmpVal, h); let fxh1 = await f(x); // f(x-h) x[idx] = await mt.math.minus(tmpVal, h); let fxh2 = await f(x); grad[idx] = await mt.math.div(await mt.math.minus(fxh1, fxh2), await mt.math.multi(2, h)); x[idx] = tmpVal; } return grad; } else { // 2次元以上の配列だった場合 let r = []; for (let i = 0, l = x.length; i < l; i += 1) { r[i] = await this.fn.numericalGradient(f, x[i]); } return r; } }, /** * # 勾配降下法 */ gradientDescent: async (f, initX, lr, stepNum) => { let x = initX; for (let i = 0, l = stepNum; i < l; i += 1) { let grad = await this.fn.numericalGradient(f, x); x = await mt.math.minus(x, await mt.math.multi(lr, grad)); } return x; }, /** * # 乗算レイヤ * @returns {Promise<*>} * @constructor */ async MulLayer () { let that = { x: null, y: null }; return { async forward (x, y) { that.x = x; that.y = y; return await mt.math.multi(x, y); }, async backward (dout) { const dx = await mt.math.multi(dout, that.y); const dy = await mt.math.multi(dout, that.x); return [dx, dy]; } }; }, /** * # 加算レイヤ * @returns {Promise<*>} * @constructor */ async AddLayer () { return { async forward (x, y) { return await mt.math.plus(x, y); }, async backward (dout) { const dx = await mt.math.multi(dout, 1); const dy = await mt.math.multi(dout, 1); return [dx, dy]; } }; }, /** * # ReLUレイヤ * @returns {Promise<*>} * @constructor */ async Relu () { let that = { mask: null }; return { async forward (x) { that.mask = await mt.math.lessEq(x, 0); let out = x.slice(); out = await mt.math.valToAnyVal(out, (_out, _mask) => { return _mask ? 0 : _out; }, that.mask); return out; }, async backward (dout) { return mt.math.valToAnyVal(dout, (_dout, _mask) => { return _mask ? 0 : _dout; }, that.mask); } }; }, /** * # Sigmoidレイヤ * @returns {Promise<*>} * @constructor */ async Sigmoid () { let that = { out: null, that: this }; return { async forward (x) { let out = that.that.sigmoid(x); that.out = out; return out; }, async backward (dout) { return await mt.math.multi(await mt.math.multi(dout, await mt.math.minus(1.0, that.out)), that.out); } }; }, /** * # Affineレイヤ * @param W * @param b * @returns {Promise<*>} * @constructor */ async Affine (W, b) { let that = { W: W, b: b, x: null, dW: null, db: null }; return { async forward (x) { that.x = x; const out = await mt.math.plus(await mt.math.dot(that.x, that.W), that.b); return out; }, async backward (dout) { const dx = await mt.math.dot(dout, mt.math.matrix.util.colToRow(that.W)); that.dW = await mt.math.dot(mt.math.matrix.util.colToRow(that.x), dout); that.db = await mt.math.sum(dout, 0); return dx; }, async getParams (key) { return that[key]; } }; }, /** * # SoftmaxWithLossレイヤ * @returns {Promise<*>} * @constructor */ async SoftmaxWithLoss () { let that = { loss: null, y: null, t: null, that: this }; return { async forward (x, t) { that.t = t; that.y = await that.that.softmax(x); that.loss = await that.that.crossEntropyError(that.y, that.t); return that.loss; }, async backward (dout) { const batchSize = mt.math.util.shape(that.t)[0]; // return await mt.math.div(await mt.math.minus(that.y, that.t), batchSize); let dx; if (that.t.length === that.y.length) { // 教師データがone-hot-vectorの場合 dx = await mt.math.div(await mt.math.minus(that.y, that.t), batchSize); } else { dx = that.y.slice(); const range = mt.math.arr.util.arange(0, batchSize, 1); for (let idx = 0, l = range.length; idx < l; idx += 1) { dx[range[idx]][that.t[idx]] = await mt.math.minus(dx[range[idx]][that.t[idx]], 1); } dx = await mt.math.div(dx, batchSize); } return dx } }; } }; }(); };