UNPKG

@ai-on-browser/data-analysis-models

Version:

Data analysis model package without any dependencies

324 lines (305 loc) 7.56 kB
import Matrix from '../util/matrix.js' /** * Restricted Boltzmann machine */ export class RBM { // https://recruit.gmo.jp/engineer/jisedai/blog/rbm_movie_recommendation_pytorch/ // https://qiita.com/t_Signull/items/f776aecb4909b7c5c116 // https://en.wikipedia.org/wiki/Restricted_Boltzmann_machine /** * @param {number} hiddenSize Size of hidden layer * @param {number} [lr] Learning rate */ constructor(hiddenSize, lr = 0.01) { this._hidden = hiddenSize this._visible = null this._w = null this._a = [] this._b = [] this._lr = lr this._k = 1 } _normalize(x) { let max = -Infinity let min = Infinity for (let k = 0; k < x.length; k++) { for (let i = 0; i < x[k].length; i++) { max = Math.max(max, x[k][i]) min = Math.min(min, x[k][i]) } } for (let k = 0; k < x.length; k++) { for (let i = 0; i < x[k].length; i++) { x[k][i] = x[k][i] < (max + min) / 2 ? 0 : 1 } } } _sgm(x) { return 1 / (1 + Math.exp(-x)) } _h(v, sample = false) { const h = [] for (let k = 0; k < v.length; k++) { h[k] = [] for (let j = 0; j < this._w[0].length; j++) { let a = this._b[j] for (let i = 0; i < this._w.length; i++) { a += this._w[i][j] * v[k][i] } h[k][j] = this._sgm(a) if (sample) { h[k][j] = h[k][j] > Math.random() ? 1 : 0 } } } return h } _v(h, sample = false) { const v = [] for (let k = 0; k < h.length; k++) { v[k] = [] for (let i = 0; i < this._w.length; i++) { let a = this._a[i] for (let j = 0; j < this._w[i].length; j++) { a += this._w[i][j] * h[k][j] } v[k][i] = this._sgm(a) if (sample) { v[k][i] = v[k][i] > Math.random() ? 1 : 0 } } } return v } /** * Fit model. * @param {Array<Array<number>>} x Training data */ fit(x) { this._normalize(x) if (!this._w) { this._visible = x[0].length this._w = Matrix.randn(this._visible, this._hidden).toArray() this._a = Array(this._visible).fill(0) this._b = Array(this._hidden).fill(0) } const v1 = x const h1 = this._h(v1) let vn = this._v(h1) let hn = this._h(vn) for (let k = 1; k < this._k; k++) { vn = this._v(hn) hn = this._h(vn) } for (let i = 0; i < this._w.length; i++) { for (let j = 0; j < this._w[i].length; j++) { let v = 0 for (let k = 0; k < x.length; k++) { v += v1[k][i] * h1[k][j] - vn[k][i] * hn[k][j] } this._w[i][j] += (this._lr * v) / x.length } } for (let i = 0; i < this._w.length; i++) { let v = 0 for (let k = 0; k < x.length; k++) { v += v1[k][i] - vn[k][i] } this._a[i] += (this._lr * v) / x.length } for (let j = 0; j < this._w[0].length; j++) { let v = 0 for (let k = 0; k < x.length; k++) { v += h1[k][j] - hn[k][j] } this._b[j] += (this._lr * v) / x.length } } /** * Return a energy value of the data. * @param {Array<number>} v Sample data * @param {Array<number>} h Target values * @returns {number} Energy value */ energy(v, h) { this._normalize([v, h]) let e = 0 for (let i = 0; i < this._w.length; i++) { for (let j = 0; j < this._w[i].length; j++) { e -= this._w[i][j] * v[i] * h[j] } } for (let i = 0; i < this._w.length; i++) { e -= this._a[i] * v[i] } for (let j = 0; j < this._w[0].length; j++) { e -= this._b[j] * h[j] } return e } /** * Returns predicted values. * @param {Array<number>} x Sample data * @returns {(0 | 1)[]} Predicted values */ predict(x) { this._normalize(x) const h1 = this._h(x, true) return this._v(h1, true) } } /** * Gaussian-Bernouili Restricted Boltzmann machine */ export class GBRBM { // https://www.ieice.org/publications/conference-FIT-DVDs/FIT2015/data/pdf/F-024.pdf // https://qiita.com/ryo_he_0/items/150b4845a8ea968cc6f0 /** * @param {number} hiddenSize Size of hidden layer * @param {number} [lr] Learning rate * @param {boolean} [fixSigma] Do not learn sigma or not */ constructor(hiddenSize, lr = 0.01, fixSigma = false) { this._hidden = hiddenSize this._visible = null this._w = null this._b = [] this._z = [] this._c = [] this._lr = lr this._fixSigma = fixSigma this._k = 1 } _randn() { return Math.sqrt(-2 * Math.log(Math.random())) * Math.cos(2 * Math.PI * Math.random()) } get _s() { return this._z.map(Math.exp) } _h(v, sample = false) { const h = [] const s = this._s for (let k = 0; k < v.length; k++) { h[k] = [] for (let j = 0; j < this._w[0].length; j++) { let a = this._c[j] for (let i = 0; i < this._w.length; i++) { a += (this._w[i][j] * v[k][i]) / s[i] } h[k][j] = 1 / (1 + Math.exp(-a)) if (sample) { h[k][j] = h[k][j] > Math.random() ? 1 : 0 } } } return h } _v(h, sample = false) { const v = [] const s = this._s for (let k = 0; k < h.length; k++) { v[k] = [] for (let i = 0; i < this._w.length; i++) { let a = this._b[i] for (let j = 0; j < this._w[i].length; j++) { a += this._w[i][j] * h[k][j] } v[k][i] = a if (sample) { v[k][i] += this._randn() * Math.sqrt(s[i]) } } } return v } /** * Fit model. * @param {Array<Array<number>>} x Training data */ fit(x) { if (!this._w) { this._visible = x[0].length this._w = Matrix.randn(this._visible, this._hidden).toArray() this._b = Array(this._visible).fill(0) this._z = Array(this._visible).fill(0) this._c = Array(this._hidden).fill(0) } const v1 = x const h1 = this._h(x) let vn = this._v(h1, true) let hn = this._h(vn, true) for (let k = 0; k < this._k; k++) { vn = this._v(hn, true) hn = this._h(vn, true) } const s = this._s if (!this._fixSigma) { for (let i = 0; i < this._w.length; i++) { let s1 = 0 let s2 = 0 for (let t = 0; t < x.length; t++) { s1 += (v1[t][i] - this._b[i]) ** 2 / 2 s2 += (vn[t][i] - this._b[i]) ** 2 / 2 for (let j = 0; j < this._w[0].length; j++) { s1 -= h1[t][j] * this._w[i][j] * v1[t][i] s2 -= hn[t][j] * this._w[i][j] * vn[t][i] } } this._z[i] += (this._lr * (s1 - s2)) / s[i] / x.length } } for (let i = 0; i < this._w.length; i++) { for (let j = 0; j < this._w[i].length; j++) { let v = 0 for (let t = 0; t < x.length; t++) { v += v1[t][i] * h1[t][j] - vn[t][i] * hn[t][j] } this._w[i][j] += (this._lr * v) / s[i] / x.length } } for (let i = 0; i < this._w.length; i++) { let v = 0 for (let t = 0; t < x.length; t++) { v += v1[t][i] - vn[t][i] } this._b[i] += (this._lr * v) / s[i] / x.length } for (let j = 0; j < this._w[0].length; j++) { let v = 0 for (let t = 0; t < x.length; t++) { v += h1[t][j] - hn[t][j] } this._c[j] += (this._lr * v) / x.length } } /** * Return a energy value of the data. * @param {Array<number>} v Sample data * @param {Array<number>} h Target values * @returns {number} Energy value */ energy(v, h) { let e = 0 for (let i = 0; i < this._w.length; i++) { for (let j = 0; j < this._w[i].length; j++) { e -= (this._w[i][j] * v[i] * h[j]) / Math.exp(this._z[i]) } } for (let i = 0; i < this._w.length; i++) { e += (v[i] - this._b[i]) ** 2 / (2 * Math.exp(this._z[i])) } for (let j = 0; j < this._w[0].length; j++) { e -= this._c[j] * h[j] } return e } /** * Returns predicted values. * @param {Array<Array<number>>} x Sample data * @returns {Array<Array<number>>} Predicted values */ predict(x) { const h1 = this._h(x) return this._v(h1, true) } }