├── 01
├── index.html
└── index.js
├── 02
├── index.html
└── index.js
├── 03
├── index.html
└── index.js
├── 04
├── index.html
└── index.js
├── LICENSE
├── README.md
├── index.html
└── lib
├── Coordinate.js
├── Linear.js
└── Perceptron.js
/01/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | 寻找线段散点里的中心点
8 |
26 |
27 |
28 |
29 | 寻找线段散点里的中心点
30 | 点击画布区域设置,重新设置散点位置
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/01/index.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | var canvas = document.getElementById("coordinate");
3 | canvas.width = canvas.height = Math.min(window.screen.width - 20, 500);
4 |
5 | var coordinate = new Coordinate(canvas);
6 | var interval = canvas.width / 10; // 坐标间隔
7 | coordinate.setOrigin(0, canvas.height / 2);
8 | coordinate.setOptions("axis", {
9 | max: [10],
10 | interval: interval
11 | });
12 |
13 | function generateTrainingData(target, amount, range) {
14 | var trainingData = [];
15 | for (var i = 0; i < amount; i++) {
16 | trainingData.push(target + range * (Math.random() - 0.5));
17 | }
18 | return trainingData;
19 | }
20 |
21 | var target = 9 * Math.random();
22 | var trainingData = generateTrainingData(target, 50, 2);
23 | var model = 10 * Math.random();
24 | var learningRate = 0.01;
25 | var dataIndex = 0;
26 |
27 | // x = a
28 | function learning(output, labeled) {
29 | var dE_dA = (labeled - output) * -1;
30 | var gradient = dE_dA;
31 | var nextModel = model + learningRate * -gradient;
32 | return nextModel;
33 | }
34 |
35 | function training() {
36 | var index = dataIndex++;
37 | dataIndex = dataIndex >= trainingData.length ? 0 : dataIndex;
38 | model = learning(model, trainingData[dataIndex]);
39 | }
40 |
41 | function handleClick(event) {
42 | var origin = coordinate.getOrigin();
43 | var clientRect = canvas.getBoundingClientRect();
44 | target = (event.clientX - clientRect.left - origin.x) / interval;
45 | trainingData = generateTrainingData(target, 50, 2);
46 | }
47 |
48 | // set custom training data
49 | canvas.addEventListener("click", handleClick);
50 |
51 | function drawing() {
52 | coordinate.clearWithTrailingEffect();
53 |
54 | // draw axis
55 | coordinate.drawAxis();
56 |
57 | // 将生成的随机数字,映射成坐标
58 | var dots = trainingData.map(function(number) {
59 | return {
60 | x: number,
61 | y: 0
62 | };
63 | });
64 |
65 | // draw traning data
66 | coordinate.drawDots(dots, {
67 | type: "stroke",
68 | color: "#333",
69 | radius: 2
70 | });
71 |
72 | // draw model
73 | coordinate.drawDot(
74 | {
75 | x: model,
76 | y: 0
77 | },
78 | {
79 | type: "fill",
80 | color: "red"
81 | }
82 | );
83 |
84 | // training
85 | training();
86 | requestAnimationFrame(drawing);
87 | }
88 |
89 | drawing();
90 | })();
91 |
--------------------------------------------------------------------------------
/02/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | 寻找矩形散点里的中心点
8 |
26 |
27 |
28 |
29 | 寻找矩形散点里的中心点
30 | 点击画布区域设置,重新设置散点位置
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/02/index.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | var canvas = document.getElementById("coordinate");
3 | canvas.width = canvas.height = Math.min(window.screen.width - 20, 500);
4 |
5 | var coordinate = new Coordinate(canvas);
6 | var interval = canvas.width / 20; // 坐标间隔
7 | coordinate.setOptions("axis", {
8 | max: 10,
9 | interval: interval
10 | });
11 |
12 | function generateTrainingData(target, amount, range) {
13 | var data = [];
14 | for (var i = 0; i < amount; i++) {
15 | var x = target.x + range * 2 * (Math.random() - 0.5);
16 | var y = target.y + range * 2 * (Math.random() - 0.5);
17 | data.push({
18 | x: x,
19 | y: y
20 | });
21 | }
22 | return data;
23 | }
24 |
25 | var target = {
26 | x: 9 * 2 * (0.5 - Math.random()),
27 | y: 9 * 2 * (0.5 - Math.random())
28 | };
29 | var trainingData = generateTrainingData(target, 200, 3);
30 | var model = {
31 | x: 10 * 2 * (0.5 - Math.random()),
32 | y: 10 * 2 * (0.5 - Math.random())
33 | };
34 | var learningRate = 0.01;
35 | var dataIndex = 0;
36 |
37 | function learning(output, labeled) {
38 | var dE_dX = (labeled.x - output.x) * -1;
39 | var dE_dY = (labeled.y - output.y) * -1;
40 | var gradientX = dE_dX;
41 | var gradientY = dE_dY;
42 | model.x += learningRate * -gradientX;
43 | model.y += learningRate * -gradientY;
44 | return model;
45 | }
46 |
47 | function training() {
48 | var index = dataIndex++;
49 | dataIndex = dataIndex >= trainingData.length ? 0 : dataIndex;
50 | model = learning(model, trainingData[dataIndex]);
51 | }
52 |
53 | function handleClick(event) {
54 | var origin = coordinate.getOrigin();
55 | var clientRect = canvas.getBoundingClientRect();
56 | target = {
57 | x: (event.clientX - clientRect.left - origin.x) / interval,
58 | y: -(event.clientY - clientRect.top - origin.y) / interval
59 | };
60 | trainingData = generateTrainingData(target, 200, 3);
61 | }
62 |
63 | // set custom training data
64 | canvas.addEventListener("click", handleClick);
65 |
66 | function drawing() {
67 | coordinate.clearWithTrailingEffect();
68 |
69 | // draw axis
70 | coordinate.drawAxis();
71 |
72 | // draw traning data
73 | coordinate.drawDots(trainingData, {
74 | type: "stroke",
75 | color: "#333",
76 | radius: 2
77 | });
78 |
79 | // draw model
80 | coordinate.drawDot(model, {
81 | type: "fill",
82 | color: "red"
83 | });
84 |
85 | // training
86 | training();
87 | requestAnimationFrame(drawing);
88 | }
89 |
90 | drawing();
91 | })();
92 |
--------------------------------------------------------------------------------
/03/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | 线性回归-寻找一条直线拟合数据点
8 |
26 |
27 |
28 |
29 | 线性回归-寻找一条直线拟合数据点
30 | 点击画布区域设置,重新设置散点位置
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/03/index.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | var canvas = document.getElementById("coordinate");
3 | canvas.width = canvas.height = Math.min(window.screen.width - 20, 500);
4 |
5 | var coordinate = new Coordinate(canvas);
6 | var interval = canvas.width / 30; // 坐标间隔
7 | coordinate.setOptions("axis", {
8 | max: 15,
9 | interval: interval
10 | });
11 |
12 | function getRandomNumber(range) {
13 | return range * 2 * (Math.random() - 0.5);
14 | }
15 |
16 | function generateTrainingData() {
17 | var linear = new Linear(getRandomNumber(5), getRandomNumber(10));
18 | var data = [];
19 | for (var i = -15; i < 15; i += 0.05) {
20 | if (Math.random() > 0.7) {
21 | // 取 70% 的点
22 | continue;
23 | }
24 | if (Math.abs(i) < 0.0001) {
25 | // 避开临近 0 点的值,容易梯度爆炸
26 | continue;
27 | }
28 | var x = i + getRandomNumber(2);
29 | var y = linear.computeWithRandom(x, getRandomNumber(3));
30 | data.push({
31 | x: x,
32 | y: y
33 | });
34 | }
35 | return data;
36 | }
37 |
38 | var trainingData = generateTrainingData();
39 | var linear = new Linear(1, 0);
40 |
41 | function handleClick(event) {
42 | trainingData = generateTrainingData();
43 | }
44 |
45 | // set custom training data
46 | canvas.addEventListener("click", handleClick);
47 |
48 | function drawing() {
49 | coordinate.clearWithTrailingEffect();
50 |
51 | // draw axis
52 | coordinate.drawAxis();
53 |
54 | // draw traning data
55 | coordinate.drawDots(trainingData, {
56 | type: "stroke",
57 | color: "#333",
58 | radius: 2
59 | });
60 |
61 | coordinate.drawLineByFunction(linear.compute, {
62 | color: "red"
63 | });
64 |
65 | linear.batchTraining(trainingData);
66 | requestAnimationFrame(drawing);
67 | }
68 |
69 | drawing();
70 | })();
71 |
--------------------------------------------------------------------------------
/04/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | 感知机-寻找一条直线分类两拨散点
8 |
26 |
27 |
28 |
29 | 感知机-寻找一条直线分类两拨散点
30 | 点击画布区域设置,重新设置散点位置
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/04/index.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | var canvas = document.getElementById("coordinate");
3 | canvas.width = canvas.height = Math.min(window.screen.width - 20, 500);
4 |
5 | var coordinate = new Coordinate(canvas);
6 | var interval = canvas.width / 30; // 坐标间隔
7 | coordinate.setOptions("axis", {
8 | max: 15,
9 | interval: interval
10 | });
11 |
12 | function getRandomNumber(range) {
13 | return range * 2 * (Math.random() - 0.5);
14 | }
15 |
16 | // 生成不均衡的两类点
17 | function generateTrainingData() {
18 | var linear = new Linear(getRandomNumber(5), getRandomNumber(10));
19 | var data = [[], []];
20 | var getDot = function(i) {
21 | var x = i + getRandomNumber(20);
22 | var y = linear.compute(x)
23 |
24 | if (Math.random() > 0.7) {
25 | var range = 0.5 + Math.abs(getRandomNumber(20))
26 | y += range
27 | data[1].push({ x: x, y: y })
28 | } else {
29 | if (Math.random() > 0.9) {
30 | var range = 0.5 + Math.abs(getRandomNumber(5))
31 | y -= range
32 | data[0].push({ x: x, y: y })
33 | }
34 | }
35 | }
36 | for (var i = -15; i < 15; i += 0.05) {
37 | if (Math.random() > 0.7) {
38 | // 取 70% 的点
39 | continue;
40 | }
41 | if (Math.abs(i) < 0.0001) {
42 | // 避开临近 0 点的值,容易梯度爆炸
43 | continue;
44 | }
45 |
46 | for (var ii = 0; ii < 5; ii += 1) {
47 | getDot(i)
48 | }
49 |
50 | }
51 | return data;
52 | }
53 |
54 | var trainingData = generateTrainingData();
55 | var perceptron = new Perceptron(2, 0.01);
56 |
57 | function handleClick(event) {
58 | trainingData = generateTrainingData()
59 | }
60 |
61 | // set custom training data
62 | canvas.addEventListener("click", handleClick);
63 |
64 | function drawing() {
65 | coordinate.clearWithTrailingEffect();
66 |
67 | // draw axis
68 | coordinate.drawAxis();
69 |
70 | // draw traning data
71 | coordinate.drawDots(trainingData[0], {
72 | type: "stroke",
73 | color: "brown",
74 | radius: 2
75 | });
76 | coordinate.drawDots(trainingData[1], {
77 | type: "stroke",
78 | color: "green",
79 | radius: 2
80 | });
81 |
82 | coordinate.drawLineByFunction(perceptron.computeLinear, {
83 | color: "red"
84 | });
85 |
86 | var batches = 20;
87 | for (var i = 0; i < batches; i++) {
88 | var randomIndexA = Math.floor(trainingData[0].length * Math.random());
89 | var randomIndexB = Math.floor(trainingData[1].length * Math.random());
90 | var dotA = coor2Array(trainingData[0][randomIndexA]);
91 | var dotB = coor2Array(trainingData[1][randomIndexB]);
92 |
93 | perceptron.training(dotA, 1);
94 | perceptron.training(dotB, 0);
95 | }
96 |
97 | requestAnimationFrame(drawing);
98 | }
99 |
100 | function coor2Array(coor) {
101 | return [coor.x, coor.y];
102 | }
103 |
104 | drawing();
105 | })();
106 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 工业聚
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Simple-Machine-Learning-Demo
2 | Simple Machine Learning Demo
3 |
4 | ## [DEMO 地址](https://lucifier129.github.io/simple-machine-learning-demo/)
5 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | simple-machine-learning-demo
8 |
9 |
10 |
11 | 简单的 Web 机器学习演示
12 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/lib/Coordinate.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | if (!Object.assign) {
3 | Object.assign = function(target) {
4 | for (var i = 1; i < arguments.length - 1; i++) {
5 | for (var key in arguments[i]) {
6 | target[key] = arguments[i][key];
7 | }
8 | }
9 | return target;
10 | };
11 | }
12 |
13 | window.Coordinate = Coordinate;
14 |
15 | var defaults = {
16 | origin: null, // 原点坐标,默认无,在运行时默认取 canvas 中点
17 | axis: {
18 | unit: [1, 1, 1, 1],
19 | color: ["blue", "blue", "blue", "blue"], // 四个子轴的颜色
20 | interval: [50, 50, 50, 50], // 每个单位所占的像素值
21 | max: [10, 10, 10, 10] // 最大单位数量
22 | },
23 | text: {
24 | font: "12px serif", // 文本样式
25 | color: "#333",
26 | offset: 14
27 | },
28 | dot: {
29 | radius: 3,
30 | color: "#333"
31 | },
32 | markHeight: 10 // 单位刻度的高度
33 | };
34 |
35 | // 坐标系
36 | function Coordinate(canvas) {
37 | this.canvas = canvas;
38 | this.ctx = canvas.getContext("2d");
39 | this.options = Object.assign({}, defaults);
40 | }
41 |
42 | // 设置 options 选项
43 | Coordinate.prototype.setOptions = function(type, options) {
44 | if (typeof type === "object") {
45 | Object.assign(this.options, type);
46 | } else {
47 | Object.assign(this.options[type], options);
48 | }
49 | };
50 |
51 | // 获取 canvas 的尺寸
52 | Coordinate.prototype.getSize = function() {
53 | return {
54 | width: this.canvas.width,
55 | height: this.canvas.height
56 | };
57 | };
58 |
59 | Coordinate.prototype.clear = function() {
60 | this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
61 | };
62 |
63 | Coordinate.prototype.clearWithTrailingEffect = function() {
64 | this.ctx.save();
65 | this.ctx.fillStyle = "rgba(255,255,255,0.3)";
66 | this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
67 | this.ctx.restore();
68 | };
69 |
70 | // 获取坐标轴的原点
71 | Coordinate.prototype.getOrigin = function() {
72 | var options = this.options;
73 |
74 | if (options.origin) {
75 | return options.origin;
76 | }
77 |
78 | var size = this.getSize();
79 | var origin = {
80 | x: size.width / 2,
81 | y: size.height / 2
82 | };
83 |
84 | options.origin = origin;
85 | return origin;
86 | };
87 |
88 | // 设置坐标轴的原点
89 | Coordinate.prototype.setOrigin = function(x, y) {
90 | var origin = {
91 | x: x,
92 | y: y
93 | };
94 | this.setOptions({
95 | origin: origin
96 | });
97 | };
98 |
99 | var axisType2index = {
100 | x: 0,
101 | "-x": 1,
102 | y: 2,
103 | "-y": 3
104 | };
105 |
106 | // 获取坐标配置信息
107 | Coordinate.prototype.getAxis = function(type) {
108 | var options = this.options;
109 | var index = axisType2index[type];
110 | var axis = {};
111 |
112 | if (Array.isArray(options.axis.unit)) {
113 | axis.unit = options.axis.unit[index] || defaults.axis.unit[index];
114 | } else {
115 | axis.unit = options.axis.unit;
116 | }
117 |
118 | if (Array.isArray(options.axis.color)) {
119 | axis.color = options.axis.color[index] || defaults.axis.color[index];
120 | } else {
121 | axis.color = options.axis.color;
122 | }
123 |
124 | if (Array.isArray(options.axis.max)) {
125 | axis.max = options.axis.max[index] || 0;
126 | } else {
127 | axis.max = options.axis.max;
128 | }
129 |
130 | if (Array.isArray(options.axis.interval)) {
131 | axis.interval =
132 | options.axis.interval[index] || defaults.axis.interval[index];
133 | } else {
134 | axis.interval = options.axis.interval;
135 | }
136 |
137 | return axis;
138 | };
139 |
140 | Coordinate.prototype.goToOrigin = function() {
141 | var origin = this.getOrigin();
142 | this.ctx.translate(origin.x, origin.y);
143 | };
144 |
145 | // 绘制文本
146 | Coordinate.prototype.drawText = function(data) {
147 | var ctx = this.ctx;
148 | ctx.save();
149 | ctx.fillStyle = data.color;
150 | ctx.font = data.font;
151 | ctx.textAlign = data.align || "center";
152 | ctx.fillText(data.text, data.x, data.y);
153 | ctx.restore();
154 | };
155 |
156 | // 绘制线段
157 | Coordinate.prototype.drawLine = function(data) {
158 | var ctx = this.ctx;
159 | ctx.save();
160 | ctx.beginPath();
161 | ctx.moveTo(data.start[0], data.start[1]);
162 | ctx.lineTo(data.end[0], data.end[1]);
163 | ctx.strokeStyle = data.color;
164 | ctx.stroke();
165 | ctx.restore();
166 | };
167 |
168 | Coordinate.prototype.getPixelX = function(x) {
169 | var axis = this.getAxis(x >= 0 ? "x" : "-x");
170 | return x * axis.unit * axis.interval;
171 | };
172 |
173 | Coordinate.prototype.getPixelY = function(y) {
174 | var axis = this.getAxis(y >= 0 ? "y" : "-y");
175 | return -y * axis.unit * axis.interval;
176 | };
177 |
178 | // 绘制坐标点
179 | Coordinate.prototype.drawDot = function(data, settings) {
180 | var ctx = this.ctx;
181 | var options = this.options;
182 | var axis = null;
183 |
184 | ctx.save();
185 | this.goToOrigin();
186 | ctx.beginPath();
187 |
188 | var radius =
189 | settings && settings.radius ? settings.radius : options.dot.radius;
190 | var color = settings && settings.color ? settings.color : options.dot.color;
191 | var isStroke = settings && settings.type === "stroke";
192 |
193 | ctx.arc(
194 | this.getPixelX(data.x),
195 | this.getPixelY(data.y),
196 | radius,
197 | 0,
198 | 2 * Math.PI
199 | );
200 |
201 | if (isStroke) {
202 | ctx.strokeStyle = color;
203 | ctx.stroke();
204 | } else {
205 | ctx.fillStyle = color;
206 | ctx.fill();
207 | }
208 |
209 | ctx.restore();
210 | };
211 |
212 | // 绘制一组坐标点
213 | Coordinate.prototype.drawDots = function(list, options) {
214 | for (var i = 0; i < list.length; i++) {
215 | this.drawDot(list[i], options);
216 | }
217 | };
218 |
219 | // 根据给定线性函数,绘制一条直线
220 | Coordinate.prototype.drawLineByFunction = function(fn, options) {
221 | options = options || {};
222 | var ctx = this.ctx;
223 | var axisNegativeX = this.getAxis("-x");
224 | var axisX = this.getAxis("x");
225 | var startX = this.getPixelX(-axisNegativeX.max);
226 | var startY = this.getPixelY(fn(-axisNegativeX.max));
227 | var endX = this.getPixelX(axisX.max);
228 | var endY = this.getPixelY(fn(axisX.max));
229 | var data = Object.assign(
230 | {
231 | color: "#333"
232 | },
233 | options,
234 | {
235 | start: [startX, startY],
236 | end: [endX, endY]
237 | }
238 | );
239 | ctx.save();
240 | this.goToOrigin();
241 | this.drawLine(data);
242 | ctx.restore();
243 | };
244 |
245 | Coordinate.prototype.drawX = function(negative) {
246 | negative = typeof negative === "number" ? negative / Math.abs(negative) : 1;
247 |
248 | var ctx = this.ctx;
249 | var options = this.options;
250 | var type = negative === -1 ? "-x" : "x";
251 | var axis = this.getAxis(type);
252 |
253 | ctx.save();
254 | this.goToOrigin();
255 |
256 | this.drawLine({
257 | start: [0, 0],
258 | end: [axis.max * axis.unit * axis.interval * negative, 0],
259 | color: axis.color
260 | });
261 |
262 | for (var i = 0; i < axis.max; i++) {
263 | var number = i * axis.unit * negative;
264 | var x = number * axis.unit * axis.interval;
265 | var y = options.text.offset;
266 |
267 | // 刻度值
268 | this.drawText({
269 | text: number,
270 | x: x,
271 | y: y,
272 | color: options.text.color,
273 | font: options.text.font,
274 | align: i === 0 ? "left" : "center"
275 | });
276 |
277 | // 绘制刻度条
278 | this.drawLine({
279 | start: [x, 0],
280 | end: [x, -options.markHeight],
281 | color: axis.color
282 | });
283 | }
284 |
285 | ctx.restore();
286 | };
287 |
288 | Coordinate.prototype.drawY = function(negative) {
289 | negative = typeof negative === "number" ? negative / Math.abs(negative) : 1;
290 |
291 | var ctx = this.ctx;
292 | var options = this.options;
293 | var type = negative === -1 ? "-y" : "y";
294 | var axis = this.getAxis(type);
295 |
296 | ctx.save();
297 | this.goToOrigin();
298 |
299 | this.drawLine({
300 | start: [0, 0],
301 | end: [0, axis.max * axis.unit * axis.interval * negative],
302 | color: axis.color
303 | });
304 |
305 | for (var i = 0; i < axis.max; i++) {
306 | var number = i * axis.unit * negative;
307 | var y = -number * axis.unit * axis.interval;
308 | var x = -options.text.offset;
309 |
310 | // 刻度值
311 | this.drawText({
312 | text: number,
313 | x: x,
314 | y: y,
315 | color: options.text.color,
316 | font: options.text.font,
317 | align: i === 0 ? "end" : "center"
318 | });
319 |
320 | // 绘制刻度条
321 | this.drawLine({
322 | start: [0, y],
323 | end: [options.markHeight, y],
324 | color: axis.color
325 | });
326 | }
327 |
328 | ctx.restore();
329 | };
330 |
331 | Coordinate.prototype.drawAxis = function() {
332 | var ctx = this.ctx;
333 | var options = this.options;
334 | var origin = this.getOrigin();
335 |
336 | ctx.save();
337 | this.drawX();
338 | this.drawX(-1);
339 | this.drawY();
340 | this.drawY(-1);
341 | ctx.restore();
342 | };
343 | })();
344 |
--------------------------------------------------------------------------------
/lib/Linear.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | window.Linear = Linear;
3 |
4 | // 线性回归
5 | function Linear(a, b, learningRate) {
6 | this.a = typeof a === "number" ? a : 1;
7 | this.b = typeof b === "number" ? b : 0;
8 | this.learningRate = learningRate || 0.02
9 | this.compute = this.compute.bind(this);
10 | }
11 |
12 | Linear.prototype.compute = function(x) {
13 | return this.a * x + this.b;
14 | };
15 |
16 | Linear.prototype.computeWithRandom = function(x, range) {
17 | return this.compute(x) + getRandomNumber(range);
18 | };
19 |
20 | Linear.prototype.computeGradient = function(input, target) {
21 | var output = this.compute(input)
22 | var dE_dY = (target - output) * -1;
23 | var dY_dA = input;
24 | var dY_dB = 1;
25 | var gradient = {
26 | a: dE_dY * dY_dA,
27 | b: dE_dY * dY_dB
28 | }
29 | return gradient
30 | }
31 |
32 | Linear.prototype.batchTraining = function(list) {
33 | var gradientA = 0;
34 | var gradientB = 0;
35 |
36 | for (var i = 0; i < list.length; i++) {
37 | var data = list[i];
38 | var input = data.x;
39 | var target = data.y;
40 | var gradient = this.computeGradient(data.x, data.y)
41 | gradientA += gradient.a;
42 | gradientB += gradient.b;
43 | }
44 |
45 | gradientA = gradientA / list.length;
46 | gradientB = gradientB / list.length;
47 |
48 | if (Math.abs(gradientA) < 0.00001 && Math.abs(gradientB) < 0.00001) {
49 | return;
50 | }
51 |
52 | gradientA = gradientA > 50 ? 50 : gradientA
53 | gradientB = gradientB > 50 ? 50 : gradientB
54 |
55 | var learningRate = this.learningRate
56 | this.a += learningRate * -gradientA;
57 | this.b += learningRate * -gradientB;
58 | };
59 |
60 | function getRandomNumber(range) {
61 | return range * 2 * (Math.random() - 0.5);
62 | }
63 | })();
64 |
--------------------------------------------------------------------------------
/lib/Perceptron.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | window.Perceptron = Perceptron;
3 |
4 | // 感知机算法
5 | function Perceptron(total, learningRate) {
6 | this.weights = [];
7 | this.bias = 0;
8 | this.learningRate = learningRate || 0.01;
9 | this.setTotal(total);
10 | this.compute = this.compute.bind(this);
11 | this.computeLinear = this.computeLinear.bind(this);
12 | }
13 |
14 | Perceptron.prototype.setTotal = function(total) {
15 | this.weights.length = 0;
16 | for (var i = 0; i < total; i++) {
17 | this.weights.push(0);
18 | }
19 | };
20 |
21 | Perceptron.prototype.sum = function(inputs) {
22 | var sum = this.bias;
23 | for (var i = 0; i < inputs.length; i++) {
24 | sum += inputs[i] * this.weights[i];
25 | }
26 | return sum;
27 | };
28 |
29 | Perceptron.prototype.activation = function(value) {
30 | return value >= 0 ? 1 : 0;
31 | };
32 |
33 | Perceptron.prototype.compute = function(inputs) {
34 | return this.activation(this.sum(inputs));
35 | };
36 |
37 | Perceptron.prototype.computeLinear = function(x) {
38 | var point1 = {
39 | x: 0,
40 | y: -this.bias / this.weights[1]
41 | };
42 | var point2 = {
43 | x: -this.bias / this.weights[0],
44 | y: 0
45 | };
46 | var bias = point1.y - point2.y;
47 | var slope = (point1.y - point2.y) / (point1.x - point2.x);
48 | return slope * x + bias;
49 | };
50 |
51 | Perceptron.prototype.training = function(inputs, target) {
52 | var output = this.compute(inputs);
53 | var dE_dY = (target - output) * -1;
54 | for (var i = 0; i < this.weights.length; i++) {
55 | var dY_dW = inputs[i]
56 | var gradient = dE_dY * dY_dW
57 | this.weights[i] += this.learningRate * -gradient;
58 | }
59 |
60 | var dY_dB = 1
61 | this.bias += this.learningRate * -(dE_dY * dY_dB);
62 | };
63 | })();
64 |
--------------------------------------------------------------------------------