前言

关于线性回归相信各位都不会陌生,当我们有一组数据(譬如房价和面积),我们输入到excel,spss等软件,我们很快就会得到一个拟合函数: h θ ( x ) = θ 0 + θ 1 x h_\theta(x)=\theta_0+\theta_1x hθ​(x)=θ0​+θ1​x
但我们有没有去想过,这个函数是如何得到的?
如果数学底子还不错的同学应该知道,当维数不多的时候,是可以通过正规方程法求得的,但如果维数过多的话,像图像识别/自然语言处理等领域,正规方程法就没法满足需求了,这时候便需要***梯度下降法***来实现了。

梯度下降法

首先我们需要知道一个概念

  • 损失函数(loss function)
    J ( θ 0 , θ 1 ) J(\theta_0,\theta_1) J(θ0​,θ1​)

损失函数是用来测量你的预测值 f ( x ) f(x) f(x)与实际值之间的不一致程度,我们需要做的就是找到一组 θ 0 , θ 1 \theta_0,\theta_1 θ0​,θ1​使得 J ( θ 0 , θ 1 ) J(\theta_0,\theta_1) J(θ0​,θ1​)最小,这组 θ 0 , θ 1 \theta_0,\theta_1 θ0​,θ1​便叫做全局最优解
【梯度下降】通过一元线性回归模型理解梯度下降法_最优解

我们需要定义一个损失函数,在线性回归问题中我们一般选择平方误差代价函数
J ( θ 0 , θ 1 ) = 1 2 m ∑ i = 1 m ( h θ ( x i ) − y i ) 2 J(\theta_0,\theta_1)= \frac{1}{2m}\sum_{i=1}^{m}(h_\theta(x_i)-y_i)^2 J(θ0​,θ1​)=2m1i=1m​(hθ​(xi​)−yi​)2
我们的目标是 m i n i m i z e J ( θ 0 , θ 1 ) minimizeJ(\theta_0,\theta_1) minimizeJ(θ0​,θ1​)
如果不好理解的话我们通过图形来理解:
【梯度下降】通过一元线性回归模型理解梯度下降法_迭代_02

假设上图是我们的 J ( θ o , θ 1 ) J(\theta_o,\theta_1) J(θo​,θ1​),那我们需要找到的就是左边箭头指向的那个点,这个点对应的 θ 0 , θ 1 \theta_0,\theta_1 θ0​,θ1​便是我们找的全局最优解。

对于线性模型,只会存在全局最优解,真正的图像模型如下图所示,是个碗状的,我们要做的是找到碗底,这样是不是很好理解了。
【梯度下降】通过一元线性回归模型理解梯度下降法_损失函数_03

那么如何到达最底呢,我们再看一张图。
【梯度下降】通过一元线性回归模型理解梯度下降法_python_04
我们需要从绿点到达红点,我们需要确定的有两件事情

  • 朝哪个方向走;
  • 走多远。

第一个问题,我们需要回忆下高中的数学知识——导数,在二维空间里面,导数是能代表函数上升下降快慢及方向的,这个各位在脑子里面想一个就明白,函数上升,导数为正,上升越快,导数越大,下降反之。扩展到多维空间,便是偏导数 ( ∂ ∂ θ 0 J ( θ 0 , θ 1 ) , ∂ ∂ θ 1 J ( θ 0 , θ 1 ) ) (\frac{\partial}{\partial\theta_0 }J(\theta_0,\theta_1),\frac{\partial}{\partial\theta_1}J(\theta_0,\theta_1)) (∂θ0​J(θ0​,θ1​),∂θ1​J(θ0​,θ1​))。
第二个问题,走多远或者说步长,这里便需要我们自己定义,在梯度下降法中叫做**学习率 ( α ) (\alpha) (α),是一个很重要的参数, α \alpha α过小的话会导致收敛速度慢,可能训练到地老天荒也无法完成收敛, α \alpha α过大的话有可能会导致损失函数无法收敛,直接发散了,如下图所示
【梯度下降】通过一元线性回归模型理解梯度下降法_python_05

接下来放公式,通过损失函数的偏导数和 α \alpha α去迭代更新 θ \theta θ:
θ 0 : = θ 0 − α ∂ ∂ θ 0 J ( θ 0 , θ 1 ) \theta_0:=\theta_0-\alpha\frac{\partial}{\partial\theta_0 }J(\theta_0,\theta_1) θ0​:=θ0​−α∂θ0​J(θ0​,θ1​)
θ 1 : = θ 1 − α ∂ ∂ θ 1 J ( θ 0 , θ 1 ) \theta_1:=\theta_1-\alpha\frac{\partial}{\partial\theta_1}J(\theta_0,\theta_1) θ1​:=θ1​−α∂θ1​J(θ0​,θ1​)
这边就不推导了,偏导数自己也快忘记的差不多了,直接放结果:
θ 0 : = θ 0 − α 1 m ∑ i = 1 m ( h θ ( x i ) − y i ) \theta_0:=\theta_0-\alpha\frac{1}{m}\sum_{i=1}^{m}(h_\theta(x_i)-y_i) θ0​:=θ0​−αm1i=1m​(hθ​(xi​)−yi​)
θ 1 : = θ 1 − α 1 m ∑ i = 1 m ( h θ ( x i ) − y i ) x i \theta_1:=\theta_1-\alpha\frac{1}{m}\sum_{i=1}^{m}(h_\theta(x_i)-y_i)x_i θ1​:=θ1​−αm1i=1m​(hθ​(xi​)−yi​)xi
接下来迭代去更新 θ 0 , θ 1 \theta_0,\theta_1 θ0​,θ1​直至收敛就好了。

python实现

我们通过 y = 2 x + 1 y = 2x+1 y=2x+1生成一些随机点,注意 y = 2 x + 1 y = 2x+1 y=2x+1并不是我们的最优解:

# 以y= 2x+1为原型生成一个散点图
# 此时最优解并不是y = 2x+1
X0 = np.ones((100, 1))
X1 = np.random.random(100).reshape(100,1)
X = np.hstack((X0,X1))
y = np.zeros(100).reshape(100,1)
for i , x in enumerate(X1):
    val = x*2+1+random.uniform(-0.2,0.2)
    y[i] = val

plt.figure(figsize=(8,6))
plt.scatter(X1,y,color='g')
plt.plot(X1,X1*2+1,color='r',linewidth=2.5,linestyle='-')
plt.show()

out
【梯度下降】通过一元线性回归模型理解梯度下降法_机器学习_06

迭代部分:

# 梯度下降法求最优解
def gradientDescent(X,Y,times = 1000, alpha=0.01):
    '''
    alpha:学习率,默认0.01
    times:迭代次数,默认1000次
    '''
    m = len(y)
    theta = np.array([1,1]).reshape(2, 1)
    loss = {}
    for i in range(times):
        diff = np.dot(X,theta)- y
        cost = (diff**2).sum()/(2.0*m)
        loss[i] = cost
        theta = theta - alpha*(np.dot(np.transpose(X), diff)/m)
    plt.figure(figsize=(8,6))
    plt.scatter(loss.keys(),loss.values(),color='r')
    plt.show()
    return theta

theta = gradientDescent(X,Y)

默认设置的迭代1000次,学习率为0.01,最后结果如下:

  • 损失函数
    【梯度下降】通过一元线性回归模型理解梯度下降法_最优解_07
  • θ 0 , θ 1 \theta_0,\theta_1 θ0​,θ1​= 1.03229637, 1.95156735
    【梯度下降】通过一元线性回归模型理解梯度下降法_损失函数_08