续上

紧接着上面一篇文章 ML01 - Regression 案例学习 (上) 讲到的 Gradient Descent,我们现在有了 ModelTraining DataLoss Function,我们的 Model:

$$
y = b + w \cdot x_{cp}
$$

经过训练呢,我们得出以下结果:

$$
b=-188.4 \\
w=2.7
$$

将 \(w\) 和 \(b\) 代入到原 Model 中,我们就能得到一个一次方程,我们将其画在坐标轴上就是这样(横轴代表进化前的 CP 值,纵轴代表进化后的 CP 值):

20180211151832902416954.jpg

计算下来的 \(L(w,b)=31.9\)。

当然,以上数据仅仅是我们用已知的 10 只宝可梦训练出来的 Model,如果现在新拿给我们一只宝可梦,代入这个 Model 进行计算,得到的预测数据与实际数据会相差多少呢?

所以我们又捉了 10 只与之前完全不同的宝可梦,代入计算了一下,如下:

20180211151832965678425.jpg

可以看到,我们 Model 计算出来的数据与新的 10 只宝可梦实际的数据还是挺契合的。实际计算下来,这 10 只宝可梦的 \(L_{w,b}=35.0\)。在坐标轴的左下方和右上方,误差会更大一些。

那我们如何才能将左下方和右上方的那些数据也都包含到我们的 Model 中呢?当前我们的 ModelLinear Model 也就是线性的,在图中我们能够很明显的看出,用一条红色的直线,再怎么都不可能将所有蓝色点都包含进去。所以,我们现在需要一个更强的 Model

第五步:Selecting Another Model

我们更进一步,由于图中的蓝色点并不可能在一条直线上,我们就尝试一下,选用二次方程的曲线来拟合它们:

$$
y = b+w_1\cdot x_{cp}+w_2\cdot x_{cp}^2
$$

然后我们通过之前的办法训练这个 Model,找出来最好的结果对应的是:

$$
b=-10.3 \\
w_1=1.0 \\
w_2=2.7*10^{-3} \\
L(w_1,w_2,b)=15.4
$$

对应的图形是这样的:

20180211151833092840731.jpg

如果我们将其用在 Testing Set 上呢,对应的 \(L(w_1,w_2,b)=18.4\),对应的图形是:

20180211151833105363807.jpg

如果我们现在将刚才的二次 Model 改成三次方程呢:

$$
y = b+w_1\cdot x_{cp}+w_2\cdot x_{cp}^2+w_3\cdot x_{cp}^3
$$

同样训练过后,得出:

$$
b=6.4 \\
w_1=0.66 \\
w_2=4.3*10^{-3} \\
w_3=-1.8*10^{-6} \\
L(w_1,w_2,w_3,b)=15.3
$$

图形长这样,看上去和二次的 Model 没有太大的区别:

20180211151833138534761.jpg

Testing Set 上得到的 \(L(w_1,w_2,w_3,b)=18.1\),有好一点点:

20180211151833145573102.jpg

那如果我们换用四次方程Model 的话,训练结果会不会更好一些呢:

$$
y = b+w_1\cdot x_{cp}+w_2\cdot x_{cp}^2+w_3\cdot x_{cp}^3+w_4\cdot x_{cp}^4
$$

Training Data 下,拟合曲线如下图,很明显的拟合更准确了一些,\(L=14.9\):

20180211151833163648570.jpg

我们再看看它在 Testing Set 上的表现如何呢:

20180211151833175341410.jpg

实际结果竟然是 \(L=28.8\),反而比三次方程更糟糕了!!如果我们换做 5 次、6 次或者更高次方程的 Model 情况会不会变好呢?

下面我们再用五次方程的 Model 来训练一下,看看效果如何:

$$
y = b+w_1\cdot x_{cp}+w_2\cdot x_{cp}^2+w_3\cdot x_{cp}^3+w_4\cdot x_{cp}^4+w_5\cdot x_{cp}^5
$$

在五次方程的 Model 下,Training Data 的 \(L=12.8\),确实变好了,而 Testing Set 的 \(L=232.1\),糟糕透顶。

20180211151833208245521.jpg

现在,我们将上面五个 Model (分别为一次方程、二次方程、三次方程、四次方程、五次方程),以及他们的 Loss Funciton \(L\) 的值都画在一张图中,我们可以发现,随着参数的增多、方程次数的增大,我们拟合的效果是越来越好,误差是越来越小的:

20180211151833241633413.jpg

这很好解释,因为,二次方程中其实就已经包含了一次方程的情况(使二次方程中\(w_2=0\),它就变为了一次方程),所以二次方程除了拟合一次方程已经拟合到的点,还能够拟合更多一次方程没有拟合到的点。所以,它的误差就会比一次方程更小。同理三次方程会比二次方程拟合更好、误差更小…五次方程会比四次方程拟合得更好、误差更小。

但是,在 Testing Data 上则不相同了,我们将不同 ModelTraining DataTesting Data 上 \(L\) 的变化画在同一张图中(蓝色线代表 Training Data 的结果,橙色线代表 Testing Data 的结果):

20180211151833290993393.jpg

我们发现,越复杂的 ModelTraining Data 下表现会越来越好,而在 Testing Data 下则不一定。

这种现象我们就把它叫做 Overfitting(过度拟合)。举个例子来说,如果我们想训练机器人识别人类,我们就把小明拿给它学习。在只让机器人记住一项特征的时候,他就记住:有两只眼睛的是人类;只让它记住两项特征的时候,他会发现:噢,有两只眼睛,一个嘴巴的是人类;当我们让它能记住三项特征的时候,他就可能会记作:有两只眼睛,一个嘴巴,戴着帽子的是人类,这种情况下,机器人对小明这个人类的个体描述得就很明确了,但是对于人类这个整体来说,就过度拟合了(并不是所有人都戴帽子,就会导致这个机器人识别不出来很多人类)。

所以我们应当合理地选取 Model,在减少拟合不够情况的同时避免过度拟合

前面我们的训练都是在 10 只宝可梦的数据上做的,那如果我们选取更多的宝可梦,情况会是怎么样的呢?下面我们就选取了 60 只宝可梦,绘制在图上:

20180211151833388334807.jpg

能看出来,这个分布并不是简单的属于一次、二次、三次…式,它其中,应该还有另一个因素在很大程度上影响进化后的结果,很明显,应该就是不同宝可梦对应的物种:

下图中,我们将这些宝可梦,按照不同的物种,将其用不同颜色绘制:

20180211151833421799216.jpg

嗯,大概是这么一个画风:

20180211151833429555232.jpg

那看来我们可能犯了一个严重的错误,忽略了物种的影响。

重回第一步:Redesign the Model

根据不同的物种,我们需要有不同的 Model,物种这个属性,就对应的是每个 \(x\) 的 \(x_s\):

$$
\mbox{输入 }x \\
\downarrow \\
y =
\begin{cases}
b_1+w_1\cdot x_{cp}, & \mbox{if }x_s=\mbox{ Pidgey} \\
b_2+w_2\cdot x_{cp}, & \mbox{if }x_s=\mbox{ Weedle} \\
b_3+w_3\cdot x_{cp}, & \mbox{if }x_s=\mbox{ Caterpie} \\
b_4+w_4\cdot x_{cp}, & \mbox{if }x_s=\mbox{ Eevee}
\end{cases} \\
\downarrow \\
\mbox{输出 }y
$$

那难道我们需要为每个不同的物种设定单独的 Linear Model(线性模型) 吗?这似乎太麻烦了,宝可梦的物种至少有十多类并且还在持续增加。我们能不能用某个特殊的函数将不同的物种都包含进一个 Linear Model 呢?答案是:有的!

我们需要引进一个函数 \(\delta()\),它的作用就是,给一个输入,他要么输出 1 要么输出 0 (信号与系统的知识)

$$
\delta(x_s=\mbox{ Pidgey}) \\
\downarrow \\
\begin{cases}
=1 & \mbox{if }x_s=\mbox{ Pidgey} \\
=0 & \mbox{ 其他情况} \\
\end{cases}
$$

那我们的 Model 就可以改写为:

$$
\begin{align}
y = & \ b_1\cdot \delta(x_s=\mbox{ Pidgey}) \\
& +w_1\cdot \delta(x_s=Pidgey)x_{cp} \\
& +b_2\cdot \delta(x_s=\mbox{ Weedle}) \\
& +w_2\cdot \delta(x_s=Weedle)x_{cp} \\
& +b_3\cdot \delta(x_s=\mbox{ Caterpie}) \\
& +w_3\cdot \delta(x_s=Caterpie)x_{cp} \\
& +b_4\cdot \delta(x_s=\mbox{ Eevee}) \\
& +w_4\cdot \delta(x_s=Eevee)x_{cp} \\
\end{align}
$$

比如,我们现在输入一个 Pidgey,那 \(x_s=\mbox{ Pidgey}\),上面的式子就会变成:

$$
\begin{align}
y = & \ b_1\cdot 1 \\
& +w_1\cdot 1 \cdot x_{cp}\\
& +b_2\cdot 0 \\
& +w_2\cdot 0 \cdot x_{cp} \\
& +b_3\cdot 0 \\
& +w_3\cdot 0 \cdot x_{cp} \\
& +b_4\cdot 0 \\
& +w_4\cdot 0 \cdot x_{cp} \\
\end{align} \\
\downarrow \\
y = b_1 +w_1 \cdot x_{cp} \\
$$

所以,现在我们这个 Model 依然是一个 Linear Model

做出来的效果怎么样呢?

20180211151833764651008.jpg

\(L=3.8\),效果和图中看到的一样,非常好的拟合了 Training Data。但我们真正在意的是它在 Testing Data 上的表现,这几乎决定了这个 Model 的好坏。

20180211151833785915019.jpg

结果是 \(L=14.3\),比我们以往的所有结果都要好,说明这个思考方式我们是做对了的!

但是仔细看,我们发现,还是有一些点拟合得不够。回想一下,我们刚才只代入了宝可梦的种类 \(x_s\) 进行运算,并没有考虑它的其他属性对进化后 CP 值的影响,比如:进化前的体重、高度、生命值等等,这些因素都有可能会影响到一只宝可梦进化后 CP 值的变化情况:

20180211151833821134514.jpg

我们该如何将这些因素都考虑进去呢?最简单也是最直接的一个方式就是:把所有属性都传进 Model 中进行运算,那我们就需要设计一个略微复杂的 Model

$$
\mbox{输入 }x \\
\downarrow \\
y’ =
\begin{cases}
b_1+w_1\cdot x_{cp}+w_5\cdot(x_{cp})^2, & \mbox{if }x_s=\mbox{ Pidgey} \\
b_2+w_2\cdot x_{cp}+w_6\cdot(x_{cp})^2, & \mbox{if }x_s=\mbox{ Weedle} \\
b_3+w_3\cdot x_{cp}+w_7\cdot(x_{cp})^2, & \mbox{if }x_s=\mbox{ Caterpie} \\
b_4+w_4\cdot x_{cp}+w_8\cdot(x_{cp})^2, & \mbox{if }x_s=\mbox{ Eevee}
\end{cases} \\
\downarrow \\
y = y’+w_9\cdot x_{hp}+w_{10}\cdot(x_{hp})^2+w_{11}\cdot x_{h}+w_{12}\cdot(x_{h})^2+w_{13}\cdot x_{w}+w_{14}\cdot(x_{w})^2 \\
\downarrow \\
\mbox{输出 }y
$$

这样,我们既顾及了每只宝可梦物种的特殊性,又增加了 HP 、height 和 weight 三个额外的属性进行分析,总共 18 个参数。

果然,我们得到了一个非常低的 Loss \(L=1.9\),但是,根据以往的经验,这么多参数,我们很有可能会造成 Overfitting 的结果。经过 Testing Data 的运算,\(L=102.3\),算是比较糟糕的一个结果了。接下来又怎么处理呢?

重回第二步:Regularization

之前我们的输出 \(y\) 定义为:

$$
y=b+ \sum w_i x_i
$$

我们需要在 Loss Function \(L\) 的基础上,增加另一个因子,也就是下面公式中,\(\color{Red}+\) 号后面的部分:

$$
L= {\sum_n (\widehat{y}^n - (b+\sum w_i x_i))^2} {\color{Red}+\lambda\sum(w_i)^2}
$$

这里的 \(\lambda\) 是一个参数,需要我们事先给出并手动调节的(它的值越大,我们的 Loss Function 就越平滑)。加入 \(w_i\) 的意思就是说,在满足 \(L\) 尽可能小的前提下,让 \(w_i\) 也尽可能地小。因为如果 \(w_i\) 越小,输入中 \(x\) 值的变化,所带来的 \(w_i\cdot x\) 的变化就越小,也就是 \(\Delta y\) 越小。这样的话,不同的输入之间,微小的差异就可以控制在一定的范围内,不会对系统产生过大的影响,也就在最大可能上避免了 Overfitting 的发生。当然,它并不需要包含 \(b\),因为 \(b\) 只是一个常数,并不会随着 \(x\) 的变化而变化,所以加上与否,对系统几乎没有影响。

那最终结果怎么样呢?

20180211151834219287501.jpg

上图中,我们将 \(\lambda\) 的值从 0 调整到 100000,可以明显看出,其在值为 100 左右的时候,效果是最好的(我们希望 Loss Function 适当地平滑,这可以去除一些噪声对结果的干扰,但过于平滑的话,训练数据对结果的影响又会变得过小,从而减弱了有益特征值对系统的影响)。

最终,我们得到了一个基本可用的宝可梦进化 CP 值预测系统。当然,由于我们所用的训练数据种类还是偏少,可能在实际的使用中会出现很多系统从来没遇到过的物种,这个时候基本上系统会给出错误率很高的预测值。

本文总结

  1. 我们最开始通过确定一个 Linear Model 来训练宝可梦的数据,结果发现,有很多的数据都是没办法拟合的(线性模型在图上看来就是一条直线);
  2. 然后我们就想到了用二次方程来拟合,结果是:在 Training Data 上,Loss Function 的输出的却变小了,并且在 Testing Data 上,也得到了同样的结果;
  3. 我们就开始思考,是否通过增加模型的复杂度,就能训练出更好的模型?然后我们就尝试了三次方程四次方程五次方程,结果发现,虽然随着方程复杂度的增大,在 Training Data 上的 \(L\) 输出越来越小,但是在 Testing Data 上,\(L\) 变得非常不稳定,并且有快速增大的趋势。我们就意识到,现在的模型 Overfitting 了;
  4. 然后我们发现,选取的宝可梦可能有点少了,才导致了个别的特性在训练中变得比较突出加重了 Overfitting 发生的可能性。所以我们又选取了 50 只,一共 60 只宝可梦来进行分析;
  5. 在将 60 只宝可梦绘制在坐标轴上时,我们又发现了一个新的问题,这些点在某些范围内,分布是比较有规律的。所以我们将各个宝可梦根据它们所属的物种进行了分类,然后重新设计了 Model,用到了 \(\delta()\) 函数,这个函数针对不同的输入,只会返回 0 或者 1;
  6. 我们将重新设计的模型通过训练后发现,效果显著,但是还有提升空间,因为有的点并没有被拟合到,所以我们猜想,会不会 CP 的变化不仅和物种 \(x_s\) 有关,还和它的生命值 \(x_{hp}\)、高度\(x_h\)、重量\(x_w\)有关呢?所以我们将这些因素都代入了 Model 进行计算,总共用到了 18 个参数。
  7. 由于之前我们有过经验,Model 过于复杂就有很大可能发生 Overfitting 的现象。果不其然,这次又过度拟合了,为了解决这个问题,我们在 Loss Function 中,加入了一个额外的式子 \(\lambda\sum(w_i)^2\),由于我们训练的依据就是尽量的让 Loss Function 变小,所以我们就需要让刚才这个式子也尽量的小,也就是让 \(w_i\) 尽量小,这样的话整个 Loss Function 就会变得比较平滑,减少噪声的影响,这也就 Regularization(正则化)的作用;
  8. 最终,我们通过调节 \(\lambda\) 的值,得到了比较满意的一个训练结果,在 Training DataTesting Data 上的 Loss \(L\) 都比较小,不过考虑到我们所使用的宝可梦种类还是太少,如果要将这个系统做到上线实用的话,还需要更丰富的训练数据来训练。

今天更新到这里,期待下一课…