梯度下降算法

梯度下降算法是机器学习基础算法,假如x是数据样本的特征,y是其目标值,我们需要通过计算,求解有个目标函数,最能拟合这个函数,我们就用最简单的线性函数进行拟合, h θ ( x ) = θ 0 + θ 1 ∗ x h_{\theta}(x) = \theta_{0} + \theta_{1} * x hθ(x)=θ0+θ1x,比如这个函数就是 y = 5 + 2 ∗ x y=5+2*x y=5+2x,现在只要能通过x与y求解出 θ 0 = 5 , θ 1 = 2 \theta_{0}=5,\theta_{1}=2 θ0=5θ1=2
在这里插入图片描述
我们用代码进行解释下

import numpy as np

max_x = 10
data_size = 10
theta_0 = 5
theta_1 = 2

def get_data():
  # (在start和stop之间返回均匀间隔的数据),在1到10之间,返回10个数字
  x = np.linspace(1, max_x, data_size)
  # 创建一个列表,中包含10个数,他们正态分布均值为0,标准差为0.2,这一步相当是创建一些干扰项,让机器更难辨别
  noise = np.random.normal(0, 0.2, len(x))
  y = theta_0 + theta_1 * x + noise
  return x, y

我们先要有数据,才能模拟,我们自己创建一下数据,增加干扰项后增加是识别难度,

x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
y = [6.66, 9.11, 11.08, 12.67, 15.12, 16.76, 18.75, 21.35, 22.77, 24.56]

在这里插入图片描述
我们现在就是要寻找出一条直线,能最大的拟合这个点
在这里插入图片描述
这个直线明显很差,我们需要重新寻找
在这里插入图片描述
那这个过程如何用代码体现出来呢,我们衡量一个目标是否准确,可以用最小二乘法,这个最小二乘法
1 / 2 ( h θ ( x i ) − y i ) 2 = 1 / 2 ( y ^ i − y i ) 2 = 1 / 2 ( θ 0 + θ 1 ∗ x i − y i ) 2 1/2(h_\theta(x^i) - y^i)^2 =1/2 (\widehat{y}^{i} - y^i)^2 = 1/2(\theta_{0} + \theta_{1} * x^{i} - y^{i})^2 1/2(hθ(xi)yi)2=1/2(y iyi)2=1/2(θ0+θ1xiyi)2
只要这个值最小,就是最好的。

def cost_function(x, y, t0, t1):
  cost_sum = 0
  for i in range(len(x)):
  	#power(x, y) 函数,计算 x 的 y 次方。
    cost_item = np.power(t0 + t1 * x[i] - y[i], 2)
    cost_sum += cost_item
  return cost_sum / len(x)

定义损失函数,计算平均损失值。
在这里插入图片描述
从这个图形中我们可以看出,当 θ 0 , θ 1 \theta_{0} , \theta_{1} θ0θ1越接近 [5, 2]时其结果(偏差)越小。相反,离得越远,结果越大。

从上面这幅图中我们可以看出,代价函数在不同的位置结果大小不同。
从三维的角度来看,这就和地面的高低起伏一样。最高的地方就好像是山顶。
而我们的目标就是:从任意一点作为起点,能够快速寻找到一条路径并以此到达图形最低点(代价值最小)的位置。
而梯度下降的算法过程就和我们从山顶想要快速下山的做法是一样的。

算法描述

梯度下降算法最开始的一点就是需要确定下降的方向,即:梯度。

我们常常用 Δ f ( θ ) \Delta f(\theta ) Δf(θ)来表示梯度。对于一个二维空间的曲线来说,梯度就是其切线的方向。如下图所示:
在这里插入图片描述
而对于更高维空间的函数来说,梯度由所有变量的偏导数决定。
[ ∇ f ( θ ) = ( ∂ f ( θ ) ∂ θ 1 , ∂ f ( θ ) ∂ θ 2 , . . . , ∂ f ( θ ) ∂ θ n ) ] [\nabla f({\theta}) = ( \frac{\partial f({\theta})}{\partial \theta_1} , \frac{\partial f({\theta})}{\partial \theta_2} , ... , \frac{\partial f({\theta})}{\partial \theta_n} )] [f(θ)=(θ1f(θ),θ2f(θ),...,θnf(θ))]
在机器学习中,我们主要是用梯度下降算法来最小化代价函数,记做:

[ θ ∗ = a r g m i n L ( θ ) ] [\theta ^* = arg min L(\theta)] [θ=argminL(θ)]

其中,L是代价函数,是参数。

梯度下降算法的主体逻辑很简单,就是沿着梯度的方向一直下降,直到参数收敛为止。

记做:

[ θ i k + 1 = θ i k − λ ∇ f ( θ k ) ] [\theta ^{k + 1}_i = \theta^{k}_i - \lambda \nabla f(\theta^{k})] [θik+1=θikλf(θk)]

这里的下标i表示第i个参数。
上标k指的是第k步的计算结果,而非k次方

收敛是指函数的变化率很小。具体选择多少合适需要根据具体的项目来确定。在演示项目中我们可以选择0.01或者0.001这样的值。不同的值将影响算法的迭代次数,因为在梯度下降的最后,我们会越来越接近平坦的地方,这个时候函数的变化率也越来越小。如果选择一个很小的值,将可能导致算法迭代次数暴增。
公式中的 称作步长,也称作学习率(learning rate)。它决定了每一步往前走多远,认为它是一个类似0.01或0.001的固定值。

线性回归的梯度下降

首先,根据代价函数我们可以得到梯度向量如下:
[ ∇ f ( θ ) = ( ∂ L ( θ ) ∂ θ 0 , ∂ L ( θ ) ∂ θ 1 ) = ( 2 m ∑ i = 1 m ( θ 0 + θ 1 ∗ x i − y i ) , 2 m ∑ i = 1 m ( θ 0 + θ 1 ∗ x i − y i ) x i ) ] [\nabla f({\theta}) = (\frac{\partial L(\theta)}{ \partial\theta_{0}}, \frac{ \partial L(\theta)}{ \partial\theta_{1}}) = (\frac {2}{m} \sum_{i=1}^{m}(\theta_{0} + \theta_{1} * x^{i} - y^{i}) , \frac {2}{m} \sum_{i=1}^{m}(\theta_{0} + \theta_{1} * x^{i} - y^{i}) x^{i})] [f(θ)=(θ0L(θ),θ1L(θ))=(m2i=1m(θ0+θ1xiyi),m2i=1m(θ0+θ1xiyi)xi)]
接着,将每个偏导数带入迭代的公式中,得到:
θ 0 : = θ 0 − λ ∂ L ( θ 0 ) ∂ θ 0 = θ 0 − 2 λ m ∑ i = 1 m ( θ 0 + θ 1 ∗ x i − y i ) \theta_{0} := \theta_{0} - \lambda \frac{\partial L(\theta_{0})}{ \partial\theta_{0}} = \theta_{0} - \frac {2 \lambda }{m} \sum_{i=1}^{m}(\theta_{0} + \theta_{1} * x^{i} - y^{i}) θ0:=θ0λθ0L(θ0)=θ0m2λi=1m(θ0+θ1xiyi)
  θ 1 : = θ 1 − λ ∂ L ( θ 1 ) ∂ θ 1 = θ 1 − 2 λ m ∑ i = 1 m ( θ 0 + θ 1 ∗ x i − y i ) x i \ \theta_{1} := \theta_{1} - \lambda \frac{\partial L(\theta_{1})}{ \partial\theta_{1}} = \theta_{1} - \frac {2 \lambda }{m} \sum_{i=1}^{m}(\theta_{0} + \theta_{1} * x^{i} - y^{i}) x^{i}  θ1:=θ1λθ1L(θ1)=θ1m2λi=1m(θ0+θ1xiyi)xi
由此就可以通过代码实现我们的梯度下降算法了

learning_rate = 0.01

def gradient_descent(x, y):
  t0 = 10
  t1 = 10
  delta = 0.001
  for times in range(1000):
    sum1 = 0
    sum2 = 0
    for i in range(len(x)):
      sum1 += (t0 + t1 * x[i] - y[i])
      sum2 += (t0 + t1 * x[i] - y[i]) * x[i]
    t0_ = t0 - 2 * learning_rate * sum1 / len(x)
    t1_ = t1 - 2 * learning_rate * sum2 / len(x)
    print('Times: {}, gradient: [{}, {}]'.format(times, t0_, t1_))
    if (abs(t0 - t0_) < delta and abs(t1 - t1_) < delta):
      print('Gradient descent finish')
      return t0_, t1_
    t0 = t0_
    t1 = t1_
  print('Gradient descent too many times')
  return t0, t1

这段代码说明如下:

  • 我们随机选择了 θ 0 θ 1 \theta_{0}\theta_{1} θ0θ1 都为10作为起点
  • 设置最多迭代1000次
  • 收敛的范围设为0.001
  • 学习步长设为0.01
Times: 125, gradient: [4.955453758569991, 2.000005017897775]
Times: 126, gradient: [4.955309381126545, 1.9956928964532015]
Times: 127, gradient: [4.9542964317327005, 1.9855674828684156]
Times: 128, gradient: [4.9536358220657, 1.9781180992510465]
Times: 129, gradient: [4.95412496254411, 1.9788858350530971]

从输出中可以看出,算法迭代了129次就收敛了。这时的结果[4.95412496254411, 1.9788858350530971],这已经比较接近目标值 [5, 2]了。如果需要更高的精度,可以将delta的值调的更小,当然,此时会需要更多的迭代次数。
高维扩展
虽然我们举的例子是二维的,但是对于更高维的情况也是类似的。同样是根据迭代的公式进行运算即可:

[ θ i = θ i − λ ∂ L ( θ ) ∂ θ i = θ i − 2 λ m ∑ i = 1 m ( h θ ( x k ) − y k ) x i k ] [\theta_{i} = \theta_{i} - \lambda \frac {\partial L(\theta)}{\partial \theta_i} = \theta_{i} - \frac{2\lambda}{m} \sum_{i=1}^{m}(h_\theta(x^{k})-y^k)x_i^k] [θi=θiλθiL(θ)=θim2λi=1m(hθ(xk)yk)xik]

这里的下标i表示第i个参数,上标k表示第k个数据。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值