September 29, 2021

ml5.js - Getting Started

这是一个关于ml5.js的学习笔记,和案例尝试。

什么是ml5.js?

ml5.js is a javascript library - friendly machine learning for web.

ml5.js是可以在web浏览器中运行的机器学习框架,通过web浏览器内置的GPU处理单元计算,这样的运算速度远远快于CPU,shader就是这样的一个解释案例。

Quickstart

尝试ml5.js的最快速的方式是:

1.结合p5.js和ml5.js一起使用,在这里查看P5.js的web模板,或从GitHub上获取p5.js和ml5.js的模版文件

2.当然也可以使用纯ml5.js的文件模板,并从GitHub上获取

3.从CDN获取ml5.js的库:

1
<script src="https://unpkg.com/ml5@latest/dist/ml5.min.js"></script>

在index.html文件中,复制粘贴,然后在浏览器中打开文件查看:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html lang="en">
<head>
<title>Getting Started with ml5.js</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://unpkg.com/ml5@latest/dist/ml5.min.js"></script>
</head>

<body>
<script>
// Your code will go here
// open up your console - if everything loaded properly you should see the correct version number
console.log('ml5 version:', ml5.version);
</script>
</body>
</html>

ml5.js的设计可以与p5.js配合得很好,可以从以下的模版代码开始构建你的项目:

在index.html文件中,复制粘贴,并且在浏览器中打开该文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<!DOCTYPE html>
<html lang="en">
<head>
<title>Getting Started with ml5.js</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- p5 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/p5.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/addons/p5.sound.min.js"></script>
<!-- ml5 -->
<script src="https://unpkg.com/ml5@latest/dist/ml5.min.js"></script>
</head>

<body>
<script>
// Your code will go here
// open up your console - if everything loaded properly you should see the latest ml5 version
console.log('ml5 version:', ml5.version);

function setup() {
createCanvas(400, 400);
}

function draw() {
background(200);
}
</script>
</body>
</html>

ml5.js introduction

ml5.js目标是让更多的人可以轻松地使用机器学习。ml5的团队也在努力创建更加友好,易于使用的机器学习功能。下面的一个案例介绍了ml5.js实现的机器学习经典用例:图像分类。

该案例演示了如何在ml5.js中使用MobileNet的预训练模型,以及设置ml5.js的项目。

要设置一个简单的ml5.js的项目并运行,你需要使用:

  • 📝 一款文本编辑器 (e.g. Atom, VSCode, Sublimetext)
  • 💻 你的网页浏览器: 推荐Chrome & Firefox
  • 🖼 运行分类的图片

你的项目目录看起来会是这样:

1
2
3
4
5
|_ /hello-ml5
|_ 📂/images
|_ 🖼 bird.png
|_ 🗒index.html
|_ 🗒sketch.js

其中:

项目的github仓库在这里

你的index.html

在这里你会发现我们读了JavaScript库,这里包含了我们的ml5.js和ps5.js的版本,你可以复制粘接进你的index.html文件,或者手动书写。确认保存,并在存储之后刷新你的浏览器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<html>

<head>
<meta charset="UTF-8">
<title>Image classification using MobileNet and p5.js</title>

<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/p5.min.js"></script>
<script src="https://unpkg.com/ml5@latest/dist/ml5.min.js"></script>
</head>

<body>
<h1>Image classification using MobileNet and p5.js</h1>
<script src="sketch.js"></script>
</body>

</html>

你的sketch.js

在你的sketch.js文件中,你可以输入下列的代码。注意在栗子中我们有引用”images/bird.png”。你可以用自己的图片取代。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// Initialize the image classifier method with MobileNet. A callback needs to be passed.
let classifier;

// Avariable to hold the image we want to classify
let img;

function preload() {
classifier = ml5.imageClassifier('MobileNet');
img = loadImage('images/bird.png');
}

function setup() {
createCanvas(400, 400);
classifier.classify(img, gotResult);
image(0, 0);
}

// A function torun when we get any errors and the results
function gotResult(error, results) {
// Display error in the console
if (error) {
console.error(error);
} else {
// The results are in an array ordered by confidence.
console.log(results);
createDiv('Label: ${results[0].label}');
createDiv('Confidence: $(nf(results[0].confidence, 0, 2))');
}
}

sketch.js在四步骤中被解释

Step 1: Define your variables

这里是我们定义了变量并且注册我们的分类器和图片:

1
2
3
4
5
// Initialize the Image Classifier method with MobileNet. A callback needs to be passed.
let classifier;

// A variable to hold the image we want to classify
let img;
Step 2: Load your imageClassifer and image

使用p5的preload()函数,载入我们的图片分类器模型和鸟类图片,在我们运行其他代码之前;因为机器学习的模型可能很大,所以需要一些时间去加载,我们使用preload(),确保我们的分类器在进入到下一步骤之前就准备好了。

1
2
3
4
function preload() {
classifier = ml5.imageCLASSIFIER('MobileNet');
img = loadImage('images/bird.png');
}
Step 3: Setup, classify, and display

在p5.js中,我们使用setup()函数来运行项目所有的函数一次。在本项目中,我们使用setup()函数:

  1. 创建一个canvas来渲染我们的图片
  2. 唤起classifier的.classify()函数,来对图片分类
  3. 将图片渲染到canvas中去

你会注意到.classify()函数有两个参数:1.你想要分类的图片,2.一个gotResult回调函数。

1
2
3
4
5
function setup() {
createCanvas(400, 400);
classifier.classify(img, gotResult);
image(img, 0, 0);
}
Step 4: 定义gotResult()回调函数

gotResult()函数也有两个参数:1.error, 2.results。当.classify()函数完成图片的分类之后,会把参数传递给gotResult()。如果有一个错误,错误会被logged。如果我们的分类器管理辨别到图片的内容,result则会被返回。

在我们的项目中,我们创建了一个div展示标签和分类结果的置信度,nf()函数是一个p5的函数,格式化我们的数字为字符串。

And voilà!

你已经创建了一个简单的机器学习项目:

1.拿出一张图片

2.分类图片的内容

3.在网页浏览器中展示你的结果。

我们不是所有的案例都像本案例一样结构氢气,但是这个可以让你快速接触ml5.js是如何让机器学习更易于上手,你可以尝试不同的图片然后看他们返回的结果是什么。

ml5的Promises and Callback支持

ml5.js受到p5.js的语法风格启发,ml5.js在所有的方法中都支持错误的优先callback和Promise。

使用callback

在p5.js中,回调座位参数传递给经常执行一些异步操作的函数。

例如,p5.js定义的loadJSON()函数如下:

1
2
3
loadJSON('https://example.com/data.json', (results) => {
// Do something with the results
});

p5.js回调的结果作为函数的唯一参数给出,不会有报错参数。

而ml5.js使用错误优先回调的模式:

使用这种模式,回调函数作为参数传递给方法,当操作完成或引发错误时,将调用回调函数,将error对象作为第一个参数传递。如果未引发错误,则第一个参数作为null传递。

例如,你可以使用imageClassifier()方法,则需要按照以下方式构造函数:

1
2
3
4
5
6
7
8
9
10
11
// Pass a callback function to constructor
// 将一个回调函数传递给构造器
const classifier = ml5.imageClassifier('MobileNet', (err, model) => {
console.log('Model Loaded!');
});

// Make a prediction with the selected image and pass a callback function with two argumetns
// 通过选择的图片进行预测,然后传递带有两个参数的回调函数
classifier.predict(image, (err, results) => {
// Check for errors. If no errors, then do something with the results
});
使用Promise

ml5.js也支持Promises,如果没有任何异步函数提供回调,则返回一个Promise。

使用Promises,可以按照以下方式使用图像分类示例:

1
2
3
4
5
6
7
// No callback needs to be passed to use Promises.
ml5
.imageClassifier('MobileNet')
.then(classifier => classifier.predict(image))
.then(results) => {
// Do something with the results
});

Neural Network

可以使用ml5.neuralNetwork创建自己的神经网络,并在浏览器中使用。收集数据训练神经网络或者使用已有的数据进行训练。训练完成后,你的神经网络就可以执行classificationregression的任务。

一般而言,使用ml5.neuralnetwork需要这些步骤:

我们可以从下面的一个例子来进行实际的了解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
// Step 1: load data or create some data

const data = [
{r:255, g:0, b:0, color:'red-ish'},
{r:254, g:0, b:0, color:'red-ish'},
{r:253, g:0, b:0, color:'red-ish'},
{r:0, g:255, b:0, color:'green-ish'},
{r:0, g:254, b:0, color:'green-ish'},
{r:0, g:253, b:0, color:'green-ish'},
{r:0, g:0, b:255, color:'blue-ish'},
{r:0, g:0, b:254, color:'blue-ish'},
{r:0, g:0, b:253, color:'blue-ish'}
];

// Step 2: set your neural network options
const options = {
task: 'classification',
debug: true
}

// Step 3: initialize your neural network
const nn = ml5.neuralNetwork(options);

// Step 4: add data to the neural network
data.forEach(item => {
const inputs = {
r: item.r,
g: item.g,
b: item.b
};

const output = {
color: item.color
};

nn.addData(inputs, output);
});

// Step 5: normalize your data;
nn.normalizeData();

// Step 6: train your neural network
const trainingOptions = {
epochs: 32,
batchSize: 12
}
nn.train(trainingOptions, finishedTraining);

// Step 7: use the trained model
function finishedTraining() {
classify();
}

// Step 8: make a classification
function classify() {
const input = {
r: 255,
g: 0,
b: 0
}
nn.classify(input, handleResults);
}

// Step 9: define a function to handle the results of your classification
function handleResults(error, result) {
if(error) {
console.error(error);
return;
}
console.log(result); // {label: 'red', confidence: 0.8};
}
载入现有数据

外部数据:"data/colorData.json"

1
2
3
4
5
6
7
8
[
{"r":255, "g":0, "b":0, "color": "red-ish"},
{"r":254, "g":0, "b":0, "color": "red-ish"},
{"r":253, "g":0, "b":0, "color": "red-ish"},
{"r":0, "g":0, "b":255, "color": "blue-ish"},
{"r":0, "g":0, "b":254, "color": "blue-ish"},
{"r":0, "g":0, "b":253, "color": "blue-ish"}
];

在JavaScript中:script.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// Step 1: set your neural network options
const options = {
dataUrl: "data/colorData.json",
task: 'classification',
debug: true
}

// Step 2: initialize your neural network
const nn = ml5.neuralNetwork(options, dataLoaded);

// Step 3: normalize data and train the model
function dataLoaded() {
nn.normalizeData();
trainModel();
}

// Step 4: train the model
function trainModel() {
const trainingOptions = {
epochs: 32,
batchSize: 12
}
nn.train(trainingOptions, finishedTraining);
}

// Step 5: use the trained model
function finishedTraining() {
classify();
}

// Step 6: make a classification
function classify() {
const input = {
r: 255,
g: 0,
b: 0
}
nn.classify(input, handleResults);
}

// Step 7: define a function to handle the results of your classification
function handleResults(error, result) {
if(error){
console. error(error);
return;
}
console.log(result); // {label: 'red', confidence: 0.8};
}
用例参考

下面列举一些参考的用途:

示例一:

1
2
3
4
5
6
7
const options = {
inputs: 1,
outputs: 1,
task: 'regression'
}

const nn = ml5.neuralNetwork(options)

示例二:将数据加载为csv

1
2
3
4
5
6
7
const options = {
dataUrl: 'weather.csv',
inputs: ['avg_temperature', 'humidity'],
outputs: ['rained'],
task: 'classification'
}
const nn = ml5.neuralNetwork(options, modelLoaded)

示例三:将数据加载为json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
The weather json looks something like:
{"data": [
{"xs": {"avg_temperature":20, "humidity": 0.2}, "ys": {"rained": "no"}},
{"xs": {"avg_temperature":30, "humidity": 0.9}, "ys": {"rained": "yes"}}
] }
**/

const options = {
dataUrl: 'weather.json',
inputs: ['avg_temperature', 'humidity'],
outputs: ['rained'],
task: 'classification'
}
const nn = ml5.neuralNetwork(options, modelLoaded)

示例四:为空白的神经网络指定标签

1
2
3
4
5
6
const options = {
inputs: ['x', 'y'],
outputs: ['label'],
task: 'classification',
};
const nn = ml5.neuralNetwork(options);

示例五:通过设置task: imageClassification来创建图像分类的神经网络

1
2
3
4
5
6
7
8
9
const IMAGE_WIDTH = 64;
const IMAGE_HEIGHT = 64;
const IMGAE_CHANNELS = 4;
const options = {
task: 'imageClassificaton',
inputs: [IMAGE_WIDTH, IMAGE_HEIGHT, IMAGE_CHANNELS],
outputs: ['label']
}
const nn = ml5. neuralNetwork(options);

初始化和参数

有多种方法可以出实话ml5.neuralNetwork。下面我们介绍几个例子:

1.最小配置方法

2.将输入和输出的标签定义为数字或标签数组

3.加载外部数据

4.加载预训练模型

5.用于图像分类任务的卷积神经网络

6.定义自定义层

最小配置方法

如果你打算创建实时数据,你可以只设置想要完成的任务类型,('regression' | 'classification')然后创建神经网络,在添加数据后,ml5才会根据添加的数据进行输入和输出。

1
2
3
4
const options = {
task: 'regression' // or 'classification'
}
const nn = ml5.neuralNetwork(options)
将输入和输出标签定义为数字或标签数组

为了更具体地处理输入和输出内容,你还可以将输入和输出的标签名称定为数组,以及输入和输出的数量。如果将输入设置为JSON,则JSON的密钥和数组应与options的匹配。

1
2
3
4
5
6
7
const options = {
task: 'classification' // or 'regression'
inputs: ['r', 'g', 'b']
outputs: ['color']
}

const nn = ml5.neuralNetwork(options)
加载外部数据

你可以初始化ml5.neuralNetwork指定外部url到一些结构为JSON或CSV文件的数据。如果将数据作为options的一部分传入,需要提供一个回调函数,该函数将在数据加载完成后被调用。此外,你需要指定ml5.neuralNetwork中哪些属性是输入和输出。

1
2
3
4
5
6
7
8
9
10
11
12
13
const options = {
dataUrl: 'data/colorData.csv'
task: 'classification' // or 'regression'
intputs: ['r', 'g', 'b'] // r, g, b
outputs: ['color'] // red-ish, blue-ish
}

const nn = ml5.neuralNetwork(options, dataLoaded)

function dataLoaded() {
// continue on your neural network journey
nn.normalizeData();
}
加载预训练模型

如果已经使用了ml5.neuralNetwork训练模型,使用ml5.neuralNetwork.save()可以保存,然后加载自己的模型,权重,和元数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const options = {
task: 'classification' // or 'regression'
}
const nn = ml5.neuralNetwork(options);

const modelDetails = {
model: 'model/model.json',
metadata: 'model/model_meta.json',
weights: 'model/model.weights.bin'
}
nn.load(modelDetails, modelLoaded)

function modelLoaded() {
// continue on your neural network journey
// use nn.classify() for classifications or nn.predict() for regression
}

用于图像分类的’’卷积神经网络

可以通过设置task:"imageClassification 来完成。

1
2
3
4
5
6
7
8
9
const IMAGE_WIDTH = 64;
const IMAGE_HEIGHT = 64;
const IMAGE_CHANNELS = 4;
const options = {
task: 'imageClassification',
inputs: [IMAGE_WIDTH, IMAGE_HEIGHT, IMAGE_CHANNELS],
outputs: ['label']
}
const nn = ml5.neuralNetwork(options);
定义自定义层

默认结构,ml5.neuralNetworkclassification, regressionimageClassification的简单结构。

classification层:

1
2
3
4
5
6
7
8
9
10
11
layers:[
{
type: 'dense',
units: this.options.hiddenUnits,
activations: 'relu',
},
{
type: 'dense',
activation: 'softmax',
},
];

regression层:

1
2
3
4
5
6
7
8
9
10
11
layers: [
{
type: 'dense',
units: this.options.hiddenUnits,
activation: 'relu',
},
{
type: 'dense',
activation: 'sigmoid',
},
];

imageClassification层:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
layers = [
{
type: 'conv2d',
filters: 8,
kernelSize: 5,
strides: 1,
activation: 'relu',
kernelInitializer: 'varianceScaling',
},
{
type: 'maxPolling2d',
poolSize: [2, 2],
strides: [2, 2],
},
{
type: 'conv2d',
filters: 16,
kernelSize: 5,
strides: 1,
activation: 'relu',
kernelInitializer: 'variancescaling',
},
{
type: 'flatten',
},
{
type: 'dense',
kernelInitializer: 'varianceScaling',
activation: 'softmax',
},
];

定义自定义层: 在options 中定义,并传递给ml5.neuralNetwork定义自定义的网络结构。

例如,一个具有3层的神经网络。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const options = {
debug: true,
task: 'classification',
layers: [
{
type: 'dense',
units: 16,
activation: 'relu'
},
{
type: 'dense',
units: 16,
activation: 'sigmoid'
},
{
type: 'dense',
activation: 'sigmoid'
}
]
};
const nn = ml5.neuralNetwork(options);

ml5.neuralNetwork(options)

可以指定的options有:

1
2
3
4
5
6
7
8
9
10
11
const DEFAULTS = {
inputs: [], //can also be a number 也可以是一个数字
outputs: [], // can also be a number 也可以是一个数字
dataUrl: null,
modelUrl: null,
layers: [], // 自定义层
task: null, // 'classification', 'regression', 'imageClassification'
debug: false, // determines whether or not to show the training visualization
learningRate: 0.2,
hiddenUnits: 16,
};

Properties

property description datatype
.callback the callback to be called after data is loaded on initialization function
.options the options for how the neuralNetwork should be configured on initialization object
.neuralNetwork the neuralNetwork class where all of the tensorflow.js model operations are organized class
.neuralNetworkData the neuralNetworkData class where all of the data handling operations are organized class
.neuralNetworkVis the neuralNetworkVis class where all of the tf-vis operations are organized class
.data The property that stores all of the training data after .train() is called class
.ready set to true if the model is loaded and ready, false if it is not. boolean

Methods

method description
.addData() adds data to the neuralNetworkData.data.raw array
.normalizeData() normalizes the data stored in neuralNetworkData.data.raw and stores the normalized values in the neuralNetwork.data.training array
.train() uses the data in the neuralNetwork.data.training array to train your model
.predict() for regression tasks, allows you to make a prediction based on an input array or JSON object.
.predictMultiple() for regression tasks, allows you to make a prediction based on an input array of arrays or array of JSON objects.
.classify() for classification tasks, allows you to make a classification based on an input array or JSON object.
.classifyMultiple() for classification tasks, allows you to make classifications based on an input array of arrays or array of JSON objects.
.saveData() allows you to save your data out from the neuralNetworkData.data.raw array
.loadData() allows you to load data previously saved from the .saveData() function
.save() allows you to save the trained model
.load() allows you to load a trained model
.addData()

如果没有使用dataUrl给构造函数提供数据,可以使用.addData()函数将数据添加到神经网络中。

1
neuralNetwork.addData(xs, ys);

About this Post

This post is written by Siqi Shu, licensed under CC BY-NC 4.0.