龙空技术网

梯度下降法

lovelyun690 200

前言:

现时小伙伴们对“梯度下降算法c语言”大致比较关切,你们都需要剖析一些“梯度下降算法c语言”的相关知识。那么小编在网摘上收集了一些有关“梯度下降算法c语言””的相关知识,希望小伙伴们能喜欢,咱们快快来了解一下吧!

本文主要解释什么是梯度,什么是梯度下降法,可以应用在哪里,如何用python实现。

梯度

梯度是微分中一个很重要的概念:

在单变量的函数中,梯度其实就是函数的微分在多变量函数中,梯度是一个向量

先说说单变量函数。

梯度是函数的微分,那微分又是什么呢?

下面我们看一下维基百科对微分的描述。

函数的微分是对函数局部变化的一种线性描述。微分可以近似地描述当函数自变量的取值作足够小的改变时,函数的值是怎样改变的。

在几何上,设 ∆x 是曲线 y=f(x) 上的点 P 在横坐标上的增量,∆y 是曲线在点 P 对应 ∆x 在纵坐标上的增量,dy 是曲线在点 P 的切线对应 ∆x 在纵坐标上的增量。当 |∆x| 很小时,|∆y−dy||∆x| 要小得多(高阶无穷小),因此在点P附近,我们可以用切线段来近似代替曲线段。

下面看一个例子。

设函数f(x) = x²,回顾一下微分的定义,函数自变量取足够小的改变时,函数值是怎么改变的,假设函数自变量 x 变到 x + dx ,那么函数值的改变是 f(x + dx) - f(x),进一步计算可以看到:

f(x + dx) - f(x)= (x + dx)² - x²= x² + 2x * dx + (dx)² - x²= 2x * dx + (dx)²= Adx + o(dx)

其中的线性主部:Adx = 2xdx ,高阶无穷小是 o(dx) = (dx)²

因此,该函数在 x 处的微分是 dy = 2xdx,函数的微分与自变量的微分之商 dy / dx = 2x = f’(x),等于函数的导数。

那么现在我们再看看一元微分的定义:

设函数 y = f(x) 在某区间内有定义,对于区间内的一点 x0 ,变动到附近的 x0 + ∆x (也在此区间内)时,如果函数的增量 ∆y = f(x0 + ∆x) - f(x) 可表示为 ∆y = A∆x + o(∆x) ,其中A是不依赖于 ∆x 的常数, o(∆x)是比 ∆x高阶的无穷小,那么称函数 f(x)x0 处是可微的,且 A∆x 称作函数在点 x0 相应于自变量增量 ∆x 的微分,记作 dy, 即 dy = A∆X, dy∆y 的线性主部,通常把自变量 x 的增量 ∆x 称为自变量的微分,记作 dx, 即 dx = ∆x

当一个函数有多个变量的时候,就有了多变量的微分,即分别对每个变量求微分。

这里,我们先了解一下什么是偏导。

偏导: 一个多变量的函数(或称多元函数),对其中一个变量(导数)微分,而保持其他变量恒定。

函数 f 关于变量 x 的偏导数写为 fx’∂f / ∂x。偏导数符号 是全导数符号 d 的变体。

比如函数 z = f(x, y) = x²y² ,对变量 x 微分,即 ∂f / ∂x = ∂(x²y²) / ∂x = 2xy²

复合函数则需要用到链式法则,比如f(x) = (x² + 1)³ 的导数 f’(x) = 3(x² + 1)² * 2x = 6x(x² + 1)²

梯度实际上就是多变量微分的一般化。

比如 f(x, y, z) = 0.55 - (5x + 2y -12z), 那么该函数的梯度为 ▽f = <∂f / ∂x, ∂f / ∂y, ∂f / ∂z> = <-5, -2, 12>

我们可以看到,梯度其实是一个向量,这个向量指出了函数在给定点变化最快的方向。

现在我们再看看什么是梯度下降法。

理解梯度下降法,有一个很经典的例子:

一个人被困在山上,需要下山。但山上有浓雾,可视度很低,下山的路无法确定。他必须利用自己周围的信息去找下山的路。

这时,他就可以利用梯度下降算法。

具体来说,以他当前的所处的位置为基准,寻找这个位置最陡峭的地方,然后朝着山的高度下降的地方走;

同理,如果我们的目标是上山,也就是爬到山顶,那么此时应该是朝着最陡峭的方向往上走。

然后每走一段距离,都反复采用同一个方法,最后就能成功的抵达山谷。

同时可以假设最陡峭的地方无法一眼看出来,而是需要一个复杂的工具来测量。

所以,此人每走一段距离,都需要一段时间来测量所在位置最陡峭的方向,这是比较耗时的。

那么为了在太阳下山之前到达山底,就要尽可能的减少测量次数。

问题是,如果测量频繁,可以保证下山的方向是绝对正确的,但又非常耗时,如果测量过少,又有偏离方向的风险。

所以需要一个合适的测量频率,来确保下山的方向不错误,同时又不至于耗时太多。

总结一下就是:

梯度下降法的实现

下面我们就用python来实现使用梯度下降法模拟求解 y=(2x + 4)² + 1 最小值,x范围[-10, 6],起点随机选取。

首先引入 numpymatplotlib.pyplotnumpy是一个数学函数库, matplotlib.pyplot用来画图。

import numpy as npimport matplotlib.pyplot as plt

然后定义目标函数:

# 目标函数: y = (2 * x + 4)^2 + 1def func(x): return np.square(2 * x + 4) + 1

接着定义目标函数的导数:

# 目标函数的一阶导数: dy / dx = 8 * x + 16def dfunc(x): return 8 * x + 16

下面实现梯度下降法。

# 梯度下降法:给定起始点目标函数的一阶导函数,求在 iterations 次迭代中x的最新值# param x_start: x 的起始点# param df: 目标函数的一阶导函数# param iterations: 迭代次数# param lr: 学习率# return: x在每次迭代后的位置(包括起始点),长度为 iterations + 1def gradient_descent(x_start, df, lr = 0.01, iterations = 1000):    xs = np.zeros(iterations + 1)    x = x_start    xs[0] = x    print(f'起点:x={x}, y={func(x)}')    for i in range(iterations):        # diff表示x要改变的幅度        diff = - df(x) * lr        if np.all(np.abs(diff) <= stopping_threshold):            print('停止')            break        x += diff        xs[i + 1] = x        print (f'迭代次数: {i + 1},结果:x={x}, y = {func(x)}')    return xs

接下来就是调用梯度下降函数。

# 起始点x_start = np.random.uniform(-10, 6)# 迭代次数iterations = 1000# 学习率lr = 0.1# 停止迭代阈值stopping_threshold = 1e-5# 梯度下降法y = gradient_descent(x_start, dfunc, lr, iterations)

最后画图。

color = 'r'x = np.arange(-10.0, 6.0, 0.01)plt.plot(x, func(x), c = 'g')plt.plot(y, func(y), c = color, label='lr={}'.format(lr))plt.scatter(y, func(y), c = color)plt.legend()plt.show()

我们运行3次就可以看到结果:

把学习率改为0.01时,迭代次数明显增加:

我们再运行3次,可以看到:

第1次,起点 x = 8.80086, y = 186.00666,迭代131次,结果x = -2.00012, y = 1.00000

第2次,起点 x = 7.31667, y = 114.0679,迭代128次,结果x = -2.00012, y = 1.00000

第3次,起点 x = 0.84411, y = 6.34431,迭代110次,结果x = -1.99988, y = 1.00000

把学习率改为0.001时,迭代次数高达1071次:

我们再运行3次,可以看到:

第1次,起点 x = -8.81178, y =186.60116,迭代1000次,结果x = -2.00221, y = 1.00002

第2次,起点 x = 2.98931, y = 100.57297,迭代1000次,结果x = -1.99838, y = 1.00001

第3次,起点 x = 1.50069, y = 1.99721,迭代746次,结果x = -199875, y = 1.00001

下面我们把学习率调大,比如0.2,可以看到迭代结果在最低点左右徘徊,27次后找到最低点。

再把学习率调到0.3时,迭代1000次后还没找到最低点:

标签: #梯度下降算法c语言