本文最后更新于:a few seconds ago
前言
本章将使用TensorFlow.js,进行线性回归的学习。
使用parcel打包工具进行打包, 此工具免配置,适用于经验不同的开发者,比webpack方便。
并且会用两个例子,一个是简单的预测两数之间的关系,一个是预测孩子的身高与父母身高的关系,来加深印象。
前置条件
您需要有前端开发的基础知识。(js,html)
了解线性回归相关知识。
拥有高中或者高等数学知识。
推荐:Google机器学习速成教程
准备环境
我们先在任意空文件夹中,打开我们的编辑器,初始化我们的编码环境
接下来我们来下载我们的TensorFlow.js包,和 parcel 打包工具。
| npm install @tensorflow/tfjs
|
全局安装parcel
| npm install -g parcel-bundler
|
我们接下来会用tfjs-vis,来可视化我们的训练过程,我们把它也顺便安装上。
| npm install @tensorflow/tfjs-vis
|
创建文件夹,进入文件夹创建index.html和index.js,目录结构类似这样:
| |-node_modules |-linearRegression |--index.html |--index.js |-package.json |-package-lock.json
|
我们等等要用到async+await语法,所以我们的在package.json声明一一些代码: last 1 Chrome version
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| { "name": "tensoflow.js", "version": "1.0.0", "description": "", "main": "index.js", "dependencies": { "@tensorflow-models/speech-commands": "^0.4.0", "@tensorflow/tfjs": "^1.3.1", "@tensorflow/tfjs-node": "^1.2.9", "@tensorflow/tfjs-vis": "^1.2.0" }, "devDependencies": {}, "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC", "browserslist": [ "last 1 Chrome version" ] }
|
用X来预测Y
创建HTML
我们首先把上面创建的HTML引入我们的js,让它来可视化我们的训练过程。
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>线性回归</title> </head> <body> <script src="./index.js"></script> </body> </html>
|
填充训练集和绘制散点图
引入我们需要的两个库
| import * as tf from '@tensorflow/tfjs'; import * as tfvis from '@tensorflow/tfjs-vis';
|
填充我们的训练集
我们填充了xs,和ys,准备用xs来预测我们的ys。并且创建了一个匿名函数,让它在html加载完执行。
(数据也可自行填充)
| import * as tf from '@tensorflow/tfjs'; import * as tfvis from '@tensorflow/tfjs-vis';
window.onload = () => { const xl = [1, 2, 3, 4]; const yl = [2, 3, 5, 7]; };
|
绘制散点图
下面调用的tfvis
的render方法里的scatterplot
,来绘制散点图。scatterplot
接收三个对象。我们只介绍部分,详情点击上面链接。
第一个对象可以接收一个name
,类型是string,代表图表的名字
第二个对象接收一个values
,类型是以{x,y}元组的数组(或嵌套数组)。
类型就是这样:[ { x: 1, y: 2 }, { x: 2, y: 3 }, { x: 3, y: 5 }, { x: 4, y: 7 } ]
第三个对象我们只用到了xAxisDomain
和yAxisDomain
,用来调整我们图表的可视范围。
| import * as tf from '@tensorflow/tfjs'; import * as tfvis from '@tensorflow/tfjs-vis';
window.onload = () => { const xl = [1, 2, 3, 4]; const yl = [2, 3, 5, 7]; }; tfvis.render.scatterplot( { name: 'linear regression' }, { values: xl.map((x, i) => ({ x, y: yl[i] })) }, { xAxisDomain: [0, 5], yAxisDomain: [0, 8] } );
|
接下来就可以运行一下我们的程序了,在命令行写入:
| parcel .\linear-regression\index.html
|
然后打开页面,就能查看到我们的散点图了
创建一个神经网络模型
用tf.sequential,创建一个连续的模型。
sequential
模型的阔谱可以理解为堆栈,没有分支和跳过。我们在解决一些简单问题的时候,一般用此模型。
| import * as tf from '@tensorflow/tfjs'; import * as tfvis from '@tensorflow/tfjs-vis';
window.onload = () => { const xl = [1, 2, 3, 4]; const yl = [2, 3, 5, 7]; }; tfvis.render.scatterplot( { name: 'linear regression' }, { values: xl.map((x, i) => ({ x, y: yl[i] })) }, { xAxisDomain: [0, 5], yAxisDomain: [0, 8] } ); const model = tf.sequential();
|
为神经网络添加层
接下来就是为神经网络添加一个全连接层,它可以实现的操作是
output = activation(dot(input, kernel) + bias)
activation
: 激活函数,我们现在还用不到,他能帮我们解决更多的问题
dot
: 点乘
kernel
: 权重矩阵,在我们这个例子里可以理解为简单的一个数值。
bias
: 表示偏置矢量
简单理解就是: 它可以帮我们解决乘以一个权重,再加上一个参数的问题。代码如下:
| const model = tf.sequential(); model.add(tf.layers.dense({ units: 1, inputShape: [1] }));
|
model.add
: 表示添加一个层
tf.layers.dense()
: 创建一个全连接层。 扩展tf.layers.dense()
units
: 神经元的个数,一个神经元就能解决我们的问题了。
inputShape
: 输入的值形状,[1]表示我们的输入是一维的数据。就是我们的x轴数据。
设置损失函数:均方误差与优化器: 随机梯度下降
神经网络在初始化的时候会瞎蒙一个权重值,这个权重值一般是错的,而这个损失函数,就是来告诉神经网络,这个权重值错得有多离谱。
而神经网络就会用优化器来降低我们的损失。降低损失
均方误差这个常用的损失函数,在我们这个线性回归问题的适用的,但不代表所有情况。
TensorFlow已经帮我们封装了公式的操作,我们只需要调用就行了,如下:
| model.compile({ loss: tf.losses.meanSquaredError, optimizer: tf.train.sgd(0.1) });
|
loss:里面传了一个均方误差tf.losses.meanSquaredError
,tf.losses里有很多的损失函数。
optimizer: 传了一个优化器,sgd
就是随机梯度下降。并且设置了一个0.1的学习速率。这是一个超参数。 扩展:更多优化器.
优化学习速率,Google的这个交互教程能加快您对学习速率的理解。
训练模型并在网页上显示训练过程
我们训练的第一步是将训练的数据转换为 Tensor,这个张量是一个高维的物理量,这是TensorFlow世界的规定。
具体了解详情点击上面链接看示例。
第二步就能调用model.fit训练模型。
第一个参数是x的张量,第二个参数是y的张量。第三个参数接收一个对象,我们主要用到以下几个:
epochs
: 迭代训练数据数组的次数,接收类型为Number。
batchSize
: 每个梯度更新的样本数,接收类型为Number。
callback
: 回调,我们要在此调用tfvis.show.fitCallbacks,来绘制我们的损失。
它返回的是一个Promise,所以我们要在前面加上await,函数前面也要记得加async。
代码如下:
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
| import * as tf from '@tensorflow/tfjs'; import * as tfvis from '@tensorflow/tfjs-vis';
window.onload = async () => { const xl = [1, 2, 3, 4]; const yl = [2, 3, 5, 7];
tfvis.render.scatterplot( { name: '线性回归训练集' }, { values: xl.map((x, i) => ({ x, y: yl[i] })) }, { xAxisDomain: [0, 5], yAxisDomain: [0, 8] } );
const model = tf.sequential(); model.add(tf.layers.dense({ units: 1, inputShape: [1] })); model.compile({ loss: tf.losses.meanSquaredError, optimizer: tf.train.sgd(0.1) });
const xInputs = tf.tensor(xl); const yInputs = tf.tensor(yl); await model.fit(xInputs, yInputs, { batchSize: 2, // 每次要去学的数据量多大: 随机梯度下降 epochs: 200, // 超参数 callbacks: tfvis.show.fitCallbacks( { name: '训练过程' }, ['loss'] ) }); };
|
当batchSize
设置为2的时候,代表每次给模型学习两个点,这样模型产生的误差抖动还是较大的。
我们将batchSize
设置为4,让模型一次学4个点看看
这次的曲线好多了,接下来我们就可以用这个训练好的模型来进行预测了。
预测模型
下面将用model.predict对输入的张量进行推理。
注意:输入的值要转换为张量。
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
| import * as tf from '@tensorflow/tfjs'; import * as tfvis from '@tensorflow/tfjs-vis';
window.onload = async () => { const xl = [1, 2, 3, 4]; const yl = [2, 3, 5, 7];
tfvis.render.scatterplot( { name: '线性回归训练集' }, { values: xl.map((x, i) => ({ x, y: yl[i] })) }, { xAxisDomain: [0, 5], yAxisDomain: [0, 8] } );
const model = tf.sequential(); model.add(tf.layers.dense({ units: 1, inputShape: [1] })); model.compile({ loss: tf.losses.meanSquaredError, optimizer: tf.train.sgd(0.1) });
const xInputs = tf.tensor(xl); const yInputs = tf.tensor(yl); await model.fit(xInputs, yInputs, { batchSize: 4, epochs: 200, callbacks: tfvis.show.fitCallbacks( { name: '训练过程' }, ['loss'] ) });
const output = model.predict(tf.tensor([5])); output.print() alert(`当x为 5 的时候,推理的 y 为 ${output.dataSync()[0]}`); };
|
到此我们就完成了线性回归的学习。
预测男女性身高
数据是伪造的,只要简单的填充一下数据,和改写一下区间就行,大体不变:
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
| import * as tf from '@tensorflow/tfjs'; import * as tfvis from '@tensorflow/tfjs-vis';
window.onload = async () => { const xl = [1.6, 1.7, 1.75, 2.3]; const yl = [1.86, 1.87, 1.875, 1.93];
tfvis.render.scatterplot( { name: '根据父母身高预测孩子身高' }, { values: xl.map((x, i) => ({ x, y: yl[i] })) }, { xAxisDomain: [1, 3], yAxisDomain: [1, 3] } );
const model = tf.sequential(); model.add(tf.layers.dense({ units: 1, inputShape: [1] })); model.compile({ loss: tf.losses.meanSquaredError, optimizer: tf.train.sgd(0.1) });
const xInputs = tf.tensor(xl); const yInputs = tf.tensor(yl); await model.fit(xInputs, yInputs, { batchSize: 4, // 每次要去学的数据量多大: 随机梯度下降 epochs: 200, // 超参数 callbacks: tfvis.show.fitCallbacks( { name: '训练过程' }, ['loss'] ) }); const output = model.predict(tf.tensor([2])); output.print() alert(`当父母平均身高为 2 米的时候,儿子的身高 为 ${output.dataSync()[0]} 米`); };
|
以上就是本次的线性回归,这是入门的基础内容,有错误请指正。
参考:
B乎
Google机器学习速成教程
TensorFlow.js官网API
结束语
怕上层楼,十日九风雨。
「祝英台近·晚春」
辛弃疾