/
ica.js
89 lines (82 loc) · 1.84 KB
/
ica.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
import Matrix from '../util/matrix.js'
import { PCA } from './pca.js'
/**
* Independent component analysis
*/
export default class ICA {
// https://www.slideshare.net/sfchaos/numpy-scipy-9039097
/**
* @param {number | null} [rd] Reduced dimension
*/
constructor(rd = null) {
this._w = null
this._alpha = 0.1
this._rd = rd ?? 0
}
/**
* Fit model.
*
* @param {Array<Array<number>>} x Training data
*/
fit(x) {
x = Matrix.fromArray(x)
const d = x.cols
if (!this._w) {
this._w = Matrix.zeros(d, d)
}
x = Matrix.sub(x, x.mean(0))
const pca = new PCA()
pca.fit(x)
const z = Matrix.fromArray(pca.predict(x))
const eps = 1.0e-12
const r = []
for (let i = 0; i < d; i++) {
let w = Matrix.randn(d, 1)
if (i > 0) {
const wi = this._w.slice(0, i)
const k = wi.dot(w)
wi.mult(k)
w.sub(wi.sum(0).t)
}
w.div(w.norm())
let maxCount = 1.0e4
while (maxCount-- > 0) {
const wx = z.dot(w)
const gwx = Matrix.map(wx, v => Math.tanh(v * this._alpha))
const xgwx = Matrix.mult(z, gwx)
const v1 = xgwx.mean(0).t
const g_wx = Matrix.map(wx, v => this._alpha * (1 - Math.tanh(this._alpha * v) ** 2))
const v2 = Matrix.mult(w, g_wx.mean())
const w1 = Matrix.sub(v1, v2)
if (i > 0) {
const wi = this._w.row(r)
const k = wi.dot(w1)
wi.mult(k)
w1.sub(wi.sum(0).t)
}
w1.div(w1.norm())
const e = Matrix.mult(w, w1)
if (Math.abs(Math.abs(e.sum()) - 1) < eps) {
break
}
w = w1
}
this._w.set(i, 0, w.t)
r.push(i)
}
}
/**
* Returns reduced datas.
*
* @param {Array<Array<number>>} x Training data
* @returns {Array<Array<number>>} Predicted values
*/
predict(x) {
x = Matrix.fromArray(x)
const w = this._w.t
if (this._rd > 0 && this._rd < w.cols) {
w.resize(w.rows, this._rd)
}
return x.dot(w).toArray()
}
}