梯度下降法介绍及利用Python实现的方法示例

本文主要给大家介绍了梯度下降法及利用Python实现的相关内容,分享出来供大家参考学习,下面话不多说,来一起看看详细的介绍吧。

梯度下降法介绍

梯度下降法(gradient descent),又名最速下降法(steepest descent)是求解无约束最优化问题最常用的方法,它是一种迭代方法,每一步主要的操作是求解目标函数的梯度向量,将当前位置的负梯度方向作为搜索方向(因为在该方向上目标函数下降最快,这也是最速下降法名称的由来)。

梯度下降法特点:越接近目标值,步长越小,下降速度越慢。

直观上来看如下图所示:

这里每一个圈代表一个函数梯度,最中心表示函数极值点,每次迭代根据当前位置求得的梯度(用于确定搜索方向以及与步长共同决定前进速度)和步长找到一个新的位置,这样不断迭代最终到达目标函数局部最优点(如果目标函数是凸函数,则到达全局最优点)。

下面我们将通过公式来具体说明梯度下降法

下面这个h(θ)是我们的拟合函数

也可以用向量的形式进行表示:

下面函数是我们需要进行最优化的风险函数,其中的每一项都表示在已有的训练集上我们的拟合函数与y之间的残差,计算其平方损失函数作为我们构建的风险函数(参见最小二乘法及其Python实现)

这里我们乘上1/2是为了方便后面求偏导数时结果更加简洁,之所以能乘上1/2是因为乘上这个系数后对求解风险函数最优值没有影响。

我们的目标就是要最小化风险函数,使得我们的拟合函数能够最大程度的对目标函数y进行拟合,即:

后面的具体梯度求解都是围绕这个目标来进行。

批量梯度下降BGD

按照传统的思想,我们需要对上述风险函数中的每个求其偏导数,得到每个对应的梯度

这里表示第i个样本点的第j分量,即h(θ)中的

接下来由于我们要最小化风险函数,故按照每个参数的负梯度方向来更新每一个

这里的α表示每一步的步长

从上面公式可以注意到,它得到的是一个全局最优解,但是每迭代一步,都要用到训练集所有的数据,如果m很大,那么可想而知这种方法的迭代速度!!所以,这就引入了另外一种方法,随机梯度下降。

随机梯度下降SGD

因为批量梯度下降在训练集很大的情况下迭代速度非常之慢,所以在这种情况下再使用批量梯度下降来求解风险函数的最优化问题是不具有可行性的,在此情况下,提出了——随机梯度下降
我们将上述的风险函数改写成以下形式:

其中,

称为样本点的损失函数

接下来我们对每个样本的损失函数,对每个求其偏导数,得到每个对应的梯度

然后根据每个参数的负梯度方向来更新每一个

与批量梯度下降相比,随机梯度下降每次迭代只用到了一个样本,在样本量很大的情况下,常见的情况是只用到了其中一部分样本数据即可将θ迭代到最优解。因此随机梯度下降比批量梯度下降在计算量上会大大减少。

SGD有一个缺点是,其噪音较BGD要多,使得SGD并不是每次迭代都向着整体最优化方向。而且SGD因为每次都是使用一个样本进行迭代,因此最终求得的最优解往往不是全局最优解,而只是局部最优解。但是大的整体的方向是向全局最优解的,最终的结果往往是在全局最优解附近。

下面是两种方法的图形展示:

从上述图形可以看出,SGD因为每次都是用一个样本点进行梯度搜索,因此其最优化路径看上去比较盲目(这也是随机梯度下降名字的由来)。

对比其优劣点如下:

批量梯度下降:

优点:全局最优解;易于并行实现;总体迭代次数不多

缺点:当样本数目很多时,训练过程会很慢,每次迭代需要耗费大量的时间。

随机梯度下降:

优点:训练速度快,每次迭代计算量不大

缺点:准确度下降,并不是全局最优;不易于并行实现;总体迭代次数比较多。

Python实现方法示例

上面我们讲解了什么是梯度下降法,以及如何求解梯度下降,下面我们将通过python来实现梯度下降法。

# _*_ coding: utf-8 _*_
# 作者: yhao
# 博客: http://blog.csdn.net/yhao2014
# 邮箱: yanhao07@sina.com 

# 训练集
# 每个样本点有3个分量 (x0,x1,x2)
x = [(1, 0., 3), (1, 1., 3), (1, 2., 3), (1, 3., 2), (1, 4., 4)]
# y[i] 样本点对应的输出
y = [95.364, 97.217205, 75.195834, 60.105519, 49.342380] 

# 迭代阀值,当两次迭代损失函数之差小于该阀值时停止迭代
epsilon = 0.0001 

# 学习率
alpha = 0.01
diff = [0, 0]
max_itor = 1000
error1 = 0
error0 = 0
cnt = 0
m = len(x) 

# 初始化参数
theta0 = 0
theta1 = 0
theta2 = 0 

while True:
 cnt += 1 

 # 参数迭代计算
 for i in range(m):
 # 拟合函数为 y = theta0 * x[0] + theta1 * x[1] +theta2 * x[2]
 # 计算残差
 diff[0] = (theta0 + theta1 * x[i][1] + theta2 * x[i][2]) - y[i] 

 # 梯度 = diff[0] * x[i][j]
 theta0 -= alpha * diff[0] * x[i][0]
 theta1 -= alpha * diff[0] * x[i][1]
 theta2 -= alpha * diff[0] * x[i][2] 

 # 计算损失函数
 error1 = 0
 for lp in range(len(x)):
 error1 += (y[lp]-(theta0 + theta1 * x[lp][1] + theta2 * x[lp][2]))**2/2 

 if abs(error1-error0) < epsilon:
 break
 else:
 error0 = error1 

 print ' theta0 : %f, theta1 : %f, theta2 : %f, error1 : %f' % (theta0, theta1, theta2, error1)
print 'Done: theta0 : %f, theta1 : %f, theta2 : %f' % (theta0, theta1, theta2)
print '迭代次数: %d' % cnt 

结果(截取部分):

 theta0 : 2.782632, theta1 : 3.207850, theta2 : 7.998823, error1 : 7.508687
 theta0 : 4.254302, theta1 : 3.809652, theta2 : 11.972218, error1 : 813.550287
 theta0 : 5.154766, theta1 : 3.351648, theta2 : 14.188535, error1 : 1686.507256
 theta0 : 5.800348, theta1 : 2.489862, theta2 : 15.617995, error1 : 2086.492788
 theta0 : 6.326710, theta1 : 1.500854, theta2 : 16.676947, error1 : 2204.562407
 theta0 : 6.792409, theta1 : 0.499552, theta2 : 17.545335, error1 : 2194.779569
 theta0 : 74.892395, theta1 : -13.494257, theta2 : 8.587471, error1 : 87.700881
 theta0 : 74.942294, theta1 : -13.493667, theta2 : 8.571632, error1 : 87.372640
 theta0 : 74.992087, theta1 : -13.493079, theta2 : 8.555828, error1 : 87.045719
 theta0 : 75.041771, theta1 : -13.492491, theta2 : 8.540057, error1 : 86.720115
 theta0 : 75.091349, theta1 : -13.491905, theta2 : 8.524321, error1 : 86.395820
 theta0 : 75.140820, theta1 : -13.491320, theta2 : 8.508618, error1 : 86.072830
 theta0 : 75.190184, theta1 : -13.490736, theta2 : 8.492950, error1 : 85.751139
 theta0 : 75.239442, theta1 : -13.490154, theta2 : 8.477315, error1 : 85.430741
 theta0 : 97.986390, theta1 : -13.221172, theta2 : 1.257259, error1 : 1.553781
 theta0 : 97.986505, theta1 : -13.221170, theta2 : 1.257223, error1 : 1.553680
 theta0 : 97.986620, theta1 : -13.221169, theta2 : 1.257186, error1 : 1.553579
 theta0 : 97.986735, theta1 : -13.221167, theta2 : 1.257150, error1 : 1.553479
 theta0 : 97.986849, theta1 : -13.221166, theta2 : 1.257113, error1 : 1.553379
 theta0 : 97.986963, theta1 : -13.221165, theta2 : 1.257077, error1 : 1.553278
Done: theta0 : 97.987078, theta1 : -13.221163, theta2 : 1.257041
迭代次数: 3443 

可以看到最后收敛到稳定的参数值。

注意:这里在选取alpha和epsilon时需要谨慎选择,可能不适的值会导致最后无法收敛。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

参考文档:

(0)

相关推荐

  • 梯度下降法介绍及利用Python实现的方法示例

    本文主要给大家介绍了梯度下降法及利用Python实现的相关内容,分享出来供大家参考学习,下面话不多说,来一起看看详细的介绍吧. 梯度下降法介绍 梯度下降法(gradient descent),又名最速下降法(steepest descent)是求解无约束最优化问题最常用的方法,它是一种迭代方法,每一步主要的操作是求解目标函数的梯度向量,将当前位置的负梯度方向作为搜索方向(因为在该方向上目标函数下降最快,这也是最速下降法名称的由来). 梯度下降法特点:越接近目标值,步长越小,下降速度越慢. 直观上

  • K-means聚类算法介绍与利用python实现的代码示例

    聚类 今天说K-means聚类算法,但是必须要先理解聚类和分类的区别,很多业务人员在日常分析时候不是很严谨,混为一谈,其实二者有本质的区别. 分类其实是从特定的数据中挖掘模式,作出判断的过程.比如Gmail邮箱里有垃圾邮件分类器,一开始的时候可能什么都不过滤,在日常使用过程中,我人工对于每一封邮件点选"垃圾"或"不是垃圾",过一段时间,Gmail就体现出一定的智能,能够自动过滤掉一些垃圾邮件了.这是因为在点选的过程中,其实是给每一条邮件打了一个"标签&qu

  • 利用python生成照片墙的示例代码

    PIL(Python Image Library)是python的第三方图像处理库,但是由于其强大的功能与众多的使用人数,几乎已经被认为是python官方图像处理库了.其官方主页为:PIL. PIL历史悠久,原来是只支持python2.x的版本的,后来出现了移植到python3的库pillow,pillow号称是friendly fork for PIL,其功能和PIL差不多,但是支持python3.本文只使用了PIL那些最常用的特性与用法,主要参考自:http://www.effbot.org

  • 利用Python实现网络测试的示例代码

    Speedtest CLI 专为软件开发人员.系统管理员和计算机爱好者等打造,是 Ookla® 提供技术支持的首款正式 Linux 本机 Speedtest 应用程序. Speedtest CLI是使用python语言开发的,不仅可以直接在命令行运行.也可以作为python模块在python IDE中直接调用. 首先,看一下如何在python应用中进行调用,使用pip直接安装. pip install speedtest-cli 将该模块直接导入到我们当前的代码块中. import speedt

  • MySQL数据库设计之利用Python操作Schema方法详解

    弓在箭要射出之前,低声对箭说道,"你的自由是我的".Schema如箭,弓似Python,选择Python,是Schema最大的自由.而自由应是一个能使自己变得更好的机会. Schema是什么? 不管我们做什么应用,只要和用户输入打交道,就有一个原则--永远不要相信用户的输入数据.意味着我们要对用户输入进行严格的验证,web开发时一般输入数据都以JSON形式发送到后端API,API要对输入数据做验证.一般我都是加很多判断,各种if,导致代码很丑陋,能不能有一种方式比较优雅的验证用户数据呢

  • 利用python获取Ping结果示例代码

    前言 本文主要跟大家分享了关于利用python获取Ping结果的相关内容,分享出来供大家参考学习,下面话不多说,来一起看看详细的介绍吧. 示例代码: # -*- coding: utf-8 -*- import subprocess import re def get_ping_result(ip_address): p = subprocess.Popen(["ping.exe", ip_address], stdin = subprocess.PIPE, stdout = subp

  • Js利用prototype自定义数组方法示例

    前言 在开始之前,先给大家介绍下js中使用使用原型(prototype)定义方法的好处 经常在前端面试或是和其他同行沟通是,在谈到构造在JS定义构造函数的方法是最好使用原型的方式:将方法定义到构造方法的prototype上,这样的好处是,通过该构造函数生成的实例所拥有的方法都是指向一个函数的索引,这样可以节省内存. 而本文主要给大家介绍了关于Js利用prototype自定义数组方法的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 题目 如何实现下列代码: [1,2,3

  • python中__call__方法示例分析

    本文实例讲述了python中__call__方法的用法,分享给大家供大家参考.具体方法分析如下: Python中的__call__允许程序员创建可调用的对象(实例),默认情况下, __call__()方法是没有实现的,这意味着大多数实例是不可调用的.然而,如果在类定义中覆盖了这个方法,那么这个类的实例就成为可调用的. test.py文件如下: #!/usr/bin/python # Filename:test.py class CallTest(): def __init__(self): pr

  • Python优先队列实现方法示例

    本文实例讲述了Python优先队列实现方法.分享给大家供大家参考,具体如下: 1. 代码 import Queue import threading class Job(object): def __init__(self, priority, description): self.priority = priority self.description = description print 'New job:', description return def __cmp__(self, ot

  • LRUCache的实现原理及利用python实现的方法

    简介 LRU(Least Recently Used)最近最少使用,最近有时间和空间最近的歧义,所以我更喜欢叫它近期最少使用算法.它的核心思想是,如果一个数据被访问过,我们有理由相信它在将来被访问的概率就越高.于是当LRU缓存达到设定的最大值时将缓存中近期最少使用的对象移除.LRUCache内部使用LinkedHashMap来存储key-value键值对,并将LinkedHashMap设置为访问顺序来体现LRU算法. 无论是对某个key的get,还是set都算做是对该key的一次使用.当set一

随机推荐