梯度是多元函数对各个自变量偏导数造成的向量。多元函数的梯度表示:
\[\nabla f(x) = \left( \frac{\partial f}{\partial x_1},...,\frac{\partial f}{\partial x_n} \right)^T \]python
若是Hessian矩阵正定,函数有极小值;若是Hessian矩阵负定,函数有极大值;若是Hessian矩阵不定,则须要进一步讨论。算法
若是二阶导数大于0,函数有极小值;若是二阶导数小于0,函数有极大值;若是二阶导数等于0,状况不定。网络
目标是求多元函数\(f(x)\)的极小值。梯度降低法是经过不断迭代获得函数极小值,即如能保证\(f(x +\Delta x)\)比\(f(x)\)小,则不断迭代,最终能获得极小值。想象你在山顶往山脚走,若是每一步到的位置比以前的位置低,就能走到山脚。问题是像哪一个方向走,能最快到山脚呢?
由泰勒展开式得:
\[f(x + \Delta x) - f(x) = (\nabla f(x))^T \Delta x + o(\Delta x) \]
若是\(\Delta x\)足够小,能够忽略\(o(\Delta x)\),则有:
\[f(x + \Delta x) - f(x) \approx (\nabla f(x))^T \Delta x\]
因而只有:
\[(\nabla f(x))^T \Delta x < 0 \]
能使
\[ f(x + \Delta x) < f(x) \]
由于\(\nabla f(x)\)与\(\Delta x\)均为向量,因而有:
\[ (\nabla f(x))^T \Delta x = \| \nabla f(x)\|\|\Delta x\|cos\theta\]
其中,\(\theta\)是向量\(\nabla f(x)\)与\(\Delta x\)的夹角,\(\| \nabla f(x)\|\)与\(\|\Delta x\|\)是向量对应的模。可见只有当
\[cos\theta < 0\]
才能使得
\[ (\nabla f(x))^T \Delta x < 0 \]
又因
\[ cos\theta \ge -1 \]
可见,只有当
\[cos\theta = -1\]
即\(\theta = \pi\)时,函数数值下降最快。此时梯度和\(\Delta x\)反向,即夹角为180度。所以当向量\(\Delta x\)的模大小必定时,取
\[\Delta x = -\alpha \nabla f(x)\]
即在梯度相反的方向函数值降低的最快。此时函数的降低值为:
\[ (\nabla f(x))^T \Delta x = -\| \nabla f(x)\|\|\Delta x\| = - \alpha \| \nabla f(x)\|^2 \]
只要梯度不为\(0\),往梯度的反方向走函数值必定是降低的。直接用可能会有问题,由于\(x+\Delta x\)可能会超出\(x\)的邻域范围以外,此时是不能忽略泰勒展开中的二次及以上的项的,所以步伐不能太大。
通常设:
\[\Delta x = -\alpha \nabla f(x)\]
其中\(\alpha\)为一个接近于\(0\)的正数,称为步长,由人工设定,用于保证\(x+\Delta x\)在x的邻域内,从而能够忽略泰勒展开中二次及更高的项,则有:
\[ (\nabla f(x))^T \Delta x = -\| \nabla f(x)\|\|\Delta x\| = - \alpha \| \nabla f(x)\|^2 < 0 \]
此时,\(x\)的迭代公式是:
\[x_{k+1} = x_k - \alpha \nabla f(x_k)\]
只要没有到达梯度为\(0\)的点,则函数值会沿着序列\(x_{k}\)递减,最终会收敛到梯度为\(0\)的点,这就是梯度降低法。
迭代终止的条件是函数的梯度值为\(0\)(实际实现时是接近于\(0\)),此时认为已经达到极值点。注意咱们找到的是梯度为\(0\)的点,这不必定就是极值点,后面会说明。app
初始值的设定
通常的,对于不带约束条件的优化问题,咱们能够将初始值设置为0,或者设置为随机数,对于神经网络的训练,通常设置为随机数,这对算法的收敛相当重要。机器学习
学习率的设定
学习率设置为多少,也是实现时须要考虑的问题。最简单的,咱们能够将学习率设置为一个很小的正数,如0.001。另外,能够采用更复杂的策略,在迭代的过程当中动态的调整学习率的值。好比前1万次迭代为0.001,接下来1万次迭代时设置为0.0001。函数
\(argmin\frac{1}{2}[(x_{1}+x_{2}-4)^2 + (2x_{1}+3x_{2}-7)^2 + (4x_{1}+x_{2}-9)^2]\)性能
如下只是为了演示计算过程,便于理解梯度降低,代码仅供参考。更好的代码我将在之后的文章中给出。学习
# 原函数 def argminf(x1, x2): r = ((x1+x2-4)**2 + (2*x1+3*x2 - 7)**2 + (4*x1+x2-9)**2)*0.5 return r # 全量计算一阶偏导的值 def deriv_x(x1, x2): r1 = (x1+x2-4) + (2*x1+3*x2-7)*2 + (4*x1+x2-9)*4 r2 = (x1+x2-4) + (2*x1+3*x2-7)*3 + (4*x1+x2-9) return r1, r2 # 梯度降低算法 def gradient_decs(n): alpha = 0.01 # 学习率 x1, x2 = 0, 0 # 初始值 y1 = argminf(x1, x2) for i in range(n): deriv1, deriv2 = deriv_x(x1, x2) x1 = x1 - alpha * deriv1 x2 = x2 - alpha * deriv2 y2 = argminf(x1, x2) if y1 - y2 < 1e-6: return x1, x2, y2 if y2 < y1: y1 = y2 return x1, x2, y2 # 迭代1000次结果 gradient_decs(1000) # (1.9987027392533656, 1.092923742270406, 0.4545566995437954)