点击上方“小白学视觉”,选择加"星标"或“置顶”
重磅干货,第一时间送达
一、各类优化算法简述
随机梯度更新:
对于普通的sgd,不细讲述。
主要需要注意的是,在深度学习中比较常用的是mini-batch 梯度下降
动量(Momentum)更新:
该方法可以看成是从物理角度上对于最优化问题得到的启发。也就是物理观点建议梯度只是影响速度,然后速度再影响位置。损失值可以理解为是山的高度(因此高度势能是,所以有
)。用随机数字初始化参数等同于在某个位置给质点设定初始速度为0。这样最优化过程可以看做是模拟参数向量(即质点)在地形上滚动的过程。其代码如下:
# 动量更新
v = mu * v - learning_rate * dx # 与速度融合(1)
x += v # 与位置融合
注意: 在这里引入了一个初始化为0的变量v和一个超参数mu,物理观点建议梯度只是影响速度,然后速度再影响位置。 从公式可以看出,我们通过重复地增加梯度项来构造速度,那么随着迭代次数的增加,速度会越来越快,这样就能够确保momentum技术比标准的梯度下降运行得更快; 同时μ的引入,一个典型的设置是刚开始将动量设为0.5而在后面的多个周期(epoch)中慢慢提升到0.99。 保证了在接近谷底时速度会慢慢下降,最终停在谷底,而不是在谷底来回震荡。 所以,由于在公式(1)中,两项都会发生变化,所以导致的后果就是V先增大,后减小。
Nesterov动量更新:
和上面的动量(Momentum)更新差不多,但是,不同的在于,当参数向量位于某个位置x时,观察上面的动量更新公式可以发现,动量部分(忽视带梯度的第二个部分)会通过mu * v稍微改变参数向量。因此,如果要计算梯度,那么可以将未来的近似位置x + mu * v看做是“向前看”,这个点在我们一会儿要停止的位置附近。因此,计算x + mu * v的梯度而不是“旧”位置x的梯度就有意义了。
代码实现如下:
x_ahead = x + mu * v
# 计算dx_ahead(在x_ahead处的梯度,而不是在x处的梯度)
v = mu * v - learning_rate * dx_ahead
x += v
等价与:
v_prev = v # 存储备份
v = mu * v - learning_rate * dx # 速度更新保持不变
x += -mu * v_prev + (1 + mu) * v # 位置更新变了形式
![2ab2355c99909f6205e4ad3ee0850190.jpeg](https://img-blog.csdnimg.cn/img_convert/2ab2355c99909f6205e4ad3ee0850190.jpeg)
上图对于Nesterov动量表示既然我们知道动量将会把我们带到绿色箭头指向的点,我们就不要在原点(红色点)那里计算梯度了。使用Nesterov动量,我们就在这个“向前看”的地方计算梯度,这样的方式也使得其比动量的方式更快收敛
自适应算法
前面的方法对学习率都是进行全局地操作,并且对所有的参数都是一样的。而学习率调参是很耗费计算资源的过程,所以很多工作投入到发明能够适应性地对学习率调参的方法,甚至是逐个参数适应学习率调参。很多这些方法依然需要其他的超参数设置,但是其观点是这些方法对于更广范围的超参数比原始的学习率方法有更良好的表现。下面就介绍一些在实践中可能会遇到的常用适应算法:
Adagrad:
Adagrad是一个由Duchi等提出的适应性学习率算法,其主要的方式是当接收到高梯度值的权重更新的效果被减弱,而接收到低梯度值的权重的更新效果将会增强。下面是实现代码:
# 假设有梯度和参数向量x
cache += dx**2
x += - learning_rate * dx / (np.sqrt(cache) + eps)
这里的变量cache的尺寸和梯度矩阵的尺寸是一样的,还跟踪了每个参数的梯度的平方和。 这个一会儿将用来归一化参数更新步长,归一化是逐元素进行的。 需要注意是平方根的操作非常重要,如果去掉,算法的表现将会糟糕很多。 用于平滑的式子eps(一般设为1e-4到1e-8之间)是防止出现除以0的情况。 Adagrad的一个缺点是,在深度学习中单调的学习率被证明通常过于激进且过早停止学习。
RMSprop:
是一个非常高效,但没有公开发表的适应性学习率方法。绝大部分人在他们的论文中都引用自Geoff Hinton的Coursera课程的第六课的第29页PPT。这个方法用一种很简单的方式修改了Adagrad方法,让它不那么激进,单调地降低了学习率。具体说来,就是它使用了一个梯度平方的滑动平均:
cache = decay_rate * cache + (1 - decay_rate) * dx**2
x += - learning_rate * dx / (np.sqrt(cache) + eps)
在上面的代码中,decay_rate是一个超参数,常用的值是[0.9,0.99,0.999]。 其中x+=和Adagrad中是一样的,但是cache变量是不同的。 因此,RMSProp仍然是基于梯度的大小来对每个权重的学习率进行修改,这同样效果不错。 但是和Adagrad不同,其更新不会让学习率单调变小。
Adam:
有点像RMSProp+momentum,效果比RMSProp稍好,而其简化的代码是下面这样:
m = beta1*m + (1-beta1)*dx
v = beta2*v + (1-beta2)*(dx**2)
x += - learning_rate * m / (np.sqrt(v) + eps)
注意这个更新方法看起来真的和RMSProp很像,除了使用的是平滑版的梯度m,而不是用的原始梯度向量dx。论文中推荐的参数值eps=1e-8, beta1=0.9, beta2=0.999。在实际操作中,我们推荐Adam作为默认的算法,一般而言跑起来比RMSProp要好一点。但是也可以试试SGD+Nesterov动量。然后注意完整的Adam更新算法也包含了一个偏置(bias)矫正机制,因为m,v两个矩阵初始为0,在没有完全准确更新之前存在偏差。
建议读者可以阅读论文查看细节。 比如Adam算法,很多细节,其论文讲解的很清晰的!
下面是图示各个优化算法的比较:来自CS231n,本是动图,详见CS231n Convolutional Neural Networks for Visual Recognition
![7c6d69802b23e1fd67a56fac29f2263c.jpeg](https://img-blog.csdnimg.cn/img_convert/7c6d69802b23e1fd67a56fac29f2263c.jpeg)
二、总结神经网络:
简略总结一下神经网络:
对于数据处理和初始化:预处理操作是很有必要的,特别是图像处理的时候,使用标准差为
的高斯分布来初始化权重,不用全零初始化。
对于神经网络的架构选择:激活函数推荐先使用ReLU,当然,其他激活函数也需要尝试的,神经网络不能太宽,深度要合适,适当的使用BN,Dropout等Trick
对于神经网络的优化:更新方法的方式属于调参的范畴,需要经验,对于自适应,Adam方法还是不错的。若是自己实现反向梯度传播,需要注意要利用小批量数据对实现进行梯度检查。
最后,神经网络有各种Trick,也需要各种调参经验,多实践,多总结,需要我们的学习探索,加油!
好消息!
小白学视觉知识星球
开始面向外开放啦👇👇👇
下载1:OpenCV-Contrib扩展模块中文版教程
在「小白学视觉」公众号后台回复:扩展模块中文教程,即可下载全网第一份OpenCV扩展模块教程中文版,涵盖扩展模块安装、SFM算法、立体视觉、目标跟踪、生物视觉、超分辨率处理等二十多章内容。
下载2:Python视觉实战项目52讲
在「小白学视觉」公众号后台回复:Python视觉实战项目,即可下载包括图像分割、口罩检测、车道线检测、车辆计数、添加眼线、车牌识别、字符识别、情绪检测、文本内容提取、面部识别等31个视觉实战项目,助力快速学校计算机视觉。
下载3:OpenCV实战项目20讲
在「小白学视觉」公众号后台回复:OpenCV实战项目20讲,即可下载含有20个基于OpenCV实现20个实战项目,实现OpenCV学习进阶。
交流群
欢迎加入公众号读者群一起和同行交流,目前有SLAM、三维视觉、传感器、自动驾驶、计算摄影、检测、分割、识别、医学影像、GAN、算法竞赛等微信群(以后会逐渐细分),请扫描下面微信号加群,备注:”昵称+学校/公司+研究方向“,例如:”张三 + 上海交大 + 视觉SLAM“。请按照格式备注,否则不予通过。添加成功后会根据研究方向邀请进入相关微信群。请勿在群内发送广告,否则会请出群,谢谢理解~