# 听说你用JavaScript写代码？本文是你的机器学习指南

JavaScript 是一种流行的高级编程语言，它被世界上的绝大多数网站所使用，也被所有主流浏览器所支持。随着深度学习的火热，越来越多开发者开始探索使用 JavaScript 实现人工智能与机器学习算法。近日，来自德国的 Robin Wieruch 发布了一系列使用 JavaScript 构建机器学习的教程，本文将主要介绍使用 JavaScript 实现神经网络的方法。 ### 神经网络的目的是什么？

``````function getAccessibleColor(rgb) {
let [ r, g, b ] = rgb;
let colors = [r / 255, g / 255, b / 255];
let c = colors.map((col) => {
if (col <= 0.03928) {
return col / 12.92;
}
return Math.pow((col + 0.055) / 1.055, 2.4);
});
let L = (0.2126 * c) + (0.7152 * c) + (0.0722 * c);
return (L > 0.179)
? [ 0, 0, 0 ]
: [ 255, 255, 255 ];
}``````

### JavaScript 中的数据集生成

``````function generateRandomRgbColors(m) {
const rawInputs = [];
for (let i = 0; i < m; i++) {
rawInputs.push(generateRandomRgbColor());
}
return rawInputs;
}
function generateRandomRgbColor() {
return [
randomIntFromInterval(0, 255),
randomIntFromInterval(0, 255),
randomIntFromInterval(0, 255),
];
}
function randomIntFromInterval(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
}``````

generateRandomRgbColors() 函数创建给定大小为 m 的部分数据集。数据集中的数据点是 RGB 颜色空间中的颜色。每种颜色在矩阵中被表征为一行，而每一列是颜色的特征。特征是 RGB 空间中的 R、G、B 编码值。数据集还没有任何标签，所以训练集并不完整，因为它只有输入值而没有输出值。

``````function getAccessibleColor(rgb) {
let [ r, g, b ] = rgb;
let color = [r / 255, g / 255, b / 255];
let c = color.map((col) => {
if (col <= 0.03928) {
return col / 12.92;
}
return Math.pow((col + 0.055) / 1.055, 2.4);
});
let L = (0.2126 * c) + (0.7152 * c) + (0.0722 * c);
return (L > 0.179)
? [ 0, 1 ] // black
: [ 1, 0 ]; // white
}
``````

``````function generateColorSet(m) {
const rawInputs = generateRandomRgbColors(m);
const rawTargets = rawInputs.map(getAccessibleColor);
return { rawInputs, rawTargets };
}``````

``````function normalizeColor(rgb) {
return rgb.map(v => v / 255);
}``````

### JavaScript 神经网络模型的设置阶段

• 首先，它使用本地电脑的 GPU 加速机器学习算法中的向量计算。这些机器学习计算与图解计算类似，因此使用 GPU 的计算比使用 CPU 更加高效。
• 其次，deeplearn.js 的结构与流行的 TensorFlow 库类似（TensorFlow 库也是谷歌开发的，不过它使用的是 Python 语言）。因此如果你想在使用 Python 的机器学习中实现飞跃，那么 deeplearn.js 可提供通向 JavaScript 各领域的捷径。

``npm install deeplearn``

``````class ColorAccessibilityModel {
normalizeColor(rgb) {
return rgb.map(v => v / 255);
}
}
export default ColorAccessibilityModel;``````

``````import {
NDArrayMathGPU,
} from 'deeplearn';
const math = new NDArrayMathGPU();
class ColorAccessibilityModel {
...
}
export default ColorAccessibilityModel;``````

``````import {
Graph,
NDArrayMathGPU,
} from 'deeplearn';
class ColorAccessibilityModel {
setupSession(trainingSet) {
const graph = new Graph();
}
..
}
export default ColorAccessibilityModel;``````

``````class ColorAccessibilityModel {
inputTensor;
targetTensor;
setupSession(trainingSet) {
const graph = new Graph();
this.inputTensor = graph.placeholder('input RGB value', );
this.targetTensor = graph.placeholder('output classifier', );
}
...
}
export default ColorAccessibilityModel;``````

``````class ColorAccessibilityModel {
inputTensor;
targetTensor;
setupSession(trainingSet) {
const graph = new Graph();
this.inputTensor = graph.placeholder('input RGB value', );
this.targetTensor = graph.placeholder('output classifier', );
let connectedLayer = this.createConnectedLayer(graph, this.inputTensor, 0, 64);
connectedLayer = this.createConnectedLayer(graph, connectedLayer, 1, 32);
connectedLayer = this.createConnectedLayer(graph, connectedLayer, 2, 16);
}
createConnectedLayer(
graph,
inputLayer,
layerIndex,
units,
) {
...
}
...
}
export default ColorAccessibilityModel;
``````

``````class ColorAccessibilityModel {
inputTensor;
targetTensor;
setupSession(trainingSet) {
const graph = new Graph();
this.inputTensor = graph.placeholder('input RGB value', );
this.targetTensor = graph.placeholder('output classifier', );
let connectedLayer = this.createConnectedLayer(graph, this.inputTensor, 0, 64);
connectedLayer = this.createConnectedLayer(graph, connectedLayer, 1, 32);
connectedLayer = this.createConnectedLayer(graph, connectedLayer, 2, 16);
}
createConnectedLayer(
graph,
inputLayer,
layerIndex,
units,
) {
return graph.layers.dense(
`fully_connected_\${layerIndex}`,
inputLayer,
units
);
}
...
}
export default ColorAccessibilityModel;``````

``````class ColorAccessibilityModel {
inputTensor;
targetTensor;
setupSession(trainingSet) {
const graph = new Graph();
this.inputTensor = graph.placeholder('input RGB value', );
this.targetTensor = graph.placeholder('output classifier', );
let connectedLayer = this.createConnectedLayer(graph, this.inputTensor, 0, 64);
connectedLayer = this.createConnectedLayer(graph, connectedLayer, 1, 32);
connectedLayer = this.createConnectedLayer(graph, connectedLayer, 2, 16);
}
createConnectedLayer(
graph,
inputLayer,
layerIndex,
units,
activationFunction
) {
return graph.layers.dense(
`fully_connected_\${layerIndex}`,
inputLayer,
units,
activationFunction ? activationFunction : (x) => graph.relu(x)
);
}
...
}
export default ColorAccessibilityModel;``````

``````class ColorAccessibilityModel {
inputTensor;
targetTensor;
predictionTensor;
setupSession(trainingSet) {
const graph = new Graph();
this.inputTensor = graph.placeholder('input RGB value', );
this.targetTensor = graph.placeholder('output classifier', );
let connectedLayer = this.createConnectedLayer(graph, this.inputTensor, 0, 64);
connectedLayer = this.createConnectedLayer(graph, connectedLayer, 1, 32);
connectedLayer = this.createConnectedLayer(graph, connectedLayer, 2, 16);
this.predictionTensor = this.createConnectedLayer(graph, connectedLayer, 3, 2);
}
...
}
export default ColorAccessibilityModel;``````

``````class ColorAccessibilityModel {
inputTensor;
targetTensor;
predictionTensor;
costTensor;
setupSession(trainingSet) {
const graph = new Graph();
this.inputTensor = graph.placeholder('input RGB value', );
this.targetTensor = graph.placeholder('output classifier', );
let connectedLayer = this.createConnectedLayer(graph, this.inputTensor, 0, 64);
connectedLayer = this.createConnectedLayer(graph, connectedLayer, 1, 32);
connectedLayer = this.createConnectedLayer(graph, connectedLayer, 2, 16);
this.predictionTensor = this.createConnectedLayer(graph, connectedLayer, 3, 2);
this.costTensor = graph.meanSquaredCost(this.targetTensor, this.predictionTensor);
}
...
}
export default ColorAccessibilityModel;``````

``````import {
Graph,
Session,
NDArrayMathGPU,
} from 'deeplearn';
class ColorAccessibilityModel {
session;
inputTensor;
targetTensor;
predictionTensor;
costTensor;
setupSession(trainingSet) {
const graph = new Graph();
this.inputTensor = graph.placeholder('input RGB value', );
this.targetTensor = graph.placeholder('output classifier', );
let connectedLayer = this.createConnectedLayer(graph, this.inputTensor, 0, 64);
connectedLayer = this.createConnectedLayer(graph, connectedLayer, 1, 32);
connectedLayer = this.createConnectedLayer(graph, connectedLayer, 2, 16);
this.predictionTensor = this.createConnectedLayer(graph, connectedLayer, 3, 2);
this.costTensor = graph.meanSquaredCost(this.targetTensor, this.predictionTensor);
this.session = new Session(graph, math);
this.prepareTrainingSet(trainingSet);
}
prepareTrainingSet(trainingSet) {
...
}
...
}
export default ColorAccessibilityModel;``````

``````import {
Graph,
Session,
NDArrayMathGPU,
} from 'deeplearn';
const math = new NDArrayMathGPU();
class ColorAccessibilityModel {
session;
inputTensor;
targetTensor;
predictionTensor;
costTensor;
...
prepareTrainingSet(trainingSet) {
math.scope(() => {
...
});
}
...
}
export default ColorAccessibilityModel;``````

``````import {
Array1D,
Graph,
Session,
NDArrayMathGPU,
} from 'deeplearn';
const math = new NDArrayMathGPU();
class ColorAccessibilityModel {
session;
inputTensor;
targetTensor;
predictionTensor;
costTensor;
...
prepareTrainingSet(trainingSet) {
math.scope(() => {
const { rawInputs, rawTargets } = trainingSet;
const inputArray = rawInputs.map(v => Array1D.new(this.normalizeColor(v)));
const targetArray = rawTargets.map(v => Array1D.new(v));
});
}
...
}
export default ColorAccessibilityModel;``````

``````import {
Array1D,
InCPUMemoryShuffledInputProviderBuilder,
Graph,
Session,
NDArrayMathGPU,
} from 'deeplearn';
const math = new NDArrayMathGPU();
class ColorAccessibilityModel {
session;
inputTensor;
targetTensor;
predictionTensor;
costTensor;
...
prepareTrainingSet(trainingSet) {
math.scope(() => {
const { rawInputs, rawTargets } = trainingSet;
const inputArray = rawInputs.map(v => Array1D.new(this.normalizeColor(v)));
const targetArray = rawTargets.map(v => Array1D.new(v));
const shuffledInputProviderBuilder = new InCPUMemoryShuffledInputProviderBuilder([
inputArray,
targetArray
]);
const [
inputProvider,
targetProvider,
] = shuffledInputProviderBuilder.getInputProviders();
});
}
...
}
export default ColorAccessibilityModel;``````

``````import {
Array1D,
InCPUMemoryShuffledInputProviderBuilder
Graph,
Session,
NDArrayMathGPU,
} from 'deeplearn';
const math = new NDArrayMathGPU();
class ColorAccessibilityModel {
session;
inputTensor;
targetTensor;
predictionTensor;
costTensor;
feedEntries;
...
prepareTrainingSet(trainingSet) {
math.scope(() => {
const { rawInputs, rawTargets } = trainingSet;
const inputArray = rawInputs.map(v => Array1D.new(this.normalizeColor(v)));
const targetArray = rawTargets.map(v => Array1D.new(v));
const shuffledInputProviderBuilder = new InCPUMemoryShuffledInputProviderBuilder([
inputArray,
targetArray
]);
const [
inputProvider,
targetProvider,
] = shuffledInputProviderBuilder.getInputProviders();
this.feedEntries = [
{ tensor: this.inputTensor, data: inputProvider },
{ tensor: this.targetTensor, data: targetProvider },
];
});
}
...
}
export default ColorAccessibilityModel;``````

``````import {
Array1D,
InCPUMemoryShuffledInputProviderBuilder,
Graph,
Session,
SGDOptimizer,
NDArrayMathGPU,
} from 'deeplearn';
const math = new NDArrayMathGPU();
class ColorAccessibilityModel {
session;
optimizer;
batchSize = 300;
initialLearningRate = 0.06;
inputTensor;
targetTensor;
predictionTensor;
costTensor;
feedEntries;
constructor() {
this.optimizer = new SGDOptimizer(this.initialLearningRate);
}
...
}
export default ColorAccessibilityModel;``````

### 训练阶段

``````class ColorAccessibilityModel {
...
train() {
math.scope(() => {
this.session.train(
this.costTensor,
this.feedEntries,
this.batchSize,
this.optimizer
);
});
}
}
export default ColorAccessibilityModel;``````

``````class ColorAccessibilityModel {
...
train(step) {
let learningRate = this.initialLearningRate * Math.pow(0.90, Math.floor(step / 50));
this.optimizer.setLearningRate(learningRate);
math.scope(() => {
this.session.train(
this.costTensor,
this.feedEntries,
this.batchSize,
this.optimizer
);
}
}
}
export default ColorAccessibilityModel;``````

``````import {
Array1D,
InCPUMemoryShuffledInputProviderBuilder,
Graph,
Session,
SGDOptimizer,
NDArrayMathGPU,
CostReduction,
} from 'deeplearn';
class ColorAccessibilityModel {
...
train(step, computeCost) {
let learningRate = this.initialLearningRate * Math.pow(0.90, Math.floor(step / 50));
this.optimizer.setLearningRate(learningRate);
let costValue;
math.scope(() => {
const cost = this.session.train(
this.costTensor,
this.feedEntries,
this.batchSize,
this.optimizer,
computeCost ? CostReduction.MEAN : CostReduction.NONE,
);
if (computeCost) {
costValue = cost.get();
}
});
return costValue;
}
}
export default ColorAccessibilityModel;``````

### 推断阶段

``````class ColorAccessibilityModel {
...
predict(rgb) {
let classifier = [];
math.scope(() => {
const mapping = [{
tensor: this.inputTensor,
data: Array1D.new(this.normalizeColor(rgb)),
}];
classifier = this.session.eval(this.predictionTensor, mapping).getValues();
});
return [ ...classifier ];
}
}
export default ColorAccessibilityModel;``````

### 在 JavaScript 中可视化学习神经网络

``````import React, { Component } from 'react';
import './App.css';
import generateColorSet from './data';
import ColorAccessibilityModel from './neuralNetwork';
const ITERATIONS = 750;
const TRAINING_SET_SIZE = 1500;
const TEST_SET_SIZE = 10;
class App extends Component {
...
}
export default App;``````

App 的组件包括生成数据集（训练集和测试集）、通过传递训练集建立神经网络会话、定义组件的初始状态。在训练阶段的时间内，代价函数的值和迭代次数会在控制台上显示，它也表示了组件的状态。

``````import React, { Component } from 'react';
import './App.css';
import generateColorSet from './data';
import ColorAccessibilityModel from './neuralNetwork';
const ITERATIONS = 750;
const TRAINING_SET_SIZE = 1500;
const TEST_SET_SIZE = 10;
class App extends Component {
testSet;
trainingSet;
colorAccessibilityModel;
constructor() {
super();
this.testSet = generateColorSet(TEST_SET_SIZE);
this.trainingSet = generateColorSet(TRAINING_SET_SIZE);
this.colorAccessibilityModel = new ColorAccessibilityModel();
this.colorAccessibilityModel.setupSession(this.trainingSet);
this.state = {
currentIteration: 0,
cost: -42,
};
}
...
}
export default App;``````

``````class App extends Component {
...
componentDidMount () {
for (let i = 0; i <= ITERATIONS; i++) {
this.colorAccessibilityModel.train(i);
}
};
}
export default App;``````

``````class App extends Component {
...
componentDidMount () {
requestAnimationFrame(this.tick);
};
tick = () => {
this.setState((state) => ({
currentIteration: state.currentIteration + 1
}));
if (this.state.currentIteration < ITERATIONS) {
requestAnimationFrame(this.tick);
this.colorAccessibilityModel.train(this.state.currentIteration);
}
};
}
export default App;``````

``````class App extends Component {
...
componentDidMount () {
requestAnimationFrame(this.tick);
};
tick = () => {
this.setState((state) => ({
currentIteration: state.currentIteration + 1
}));
if (this.state.currentIteration < ITERATIONS) {
requestAnimationFrame(this.tick);
let computeCost = !(this.state.currentIteration % 5);
let cost = this.colorAccessibilityModel.train(
this.state.currentIteration,
computeCost
);
if (cost > 0) {
this.setState(() => ({ cost }));
}
}
};
}
export default App;``````

``````class App extends Component {
...
render() {
const { currentIteration, cost } = this.state;
return (
<div className="app">
<div>
<h1>Neural Network for Font Color Accessibility</h1>
<p>Iterations: {currentIteration}</p>
<p>Cost: {cost}</p>
</div>
<div className="content">
<div className="content-item">
<ActualTable
testSet={this.testSet}
/>
</div>
<div className="content-item">
<InferenceTable
model={this.colorAccessibilityModel}
testSet={this.testSet}
/>
</div>
</div>
</div>
);
}
}
const ActualTable = ({ testSet }) =>
<div>
<p>Programmatically Computed</p>
</div>
const InferenceTable = ({ testSet, model }) =>
<div>
<p>Neural Network Computed</p>
</div>
export default App;``````

``````const ActualTable = ({ testSet }) =>
<div>
<p>Programmatically Computed</p>
{Array(TEST_SET_SIZE).fill(0).map((v, i) =>
<ColorBox
key={i}
rgbInput={testSet.rawInputs[i]}
rgbTarget={fromClassifierToRgb(testSet.rawTargets[i])}
/>
)}
</div>
const fromClassifierToRgb = (classifier) =>
classifier > classifier
? [ 255, 255, 255 ]
: [ 0, 0, 0 ]``````

ColorBox 组件是一个通用组件，以输入颜色（背景颜色）和目标颜色（字体颜色）为输入。它能简单地用一个矩形展示输入颜色的类型、输入颜色的 RGB 代码字符串，并用字体的 RGB 代码将给定的目标颜色上色。

``````const ColorBox = ({ rgbInput, rgbTarget }) =>
<div className="color-box" style={{ backgroundColor: getRgbStyle(rgbInput) }}>
<span style={{ color: getRgbStyle(rgbTarget) }}>
<RgbString rgb={rgbInput} />
</span>
</div>
const RgbString = ({ rgb }) =>
`rgb(\${rgb.toString()})`
const getRgbStyle = (rgb) =>
`rgb(\${rgb}, \${rgb}, \${rgb})```````

``````const InferenceTable = ({ testSet, model }) =>
<div>
<p>Neural Network Computed</p>
{Array(TEST_SET_SIZE).fill(0).map((v, i) =>
<ColorBox
key={i}
rgbInput={testSet.rawInputs[i]}
rgbTarget={fromClassifierToRgb(model.predict(testSet.rawInputs[i]))}
/>
)}
</div>``````
{"ft_lang_label":"__label__zh","ft_lang_prob":0.5249237,"math_prob":0.88996375,"size":21537,"snap":"2019-35-2019-39","text_gpt3_token_len":9628,"char_repetition_ratio":0.17638044,"word_repetition_ratio":0.46584186,"special_character_ratio":0.22909412,"punctuation_ratio":0.27234042,"nsfw_num_words":0,"has_unicode_error":false,"math_prob_llama3":0.97613734,"pos_list":[0,1,2,3,4],"im_url_duplicate_count":[null,6,null,null,null],"WARC_HEADER":"{\"WARC-Type\":\"response\",\"WARC-Date\":\"2019-09-15T14:50:46Z\",\"WARC-Record-ID\":\"<urn:uuid:c4a42eab-baf5-46ba-844c-98d09160dea4>\",\"Content-Length\":\"78993\",\"Content-Type\":\"application/http; msgtype=response\",\"WARC-Warcinfo-ID\":\"<urn:uuid:6b5b15d1-5728-47c9-bb58-b5ef12c90149>\",\"WARC-Concurrent-To\":\"<urn:uuid:c8c65e7b-3f7f-43b6-92af-c10b8104f0e0>\",\"WARC-IP-Address\":\"39.106.131.93\",\"WARC-Target-URI\":\"https://www.jiqizhixin.com/articles/2017-12-06-3\",\"WARC-Payload-Digest\":\"sha1:GJIJ37T4JMV6HAP7ZG77VTDZZMW6HERI\",\"WARC-Block-Digest\":\"sha1:Q6DKPJXWQJPWKDCAV7NAO7WSFE4CAKL2\",\"WARC-Identified-Payload-Type\":\"text/html\",\"warc_filename\":\"/cc_download/warc_2019/CC-MAIN-2019-39/CC-MAIN-2019-39_segments_1568514571506.61_warc_CC-MAIN-20190915134729-20190915160729-00357.warc.gz\"}"}