matrixlib-js
Version:
MatrixLib.js is a JavaScript simple matrix calculation library.
374 lines (334 loc) • 10.9 kB
JavaScript
/**
* # 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
}
};
}
};
}();
};