/
huber_regression.js
91 lines (83 loc) · 2 KB
/
huber_regression.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
90
91
import Matrix from '../util/matrix.js'
/**
* Huber regression
*/
export default class HuberRegression {
// https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.HuberRegressor.html
// https://www.smartbowwow.com/2019/05/blog-post.html
/**
* @param {number} [e] Threshold of outliers
* @param {'rls' | 'gd'} [method] Method name
* @param {number} [lr] Learning rate
*/
constructor(e = 1.35, method = 'rls', lr = 1) {
this._e = e
this._s = 1
this._lr = lr
this._w = null
this._method = method
}
/**
* Fit model.
*
* @param {Array<Array<number>>} x Training data
* @param {Array<Array<number>>} y Target values
*/
fit(x, y) {
x = Matrix.fromArray(x)
const xh = Matrix.resize(x, x.rows, x.cols + 1, 1)
y = Matrix.fromArray(y)
if (!this._w) {
this._w = xh.tDot(xh).solve(xh.tDot(y))
}
if (this._method === 'rls') {
this._rls(xh, y)
} else if (this._method === 'gd') {
this._gd(xh, y)
}
}
_gd(x, y) {
const r = x.dot(this._w)
r.sub(y)
const dr = Matrix.map(r, v => {
if (Math.abs(v / this._s) <= this._e) {
return v / this._s
} else if (v / this._s > this._e) {
return this._e
} else {
return -this._e
}
})
const dw = x.tDot(dr)
dw.mult(this._lr / x.rows)
this._w.sub(dw)
const rds = Matrix.map(dr, (v, i) => v * (-r.at(i) / this._s ** 2))
this._s -= (rds.sum() * this._lr) / x.rows
}
_rls(x, y) {
const r = x.dot(this._w)
r.sub(y)
const w = Matrix.map(r, v => {
if (Math.abs(v / this._s) <= this._e) {
return 1
}
return this._e / Math.abs(v / this._s)
})
for (let i = 0; i < this._w.cols; i++) {
const xhw = Matrix.mult(x, w.col(i))
const wi = xhw.tDot(x).solve(xhw.tDot(y.col(i)))
this._w.set(0, i, wi)
}
}
/**
* Returns predicted values.
*
* @param {Array<Array<number>>} x Sample data
* @returns {Array<Array<number>>} Predicted values
*/
predict(x) {
x = Matrix.fromArray(x)
const xh = Matrix.resize(x, x.rows, x.cols + 1, 1)
return xh.dot(this._w).toArray()
}
}