├── 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 | --------------------------------------------------------------------------------