Skip to content

Commit 96b2097

Browse files
committed
Separate model and UI
1 parent cb4bb6c commit 96b2097

File tree

390 files changed

+13829
-13947
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

390 files changed

+13829
-13947
lines changed

.prettierrc.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@
44
"useTabs": true,
55
"printWidth": 120,
66
"semi": false,
7-
"singleQuote": true
7+
"singleQuote": true,
8+
"arrowParens": "avoid"
89
}

model/adaline.js

Lines changed: 2 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
class ADALINE {
1+
export default class ADALINE {
22
// https://qiita.com/ruka38/items/2f2f958c1d45728ea577
33
// https://qiita.com/kazukiii/items/958fa06079a0e5a73007
44
constructor(rate) {
@@ -28,55 +28,6 @@ class ADALINE {
2828

2929
predict(data) {
3030
const x = Matrix.fromArray(data)
31-
return x.dot(this._w).value.map(v => this._a(v + this._b) <= 0 ? -1 : 1)
31+
return x.dot(this._w).value.map(v => (this._a(v + this._b) <= 0 ? -1 : 1))
3232
}
3333
}
34-
35-
var dispADALINE = function(elm, platform) {
36-
let model = null
37-
const calc = (cb) => {
38-
const method = elm.select("[name=method]").property("value")
39-
const rate = +elm.select("[name=rate]").property("value");
40-
platform.fit((tx, ty) => {
41-
ty = ty.map(v => v[0])
42-
if (!model) {
43-
model = new EnsembleBinaryModel(ADALINE, method, null, [rate])
44-
model.init(tx, ty);
45-
}
46-
model.fit()
47-
48-
platform.predict((px, pred_cb) => {
49-
const categories = model.predict(px);
50-
pred_cb(categories)
51-
cb && cb()
52-
}, 3)
53-
})
54-
}
55-
56-
elm.append("select")
57-
.attr("name", "method")
58-
.selectAll("option")
59-
.data(["oneone", "onerest"])
60-
.enter()
61-
.append("option")
62-
.property("value", d => d)
63-
.text(d => d)
64-
elm.append("span")
65-
.text(" Learning rate ");
66-
elm.append("input")
67-
.attr("type", "number")
68-
.attr("name", "rate")
69-
.attr("min", 0)
70-
.attr("max", 100)
71-
.attr("step", 0.1)
72-
.attr("value", 0.1)
73-
platform.setting.ml.controller.stepLoopButtons().init(() => {
74-
model = null
75-
platform.init()
76-
}).step(calc).epoch()
77-
}
78-
79-
export default function(platform) {
80-
platform.setting.ml.usage = 'Click and add data point. Then, click "Step".'
81-
dispADALINE(platform.setting.ml.configElement, platform)
82-
}

model/adaptive_thresholding.js

Lines changed: 3 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
class AdaptiveThresholding {
1+
export default class AdaptiveThresholding {
22
// https://algorithm.joho.info/image-processing/adaptive-thresholding/
33
// https://docs.opencv.org/master/d7/d4d/tutorial_py_thresholding.html
44
constructor(method = 'mean', k = 3, c = 2) {
@@ -9,7 +9,7 @@ class AdaptiveThresholding {
99

1010
_kernel() {
1111
const k = []
12-
for (let i = 0; i < this._k; k[i++] = Array(this._k).fill(1 / (this._k ** 2)));
12+
for (let i = 0; i < this._k; k[i++] = Array(this._k).fill(1 / this._k ** 2));
1313
if (this._method === 'gaussian') {
1414
const s = 1.3
1515
const offset = Math.floor(this._k / 2)
@@ -57,60 +57,9 @@ class AdaptiveThresholding {
5757
ksum += kernel[s][t]
5858
}
5959
}
60-
p[i][j] = m.map((v, u) => x[i][j][u] < v / ksum - this._c ? 0 : 255)
60+
p[i][j] = m.map((v, u) => (x[i][j][u] < v / ksum - this._c ? 0 : 255))
6161
}
6262
}
6363
return p
6464
}
6565
}
66-
67-
var dispAdaptiveThresholding = function(elm, platform) {
68-
platform.colorSpace = 'gray'
69-
const fitModel = () => {
70-
const method = elm.select("[name=method]").property("value")
71-
const k = +elm.select("[name=k]").property("value")
72-
const c = +elm.select("[name=c]").property("value")
73-
platform.fit((tx, ty, pred_cb) => {
74-
const model = new AdaptiveThresholding(method, k, c)
75-
const y = model.predict(tx)
76-
pred_cb(y.flat())
77-
}, 1);
78-
}
79-
80-
elm.append("select")
81-
.attr("name", "method")
82-
.selectAll("option")
83-
.data(["mean", "gaussian"])
84-
.enter()
85-
.append("option")
86-
.property("value", d => d)
87-
.text(d => d);
88-
elm.append("span")
89-
.text(" k ");
90-
elm.append("input")
91-
.attr("type", "number")
92-
.attr("name", "k")
93-
.attr("value", 3)
94-
.attr("min", 3)
95-
.attr("max", 99)
96-
.attr("step", 2)
97-
.on("change", fitModel)
98-
elm.append("span")
99-
.text(" c ");
100-
elm.append("input")
101-
.attr("type", "number")
102-
.attr("name", "c")
103-
.attr("value", 2)
104-
.attr("min", 0)
105-
.attr("max", 100)
106-
.on("change", fitModel)
107-
elm.append("input")
108-
.attr("type", "button")
109-
.attr("value", "Fit")
110-
.on("click", fitModel)
111-
}
112-
113-
export default function(platform) {
114-
platform.setting.ml.usage = 'Click "Fit" button.'
115-
dispAdaptiveThresholding(platform.setting.ml.configElement, platform);
116-
}

model/affinity_propagation.js

Lines changed: 49 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,25 @@
1-
class AffinityPropagation {
1+
export default class AffinityPropagation {
22
// https://qiita.com/daiki_yosky/items/98ce56e37623c369cc60
33
// https://tjo.hatenablog.com/entry/2014/07/31/190218
44
constructor() {
5-
this._epoch = 0;
6-
this._l = 0.8;
5+
this._epoch = 0
6+
this._l = 0.8
77

8-
this._x = [];
9-
this._y = null;
8+
this._x = []
9+
this._y = null
1010
}
1111

1212
get centroidCategories() {
13-
const y = this.predict();
13+
const y = this.predict()
1414
return [...new Set(y)]
1515
}
1616

1717
get centroids() {
18-
return this.centroidCategories.map(i => this._x[i]);
18+
return this.centroidCategories.map(i => this._x[i])
1919
}
2020

2121
get size() {
22-
const y = this.predict();
22+
const y = this.predict()
2323
return new Set(y).size
2424
}
2525

@@ -28,49 +28,49 @@ class AffinityPropagation {
2828
}
2929

3030
init(datas) {
31-
this._x = datas;
32-
const n = datas.length;
33-
this._r = Array(n);
34-
this._a = Array(n);
35-
this._ar = Array(n);
36-
this._s = Array(n);
37-
this._as = Array(n);
31+
this._x = datas
32+
const n = datas.length
33+
this._r = Array(n)
34+
this._a = Array(n)
35+
this._ar = Array(n)
36+
this._s = Array(n)
37+
this._as = Array(n)
3838
for (let i = 0; i < n; i++) {
3939
this._r[i] = Array(n).fill(0)
4040
this._a[i] = Array(n).fill(0)
4141
this._ar[i] = Array(n).fill(0)
4242
this._s[i] = Array(n)
4343
this._as[i] = Array(n)
4444
}
45-
this._y = null;
46-
this._epoch = 0;
45+
this._y = null
46+
this._epoch = 0
4747

4848
let min = Infinity
4949
for (let i = 0; i < n; i++) {
5050
for (let j = 0; j < i; j++) {
5151
if (i === j) continue
5252
const d = -this._x[i].reduce((s, v, k) => s + (v - this._x[j][k]) ** 2, 0)
53-
this._s[i][j] = this._s[j][i] = d;
54-
this._as[i][j] = this._as[j][i] = d;
53+
this._s[i][j] = this._s[j][i] = d
54+
this._as[i][j] = this._as[j][i] = d
5555
min = Math.min(min, d)
5656
}
5757
}
5858
for (let i = 0; i < n; i++) {
59-
this._s[i][i] = this._as[i][i] = min;
59+
this._s[i][i] = this._as[i][i] = min
6060
}
6161
}
6262

6363
fit() {
6464
// Frey. et al. "Clustering by Passing Messages Between Data Points" (2007)
6565
// "Fast Algorithm for Affinity Propagation"
66-
const x = this._x;
67-
const n = x.length;
66+
const x = this._x
67+
const n = x.length
6868
const l = this._l
6969

7070
for (let i = 0; i < n; i++) {
7171
for (let k = 0; k < n; k++) {
7272
let m = -Infinity
73-
const ss = (i === k) ? this._s[i] : this._as[i];
73+
const ss = i === k ? this._s[i] : this._as[i]
7474
for (let kd = 0; kd < n; kd++) {
7575
if (k === kd) continue
7676
m = Math.max(m, ss[kd])
@@ -81,52 +81,52 @@ class AffinityPropagation {
8181

8282
for (let i = 0; i < n; i++) {
8383
for (let k = 0; k < n; k++) {
84-
let s = (i === k) ? 0 : this._r[k][k];
84+
let s = i === k ? 0 : this._r[k][k]
8585
for (let id = 0; id < n; id++) {
8686
if (id !== i && id !== k) {
8787
s += Math.max(0, this._r[id][k])
8888
}
8989
}
9090
if (i !== k) s = Math.min(0, s)
91-
const aik = l * this._a[i][k] + (1 - l) * s;
92-
this._a[i][k] = aik;
93-
this._ar[i][k] = aik + this._r[i][k];
94-
this._as[i][k] = aik + this._s[i][k];
91+
const aik = l * this._a[i][k] + (1 - l) * s
92+
this._a[i][k] = aik
93+
this._ar[i][k] = aik + this._r[i][k]
94+
this._as[i][k] = aik + this._s[i][k]
9595
}
9696
}
9797

98-
this._y = null;
99-
this._epoch++;
98+
this._y = null
99+
this._epoch++
100100
}
101101

102102
__fit() {
103103
// Frey. et al. "Mixture Modeling by Affinity Propagation" (2006)
104-
const x = this._x;
105-
const n = x.length;
104+
const x = this._x
105+
const n = x.length
106106

107107
for (let i = 0; i < n; i++) {
108108
for (let j = 0; j < n; j++) {
109-
let s = 0;
109+
let s = 0
110110
for (let k = 0; k < n; k++) {
111111
if (k === j) continue
112-
s += this._a[i][k] * this._s[i][k];
112+
s += this._a[i][k] * this._s[i][k]
113113
}
114114
this._r[i][j] = this._s[i][j] / s
115115
}
116116
}
117117

118118
for (let i = 0; i < n; i++) {
119-
let p = 1;
119+
let p = 1
120120
for (let k = 0; k < n; k++) {
121121
if (k === i) continue
122-
p *= (1 + this._r[k][i]);
122+
p *= 1 + this._r[k][i]
123123
}
124124
this._a[i][i] = p - 1
125125
this._ar[i][i] = this._a[i][i] + this._r[i][i]
126126

127127
for (let j = 0; j < n; j++) {
128-
if (i === j) continue;
129-
p = 1 / this._r[i][i] - 1;
128+
if (i === j) continue
129+
p = 1 / this._r[i][i] - 1
130130
for (let k = 0; k < n; k++) {
131131
if (k === i || k === j) continue
132132
p *= 1 / (1 + this._r[k][i])
@@ -135,64 +135,27 @@ class AffinityPropagation {
135135
this._ar[i][j] = this._a[i][j] + this._r[i][j]
136136
}
137137
}
138-
this._y = null;
139-
this._epoch++;
138+
this._y = null
139+
this._epoch++
140140
}
141141

142142
predict() {
143143
if (!this._y) {
144-
this._y = [];
145-
const n = this._x.length;
144+
this._y = []
145+
const n = this._x.length
146146
for (let i = 0; i < n; i++) {
147-
let max_v = -Infinity;
148-
let max_i = -1;
147+
let max_v = -Infinity
148+
let max_i = -1
149149
for (let j = 0; j < n; j++) {
150-
const v = this._ar[i][j];
150+
const v = this._ar[i][j]
151151
if (max_v < v) {
152-
max_v = v;
153-
max_i = j;
152+
max_v = v
153+
max_i = j
154154
}
155155
}
156-
this._y.push(max_i);
156+
this._y.push(max_i)
157157
}
158158
}
159-
return this._y;
159+
return this._y
160160
}
161161
}
162-
163-
var dispAffinityPropagation = function(elm, platform) {
164-
let model = null
165-
166-
const fitModel = (cb) => {
167-
platform.fit(
168-
(tx, ty, pred_cb) => {
169-
if (!model) {
170-
model = new AffinityPropagation();
171-
model.init(tx);
172-
}
173-
model.fit()
174-
const pred = model.predict();
175-
pred_cb(pred.map(v => v + 1))
176-
elm.select("[name=clusters]").text(model.size);
177-
platform.centroids(model.centroids, model.centroidCategories.map(v => v + 1))
178-
cb && cb()
179-
}
180-
);
181-
}
182-
183-
platform.setting.ml.controller.stepLoopButtons().init(() => {
184-
model = null
185-
elm.select("[name=clusters]").text(0);
186-
platform.init()
187-
}).step(fitModel).epoch()
188-
elm.append("span")
189-
.text(" Clusters: ");
190-
elm.append("span")
191-
.attr("name", "clusters");
192-
}
193-
194-
export default function(platform) {
195-
platform.setting.ml.usage = 'Click and add data point. Then, click "Step" button repeatedly.'
196-
dispAffinityPropagation(platform.setting.ml.configElement, platform)
197-
}
198-

0 commit comments

Comments
 (0)