Skip to content

Commit f66ad6a

Browse files
authored
Add HLLE (#40)
1 parent 8410118 commit f66ad6a

File tree

5 files changed

+137
-1
lines changed

5 files changed

+137
-1
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ for (let i = 0; i < n; i++) {
127127
| regression | Least squares, Ridge, Lasso, Elastic net, RLS, Bayesian linear, Poisson, Least absolute deviations, Huber, Tukey, Least trimmed squares, Least median squares, Lp norm linear, SMA, Deming, Segmented, LOWESS, spline, Gaussian process, Principal components, Partial least squares, Projection pursuit, Quantile regression, k-nearest neighbor, Radius neighbor, IDW, Nadaraya Watson, Priestley Chao, Gasser Muller, RBF Network, RVM, Decision tree, Random forest, Extra trees, GBDT, XGBoost, SVR, MLP, GMR, Isotonic, Ramer Douglas Peucker, Theil-Sen, Passing-Bablok, Repeated median |
128128
| interpolation | Nearest neighbor, IDW, Linear, Spherical linear, Brahmagupta, Logarithmic, Cosine, (Inverse) Smoothstep, Cubic, (Centripetal) Catmull-Rom, Hermit, Polynomial, Lagrange, Trigonometric, Spline, RBF Network, Akima, Natural neighbor, Delaunay |
129129
| anomaly detection | Percentile, MAD, Tukey's fences, Grubbs's test, Thompson test, Tietjen Moore test, Generalized ESD, Hotelling, MT, MCD, k-nearest neighbor, LOF, PCA, OCSVM, KDE, GMM, Isolation forest, Autoencoder, GAN |
130-
| dimensionality reduction | Random projection, (Dual/Kernel) PCA, Incremental PCA, Probabilistic PCA, GPLVM, LSA, MDS, Linear discriminant analysis, NCA, ICA, Principal curve, Sammon, FastMap, Sliced inverse regression, LLE, Laplacian eigenmaps, Isomap, Diffusion map, SNE, t-SNE, UMAP, SOM, GTM, NMF, MOD, Autoencoder, VAE |
130+
| dimensionality reduction | Random projection, (Dual/Kernel) PCA, Incremental PCA, Probabilistic PCA, GPLVM, LSA, MDS, Linear discriminant analysis, NCA, ICA, Principal curve, Sammon, FastMap, Sliced inverse regression, LLE, HLLE, Laplacian eigenmaps, Isomap, Diffusion map, SNE, t-SNE, UMAP, SOM, GTM, NMF, MOD, Autoencoder, VAE |
131131
| feature selection | Mutual information, Ridge, Lasso, Elastic net, Decision tree, NCA |
132132
| transformation | Box-Cox, Yeo-Johnson |
133133
| density estimation | Histogram, Average shifted histogram, Polynomial histogram, Maximum likelihood, Kernel density estimation, k-nearest neighbor, Naive Bayes, GMM, HMM |

js/model_selector.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,7 @@ const AIMethods = [
299299
{ value: 'fastmap', title: 'FastMap' },
300300
{ value: 'sir', title: 'Sliced Inverse Regression' },
301301
{ value: 'lle', title: 'LLE' },
302+
{ value: 'hlle', title: 'HLLE' },
302303
{ value: 'laplacian_eigenmaps', title: 'Laplacian eigenmaps' },
303304
{ value: 'isomap', title: 'Isomap' },
304305
{ value: 'diffusion_map', title: 'Diffusion map' },

js/view/hlle.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import HLLE from '../../lib/model/hlle.js'
2+
3+
var dispHLLE = function (elm, platform) {
4+
const fitModel = () => {
5+
platform.fit((tx, ty, pred_cb) => {
6+
const neighbor = +elm.select('[name=neighbor_size]').property('value')
7+
const dim = platform.dimension
8+
const y = new HLLE(neighbor).predict(tx, dim)
9+
pred_cb(y)
10+
})
11+
}
12+
13+
elm.append('span')
14+
.text('Select neighbor #')
15+
.append('input')
16+
.attr('type', 'number')
17+
.attr('name', 'neighbor_size')
18+
.attr('value', 20)
19+
.attr('min', 1)
20+
elm.append('input')
21+
.attr('type', 'button')
22+
.attr('value', 'Fit')
23+
.on('click', () => fitModel())
24+
}
25+
26+
export default function (platform) {
27+
platform.setting.ml.usage = 'Click and add data point. Next, click "Fit" button.'
28+
dispHLLE(platform.setting.ml.configElement, platform)
29+
}

lib/model/hlle.js

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import Matrix from '../util/matrix.js'
2+
3+
/**
4+
* Hessian Locally Linear Embedding
5+
*/
6+
export default class HLLE {
7+
// https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.3.673&rep=rep1&type=pdf
8+
// https://github.com/gdkrmr/dimRed
9+
// https://github.com/scikit-learn/scikit-learn/blob/7e1e6d09bcc2eaeba98f7e737aac2ac782f0e5f1/sklearn/manifold/_locally_linear.py#L344
10+
/**
11+
* @param {number} [k=1]
12+
*/
13+
constructor(k = 1) {
14+
this._k = k
15+
}
16+
17+
/**
18+
* Returns reduced values.
19+
*
20+
* @param {Array<Array<number>>} x
21+
* @param {number} [rd=0]
22+
* @returns {Array<Array<number>>}
23+
*/
24+
predict(x, rd = 0) {
25+
x = Matrix.fromArray(x)
26+
const d = x.cols
27+
const n = x.rows
28+
29+
const distance = []
30+
const neighbors = []
31+
for (let i = 0; i < n; distance[i++] = []);
32+
for (let i = 0; i < n; i++) {
33+
for (let j = i + 1; j < n; j++) {
34+
let dt = 0
35+
for (let k = 0; k < d; k++) {
36+
dt += (x.at(i, k) - x.at(j, k)) ** 2
37+
}
38+
distance[i][j] = distance[j][i] = dt
39+
}
40+
41+
const nns = []
42+
for (let j = 0; j < n; j++) {
43+
if (j === i) continue
44+
const dt = distance[i][j]
45+
if (nns.length < this._k || dt < nns[this._k - 1].dt) {
46+
if (nns.length === this._k) nns.pop()
47+
nns.push({
48+
dt: dt,
49+
idx: j,
50+
})
51+
for (let k = nns.length - 1; k > 0; k--) {
52+
if (nns[k].dt < nns[k - 1].dt) {
53+
;[nns[k], nns[k - 1]] = [nns[k - 1], nns[k]]
54+
}
55+
}
56+
}
57+
}
58+
neighbors.push(nns)
59+
}
60+
61+
const m = Matrix.eye(n, n)
62+
for (let i = 0; i < n; i++) {
63+
const z = x.row(neighbors[i].map(v => v.idx))
64+
z.sub(z.mean(0))
65+
66+
const [u] = z.svd()
67+
const yi = Matrix.ones(this._k, 1 + rd + (rd * (rd + 1)) / 2)
68+
yi.set(0, 1, u.slice(0, rd, 1))
69+
70+
let j = 1 + rd
71+
for (let k = 0; k < rd; k++) {
72+
yi.set(0, j, u.col(k).copyMult(u.slice(k, rd, 1)))
73+
j += rd - k
74+
}
75+
76+
const [q] = yi.qr()
77+
const w = q.slice(rd + 1, null, 1)
78+
w.div(w.sum(0))
79+
const wwt = w.dot(w.t)
80+
81+
for (let k = 0; k < this._k; k++) {
82+
for (let l = 0; l < this._k; l++) {
83+
m.addAt(neighbors[i][k].idx, neighbors[i][l].idx, wwt.at(k, l))
84+
}
85+
}
86+
}
87+
88+
const ev = m.eigenVectors()
89+
ev.flip(1)
90+
return ev.slice(1, rd + 1, 1).toArray()
91+
}
92+
}

tests/lib/model/hlle.test.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { jest } from '@jest/globals'
2+
jest.retryTimes(3)
3+
4+
import Matrix from '../../../lib/util/matrix.js'
5+
import HLLE from '../../../lib/model/hlle.js'
6+
7+
import { coRankingMatrix } from '../../../lib/evaluate/dimensionality_reduction.js'
8+
9+
test('dimensionality reduction', () => {
10+
const x = Matrix.randn(50, 5, 0, 0.2).concat(Matrix.randn(50, 5, 5, 0.2)).toArray()
11+
const y = new HLLE(10).predict(x, 2)
12+
const q = coRankingMatrix(x, y, 20, 20)
13+
expect(q).toBeGreaterThan(0.9)
14+
})

0 commit comments

Comments
 (0)