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)})\)。

计算步骤如下:

  1. 给定初始点\(x^{(1)} \in R^n\),允许误差\(\epsilon > 0\),置 \(k=1\)。
  2. 计算搜索方向\(d^{(k)} = -\nabla f(x^{(k)})\)。
  3. 若\(\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)})\)。
  4. 令\(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)的更多相关文章

  1. <反向传播(backprop)>梯度下降法gradient descent的发展历史与各版本

    梯度下降法作为一种反向传播算法最早在上世纪由geoffrey hinton等人提出并被广泛接受.最早GD由很多研究团队各自发表,可他们大多无人问津,而hinton做的研究完整表述了GD方法,同时hin ...

  2. (3)梯度下降法Gradient Descent

    梯度下降法 不是一个机器学习算法 是一种基于搜索的最优化方法 作用:最小化一个损失函数 梯度上升法:最大化一个效用函数 举个栗子 直线方程:导数代表斜率 曲线方程:导数代表切线斜率 导数可以代表方向, ...

  3. 梯度下降法Gradient descent(最速下降法Steepest Descent)

    最陡下降法(steepest descent method)又称梯度下降法(英语:Gradient descent)是一个一阶最优化算法. 函数值下降最快的方向是什么?沿负梯度方向  d=−gk

  4. matlab实现梯度下降法(Gradient Descent)的一个例子

    在此记录使用matlab作梯度下降法(GD)求函数极值的一个例子: 问题设定: 1. 我们有一个$n$个数据点,每个数据点是一个$d$维的向量,向量组成一个data矩阵$\mathbf{X}\in \ ...

  5. 梯度下降(gradient descent)算法简介

    梯度下降法是一个最优化算法,通常也称为最速下降法.最速下降法是求解无约束优化问题最简单和最古老的方法之一,虽然现在已经不具有实用性,但是许多有效算法都是以它为基础进行改进和修正而得到的.最速下降法是用 ...

  6. 梯度下降法及一元线性回归的python实现

    梯度下降法及一元线性回归的python实现 一.梯度下降法形象解释 设想我们处在一座山的半山腰的位置,现在我们需要找到一条最快的下山路径,请问应该怎么走?根据生活经验,我们会用一种十分贪心的策略,即在 ...

  7. Gradient Descent 和 Stochastic Gradient Descent(随机梯度下降法)

    Gradient Descent(Batch Gradient)也就是梯度下降法是一种常用的的寻找局域最小值的方法.其主要思想就是计算当前位置的梯度,取梯度反方向并结合合适步长使其向最小值移动.通过柯 ...

  8. [机器学习] ML重要概念:梯度(Gradient)与梯度下降法(Gradient Descent)

    引言 机器学习栏目记录我在学习Machine Learning过程的一些心得笔记,涵盖线性回归.逻辑回归.Softmax回归.神经网络和SVM等等,主要学习资料来自网上的免费课程和一些经典书籍,免费课 ...

  9. 机器学习基础——梯度下降法(Gradient Descent)

    机器学习基础--梯度下降法(Gradient Descent) 看了coursea的机器学习课,知道了梯度下降法.一开始只是对其做了下简单的了解.随着内容的深入,发现梯度下降法在很多算法中都用的到,除 ...

随机推荐

  1. 全景智慧城市——VR全景,开启VR营销新时代

    全景是一种新兴的富媒体技术. 与视频.声音.图片等传统主流媒体最大的区别是"可操作,可交互". 全景给人以三维立体感觉的实景360°全方位图像,此图像最大的三个特点: 全方位:展示 ...

  2. 自己积累写的winfrom 操作api 类

    引用 类 using System; using System.Collections.Generic; using System.Linq; using System.Text; using Sys ...

  3. Oracle exp/imp数据导入导出工具基本用法

    一.获取帮助 exp/imp help=y 二.数据导出 1.将数据库完全导出,设置full选项exp system/manager@orcl file=d:\db.dmp full=y 2.导出数据 ...

  4. post和get方式区别

    Ajax中我们经常用到get和post请求.那么什么时候用get请求,什么时候用post方式请求呢? 在做回答前我们首先要了解get和post的区别.1. get是把参数数据队列加到提交表单的ACTI ...

  5. 东北育才 DAY2组合数取mod (comb)

    组合数取模(comb) [问题描述] 计算C(m,n)mod 9901的值 [输入格式] 从文件comb.in中输入数据. 输入的第一行包含两个整数,m和n [输出格式] 输出到文件comb.out中 ...

  6. 关于MyEclipse修改项目名称后,部署到tomcat显示旧的项目名称

    问题:用Myeclipse部署项目的时候,     出现部署到tomcat下的项目是之前的项目,而不是当前的项目.   解决方案:工程名->右键->Properties->MyEcl ...

  7. 关于java中使用数组的几点理解笔记

    1.数组元素就是变量: 2.在已有数据类型之后加方括号[],就会产生一个新的数组类型: 分两类:1)基本数据类型,如:int[],string[]; 2)引用数据类型,如:Person[](类): 3 ...

  8. redmine测试使用小结

    在尽量不影响当前项目活动的情况下,可以对测试过程作部分改进,能够支持建立项目的BUG管理过程,简述如下: 1.配置角色和权限->新建角色:测试人员 (1)主要配置问题跟踪权限 (2)提前规划好再 ...

  9. jqueryui autocomplete的使用与angular配合的小坑

    刚开始在做搜索联想功能时,使用了jquery.autocomplete.js插件,当并不理想,首先插件老旧,也只适合老版的jquery.其次在数组中只能联想到首字母一样的数据,比如[12,23,222 ...

  10. 【CC2530入门教程-03】CC2530的中断系统及外部中断应用

    第3课  CC2530的中断系统及外部中断应用 广东职业技术学院  欧浩源 一.中断相关的基础概念  内核与外设之间的主要交互方式有两种:轮询和中断. 轮询的方式貌似公平,但实际工作效率很低,且不能及 ...