Stanford CS230深度学习(三)调参、正则化和优化算法
lecture3中主要讲了如何构建一个ML/DL任务,主要包括:选择问题、获得数据、设计模型、训练模型、测试模型、部署以及维护。然后coursera中的课程主要讲实际的应用例如调参、正则化等,以及几个DL常用优化算法。
目录
- 调参
- 正则化
- 1、Frobenius范数/ L2范数
- 2、dropout 随机失活
- 3、early stop 早停法
- 4、data augmentation 数据增强
- 优化算法
- 1、Mini-batch Gradient Descent 小批量梯度下降
- EWMA(Exponentially Weighted Moving-Average) 指数加权滑动平均
- 2、Momentum 动量法
- 3、RMSprop(Root Mean Square propagation) 均方根传递
- 4、Adam(Adaptive Moment Estimation) 自适应矩估计
- 实际效果
- 代码
调参
深度学习的超参数一般包括:网络的深度(层数)LLL、隐层的大小n[l]n^{[l]}n[l]、学习率α\alphaα、迭代次数、以及正则化参数等。
一般把数据集分为:训练集(Train set)、开发/验证集(Dev set)、测试集(Test set)。
在大数据量下一般开发集和测试集所占比例都很小;
调参过程是在训练集上训练出模型,然后再开发集上看效果来调整具体的超参数。高偏差(high bias)/ 欠拟合(underfitting):
更大、更深的网络;
增加迭代次数 / 尝试其他的优化算法;
尝试其他的神经网络模型高方差(high variance)/ 过拟合(overfitting):
更多的数据;
采取正则化手段;
尝试其他的神经网络模型初始参数中心化:
随机初始化参数用来打破对称性,确保不同的隐藏单元可以学习不同的东西;合适的参数初始化可以在一定程度上缓解梯度消失/梯度爆炸(vanishing / exploding gradients)的问题。
随机初始化不要过大,保证随机生成的参数满足0为均值,2n[l−1]\sqrt{2\over n^{[l-1]}}n[l−1]2或1n[l−1]\sqrt{1\over n^{[l-1]}}n[l−1]1为标准差的正态分布。
He初始化适用于ReLu,即初始化需要乘上2n[l−1]\sqrt{2\over n^{[l-1]}}n[l−1]2;
Xavier初始化的乘数为1n[l−1]\sqrt{1\over n^{[l-1]}}n[l−1]1输入特征标准化(Normalizing inputs)
让输入特征满足0为均值,1为标准差的正态分布;
这样做可以加速训练,让代价函数优化起来更简单快速。
但值得注意的是,训练集和测试集需要做同样的归一化操作,即用同样的方法调整测试集,而不是在训练集和测试集上分别进行归一化处理。
正则化
1、Frobenius范数/ L2范数
- ∥W∥F2=∥W∥22=∑i∑jwi.j2\|W\|^2_F=\|W\|^2_2=\sum_i\sum_j w_{i.j}^2∥W∥F2=∥W∥22=∑i∑jwi.j2
- L2正则化依赖于这样一个假设,即具有小权重的模型比具有大权重的模型更简单。因此,通过惩罚成本函数中权重的平方值,可以对权重进行压缩J=1n∑i=1nℓ(y^i,yi)+λ2n∑l=1L∥W∥F2J={1\over n}\sum_{i=1}^n\ell(\hat y_i,y_i)+{\lambda\over 2n}\sum_{l=1}^L\|W\|^2_FJ=n1i=1∑nℓ(y^i,yi)+2nλl=1∑L∥W∥F2
- 权重衰减(weight decay):导致在每次迭代中梯度下降使权重变小的一种正则化技术(如L2正则化)。
因为在反向传播时,会多出一项关于权重的导数项(根据链式求导法则就可以求出来)
dW3 = 1./n * np.dot(dZ3, A2.T) + lambd/n * W3
这样在后面更新权重时需要多减去一项,即W[l]=W[l]−α⋅dW[l]=W[l]−α⋅(dWo[l]+λnW[l])=(1−αλn)W[l]−α⋅dWo[l]\begin{aligned}W^{[l]}&=W^{[l]}-\alpha \cdot dW^{[l]}\\ &=W^{[l]}-\alpha \cdot (dW^{[l]}_o+{\lambda\over n}W^{[l]})\\ &=(1-{\alpha\lambda\over n})W^{[l]}-\alpha \cdot dW^{[l]}_o\end{aligned}W[l]=W[l]−α⋅dW[l]=W[l]−α⋅(dWo[l]+nλW[l])=(1−nαλ)W[l]−α⋅dWo[l]
其中dWo[l]dW^{[l]}_odWo[l]表示没有正则化之前的导数。因此,由于(1−αλn)<1(1-{\alpha\lambda\over n})<1(1−nαλ)<1,所以每次迭代更新参数都会让原本的参数衰减之后再进行更新。
2、dropout 随机失活
- dropout是随机地消除一些神经单元,这种消除并不是真正意义上拿掉这些神经单元,只是暂时性的让他们失活,这样可以在某种程度上使得整个网络变小,而且会起到正则化的作用。
- 下面是反向随机失活(inverted dropout)的做法:
# 在前向传播时:
D1 = np.random.randn(A1.shape[0], A1.shape[1]) < keep_prob # 设置一个阈值
A1 = A1 * D1 # 超过阈值的位置就dropout
A1 = A1 / keep_prob # 使得期望值不变# 在反向传播是需要进行相同的操作:
dA1 = np.dot(W2.T, dZ2) * D1 / keep_prob # 这里的D1要一样
为什么能起到正则化的作用?
使用dropout可以使得下一层的神经元不能完全依赖上层的某一个神经元(因为他们可以随机失活),这样减少了对单个神经元的信任程度,所以只能分散权重(shrink weight),这样就起到了类似L2正则化的作用。缺点:
使用dropout后由于每次迭代都会随机地移除一些神经元,导致代价函数并不是每次迭代后都会下降,尽管总的趋势还是随着迭代次数增加而下降,所以不能根据这样的调试工具来直接地评价优化算法的性能以及学习率的调整。
可以让每层的留存率都一样,或者不一样。一般说来,神经元数量多的隐层可以设置一个较小的留存率。
3、early stop 早停法
- 训练误差随着迭代次数增加而持续减小,开发集误差则先减少后增加,绘制出如下过程,然后选择在两种误差都比较小的阶段选择停止训练,这样可以起到防止过拟合的效果。
- 当迭代过程刚开始时,参数接近0,因为随机初始化时,它的值可能都是较小的随机值,而在迭代过程和训练过程中的值会变得越来越大,所以early stop做就是在中间点停止迭代过程,这样就得到一个值中等大小的∥W∥F2\|W\|^2_F∥W∥F2。这样来看,early stop作用与L2正则化类似,都是偏向参数范数较小的神经网络。
- 缺点:
它需要在“降低代价”和“防止过拟合”之间平衡,不能单独对以上某一目标进行优化,若想要降低代价,则需要牺牲泛化性能,若想要泛化性能较好则代价值不会很低。而其他正则化手段例如L2正则化是可以将两项任务进行分开操作的。
4、data augmentation 数据增强
- 想增加数据量有时很难做到,但是可以通过旋转、缩放、尺度变换、扭曲、噪声扰动等手段,可以对图片进行变换,以达到人工增加训练集的大小的目的。
优化算法
1、Mini-batch Gradient Descent 小批量梯度下降
批量梯度下降(GD)就是将所有的训练样本全都进行一次计算,得到参数的梯度来更新一次;而随机梯度下降(SGD)则是只随机的选取其中一个样本进行计算,来更新一次参数;小批量梯度下降则是介于两者中间,每次迭代只随机选取部分(大于1且小于n)的样本来进行计算,然后更新一次参数,这样将所有的样本全部用完一次就称为一次epoch。
具体做法是:
1、先随机打乱训练集,得到一个重排的训练集
2、给定mini_batch_size,第一次迭代选取前mini_batch_size的样本来更新参数,第二次迭代选择第mini_batch_size到2*mini_batch_size的样本来更新参数,这样依次直到最后一个batch中样本数量小于或等于mini_batch_size,这样就完成了一次epoch优点:
1、与GD相比,这样可以不用一次就遍历所有的数据,特别是在数据量很大时,可以加速优化过程
2、与SGD相比,可以利用向量化并行加速,也在一定程度上减少了随机性
EWMA(Exponentially Weighted Moving-Average) 指数加权滑动平均
Vt=βVt−1+(1−β)θt,0<β<1V_t=\beta V_{t-1}+(1-\beta)\theta_t,\ \ 0<\beta<1Vt=βVt−1+(1−β)θt, 0<β<1
其中VtV_tVt表示到ttt为止的累积量,θt\theta_tθt为ttt时刻的量。上式表示到当前时刻的累积量是由之前的累积量与当前量加权求和得到。将上式展开得到:Vt=(1−β)θt+β[(1−β)θt−1+βVt−2]=(1−β)θt+β(1−β)θt−1+β2[(1−β)θt−2+βVt−3]=(1−β)θt+β(1−β)θt−1+β2(1−β)θt−2+⋯+βt−1(1−β)θ1+βtV0\begin{aligned} V_t&=(1-\beta)\theta_t+\beta[ (1-\beta)\theta_{t-1}+\beta V_{t-2}]\\ &=(1-\beta)\theta_t+\beta(1-\beta)\theta_{t-1}+\beta^2[(1-\beta)\theta_{t-2}+\beta V_{t-3}]\\ &=(1-\beta)\theta_t+\beta(1-\beta)\theta_{t-1}+\beta^2(1-\beta)\theta_{t-2}+\cdots+\beta^{t-1} (1-\beta)\theta_{1}+\beta^tV_0\\ \end{aligned}Vt=(1−β)θt+β[(1−β)θt−1+βVt−2]=(1−β)θt+β(1−β)θt−1+β2[(1−β)θt−2+βVt−3]=(1−β)θt+β(1−β)θt−1+β2(1−β)θt−2+⋯+βt−1(1−β)θ1+βtV0由于V0=0V_0=0V0=0,所以VtV_tVt就是前面ttt次量的一个加权平均,而且这个权重是指数衰减的,离当前时间越近权重越大。偏差修正(bias correction)解决冷启动问题
一般来说,β⩾0.9\beta\geqslant0.9β⩾0.9,如果取β=0.9\beta=0.9β=0.9,由于V0=0V_0=0V0=0,所以V1=0.1θ1,V2=0.09θ1+0.1θ2,…V_1=0.1\theta_1, V_2=0.09\theta_1+0.1\theta_2, \dotsV1=0.1θ1,V2=0.09θ1+0.1θ2,…,这样导致刚开始的值都会特别小,随着ttt的增加才会慢慢接近正常值。因此为了避免这种冷启动问题,需要对每个权重都进行偏差修正,即每个权重乘上一个修正系数11−βt1\over 1-\beta^t1−βt1
从这个系数可以看出,当ttt很小时βt\beta^tβt还是接近于1,例如t=2t=2t=2时β2=0.81\beta^2=0.81β2=0.81,那么乘上这个系数就相当于乘上一个比较大的数,将比较小的初始值放大;而当ttt越来越大时,βt\beta^tβt趋于0,这时这个系数趋于1,基本上就不进行放大了,就和正常的值差不多。
但是为什么是11−βt1\over 1-\beta^t1−βt1呢?因为Vt=(1−β)θt+β(1−β)θt−1+β2(1−β)θt−2+⋯+βt−1(1−β)θ1V_t=(1-\beta)\theta_t+\beta(1-\beta)\theta_{t-1}+\beta^2(1-\beta)\theta_{t-2}+\cdots+\beta^{t-1} (1-\beta)\theta_{1}Vt=(1−β)θt+β(1−β)θt−1+β2(1−β)θt−2+⋯+βt−1(1−β)θ1这ttt个权重构成一个等比数列,求和得到权重之和为1−βt1-\beta^t1−βt,因此,修正系数取为11−βt1\over 1-\beta^t1−βt1,这样VtV_tVt就是所有θ\thetaθ的加权求和了(权重之和为1)。近似解释:
在数学上有limn→∞(1−1n)n=1e≈0.3679\lim_{n\to\infin}(1-{1\over n})^{n}={1\over e}\approx0.3679limn→∞(1−n1)n=e1≈0.3679
令n=11−βn={1\over 1-\beta}n=1−β1,则(1−1n)n=(1−(1−β))n=β11−β(1-{1\over n})^{n}=(1-(1-\beta))^n=\beta^{1\over 1-\beta}(1−n1)n=(1−(1−β))n=β1−β1,当β→1\beta\to1β→1时,n→∞n\to\infinn→∞,所以β11−β→1e\beta^{1\over 1-\beta}\to{1\over e}β1−β1→e1
这意味着当β\betaβ的次数等于11−β{1\over 1-\beta}1−β1时,θ\thetaθ的权重就已经衰减为(1−β)(1-\beta)(1−β)的大概1/3了。
因此,若将1e≈0.3679{1\over e}\approx0.3679e1≈0.3679当做阈值,即小于它就认为权重过小可以忽略不计,那么VtV_tVt就相当于只是对最近的11−β{1\over 1-\beta}1−β1个θ\thetaθ值进行加权平均。
所以当β=0.9\beta=0.9β=0.9时,11−0.9=10{1\over 1-0.9}=101−0.91=10,VtV_tVt近似是对最近的10个θ\thetaθ值进行加权平均。
所以当β=0.98\beta=0.98β=0.98时,11−0.98=50{1\over 1-0.98}=501−0.981=50,VtV_tVt近似是对最近的50个θ\thetaθ值进行加权平均。
2、Momentum 动量法
- 动量法在梯度下降中加入了EWMA,它用梯度的指数加权滑动平均,而不仅仅是梯度来更新参数。
- 这样做可以减少由梯度的波动导致的参数优化过慢,因为使用了指数加权滑动平均,所以更新参数时参考的是以前多个梯度的方向,而不仅仅是本次更新的这个梯度的方向,即使是本次梯度方向有些偏移,但是经过加权平均,那些震荡的方向有一部分相互抵消,而保持主要的优化方向一致,所以动量法会比梯度下降更优。
- 具体的做法是:
先初始化EWMA为零(矩阵)
在每次迭代时,计算当前梯度的EWMA,即:VdW=βVdW+(1−β)dWVdb=βVdb+(1−β)dbV_{dW}=\beta V_{dW}+(1-\beta)dW\\V_{db}=\beta V_{db}+(1-\beta)dbVdW=βVdW+(1−β)dWVdb=βVdb+(1−β)db然后用梯度的EWMA来更新参数,即:W=W−αVdWb=b−αVdbW=W-\alpha V_{dW}\\b=b-\alpha V_{db}W=W−αVdWb=b−αVdb - β\betaβ一般取0.9,且一般来说动量法不需要做偏差修正。
3、RMSprop(Root Mean Square propagation) 均方根传递
RMSprop也采用了EWMA,但与动量法不同的是,它是对梯度的平方求指数加权滑动平均,然后用梯度除以这个EWMA的均方根来更新参数。
我个人结合参考资料从两个方面来理解RMSprop:
1、从梯度的角度来理解:RMSprop计算了梯度平方的指数加权滑动平均,对于震荡较大的梯度方向,它的平方的EWMA也比较大,我们希望在更新的时候尽可能减少它对于优化方向的影响,所以除上较大的EWMA的均方根后这个梯度变小;而对于震荡较小的梯度方向,则它的平方的EWMA也较小,所以除上较小的EWMA的均方根后放大了这个梯度对于优化方向的指导性,所以能保持较为一致的优化方向。
2、从学习率的角度来理解:RMSprop适应地调整所有参数的学习率,它将参数的学习率缩放反比于其所有历史梯度平方的指数加权滑动平均的平方根。这样,如果参数的梯度较大,那么这个参数的学习率将下降较大,而梯度小的参数在学习率上有相对较小的下降,从而达到加速的目的,其净效果是在参数空间中更为平缓的倾斜方向会取得更大的进步。具体的做法是:
先初始化EWMA为零(矩阵)
在每次迭代时,计算当前梯度平方的EWMA,即:SdW=βSdW+(1−β)dW2Sdb=βSdb+(1−β)db2S_{dW}=\beta S_{dW}+(1-\beta)dW^2\\S_{db}=\beta S_{db}+(1-\beta)db^2SdW=βSdW+(1−β)dW2Sdb=βSdb+(1−β)db2然后除以梯度的EWMA的均方根(这里对应第一种理解方式),即:W=W−αdWSdW+ϵb=b−αdbSdb+ϵW=W-\alpha {dW\over \sqrt{S_{dW}+\epsilon}}\\b=b-\alpha {db\over \sqrt{S_{db}+\epsilon}}W=W−αSdW+ϵdWb=b−αSdb+ϵdb为了防止分母为零,ϵ\epsilonϵ是为了维持数值稳定性而添加的常数,一般取值为1e-6(如果放在根号外面就取1e-8)
4、Adam(Adaptive Moment Estimation) 自适应矩估计
- Adam结合了动量法和RMSprop,并且添加了偏差修正。
- 具体的做法是:
先初始化EWMA为零(矩阵)
在迭代ttt时,计算当前梯度以及梯度平方的EWMA,即:VdW=β1VdW+(1−β1)dW,Vdb=β1Vdb+(1−β1)dbSdW=β2SdW+(1−β2)dW2,Sdb=β2Sdb+(1−β2)db2V_{dW}=\beta_1 V_{dW}+(1-\beta_1)dW,\ V_{db}=\beta_1 V_{db}+(1-\beta_1)db\\S_{dW}=\beta_2 S_{dW}+(1-\beta_2)dW^2,\ S_{db}=\beta_2 S_{db}+(1-\beta_2)db^2VdW=β1VdW+(1−β1)dW, Vdb=β1Vdb+(1−β1)dbSdW=β2SdW+(1−β2)dW2, Sdb=β2Sdb+(1−β2)db2然后计算修正后的EWMA:VdWcorrected=VdW/(1−β1t),Vdbcorrected=Vdb/(1−β1t)SdWcorrected=SdW/(1−β2t),Sdbcorrected=Sdb/(1−β2t)V_{dW}^{corrected}=V_{dW}/(1-\beta_1^t),\ V_{db}^{corrected}=V_{db}/(1-\beta_1^t)\\S_{dW}^{corrected}=S_{dW}/(1-\beta_2^t),\ S_{db}^{corrected}=S_{db}/(1-\beta_2^t)VdWcorrected=VdW/(1−β1t), Vdbcorrected=Vdb/(1−β1t)SdWcorrected=SdW/(1−β2t), Sdbcorrected=Sdb/(1−β2t)用修正后的EWMA更新参数,即:W=W−αVdWcorrectedSdWcorrected+ϵb=b−αVdbcorrectedSdbcorrected+ϵW=W-\alpha {V_{dW}^{corrected}\over \sqrt{S_{dW}^{corrected}+\epsilon}}\\b=b-\alpha {V_{db}^{corrected}\over \sqrt{S_{db}^{corrected}+\epsilon}}W=W−αSdWcorrected+ϵVdWcorrectedb=b−αSdbcorrected+ϵVdbcorrected - RMSProp可能在训练初期有很高的偏置,而Adam通常被认为对超参数的选择相当鲁棒。一般来说,β1\beta_1β1取0.9,β2\beta_2β2取0.999,ϵ\epsilonϵ取1e-6。
实际效果
在对比了Batch Gradient Descent、Mini-batch Gradient Descent、Momentum、RMSprop以及Adam在数据集上的效果之后,得到他们的精度分别为0.66、0.796667、0.796667、0.94以及0.94,收敛速度反应在下图(第一幅是Batch Gradient Descent,也就是mini_batch_size=n时):
由于小批量的随机性导致损失函数的震荡,这里我把每100次或者每1000次epoch的损失改成这100次或者这1000次epoch的平均损失,然后得到下图:
可以看出在这个数据集上,动量法作用基本上可以忽略不计,作业中也指出在学习率较小且数据集较简单时,单纯的动量法与普通参数更新效果差异不大。而RMSprop带来的效果十分明显,而且Adam效果好也是由于RMSprop所带来的,他们俩之间有些微区别是因为在RMSprop中没有使用偏差修正,这似乎让RMSprop在实际表现中下降得更快,但是从震荡的代价图中看出Adam在迭代初期似乎更加稳健一些。
代码
在核对我的代码时发现GitHub上的那个同学在写random_mini_batches函数的时候把打乱后的数据和源数据用混了,所以导致那个作业结果有错误,根据作业最后的描述来说我这里是正确的。
import numpy as np
import matplotlib.pyplot as plt
import scipy.io
import math
import sklearn
import sklearn.datasetsfrom opt_utils import load_params_and_grads, initialize_parameters, forward_propagation, backward_propagation
from opt_utils import compute_cost, predict, predict_dec, plot_decision_boundary, load_dataset
from testCases import *%matplotlib inline
plt.rcParams['figure.figsize'] = (7.0, 4.0) # set default size of plots
plt.rcParams['image.interpolation'] = 'nearest'
plt.rcParams['image.cmap'] = 'gray'# 导入数据
train_X, train_Y = load_dataset()'''批量梯度下降'''def update_parameters(parameters, grads, learning_rate):L = len(parameters) // 2for i in range(1, L+1):parameters['W'+str(i)] += - learning_rate * grads['dW'+str(i)]parameters['b'+str(i)] += - learning_rate * grads['db'+str(i)]return parametersdef model_gb(X, Y, learning_rate = 0.0007, num_iterations = 10000, print_cost = True):grads = {}costs = [] # to keep track of the lossm = X.shape[1] # number of exampleslayers_dims = [X.shape[0], 5, 2, 1]# Initialize parameters dictionary.parameters = initialize_parameters(layers_dims)# Loop (gradient descent)for i in range(0, num_iterations):# Forward propagation: LINEAR -> RELU -> LINEAR -> RELU -> LINEAR -> SIGMOID.a3, cache = forward_propagation(X, parameters)# costcost = compute_cost(a3, Y)# Backward propagation.grads = backward_propagation(X, Y, cache)# Update parameters.parameters = update_parameters(parameters, grads, learning_rate)# Print the loss every 1000 iterationsif print_cost and i % 1000 == 0:print("Cost after iteration {}: {}".format(i, cost))costs.append(cost)# plot the lossplt.plot(costs)plt.ylabel('cost')plt.xlabel('iterations (per hundreds)')plt.title("Learning rate =" + str(learning_rate))plt.show()return parametersparameters = model_gb(train_X, train_Y)
predictions = predict(train_X, train_Y, parameters)
# Plot decision boundary
plt.title("Model with Gradient Descent optimization")
axes = plt.gca()
axes.set_xlim([-1.5,2.5])
axes.set_ylim([-1,1.5])
plot_decision_boundary(lambda x: predict_dec(parameters, x.T), train_X, np.squeeze(train_Y))# 在上面的基础上一点点扩充优化方法
def model(X, Y, optimizer, num_epochs=10000, mini_batch_size=64, learning_rate=0.0007, beta1=0.9, beta2=0.999, epsilon=1e-6, print_cost=True):grads = {}costs = [] # to keep track of the losscosts_ave = [] # to plotm = X.shape[1] # number of examplest = 0layers_dims = [X.shape[0], 5, 2, 1]# Initialize parameters dictionary.parameters = initialize_parameters(layers_dims)if optimizer == 'mini_batch_gd':passif optimizer == 'momentum':V = initialize_v(parameters)if optimizer == 'RMSprop':S = initialize_v(parameters)if optimizer == 'Adam':V = initialize_v(parameters)S = initialize_v(parameters)# Loopfor i in range(num_epochs):mini_batches = random_mini_batches(X, Y, mini_batch_size)for mini_batch in mini_batches:(mini_batch_X, mini_batch_Y) = mini_batch# Forward propagation: LINEAR -> RELU -> LINEAR -> RELU -> LINEAR -> SIGMOID.a3, cache = forward_propagation(mini_batch_X, parameters)# cost 代价应该是一次epoch的平均代价cost = compute_cost(a3, mini_batch_Y)costs.append(cost)# Backward propagation.grads = backward_propagation(mini_batch_X, mini_batch_Y, cache)# Update parameters.if optimizer == 'mini_batch_gd':parameters = update_parameters(parameters, grads, learning_rate)if optimizer == 'momentum':parameters, V = update_parameters_with_momentum(parameters, grads, V, beta1, learning_rate)if optimizer == 'RMSprop':parameters, S = update_parameters_with_RMSprop(parameters, grads, S, beta2, epsilon, learning_rate)if optimizer == 'Adam':t = t + 1parameters, V, S = update_parameters_with_Adam(parameters, grads, V, S, beta1, beta2, t, epsilon, learning_rate)# Print the cost every 1000 epochif print_cost and i % 1000 == 0:if i == 0:print("Average cost after epoch {}: {}".format(i, cost))costs_ave.append(cost)else:cost_ave = np.sum(costs[i-1000:i]) / 1000print("Average cost after epoch {}: {}".format(i, cost_ave))if print_cost and i % 100 == 0 and i > 0:costs_ave.append(np.sum(costs[i-100:i]) / 100)plt.plot(costs_ave)plt.title(optimizer)plt.ylabel('average cost')plt.xlabel('epoch (per hundreds)')plt.show()return parameters''' mini_batch gradient descent '''# 对训练集进行分批,输出一个list,里面每个元素是X和Y切片的tuple
def random_mini_batches(X, Y, mini_batch_size = 64):n = X.shape[1]mini_batches = []
# np.random.seed(0)permutation = np.random.permutation(n)shuffled_X = X[:, permutation]shuffled_Y = Y[:, permutation]batch_floor = n//mini_batch_sizefor i in range(0, batch_floor):mini_batch_X = shuffled_X[:, i*mini_batch_size:(i+1)*mini_batch_size]mini_batch_Y = shuffled_Y[:, i*mini_batch_size:(i+1)*mini_batch_size]mini_batch = (mini_batch_X, mini_batch_Y)mini_batches.append(mini_batch)if n % mini_batch_size != 0:mini_batch_X = shuffled_X[:, batch_floor*mini_batch_size:n]mini_batch_Y = shuffled_Y[:, batch_floor*mini_batch_size:n]mini_batch = (mini_batch_X, mini_batch_Y)mini_batches.append(mini_batch)return mini_batchesp_mini_batch = model(train_X, train_Y, optimizer='mini_batch_gd')# Predict
predictions = predict(train_X, train_Y, p_mini_batch)# Plot decision boundary
plt.title("Model with Gradient Descent optimization")
axes = plt.gca()
axes.set_xlim([-1.5,2.5])
axes.set_ylim([-1,1.5])
plot_decision_boundary(lambda x: predict_dec(p_mini_batch, x.T), train_X, np.squeeze(train_Y))''' momentum '''def initialize_v(parameters):V = {}L = len(parameters) // 2for i in range(1, L+1):V['dW'+str(i)] = np.zeros(parameters['W'+str(i)].shape)V['db'+str(i)] = np.zeros(parameters['b'+str(i)].shape)return Vdef update_parameters_with_momentum(parameters, grads, V, beta1, learning_rate):L = len(parameters) // 2for i in range(1, L+1):V['dW'+str(i)] = beta1 * V['dW'+str(i)] + (1-beta1) * grads['dW'+str(i)]V['db'+str(i)] = beta1 * V['db'+str(i)] + (1-beta1) * grads['db'+str(i)]parameters['W'+str(i)] += - learning_rate * V['dW'+str(i)]parameters['b'+str(i)] += - learning_rate * V['db'+str(i)]return parameters, V p_momentum = model(train_X, train_Y, optimizer='momentum')# Predict
predictions = predict(train_X, train_Y, p_momentum)# Plot decision boundary
plt.title("Model with Momentum optimization")
axes = plt.gca()
axes.set_xlim([-1.5,2.5])
axes.set_ylim([-1,1.5])
plot_decision_boundary(lambda x: predict_dec(p_momentum, x.T), train_X, np.squeeze(train_Y))''' RMSprop '''def update_parameters_with_RMSprop(parameters, grads, S, beta2, epsilon, learning_rate):L = len(parameters) // 2for i in range(1, L+1):S['dW'+str(i)] = beta2 * S['dW'+str(i)] + (1-beta2) * grads['dW'+str(i)]**2S['db'+str(i)] = beta2 * S['db'+str(i)] + (1-beta2) * grads['db'+str(i)]**2parameters['W'+str(i)] += - learning_rate * (grads['dW'+str(i)] / (np.sqrt(S['dW'+str(i)] + epsilon)))parameters['b'+str(i)] += - learning_rate * (grads['db'+str(i)] / (np.sqrt(S['db'+str(i)] + epsilon)))return parameters, Sp_RMSprop = model(train_X, train_Y, optimizer='RMSprop')# Predict
predictions = predict(train_X, train_Y, p_RMSprop)# Plot decision boundary
plt.title("Model with RMSprop optimization")
axes = plt.gca()
axes.set_xlim([-1.5,2.5])
axes.set_ylim([-1,1.5])
plot_decision_boundary(lambda x: predict_dec(p_RMSprop, x.T), train_X, np.squeeze(train_Y))''' Adam '''def update_parameters_with_Adam(parameters, grads, V, S, beta1, beta2, t, epsilon, learning_rate):L = len(parameters) // 2for i in range(1, L+1):V['dW'+str(i)] = beta1 * V['dW'+str(i)] + (1-beta1) * grads['dW'+str(i)]V['db'+str(i)] = beta1 * V['db'+str(i)] + (1-beta1) * grads['db'+str(i)]S['dW'+str(i)] = beta2 * S['dW'+str(i)] + (1-beta2) * grads['dW'+str(i)]**2S['db'+str(i)] = beta2 * S['db'+str(i)] + (1-beta2) * grads['db'+str(i)]**2parameters['W'+str(i)] += - learning_rate * ((V['dW'+str(i)]/(1-beta1**t)) / (np.sqrt(S['dW'+str(i)]/(1-beta2**t) + epsilon)))parameters['b'+str(i)] += - learning_rate * ((V['db'+str(i)]/(1-beta1**t)) / (np.sqrt(S['db'+str(i)]/(1-beta2**t) + epsilon)))return parameters, V, Sp_Adam = model(train_X, train_Y, optimizer='Adam')# Predict
predictions = predict(train_X, train_Y, p_Adam)# Plot decision boundary
plt.title("Model with Adam optimization")
axes = plt.gca()
axes.set_xlim([-1.5,2.5])
axes.set_ylim([-1,1.5])
plot_decision_boundary(lambda x: predict_dec(p_Adam, x.T), train_X, np.squeeze(train_Y))# 用震荡的代价
def model(X, Y, optimizer, num_epochs=10000, mini_batch_size=64, learning_rate=0.0007, beta1=0.9, beta2=0.999, epsilon=1e-6, print_cost=True):grads = {}costs = [] # to keep track of the lossm = X.shape[1] # number of examplest = 0layers_dims = [X.shape[0], 5, 2, 1]# Initialize parameters dictionary.parameters = initialize_parameters(layers_dims)if optimizer == 'mini_batch_gd':passif optimizer == 'momentum':V = initialize_v(parameters)if optimizer == 'RMSprop':S = initialize_v(parameters)if optimizer == 'Adam':V = initialize_v(parameters)S = initialize_v(parameters)# Loopfor i in range(num_epochs):mini_batches = random_mini_batches(X, Y, mini_batch_size)for mini_batch in mini_batches:(mini_batch_X, mini_batch_Y) = mini_batch# Forward propagation: LINEAR -> RELU -> LINEAR -> RELU -> LINEAR -> SIGMOID.a3, cache = forward_propagation(mini_batch_X, parameters)# cost 代价应该是一次epoch的平均代价cost = compute_cost(a3, mini_batch_Y)# Backward propagation.grads = backward_propagation(mini_batch_X, mini_batch_Y, cache)# Update parameters.if optimizer == 'mini_batch_gd':parameters = update_parameters(parameters, grads, learning_rate)if optimizer == 'momentum':parameters, V = update_parameters_with_momentum(parameters, grads, V, beta1, learning_rate)if optimizer == 'RMSprop':parameters, S = update_parameters_with_RMSprop(parameters, grads, S, beta2, epsilon, learning_rate)if optimizer == 'Adam':t = t + 1parameters, V, S = update_parameters_with_Adam(parameters, grads, V, S, beta1, beta2, t, epsilon, learning_rate)# Print the cost every 1000 epochif print_cost and i % 1000 == 0:print ("Cost after epoch %i: %f" %(i, cost))if print_cost and i % 100 == 0:costs.append(cost)# plot the costplt.plot(costs)plt.ylabel('cost')plt.xlabel('epochs (per 100)')plt.title(optimizer)plt.show()return parameters
额外的参考资料:
《动手学深度学习》李沐
《深度学习》‘’花书‘’
Stanford CS230深度学习(三)调参、正则化和优化算法相关推荐
- Stanford CS230深度学习(二)手动搭建DNN
这篇博客主要是对第二次课进行学习总结,并完成lecture2中要求的C1M3以及C1M4的编程作业.课程资源详见Stanford CS230深度学习(一) 由于这个课的编程作业基本上是一边给任务一边给 ...
- 深度学习网络调参技巧
深度学习网络调参技巧 本文转载自[炼丹实验室],讲了一些深度学习训练的技巧,其中包含了部分调参心得:深度学习训练心得.不过由于一般深度学习实验,相比普通机器学习任务,时间较长,因此调参技巧就显得尤为重 ...
- 【深度学习】知乎高赞:深度学习如何调参?
深度学习如何调参?看看高赞的回答: 高赞回答一 作者:Towser 链接:https://www.zhihu.com/question/41631631/answer/862075836 总结一下我遇 ...
- python贝叶斯优化算法_自动调参——贝叶斯优化算法hyperopt
注:转载请注明出处. 本篇文章主要记录了贝叶斯优化算法hyperopt的学习笔记,如果想看自动化调参中的网格调参和遗传优化算法TPOT,请查看我另外两篇文章:网格搜索gridSearchCV和遗传优化 ...
- Stanford CS230深度学习(四)TensorFlow2.1
本周CS230的课程主要是介绍了GAN相关的一些东西,但是这个需要深入了解,所以本次博客主要对coursera上的课程以及作业进行总结. 目录 一.一些重要的概念 1.深度学习中的超参数 2.Batc ...
- Stanford CS230深度学习(一)
斯坦福CS230可以作为深度学习的入门课,最近我也在跟着看视频.完成编程作业.首先列出使用的资源链接,然后给出第一课的理解和编程作业的代码. 所有资料如下: 一.课程连接: b站课堂讲授版:Stanf ...
- Stanford CS230深度学习(七)RNN和LSTM
在CS230的lecture 7中主要讲了神经网络的解释性,包括: 显著性图saliency maps(计算pre-softmax分数关于输入层的梯度并可视化) 遮挡敏感性occlusion sens ...
- Stanford CS230深度学习(八)词嵌入与文本情感分析
在CS230的lecture 8中主要吴恩达老师如何阅读文献,以及一些职业建议,都是一些比较实用的建议和指导. 关于阅读文献,吴恩达老师提倡先列出一个这个领域的文献列表,可能只包含几篇文章,然后精读其 ...
- 一文整理深度学习【调参小技巧】
调参小技巧 一.训练加速篇 1.1. 数据加载加速 1.1.1. 数据转为npy或npz格式加速 1.1.2. 多核并行多进程图像读取 1.1.3. numba代替CPython编译 1.2. 分布式 ...
最新文章
- IOS长按识别二维码失败
- android平台水波效果 源码
- sql 游标_SQL基础丨游标
- Java中classpath配置
- ul li前面的点怎么变大_亚马逊产品被投诉需要UL认证,该如何办理?
- IDEA MAVEN Project 显示问题
- 使用MAP文件快速定位程序崩溃代码行(转)
- JavaScript高级之正则表达式
- 华为服务器改硬盘启动不了怎么办,华为服务器装系统加载os怎么回事
- #周末课堂# 【Linux + JVM + Mysql高级性能优化班】(火热报名中~~~)
- 【SAP HANA】关于SAP HANA中带层次结构的Analytic View创建、激活状况下在系统中生成对象的研究...
- 蓝桥杯 ALGO-151 算法训练 6-2递归求二进制表示位数
- openstack虚拟机迁移live-migration中libvirt配置
- CSS hack——不同浏览器的CSS应对法
- 20170813xlVBA跨表筛选数据
- python3 annotations
- 眼镜蛇效应:事与愿违的经济学教训
- SSH框架调用scrapy爬虫
- Android adb shell执行mv等操作时,提示:Read-only file system的解决办法
- 阿里云Web播放器使用