前言

这个课程是来自于 YouTube 上 NTU 李宏毅老师的视频课程,老师的课讲得非常有趣,通过引入 Pokémon 来生动的讲解机器学习中一些技术的应用,只要你有一定的高数、线代以及概率基础,看这个课程无压力。
我在学习的同时将其搬运并做简单的英文翻译,并加上自己的理解与更通俗的解释。加深自己印象的同时希望能对国内不能使用 YouTube 的读者们提供一个方便。

Regression 回归运算

回归运算其实就是用一个函数去拟合当前给出的数据,如下图:

20180210151826989117180.jpg

图中蓝色点代表数据,我们假设这组数据是 Linear Regression 线性回归的,那我们就需要用一条直线去拟合它们,也就是那条红色的线。

Regression 的使用范围也是很广的:

  • 股票预测:
    201802101518270933581.png
    $$
    \begin{eqnarray}
    f ( 以往多年股票的走势情况 ) = 明天的点数
    \end{eqnarray}
    $$
    当然真正的股市也不可能这么简单,你能预测你就发了😂
  • 自动驾驶
    20180210151827101157873.png
    $$
    \begin{eqnarray}
    f ( 传感器得到的数据 ) = 方向盘与油门的控制
    \end{eqnarray}
    $$
  • 推荐系统
    像淘宝、京东等的购物网站,会推送一些商品给你,这些商品肯定要是你喜欢的或者需要的,你才可能去购买。一个好的推荐系统可能会让这些网站的利润成倍提高。
    $$
    \begin{eqnarray}
    \ f ( 用户 A,商品 B ) = 用户 A 购买商品 B 的可能性
    \end{eqnarray}
    $$
    如果这个可能性很大的话,购物网站就会更倾向于向用户 A 推荐此商品

但今天,我们要做的是一件更加实际的事情。

用数据估测宝可梦的攻击力(CP值)

比如,下面是一只妙蛙种子,你给他吃一些糖果或者星辰,它就会进化成妙蛙草,进化以后,它的 CP 值就变了。

20180210151827184555492.png

如果我们有能力预测它进化后 CP 值的变化的话,我们就能够事先决定是否进化这只宝可梦。如果它 CP 值比较低的话,有可能你就把它拿去做糖果了😂 就不进化它了,这就可以节省一些糖果用来进化更强的宝可梦。

那我们现在要做的就是,找到这么一个 function,我们输入这只宝可梦的相关数据,他就能返回给我们,进化过后,可能的 CP 值是多少。

$$
\begin{eqnarray}
\ f ( 宝可梦的信息 ) = 进化后的 CP 值
\end{eqnarray}
$$

这里,我们用 \(x\) 代表这只宝可梦,则:

  • \( x_{cp} \) 代表其进化前的 CP 值,为 14
  • \( x_s \) 代表它所属的物种,为 妙蛙种子
  • \( x_{hp} \) 代表它进化前的生命值,为 10
  • \( x_w \) 代表它进化前的重量,为 11.62 kg
  • \( x_{hp} \) 代表它进化前的生命值,为 0.88 m
  • \(y\) 则代表进化后的 CP 值

这里 \(x\) 的下标表示:这些都是 \(x\) 这个个体的某个属性(比如 \(小明_{体重}\)、\(小明_{身高}\))。

那我们怎么来解这个问题呢,我们知道,ML 其实就是寻找一个 Model(模型),将我们的数据代进去,经过复杂的运算后就能得到我们想要的结果。

所以我们首先需要找到这个 Model

第一步:Model

一个 Model 其实就是一个 Function set(一组方法)。那我们要寻找的这个 Model,它应该长什么样子呢?

我们当然还是期望它能简单点,所以呢,我们就假设它是这么一个方程组:
$$
y = b + w \cdot x_{cp}
$$

就是一个常数 \(b\) 和一个数 \(w\),它们组合起来,\(x\) 与 \(y\) 就构成一种线性关系。当然,我们现在的 \(b\) 和 \(w\) 都是不确定的,它可以是任何的数字,不过我们能从直觉上排除一些组合,比如 \(w\) 和 \(b\) 都为负数:

$$
f_1: y = -1 - 2 \cdot x
$$

这样子 \(y\) 就成了负数,我们知道,一个宝可梦的 CP 值是不可能为负数的,所以我们能够直接排除这类组合。

前面提到,我们假设的这个方程是一个线性的函数,所以我们这个 Model 就是一个 Linear Model(线性模型)

$$
\ Linear\ Model: y = b + \sum w_i x_i
\\ x_i:输入\ x\ 的某个属性(如 CP)
\\ b:bias(误差)
\\ w:weight(权重)
$$

有了 Model 我们就需要考虑下一个问题:

第二步:Training Data

我们需要训练数据,因为 ML 就像人一样,本来就是通过一定量的基础练习,才能够学到这类数据的共通点。像下面这个图,左侧是杰尼龟,右侧则是杰尼龟进化后,变成的卡咪龟

20180210151827722460431.png

那现在,整个 Model 的 输入就是这只杰尼龟,我们用 \(x^1\) 来表示,那这只 卡咪龟 我们就用 \(\widehat{y}^1\) 来表示。这里 \(x\) 和 \(\widehat{y}\) 的上标表示:这是一个完整的个体,1 只是它的编号(比如 \(学生^1\) 、\(学生^2\)),至于 hat(就是 \(\widehat{y}\) 头上的那个尖尖符号),它代表这是一个准确的值(因为这是真实的数据,为了和后面预测出来的数据 \(y\) 做区分)。

只有一只肯定不够呀,我们需要很多的数据,就要抓足够多的宝可梦,比如这只 伊布,它进化过后就是 雷精灵

20180210151827811541481.png

那我们同样就能够得到\(x^2\) 和 \(\widehat{y}^2\),就像这样:

20180210151827831581456.png

嗯,我们搜集了十只宝可梦🐶🐱🐢🐹🦄🦊🐻🐼🐨🐯 ,数据可以从这里找到。

我们就有了 10Training Data(训练数据)

$$
(x^1,\widehat{y}^1) \\
(x^2,\widehat{y}^2) \\
\vdots \\
(x^{10},\widehat{y}^10)
$$

我们将其进化前的 CP 值和进化后的 CP 值画在一个二维坐标图上(横轴为进化前的 CP 值),那每个蓝色的点都代表一只宝可梦:

2018021115182792145885.png

现在我们有了 ModelTraining Data,但是我们还需要一个函数来将它们连接起来,这个函数就是接下来要讲的:

第三步:Loss Function

2018021115182800497363.png

中间蓝色的块就是 Loss Function(误差函数)。为什么需要这个函数呢,因为我们现在需要根据这 10 只已知宝可梦数据,代入 Model 推测出它们进化后的 CP 值,然后再与实际的 CP 值进行比较,来慢慢调整 Model 中的 weightbias。所以,我们需要有一个函数来评判这次推测的误差度,这就是 Loss Function

$$
\begin{cases}
L(f) = L(w,b) = \sum_{n=1}^{10} (\widehat{y}^n - y^n)^2 \\
y^n = b + w\cdot x_{cp}^n
\end{cases} \\
\Downarrow \\
L(f) = \sum_{n=1}^{10} (\widehat{y}^n - (b + w\cdot x_{cp}^n))^2
$$

上面的方程其实就是将 \(\widehat{y}\) 准确值减去\(y\)估测值,然后将其平方,再将 10 只宝可梦都这样计算并加起来。

我们要做的就是从我们的 Model 中挑选出一个 function \(f\),它能够让 Loss Function \(L(f)\) 的计算结果最小,这个 function 我们就将它命名为 \(f^*\):

$$
f^* = arg \min_f L(f)
$$

或者从另一个角度考虑,我们的 function 其实就只由 weight \(w\) 和 bias \(b\) 来确定的,那上面的公式还可以写成如下:

$$
w^*, b^* = arg \min_{w,b} L(w,b)
$$

我们需要做的就是 穷举所有的 𝑤 和 𝑏,直到找到最佳的那一对,让 Loss Function \(L\) 最小。

第四步:Gradient Descent

上面说的穷举真不是一个好办法(基本没法实现),那我们能不能找一个更好、更有效率的办法解决这个问题呢?有!

  1. 用线性代数的办法:求解非齐次线性方程组(由于这里的方程并不是同解,所以这个办法还是会有些折腾)
  2. 用高等数学的办法:L 可微分,求梯度即可

如下曲线,我们随机选择一个起点 \(w^0\),然后在这个点上对 \(L\) 求 \(w\) 的微分 \(\frac{dL}{dw}|_{w=w^0}\):

20180211151828415490517.jpg

如果你还搞不太懂微分是啥,那就假想这个曲线是一个山坡,你站在 \(w^0\) 上,向左迈一步(\(w\) 减小)或者向右迈一步 (\(w\) 增大),然后看往哪边走 \(L\) 会变小就往那个方向走一步,得到一个新的位置 \(w^1\),然后这样不断重复,就有机会走到最低点。

$$
w^1 \gets w^0 - \eta \frac{dL}{dw}|_{w=w^0}
$$

20180211151828471476871.jpg

那新问题出现了,我们步子该迈多大呢?Gradient Descent 中,这个步子的大小为:

$$
- {\color{red}\eta} \frac{dL}{dw}|_{w=w^0}
$$

其中,\(\frac{dL}{dw}|_{w=w^0}\) 为 \(w^0\) 处的梯度,\({\color{red}\eta}\) 是一个常数项,我们把它叫做 Learning Rate(学习速率),它是一个事先定好的数值,越大,每踏出一步的距离就越大,这意味着学习得就越快。负号则是因为,我们所求的梯度如果是负值说明我们往这个方向前进,\(L\) 就会变小,我们就需要增大 \(w\),所以,梯度的符号和我们前进的方向是相反的关系。

到了 \(w^1\) 后重新计算梯度,很明显这里的梯度比 \(w^0\) 小了很多,所以迈的下一步也会随之变小。经过很多次的调整行走后,我们终于走到了 \(w^T\),在这个地方,微分趋近于 0,算法就认为这里是最好的点了(让 \(L\) 最小的点)。

20180211151828585017243.jpg

但很明显我们能看到,这只是一个 Local Minimum(局部最小),全局最小的点并不在这里,当然 Linear Model 不太会存在这种走到 Local Minimum 的情况,至于为什么,后面会给予说明。

我们总的过程用公式表达就是这样的:

$$
随机选择一个初始的 w^0 和 b^0 \\
\Downarrow \\
分别对 w^0 和 b^0 做微分并计算下一个点 w^1 和 b^1:
\begin{cases}
w^1 \gets w^0 - {\color{red}\eta} \frac{dL}{dw}|_{w=w^0,b=b^0} \\
b^1 \gets b^0 - {\color{red}\eta} \frac{dL}{db}|_{w=w^0,b=b^0}
\end{cases} \\
\Downarrow \\
分别对 w^1 和 b^1 做微分并计算下一个点 w^2 和 b^2:
\begin{cases}
w^2 \gets w^1 - {\color{red}\eta} \frac{dL}{dw}|_{w=w^1,b=b^1} \\
b^2 \gets b^1 - {\color{red}\eta} \frac{dL}{db}|_{w=w^1,b=b^1}
\end{cases} \\
\vdots \\
最终得到一组让 L 最小的 w^T 和 b^T
$$

Gradient Descent 到底是什么呢?其实就是将 \(L\) 分别对 \(w\) 和 \(b\) 做偏微分,最后组成一个向量:

$$
\nabla L =
\begin{bmatrix}
\frac{\partial L}{\partial w} \\
\frac{\partial L}{\partial b}
\end{bmatrix}
_{gradient}
$$

当然如果你还是有点点糊涂的话,下面这张图就能更好地向你说明,Gradient Descent 的原理:

20180211151828747733608.png

Gradient Descent 其实就相当于每次计算所处位置圆弧的法线方向,这个方向是数值变化最明显的方向,所以照着这个方向能最快的走到最低点。

但是,遇到这种图怎么办?

2018021115182877412672.jpg

不用担心(至少现在),因为 Linear Model 的图形其实不会像上面的图那样,而是像上面第二张图那样的,一圈一圈很规整的凹面图形,几乎不存在 Local Minimum 的问题。

最后,我们看一下 \(\frac {\partial L} {\partial w} \) 和 \(\frac {\partial L} {\partial b} \) 都怎么计算的(如果你不会的话,可要恶补一下高等数学了)

$$
L(f) = \sum_{n=1}^{10} (\widehat{y}^n - (b + w\cdot x_{cp}^n))^2 \\
\Downarrow \\
\begin{cases}
\frac {\partial L} {\partial w} = \sum_{n=1}^{10} 2\cdot (\widehat{y}^n - (b + w\cdot x_{cp}^n))(-x_{cp}^n) \\
\frac {\partial L} {\partial b} = \sum_{n=1}^{10} 2\cdot (\widehat{y}^n - (b + w\cdot x_{cp}^n)){-1}
\end{cases}
$$

本文总结

  1. 我们讲了 Regression(线性回归) 的一些作用与高大上的一些应用场景,不过这些都是很复杂的应用;
  2. 然后我们就提出了一个用 Regression 来解决宝可梦升级 CP 值预测的系统;
  3. 通过将宝可梦的属性代数化:\(x\) 代表某个宝可梦个体、\(x_{cp}\) 代表它的 CP 值、\(x_h\) 代表它的高度等等,让我们能够通过代数的方法解决这个预测 CP 的问题;
  4. 在最开始,我们需要建立一个 Model(模型)Model 其实就是一堆 Function(计算方法)的集合,我们将一些数据输入进去,他就能输出我们想要的结果:比如我们输入一只宝可梦的数据,他就能输出这只宝可梦进化后的 CP 值;
  5. 在第二步,我们需要 Training Data(训练数据)。我们需要有大量的训练数据,才能够教会我们的模型正确预测可能的 CP 值变化;
  6. 有了 ModelTraining Data,我们还需要有 Loss Function(误差函数)来评价我们当前模型的好坏,其实它的实现很简单,就是一个普通的函数,然后将 Model 预测的 CP 值,和实际已知进化后的 CP 值做比较,他们差距越大,Loss Funciton 输出的 \(L\) 也就越大,越小说明预测得越准确;
  7. 最后我们讲了如何训练这个 Model,用 Gradient Descent(梯度下降)算法来调节模型,梯度下降算法其实就是通过高等数学中的微分运算,找到一个能让 \(L\) 变得更小的方向(并且这个方向是能让 \(L\)减小得最快的),根据这个方向来决定是增大参数的大小还是减小参数的大小,总之,我们能够通过不断地训练调节,得到一个能比较合理的、误差尽可能小的模型。

今天就先写到这里,小步快跑,下次更新见…

[ML01 - Regression 案例学习 (下)][1]
[1]: https://aiellochan.github.io/2018/02/11/Regression-案例学习-(下)