深度学习中的优化
深度学习中的优化
听着挺高端的是不,还听着挺难的,确实,单单来讲里边的数学确实有点子麻烦,但是我们有了一些计算机工具,我们就不需要从数学推导角度去深度的理解了。
优化与凸优化
无论如何我们都是无法避开讲一些数学的,(虽然俺的数学是个二把刀)我们尽力少用一些抽象的数学表达来描述清楚,但也会有一些朴素且不严谨(对于理解是无伤大雅的)
优化
首先优化是什么呢?为了变得更好而采取的动作。优化实际上和规划是很像的(其实优化是规划的子集),(有一说一现在高考连线性规划都不学了)都有某个目标函数和某些约束模型,最终得到最佳的参数组合。举个例子,我们知道找一个函数的最值我们需要用导数来寻找,但是可不可以通过某种搜索的方法穷举出最值呢?就拿一个二次函数来讲:
右上角的点为随机选择的初始点,我们希望找一个下坡路从而找到一个低点(是不是有点贪心的感觉,所以他也会有贪心出现的错误),然后逐渐逼近最低点,用数学的方式表达: \[ argmin \ x^2 \] 没有约束条件,用到的求解方法表现为: \[ x_{t}=x_{t-1}-\eta \ \omega \] 其中各种符号具体是什么意思,稍后我们在解释,我们先来看另一种情况:
我们依旧采用原来的算法,但是会发现:诶好像这个不是最小值,这也就是我们刚才提到的贪心的弊端,他没有全局视野,就很容易搜索到局部(极值)而不是最值,这也是我们接下来要面对的挑战之一。
回到深度学习上来,深度学习中的优化主要体现在使模型的loss函数的值尽可能小,从而训练得到尽可能完美的参数。同时我们会认识到不是所有的机器学习都涉及优化,比如一些有解析解的机器学习方法我们就不需要进行优化。
凸优化
至于凸优化我们就迅速略过,凸优化涉及的数学知识就有点难理解了,挑两个重点公式和概念提一嘴:
凸集:对于一个集合\(X\),任意一个\(a,b\in X\),都有\(\lambda a+(1-\lambda)b \in X\),其中\(\lambda \in [0,1]\),那么称集合\(X\)为凸的。
用人话来说的话就是一个集合中两点的连线扔在集合中,为什么是这样表示呢,我们设想一个点集,那么\(a,b\)最佳的表达方式其实一个向量对吧,是的,当我们进入深度学习后要习惯以向量视角看问题,那么我们对上面的公式进行整型: \[ \lambda a+(1-\lambda)b=\lambda(a-b)+b \] 这样我们发现这刚好是一条线段上的所有点
凸函数:这里我们就按照高数中的凸函数进行理解即可 \[ f(\lambda x+(1-\lambda)x^{'}) \leq \lambda f(x)+(1-\lambda)f(x^{'}) \] p.s.这里还有个小故事,大家感兴趣可以去查一下,国内和国外的数学教材凸凹定义其实是反的
性质
性质大多是比较重要的,我们就记一下就好了(就别证明了罢)
- 凸优化中局部极小值即为全局极小值
- 凸优化下的约束可以转化为拉格拉日乘子法
- 也可以采用罚函数法或者投影法
梯度下降
数学概念
先回忆一些数学概念,梯度,一种向量,是各个轴上的偏导数,函数沿着梯度方向变化率最大。
然后我们来回到一元函数,这很有有益于理解:
\[ x_{t}=x_{t-1}-\eta \ f^{'}(x) \] 现在我们可以解释之前的公式了,其中\(\eta\)被称为学习率,表现为梯度下降的步长,而这个一阶导数提供了梯度方向,这里可以自己分类讨论一下(当其大于0?当其小于0?)就能体会到这个数学机器是如何工作的了。
代码示例
虽然之前是通过numpy
手搓过梯度下降的方法的,但是现在pytorch
提供了更多的方便。
1 | from torch.autograd import Variable |
ok,问题我们之前大部分已经提过了,比如什么局部最优啊,现在我们要抛出一个新问题,学习率能瞎写吗?答案是也许能,但不好,所以我们要研究怎么搞好学习率。下面是一个瞎写学习率的情况(初始点为10):
学习率
再研究学习率之前,我们要先提一嘴多维的梯度下降,其实公式也没啥: \[ x_{t}=x_{t-1}-\eta \nabla f(x) \] 梯度一般由一个雅克比矩阵给出,其为一个向量。
Hessian矩阵\(H\),我想应该不需要介绍了,这里我们不去管什么泰勒展开的推导,我们仅仅记住结论:
牛顿法:最优的梯度下降(吗?),\(x_{t}=x_{t-1}-H^{-1} \nabla f(x)\),显然是有问题的,存储Hessian矩阵可能要花费大量内存,在凸优化上牛顿法非常好用
预处理:\(x_{t}=x_{t-1}-\eta \ diag(H)^{-1} \nabla f(x)\),相当于在每个方向(变量)上选择了不同的学习率
随机梯度下降
数学理论
这个就是我们常用的SGD了,其核心也就是一个用抽样来估计总体,从而降低运算所需的时间,假设使用梯度下降法,样本记做\(x_i\),那么计算总的loss为: \[ Loss(X)=\frac{1}{n}\sum_{i=1}^{n}{Loss(x_i)} \] 其中我们需要计算梯度: \[ \nabla Loss(X)=\frac{1}{n}\sum_{i=1}^{n}{\nabla Loss(x_i)} \] 于是我们有了一个朴实无华的想法,那我直接少算几个不就行了()你别说,你还真别说,还真是这么搞得。
随机梯度下降就是均匀随机采样一个\(x_i\),然后直接算一个$Loss(x_i) \(,然后扔进梯度下降公式。\)$ x_{t+1}=x_t- Loss(x_i) \[ 那这么搞是否有点问题呢?只算一个有没有可能算的不准呢?当然有可能,但是在统计学上其实问题不大: \] iLoss(x_i)= {i=1}^{n}{Loss(x_i)}=Loss(X) $$
代码示例
1 | import torch |
动态学习率
这块直接摆一下公式,俺表示这就是炼丹的玄学部分了:
- $(t)=i if t_i <t <t{t+1} $分段常数
- \(\eta (t)=\eta_0 \cdot e^{-\lambda t}\)指数衰减
- \(\eta (t)=\eta_0 \cdot (\beta t+1)^{-\alpha}\)多项式衰减
总结
喵的写太多了,到这里基本上就可以入门了,也有一定的数学基础了,但是我不认为去仔细研究数学的一些严谨的收敛证明是有用的。后边就要靠自己去看一看啦,比如什么Ada,Adam,小批量随机梯度下降之类的。