从零开始学习CNN系列——(二)一次简单的线性回归实践,初探深度学习的那些术语

从零开始学习CNN系列——(二)一次简单的线性回归实践,初探深度学习的那些术语

    从零开始学习CNN系列,是本人学习李宏毅老师的深度学习课、cs231n及吴恩达老师的深度学习课后总结的适合无基础小白入门深度学习的教程,会跟随本人的炼丹水平不断更改,如有错误,请多加指正。

    此小节,我们将从一次简单的线性回归实践开始,解释平常听到的学习率(learning rate)、梯度下降(gradient decent)等机器学习术语的概念。

1、使用机器学习求解经典的线性回归问题

    所谓经典的线性回归问题,就是寻找多个自变量和一个因变量之间的函数关系,即:
$$y=b+w \cdot x=b+\sum_{i=1}^n w_{i}*x_{i}$$
    对于含n个自变量x和一个因变量y的问题,我们需要求出上式的参数b和向量w。

    是否还记得 系列(一) 中所说的利用机器学习解决问题的通用架构?我们现在即利用这个架构来解决这个线性回归问题。

  • step 1:建立模型。
    既然我们已经将其定义为线性回归问题,那么我们使用的函数模型就是上述的线性模型:
    $$y=b+w \cdot x=b+\sum_{i=1}^n w_{i}*x_{i}$$

  • step 2:针对每个函数(function),利用训练集数据计算拟合度,即描述这个选定函数的好坏。
    首先设定$y_{p}$为模型预测值,$y$为真实值。暂且不管我们学过的任何误差函数,思考一下我们想要描述这函数的好坏最常规的思路是什么。那当然是直接将两者做差值处理:
    $$L=\sum_{i=1}^{n}|y_{i}^{p}-y_{i}|$$
    显然,$|y_{p}^{i}-y{i}|$越大,代表选定的函数越“坏”。这即是机器学习里常说的L1函数。不过最常用的误差函数还是之前所说的L2函数
    $$L =\sum_{i=1}^{n}\left(y_{i}-y_{i}^{p}\right)^{2}$$
    这次例子我们选择用L2函数。所以:
    $$L(w,b) = \sum_{i=1}^{n}\left(y_{i}-y_{i}^{p}\right)^{2}=\sum_{i=1}^{n}\left(y_{i}-(b+w \cdot x_{i}\right))^2$$
    此处的$w$和$x_{i}$都是代表向量。

  • step 3:寻找使误差函数最小的函数。
    $b$与$w$的可能性多种多样,即step 1函数模型下会有很多函数$f_{1}、f_{2}…$。最终选出的目标函数:
    $$
    f^{}=\arg \min _{f} L(f)
    $$
    $$
    w^{
    }, b^{}=\arg \min _{w, b} L(w, b)
    $$
    如何快速寻找出这个函数是解决本问题的关键。这就引出了本文最核心的内容——梯度下降法(Gradient Decent)。下面我用李老师的方法带领大家一探梯度下降法的奥秘!
    首先我们尝试用其求出$w^{
    }$,$w^{*}$是一个向量,其可能含有多个标量$w_{i}$,针对其中任意标量$w$,误差函数$L(w)$都是$w$的函数,假定其形状如图:

    设$w$的初始值为$w_{0}$,即处于第一只猴子占领的位置,可以注意到,$L(w)$上的每一个点都代表一个函数,而 全局最优点(global minima) 位于最后一个红点,我们需要思考如何让猴子从当前位置,快速移动到全局最优点(global minima)
    梯度下降法给出的思路是:计算当前点的斜率(即一维情况下的梯度)$\left.\frac{d L}{d w}\right|{w=w{0}}$,若其为负,即图中的情况,显然猴子应该往$w$增大的方向移动;若其为正,显然猴子应该往$w$减小的方向移动。这样才能更接近全局最优点(global minima)。现在我们确定了行进的方向,但具体要行进多远还没确定,这时候学习率(learning rate)就要出马了,它正是用来控制行进的幅度:
    $$
    -\left.\eta \frac{d L}{d w}\right|{w=w{0}}
    $$
    关于梯度下降法,cs231n中有个很形象的比喻——其好比蒙眼者徒步下山,每个点的梯度代表了地形的倾斜方向和程度,知道方向后,要跨出多长的步长呢?想尽快下山,则比如步幅大,但可能错过洼点;步幅小,虽然不容易错过洼点,但用时过长(通过学习率动态调节)。下面是大家最讨厌的公式推导环节(很基础的推导):
    因为$L(w,b)$实际上是多变量函数,所以严谨来说这里应该使用偏微分。对于任意$w_{i}$和b,初始值为$w_{i,0}$和$b_{0}$,学习率为$\eta$:
    将前面的$L(w,b)$进一步展开
    $$
    L(w,b)=\sum_{j=1}^{n}\left(y_{j}-\left(b+\sum_{i=1}^{m} w_{i} * x_{j,i}\right)\right)^{2}
    $$
    $$
    \frac{\partial L}{\partial w_{i}}=2\sum_{j=1}^{n}\left(y_{j}-\left(b+\sum_{i=1}^{m} w_{i} * x_{j,i}\right)\right)(-x_{j,i})
    $$
    $$
    \frac{\partial L}{\partial b}=2\sum_{j=1}^{n}\left(y_{j}-\left(b+\sum_{i=1}^{m} w_{i} * x_{j,i}\right)\right)
    (-1)
    $$
    知道偏微分后,可以对$w_{i,0}$和$b_{0}$不断更新迭代:
    $$
    w_{i,1}=w_{i,0}-\left.\eta \frac{\partial L}{\partial w_{i}}\right|{w=w{i,0}, b=b_{0}}, b_{1}=b_{0}-\left.\eta \frac{\partial L}{\partial b}\right|{w=w{i,0}, b=b_{0}}
    $$
    $$
    w_{i,2}=w_{i,1}-\left.\eta \frac{\partial L}{\partial w_{i}}\right|{w=w{i,1}, b=b_{1}}, b_{2}=b_{1}-\left.\eta \frac{\partial L}{\partial b}\right|{w=w{i,1}, b=b_{1}}
    $$
    $$

    $$
    如此不断迭代, 理论上当到达某点,使$\frac{\partial L}{\partial w_{i}}=0$和$\frac{\partial L}{\partial b}=0$时,停止迭代,这个点代表的函数即是我们寻找的误差最小的函数

2、用代码实践上述理论推导

    以上是基于机器学习知识的理论推导。本小节我们用代码进行实践,以直观地观察梯度下降法(Gradient Descent) 解决线性回归问题的效果。

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
python3,gradient descent:
import matplotlib.pyplot as plt
import numpy as np
x_data = [338.,333.,328.,207.,226.,25.,179.,60.,208.,606.]
y_data = [640.,633.,619.,393.,428.,27.,193.,66.,226.,1591.]
#y_data = b + w*x_data
x = np.arange(-200,-100,1)#bias的集合
y = np.arange(-5,5,0.1)#weight的集合
Z = np.zeros((len(x),len(y)))
X,Y = np.meshgrid(x,y)
#bia与weight集合随意两个搭配,组成function set
for i in range(len(x)):
for j in range(len(y)):
b = x[i]
w = y[j]
Z[j][i] = 0
for n in range(len(x_data)):
Z[j][i] = Z[j][i] + (y_data[n]-b-w*x_data[n])**2#L(w,b)
Z[j][i] = Z[j][i]/len(x_data)#实质上就是平均均方误差,L(w,b)/length
#梯度下降法起点(b0,w0)
b = -120
w = -4
lr = 0.0000001#learning rate
iteration = 100000
b_history = [b]
w_history = [w]
#iterations,这里迭代一万次,用尽量多的次数来接近理论上的谷点
for i in range(iteration):
b_grad = 0.0
w_grad = 0.0
for n in range(len(x_data)):
b_grad = b_grad - 2.0*(y_data[n]-b-w*x_data[n])*1.0#dL/db
w_grad = w_grad - 2.0*(y_data[n]-b-w*x_data[n])*x_data[n]#dL/dw
#(b,w)迭代
b = b - lr*b_grad
w = w - lr*w_grad

#存储(b,w)的历史值,方便后面绘制轨迹
b_history.append(b)
w_history.append(w)

#plot the figure
plt.contourf(x,y,Z,50,alpha=0.5,cmap=plt.get_cmap('jet'))#等高线函数
plt.plot([-188.4],[2.67],'x',ms=12,markeredgewidth=3,color='orange')#[-188.4][2.67]是最终找到的谷点
plt.plot(b_history,w_history,'o-',ms=3,lw=1.5,color='black')
plt.xlim(-200,-100)
plt.ylim(-5,5)
plt.xlabel(r'$b$',fontsize=16)
plt.ylabel(r'$w$',fontsize=16)
plt.show()

    代码完全和李老师课上讲的一样,相信一定有读者对前面的数学公式推导手到擒来,但看到代码就头皮发麻,不要慌! 我对其中的关键点都做了详细的注释,另外再对其中的关键点解释一下:
    首先问题背景依然是1小节提到的线性回归问题,只不过问题进一步简化,相当于
$$y=b+w \cdot x=b+\sum_{i=1}^n w_{i}*x_{i}$$
中$w$与$x$变成简单的一维向量)(n=1),用李老师的例子来说就是:进化后的宝可梦的CP值(y)只受一个特征值影响(x),比如只与生命值(hp)有关。
    其次,代码中为了寻找函数集合(function set),将$b$和$w$各自限定在一定范围内,各取100个值:

1
2
x = np.arange(-200,-100,1)#bias的集合
y = np.arange(-5,5,0.1)#weight的集合

然后将两个集合中的$b$和$w$任意组合,每个$(b,w)$组合都代表一个函数
$$y=b+w \cdot x$$
明白这两点,你会发现后续代码不过是依照我们前面理论推导的过程,一步步用代码翻译公式罢了。

learning rate = 0.0000001结果

ps: 这里的学习率(learning rate)很难调,你可以初步感受下炼丹调参的“乐趣”,当然后面有更高级的方法。

3、梯度下降法(Gradient Descent)的进一步思考——正则化(Regularization)

    喜欢思考的读者心里应该会有一个疑问——你所有的推导都是基于确定的模型:
$$y=b+w \cdot x=b+\sum_{i=1}^n w_{i}*x_{i}$$
进行一系列理论推导,然而凭什么你就能确定理想的模型就只能是简单的线性模型?为什么不可以是下面的更复杂的函数?
$$y=b+w \cdot x=b+w_1 \cdot x + w_2 \cdot x^2$$
$$y=b+w \cdot x=b+w_1 \cdot x + w_2 \cdot x^2 + w_3 \cdot x^3$$
$$…$$
(注:式中的$x^2$不是常规意义上的向量的平方,而是例如$x=[x_1,x_2,x_3]$,$x^2=[x_1^2,x_2^2,x_3^2]$,这里只是为了便于书写,见谅)
    针对这个问题,我们来逐步讨论。首先,如果将这些更复杂的函数模型代入2小节中的情形运算,结果会如何呢?
    利用matlab的curve fitting tool工具箱对2中的数据进行多项式拟合,结果如下,依次为2、3、4、9阶多项式。

    可以显而易见的发现,随着函数阶数的增加,模型拟合的误差越来越小,甚至到了9阶时,模型函数完全与$(x,y)$数据点重合,误差达到了0。这时候你也许会瞬间提出一个疑问:既然模型越复杂,误差越小,那我们每次都选择最复杂的模型训练不就可以了吗?
    这个问题引发我们第一个讨论——是不是模型越复杂,训练结果就一定越好呢? 并不是。用来拟合的$(x,y)$数据我们通常称为训练集(training data) ,但是经过训练得出的最好的函数还需要在实际数据上检验,通常这部分数据我们称为测试集(testing data,请暂时忽略验证集,后续会讲解),而核心问题是在训练集上误差很小的函数,在测试集上不一定很小,现实中,像9阶这种复杂模型函数,在测试集上误差可能会非常大。
    进一步思考:为什么像9阶多项式这种复杂模型,在训练集上表现优异,而在测试集上往往表现很差?聪明的读者,肯定能发现是由于复杂模型的输出($y$)受输入($x$)影响较大,比如上述的9阶多项式中,因为存在$x^9$这一高阶项,导致输入($x$)发生轻微变化,输出($y$)就变化巨大,因此,就算测试集和训练集只有微小差异,最终在测试集的表现也远不如训练集,例如训练集中有(226,428)一点,而测试集中有其附近的点(227,430),训练出的模型函数导致就算x只增加了1,预测的y值也极具增加,在(227,430)产生极大误差。
    简单的思考之后,我们可以发现,我们并不是讨厌所有复杂的模型,只是讨厌输出($y$)受输入($x$)影响较大的模型,换言之,我们喜欢更平滑(smooth)的模型,保证其在训练集上表现良好的同时,输入对输出的影响尽可能小。而正则化(Regularization)正是为了寻找平滑(smooth)模型而诞生的,其具体方法是通过更改$L(w,b)$的表达式:
$$L(w,b)=\sum_{j=1}^{n}\left(y_{j}-\left(b+\sum_{i=1}^{m} w_{i} * x_{j,i}\right)\right)^{2}+\lambda \sum_{i=1}^{m}w_i^2$$
看到这个你肯定会想问:为什么简单的加入$\lambda \sum_{i=1}^{m}w_i^2$(被称为正则项),就能使模型更平滑呢? 首先说句题外话,边提问,边思考,然后自己给出答案,是一种很不错的学习方式。其次,观察$L(w,b)$函数,要使$L(w,b)$尽可能小,$w_i$肯定要尽可能小,即 新的$L(w,b)$函数期望找到$w_i$和误差都尽可能小的模型。但$w_i$尽可能小,代表函数越平滑(smooth)吗?且看下式,我们探讨下训练出的模型中,$x_i$变化时$y_i$会发生什么样的变化。
$$y_p = b+\sum_{i=1}^{n} w_{i} * x_{i}$$
$$若 x_i = x_i + \Delta x_{i}$$
$$则 y_p = y_p + \sum_{i=1}^{n} w_{i} * \Delta x_{i}$$
显然,当$w_i$非常小时,即使$x_i$变化较大,预测值$y_p$也不会受很大影响,即正则化后,新的L(w,b)的确能找到更平滑的模型。

小结

    这篇文章主要主要讲述了以下几点知识点:

  • 梯度下降法(Gradient Descent)
  • 什么是常说的学习率(learning rate)
  • 什么是正则化
  • L2损失函数(or误差函数)

评论


当我沿着一条路走下去的时候,心里总想着另一条路上的事。这种时候,我心里很乱。放声大哭从一个梦境进入另一个梦境,这是每个人都有的奢望。

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×