谈到人工智能、机器学习,我们可能会觉得很神秘,其实机器学习背后的理论并不复杂。就如同原子弹这么尖端的科技,其背后的理论就是一个很简单的公式:
代码语言:javascript复制E = mc²
机器学习的最基础理论其实也不复杂,本文先尝试从一个线性回归问题出发,探讨一下机器学习的一般步骤。通过本文,你将学习到:
- 定义机器学习模型
- 定义损失函数
- 模型训练
- 从训练的模型进行推导
线性回归
想必大家应该做过物理实验,还记得有这样的实验吗?就是观察一系列根据某个因素变化(比如质量)的实验数据,然后建立平面坐标,在坐标系中标记一系列离散的点,然后拟合出曲线或直线。如果结果和因子之间是线性关系,那么拟合出来就近似于一条直线。一般而言,线性关系可以用如下公式表示:
代码语言:javascript复制y = a * x b
现在我们有一系列的x值以及与之对应的y值,如何得到a和b的值呢?我们以前的方法可能是将这些点描在坐标上,然后描一条近似连接所有点的直线(在实际实验中,可能有一些干扰因素,所有的点连接起来并非一条直线),在坐标轴上可以很容易看出a和b的值。
机器学习所要解决的问题也是一样,就是知道一系列x和y的值(数据集),需要找出它们之间的关系。如果是线性问题,就是求解a和b的值。
机器学习通常采用回归解决参数求解问题。
为了简单起见,我们以线性回归为例。所谓线性回归,就是首先给一个a和b的值(通常给0值),然后计算出y的值,和实际的y值进行比较,如果发现误差比较大,就调整a和b的值,比如a和b都加上0.0001,然后再计算y的值,和实际的y值比较,依次反复,只要误差是在逐步减小,最终选择的a和b的值,就接近理论的a和b的值。
比如下面几个x、y的对应值:
代码语言:javascript复制x = [1, 2, 3, 4];
y = [1, 3, 5, 7];
你可能立刻就能看出x和y之间的关系是:
代码语言:javascript复制y = 2 * x - 1;
这样理论上 a = 2,b = -1。但对于计算机,它没有这样的直觉,我们就先给它a = 0, b = 0,让它通过递归算法,逐步接近这个值,可能最终得到的值是 a = 1.99,b = -0.99。
所以我们需要理解机器学习的结果,最后推算出的结果并非理论上的真实值,而是一个接近真实的值。
至于机器学习如何保证调整a和b的值,误差能够越来越小,这背后也有复杂的算法。对于机器学习应用开发者而言,我们并不需要去实现算法,只需要做简单的了解即可,机器学习框架,如tensorflow,帮我们解决了这些问题。
好吧,我们就以实际代码,来说明如何完成一个机器学习任务。
线性回归代码实例
我们就开门见山,直接放代码:
代码语言:javascript复制 const tf = require('@tensorflow/tfjs');
//定义一个线性回归模型。
const model = tf.sequential();
model.add(tf.layers.dense({ units: 1, inputShape: [1] }));
model.compile({ loss: 'meanSquaredError', optimizer: 'sgd' });
// 为训练生成一些合成数据
const xs = tf.tensor2d([1, 2, 3, 4], [4, 1]);
const ys = tf.tensor2d([1, 3, 5, 7], [4, 1]);
// 使用数据训练模型
model.fit(xs, ys, { epochs: 10 }).then(() => {
// 在该模型从未看到过的数据点上使用模型进行推理
model.predict(tf.tensor2d([5], [1, 1])).print();
});
第1行代码引入 tfjs 模块,需要注意,在上一篇文章中引入的是 tfjs-core ,这里为了方便,直接引入tfjs,它包含了 tfjs-core , tfjs-layers 等。所以需要修改 package.json 文件,将其中的 @tensorflow/tfjs-core 替换为 @tensorflow/tfjs 。
第4、5行代码定义线性回归模型。sequential()返回一个序列模型,dense为全连接层。一般来说,我们不用自行设计模型,所以一般模型代码照搬即可。
第7行代码编译模型,其中有两个重要的参数,一个是损失函数,一个是优化器,它们决定着模型是否能够很快收敛。对于不同的任何以及模型,在损失函数的选择有考量,不过我们一般也是按照模型设计者的建议使用对应的损失函数。优化器就那几种选择,翻翻文档,这里选择最普通的 sgd (随机梯度下降)优化器。
第10、11行代码是给出训练数据,这里出于演示的目的,直接在代码中写定,一般情况需要从文件或网络读取。
第14行代码是进行模型训练,就是逐步尝试获得最优解的过程,我们不能无限训练下去,所以这里指定 10 个轮次。
第16行代码根据训练的模型,求 x=5 时的 y 值。
怎么样?有了机器学习框架,从定义模型、训练,到最后的推理,是不是很简单?我们并不需要理解复杂的随机梯度下降算法,就可以完成机器学习的任务。
如果我们在微信小程序中运行上述代码,会得出以下结果:
代码语言:javascript复制Tensor
[[6.0889206],]
而且每次的结果还不同,有同学可能会问,正确结果不是应该接近 9 吗?这个结果也偏差太大了吧!
因为上述代码只是出于演示目的,还存在如下问题:
- 数据不足,我们只给出了4个样本数据,样本太少,很难找出x,y之间的关系。这也是现代机器学习,特别是深度学习,需要大量训练数据的原因。
- 迭代次数太少,在误差还在减少的中途,我们就退出了计算,这样得到的参数,就存在比较大的误差。
- 每次推理之前都需要训练,而在实际的项目中,一般是训练和推理是两个分开的过程。
没关系,这只是一个入门的机器学习程序,后续我们会学习到比较完善的例子。
机器学习应用编写过程
虽然上面的代码非常简短,但也具有机器学习应用的基本框架。机器学习应用的通常过程是:
- 准备训练数据
- 定义模型
- 编译模型(定义损失函数、优化器等参数)
- 训练模型
- 模型推理
在后面的文章,我们可以看到,不管多复杂的机器学习应用,都不外乎这些步骤,区别就在于复杂程度。就如同虽然有了 E = mc² 这个理论,要造成原子弹,还有很多路需要走。
小结
本章主要讲解了建立线性回归模型,虽然是一个精简的程序,但具备了机器学习程序的基本框架。在下一篇文章中,我将说明如何在微信小程序中加载训练数据。没有数据,就没有机器学习。如果你有什么建议,欢迎留言。
本系列文章的源码请访问:
https://github.com/mogotech/wechat-tfjs-examples