理解梯度下降法(Gradient Decent)
1. 什么是梯度下降法?
梯度下降法(Gradient Decent)是一种常用的最优化方法,是求解无约束问题最古老也是最常用的方法之一。也被称之为最速下降法。梯度下降法在机器学习中十分常见,多用于求解参数的局部最小值问题。
2. 梯度下降法的原理
引用维基百科中的一张图
简单来说,梯度下降法就是利用了函数沿梯度方向下降最快的原理来求解极小值,当然也可以沿梯度上升方向求解极大值。具体的原理就不赘述了,可以参考Gradient Decent 的维基百科 梯度下降法。
3. 梯度下降法的求解步骤
这里参考陈宝林的《最优化理论与算法》。
梯度下降法的迭代公式是:\(x^{(k+1)} = x^{(k)} + \lambda _k d^{(k)}\),其中 \(d^{(k)}\)是从\(x^{(k)}\)出发的搜索方向,这里取在点\(x^{(k)}\)处的梯度下降方向,即
\(d^{(k)} = - \nabla f(x^{(k)})\)。
\(\lambda _k\)是从\(x^{(k)}\)出发沿方向\(d^{(k)}\)进行一维搜索的步长,即\(\lambda _k\)满足:
\(f(x^{(k)} + \lambda _k d^{(k)}) = min _{\lambda \geq 0}f(x^{(k)} + \lambda d^{(k)})\)。
计算步骤如下:
- 给定初始点\(x^{(1)} \in R^n\),允许误差\(\epsilon > 0\),置 \(k=1\)。
- 计算搜索方向\(d^{(k)} = -\nabla f(x^{(k)})\)。
- 若\(\parallel d^{(k)}\parallel \leq \epsilon\),则停止计算;否则,从\(x^{(k)}\)出发,沿\(d^{(k)}\)进行一维搜索,求\(\lambda _k\),使:
\(f(x^{(k)} + \lambda _k d^{(k)}) = min _{\lambda \geq 0}f(x^{(k)} + \lambda d^{(k)})\)。 - 令\(x^{(k+1)} = x^{(k)} + \lambda _k d^{(k)}\),令 \(k = k+1\),转步骤2。
4. 举例
1. 利用梯度下降法求解一元函数 \(f(x) = x^2 -3x + 1\)的最小值。
设初始点 \(x^{(1)} = 6\),\(\epsilon = 0.1\)。
第1次迭代:目标函数 f(x)在点\(x\)处的梯度 \(\nabla f(x) = 2x -3\)。令搜索方向 \(d^{(1)} = -\nabla f(x^{(1)}) = -9\),\(\parallel d^{(1)} \parallel = 9 > \epsilon = 0.1\)。
从 \(x^{(1)} = 6\)出发,沿\(d^{(1)}\)方向进行搜索,求步长\(\lambda _1\),即:\(min _{\lambda _1\geq 0} \phi(\lambda _1) = f(x^{(1)} + \lambda _1 d^{(1)})\),
\(x^{(1)} + \lambda _1 d^{(1)} = 6-9\lambda _1\) ,\(\phi(\lambda _1) = (6-9\lambda _1)^2 -3(6-9\lambda _1) + 1\),\(\phi ^{'}(\lambda _1) = -18(6-9\lambda _1) + 27\),令
\(\phi^{'}(\lambda _1) = 0\)得:\(\lambda _1 = \frac{1}{2}\)。于是可以得\(x^{(2)} = x^{(1)} + \lambda _1 d^{(1)} = \frac{3}{2}\)。
第2次迭代:\(d^{(2)} = -\nabla f(x^{(2)}) = 0\),\(\parallel d^{(2)} \parallel = 0 < \epsilon = 0.1\)。所以停止计算。
于是得到 \(f(x) = x^2 -3x + 1\)在 \(x = \frac{3}{2}\)处取得极小值。实际上,\(f(x)\)在\(x = \frac{3}{2}\)处取得最小值。
2. 利用梯度下降法求解多元函数的极小值。这里以三元函数\(f(x_1,x_2,x_3) = (x_1 -1)^2 + (x_2 -2)^2 + (x_3 - 3)^2\)求极小值为例。
设初始点 \(x^{(1)} = (5,5,5)\),\(\epsilon = 0.1\)。
第1次迭代:目标函数 \(f(x)\)在点\(x\)处的梯度 \(\nabla f(x) = [2(x_1 -1),2(x_2 -2),2(x_3 -3)]\)。令搜索方向 \(d^{(1)} = -\nabla f(x^{(1)}) = [-8,-6,-4]\), \(\parallel d^{(1)} \parallel = \sqrt{8^2 + 6^2 + 4^2} > \epsilon = 0.1\)。从 \(x^{(1)} = [5,5,5]\)出发,沿\(d^{(1)}\)方向进行搜索,求步长\(\lambda _1\),即:\(min _{\lambda _1\geq 0} \phi(\lambda _1) = f(x^{(1)} + \lambda _1 d^{(1)})\),\(x^{(1)} + \lambda _1 d^{(1)} = [5-8\lambda _1,5 -6\lambda _1,5-4\lambda _1]\),\(\phi(\lambda _1) = (4-8\lambda _1)^2 + (3-6\lambda _1)^2 + (2-4\lambda _1)^2\),\(\phi ^{'}(\lambda _1) = -16(4-8\lambda _1) -12 (3-6\lambda _1) -8(2-4\lambda _1) = 232\lambda _1 -116\),令\(\phi ^{'}(\lambda _1) =0\)得:\(\lambda _1=\frac{1}{2}\)。于是可以得到 \(x^{(2)} = x^{(1)} + \lambda _1 d^{(1)} = [1,2,3]\)。
第2次迭代:\(d^{(2)} = -\nabla f(x^{(2)}) = [0,0,0]\),\(\parallel d^{(2)} \parallel = 0 < \epsilon = 0.1\)。所以停止计算。
于是得到 \(f(x_1,x_2,x_3) = (x_1 -1)^2 + (x_2 -2)^2 + (x_3 - 3)^2\)在 \(x = [1,2,3]\)处取得极小值。实际上,\(f(x)\)在\(x = [1,2,3]\)处取得最小值。
5.代码实现(Python)。
这里以求 \(f(x) = x_1^2 + (x_2-7)^2 + (x_3-6)^2 + (cos(x_4)+1)^2 + (sin(x_5))^2\)的极小值为例,初始点设为\((10,10,10,10,10)\),\(\epsilon = 0.1\)。
from __future__ import print_function
from sympy import *
x1,x2,x3,x4,x5 = Symbol('x1'),Symbol('x2'),Symbol('x3'),Symbol('x4'),Symbol('x5') # 定义符号
x = [x1,x2,x3,x4,x5]
lam = Symbol('lam') # 步长变量
z = [10,10,10,10,10] # 定义初始点
epsilon = 0.1
n = 5 # 定义变量个数
num = 100.0 # 求解步长时在[0,1]之间分割的数目,这里假设将步长限定为(0,1)之间
# 定义求极值的函数
def function(x):
return x[0]**2 + (x[1]-7)**2 + (x[2] - 6)**2 + (cos(x[3]) + 1)**2 + sin(x[4])**2
# 求解步长的函数
# 将(0,1)分为 num 份,第i个(i=1,2,...,num-1)步长为 i/num
# 通过遍历,我们找使得f最小(待求函数值最小)的步长
def solve_lam(f):
f_list = [f.subs(lam,i/num) for i in range(1,int(num))]
min_value = min(f_list)
index = f_list.index(min_value)
return (index+1)/num # 返回步长
# 计算梯度向量的长度(二阶范数)
def distance(gradient):
return sqrt(sum([i**2 for i in gradient]))
# 计算y在z点处的梯度
def compute_gradient(y,z):
return [float(diff(y,x[i]).subs(x[i],z[i])) for i in range(n)]
# 更新点的坐标
def compute_new_x(z,lam,gradient):
return [z[i] -lam*gradient[i] for i in range(n)]
y = function(x) # 定义函数表达式
gradient = compute_gradient(y,z)
index = 1 # 初始化梯度下降次数
while(distance(gradient) > epsilon):
s = [z[i] -lam*gradient[i] for i in range(n)]
f = function(s)
step = solve_lam(f) # 计算的实际步长
z = [float(z[i] - step*gradient[i]) for i in range(n)]
print("第%d次梯度下降,点为:" % index,z)
print("第%d次梯度下降,梯度的模长为:" % index,distance(gradient))
gradient = compute_gradient(y,z)
index += 1
print("梯度下降的最终点为:",z)
print("梯度下降最终梯度模长为:",distance(gradient))
print("最终函数极小值为:",function(z))
输出结果为:
第1次梯度下降,点为: [0.0, 7.0, 6.0, 9.912451514474444, 9.543527374636186]
第1次梯度下降,梯度的模长为: 22.3799939227002
第2次梯度下降,点为: [0.0, 7.0, 6.0, 9.849088133287399, 9.407069381569496]
第2次梯度下降,梯度的模长为: 0.259399413509407
梯度下降的最终点为: [0.0, 7.0, 6.0, 9.849088133287399, 9.407069381569496]
梯度下降最终梯度模长为: 0.0811485412629629,
最终函数极小值为: 0.00817718083812317
可以看出,只经过了两次梯度下降,就找到了极小值点。这里solve_lam 函数的作用是求解使得一次梯度下降,函数沿负梯度方向下降得最快的步长。这样每次步长都是不同的且最优的,所以寻找局部极小值点的速度非常快。
还有一种方法是使用固定的步长\(\lambda\),但是要注意\(\lambda\)的取值。一般来说\(\lambda\)的取值既不能太大,又不能太小。如果步长过大,那么函数可能会在局部极小值点附近震荡或者取不到极小值;而如果步长过小,则函数收敛到局部极小值点的速度会非常慢,效率低。一般来说,可以多次尝试取不同的\(\lambda\)的值,以最接近局部极小值点的为最佳。这里假设取\(\lambda = 0.1\),程序如下:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2017/7/19 16:09
# @Author : Lyrichu
# @Email : 919987476@qq.com
# @File : gradient_decent.py
'''
@Description:使用梯度下降法求解f(x) = x1^2 + (x2-7)^2 + (x3-6)^2 + (cos(x4)+1)^2 + (sin(x5))^2的极小值
这里采用固定步长
'''
from __future__ import print_function
from sympy import *
x1,x2,x3,x4,x5 = Symbol('x1'),Symbol('x2'),Symbol('x3'),Symbol('x4'),Symbol('x5') # 定义符号
x = [x1,x2,x3,x4,x5]
z = [10,10,10,10,10] # 定义初始点
epsilon = 0.1
n = 5 # 定义变量个数
num = 100.0 # 求解步长时在[0,1]之间分割的数目,这里假设将步长限定为(0,1)之间
step = 0.1 # 定义步长为固定值
# 定义求极值的函数
def function(x):
return x[0]**2 + (x[1]-7)**2 + (x[2] - 6)**2 + (cos(x[3]) + 1)**2 + sin(x[4])**2
# 计算梯度向量的长度(二阶范数)
def distance(gradient):
return sqrt(sum([i**2 for i in gradient]))
# 计算y在z点处的梯度
def compute_gradient(y,z):
return [float(diff(y,x[i]).subs(x[i],z[i])) for i in range(n)]
# 更新点的坐标
def compute_new_x(z,lam,gradient):
return [z[i] -lam*gradient[i] for i in range(n)]
y = function(x) # 定义函数表达式
gradient = compute_gradient(y,z)
index = 1 # 初始化梯度下降次数
while(distance(gradient) > epsilon):
z = [float(z[i] - step*gradient[i]) for i in range(n)]
print("第%d次梯度下降,点为:" % index,z)
print("第%d次梯度下降,梯度的模长为:" % index,distance(gradient))
gradient = compute_gradient(y,z)
index += 1
print("梯度下降的最终点为:",z)
print("梯度下降最终梯度模长为:",distance(gradient))
print("最终函数极小值为:",function(z))
输出结果为:
第1次梯度下降,点为: [8.0, 9.4, 9.2, 9.982490302894888, 9.908705474927237]
第1次梯度下降,梯度的模长为: 22.3799939227002
第2次梯度下降,点为: [6.4, 8.92, 8.559999999999999, 9.966450751253301, 9.826338348410065]
第2次梯度下降,梯度的模长为: 17.9082149047513
第3次梯度下降,点为: [5.12, 8.536, 8.047999999999998, 9.951689717070877, 9.754385662490165]
第3次梯度下降,梯度的模长为: 14.3296722821606
第4次梯度下降,点为: [4.096, 8.2288, 7.638399999999999, 9.93804837678198, 9.693135978676548]
第4次梯度下降,梯度的模长为: 11.4658519523503
第5次梯度下降,点为: [3.2768, 7.98304, 7.310719999999999, 9.925393904908995, 9.642004324696792]
第5次梯度下降,梯度的模长为: 9.17406879033011
......
第25次梯度下降,点为: [0.037778931862957166, 7.011333679558887, 6.015111572745182, 9.78022077224335, 9.427338222547055]
第25次梯度下降,梯度的模长为: 0.115020432780820
梯度下降的最终点为: [0.037778931862957166, 7.011333679558887, 6.015111572745182, 9.78022077224335, 9.427338222547055]
梯度下降最终梯度模长为: 0.0951589383799565
最终函数极小值为: 0.00569780461165563
从结果来看,使用固定步长\(\lambda = 0.1\)需要梯度下降25次才能最终到达极小值点,最终结果与前一次基本相同,但是迭代次数却大大增加。所以实践中,还是使用方法一效率会更高一些,但是方法一对于复杂函数的求极值可能会涉及到复杂一元函数求最小值的问题,我是直接采用遍历穷举近似求解的。实践中还可以求导,然后利用二分法等方法求解导函数的零点,从而求最小值。
需要注意的是,梯度下降法只能求解函数的局部极小值,并不能保证可以达到全局最小值。对于有多个极小值点的复杂函数而言,梯度下降法往往只能求到其近似最小值点,而如果想要求全局最小值点,需要使用其他的方法,比如遗传算法、模拟退火算法等。
理解梯度下降法(Gradient Decent)的更多相关文章
- <反向传播(backprop)>梯度下降法gradient descent的发展历史与各版本
梯度下降法作为一种反向传播算法最早在上世纪由geoffrey hinton等人提出并被广泛接受.最早GD由很多研究团队各自发表,可他们大多无人问津,而hinton做的研究完整表述了GD方法,同时hin ...
- (3)梯度下降法Gradient Descent
梯度下降法 不是一个机器学习算法 是一种基于搜索的最优化方法 作用:最小化一个损失函数 梯度上升法:最大化一个效用函数 举个栗子 直线方程:导数代表斜率 曲线方程:导数代表切线斜率 导数可以代表方向, ...
- 梯度下降法Gradient descent(最速下降法Steepest Descent)
最陡下降法(steepest descent method)又称梯度下降法(英语:Gradient descent)是一个一阶最优化算法. 函数值下降最快的方向是什么?沿负梯度方向 d=−gk
- matlab实现梯度下降法(Gradient Descent)的一个例子
在此记录使用matlab作梯度下降法(GD)求函数极值的一个例子: 问题设定: 1. 我们有一个$n$个数据点,每个数据点是一个$d$维的向量,向量组成一个data矩阵$\mathbf{X}\in \ ...
- 梯度下降(gradient descent)算法简介
梯度下降法是一个最优化算法,通常也称为最速下降法.最速下降法是求解无约束优化问题最简单和最古老的方法之一,虽然现在已经不具有实用性,但是许多有效算法都是以它为基础进行改进和修正而得到的.最速下降法是用 ...
- 梯度下降法及一元线性回归的python实现
梯度下降法及一元线性回归的python实现 一.梯度下降法形象解释 设想我们处在一座山的半山腰的位置,现在我们需要找到一条最快的下山路径,请问应该怎么走?根据生活经验,我们会用一种十分贪心的策略,即在 ...
- Gradient Descent 和 Stochastic Gradient Descent(随机梯度下降法)
Gradient Descent(Batch Gradient)也就是梯度下降法是一种常用的的寻找局域最小值的方法.其主要思想就是计算当前位置的梯度,取梯度反方向并结合合适步长使其向最小值移动.通过柯 ...
- [机器学习] ML重要概念:梯度(Gradient)与梯度下降法(Gradient Descent)
引言 机器学习栏目记录我在学习Machine Learning过程的一些心得笔记,涵盖线性回归.逻辑回归.Softmax回归.神经网络和SVM等等,主要学习资料来自网上的免费课程和一些经典书籍,免费课 ...
- 机器学习基础——梯度下降法(Gradient Descent)
机器学习基础--梯度下降法(Gradient Descent) 看了coursea的机器学习课,知道了梯度下降法.一开始只是对其做了下简单的了解.随着内容的深入,发现梯度下降法在很多算法中都用的到,除 ...
随机推荐
- OpenGL判断一个点是否可见
关于OpenGL中判断一个点是否可见,可以分成两种情况讨论:点在2D空间中和3D空间中的时候.并且"在2D空间中"可以看作"在3D空间中"的特殊情况. 温馨提示 ...
- mongoDB数据库的简单使用
我的第一篇小文章,以前总是写Evernote. mongodb属于非关系型数据库中的文档型数据库. 1.下载安装mongoDB, 文件自动 存放在这个目录下:C:\Program Files\Mong ...
- sql 经典面试题
如果数据库里两个日期型字段d1,d2,怎样用sql语句列出按月的所有区间,比如表结构如下localid d1 d21 2014-1-15 2014-3- ...
- jdbc的配置及jdbc连接常用数据库(mysql、sqlserver、Oracle)
1.连接SQL Server数据库 import java.sql.*; publicclassMain{publicstaticvoid main(String[] args){String dri ...
- 什么是Web Worker?
简单点说,Web Worker就是一个运行在后台的JavaScript线程,不会影响页面的响应. 我们知道,JavaScript是单线程的脚本语言,即同一时刻只能做一件事情,否则会带来极其复杂的同步问 ...
- [0] 解决版本冲突-使用SVN主干与分支功能
解决版本冲突-使用SVN主干与分支功能 1 前言 大多数产品开发存在这样一个生命周期:编码.测试.发布,然后不断重复.通常是这样的开发步骤: 1) 开发人员开发完毕某一版本(如版本A)功能后, ...
- Python 内置函数汇总
循环设计与循环对象 range() enumerate() zip() iter() 函数对象 map() filter() reduce() 序列操作 all([True, 1, "hel ...
- python 文件操作(pickle)
>>> with open('text.txt','wb') as data:pickle.dump(['a','b',2],data) 保存到文件 >>> wit ...
- R语言重要数据集分析研究——R语言数据集的字段含义
R语言数据集的字段含义 作者:马文敏 选择一种数据结构来储存数据 将数据输入或导入到这个数据结构中 数据集的概念 数据集通常是有数据结构的一个矩形数组,行表示规则,列表示变量. 不同的行业对数据集的行 ...
- CSS3学习系列之盒样式(一)
盒的基本类型 在css中,使用display属性来定义盒的类型,总体上来说,css中的盒分为block类型与inline类型 inline-block类型 inline-block类型是在css2.1 ...