神经网络基础常识(mini_batch梯度下降,指数加权平均、动量梯度下降、RMSPROP算法、Adam优化算法、学习率衰减)
mini_batch梯度下降算法在训练网络时,如果训练数据非常复杂,那么把所有训练数据都输入一次神经网络需要非常长的时间,此外,这些数据可能底子无法一次性装入内存。为了加快训练速度
batch梯度下降:每次迭代都需要遍历整个训练集,可以预期每次迭代损掉城市下降。
随机梯度下降:每次迭代中,只会使用1个样本。当训练集较大时,随机梯度下降可以更快,但是参数会向最小值摆动,而不是平稳的收敛。
mini_batch:把大的训练集分成多个小的子集(mini_batch),每次迭代就取一个子集(mini_batch),所以每次迭代都是在训练分歧的样本((mini_batch),其损掉会有震荡,但是总体趋势是下降的。
数据量斗劲小的化(小于2000),一般采用batch梯度下降。
样本量斗劲大的情况,一般采用mini_batch ,mini_batch一般设置为2的n次方,一般是2的6次方到2的9次方之间。
指数加权平均值
具体参考:
优化算法之指数加权平均详解_修炼之路的博客-CSDN博客
以气温为例
局部平均值:
V_{t}=\beta*V_{t-1} +(1-\beta)*\theta_{t}
\theta 暗示当天的温度,V暗示局部平均值, V_{t} 暗示第t天的局部平均温度, \theta_{t} 暗示第t天的温度。当 \beta =0.9时, V_{t} 可看作是1/(1- \beta )=10天的平均温度。
如果你想要计算10天局部温度的平均值,你需要保留比来10天的温度,此刻,在计算局部平均值的时候,你只需要保留前一个的加权平均值,相对于直接计算平均值,精确度没有那么高。
修正偏差
通过 V_{t}=1/(1-\beta_{t}) 来修正指数加权平均的误差,随着t的增大,1- \beta_{t} 会趋近于1, \beta_{t} 趋近于0,所以偏差修正对于后期的指数加权平均没有影响。
注意:如果你对前期的局部的平均值的精度没有要求,可以使用偏差修正,偏差修正主要修正的是前期的局部平均值的误差。
动量梯度下降
普通的梯度下降法:
w=w-\alpha *dw
采用动量梯度下降之后
V_{dw}=\beta dw +(1-\beta)dw
更新参数时:
w=w-\alpha V_{dw}
颠末实践证明:
\beta 取0.9时成果斗劲好
RMSPROP算法
RMSPROP 算法,全称 root mean square prop。为了进一步优化损掉函数在更新中的存在摆动幅度更大的问题,而且进一步加快函数的收敛速度。RMSPROP算法对权重w和偏置b的梯度使用微分平方和加权平均数。
在第t轮的迭代中
dw的平方和是 dw^{2} ,db的平方是 db^{2} ,如果严谨些,防止分母为0,在分数下加一个非常小的值 \varepsilon ,凡是取为 10^{-8}
S_{dw}=\beta* S_{dw}+(1-\beta)dw^{2}
S_{db}=\beta* S_{db}+(1-\beta)db^{2}
w=w-\alpha\frac{dw}{\sqrt{\sqrt{S_{dw}}}+\varepsilon}
b=b-\alpha\frac{db}{\sqrt{\sqrt{S_{db}}}+\varepsilon}
这样的作法 有利于消除摆动幅度大的标的目的,用来修正摆动幅度,是的各个维度的摆动幅度都斗劲小,另一方面也使得网络函数的收敛速度更快,比如dw和db有一个值斗劲大的时候,那么我们更新权重或者偏置的时候,除以之前累积的梯度的平方根,这样就使得更新的幅度更小。
Adam优化算法:结合了Momentum和RMSprop算法
V_{dw}=0 S_{dw}=0 V_{db}=0 S_{db}=0
compute dw,db usingmin_batch
V_{dw}=\beta_{1}*V_{dw} + (1-\beta_{1})dw
V_{db}=\beta_{1}*V_{db} + (1-\beta_{1})db
S_{dw}=\beta_{2}*V_{dw} + (1-\beta_{2})dw^{2}
S_{db}=\beta_{2}*V_{db} + (1-\beta_{2})db^{2}
V_{dw}^{corrected}=\frac{V_{dw}}{(1-\beta_{1}^{t})}
V_{db}^{corrected}=\frac{V_{db}}{(1-\beta_{1}^{t})}
S_{dw}^{corrected}=\frac{S_{dw}}{(1-\beta_{2}^{t})}
S_{db}^{corrected}=\frac{S_{db}}{(1-\beta_{2}^{t})}
w=w-\alpha*\frac{V_{dw}^{corrected}}{\sqrt{S_{dw}^{corrected}+\varepsilon}}
b=b-\alpha*\frac{V_{db}^{corrected}}{\sqrt{S_{db}^{corrected}+\varepsilon}}
\alpha 为学习
\beta_{1}为0.9dw
\beta_{2} 为0.999 dw^{2}
\varepsilon 为 10^{-8}
学习率衰减
慢慢减少 \alpha 的本质在于,在学习初期,你能承受较大的法式,当开始收敛时,小一些的学习率能让 法式小一些。
方式1: \alpha=\frac{1}{(1+decayrate+epochnum)}*\alpha_{0}
此中,decayrate是衰减率,epochnum是迭代次数, \alpha_{0} 是初始学习率
方式2: \alpha=0.95^{epochnum}\alpha_{0}
方式3: \alpha=\frac{k}{epochnum}*\alpha_{0} 或 \frac{k}{\sqrt{t}} t为mini_batch
方式4:离散学习率,某个法式某个学习率,一会之后学习率减半,然后再减半。
方式5:手动调。
代码来自:【中文】【吴恩达课后编程作业】Course 2 - 改善深层神经网络 - 第二周作业_何宽的博客-CSDN博客
import numpy asnp
import matplotlib.pyplot as plt
import scipy.io
import math
import sklearn
import sklearn.datasets
import opt_utils
import testCase
plt.rcParams['figure.figsize']=(7.0,4.0)
plt.rcParams[”image.interpolation”]='nearest'
plt.rcParams['image.cmap']='gray'
#梯度下降
def update_parameters_with_gd(parameters,grads,learning_rate):
”””
使用梯度下降更新参数
参数
:param parameters: 字典,包含了要更新的参数
parameter['W'+str(l)]=Wl
parameter['b'+str(l)]=bl
grads -字典,包含了每一个梯度值用以更新参数
:param grads:
grads['dw'+str(l)]=dwl
grads['db'+str(l)]=dbl
:param learning_rate: 学习率
:return:
返回值:
:parameter-字典,包含了更新后的参数
”””
L=len(parameters)//2 #神经网络的层数
#更新每个参数
for l in range(L):
parameters[”W”+str(l+1)]=parameters[”W”+str(l+1)]-learning_rate*grads[”dW”+str(l+1)]
parameters[”b”+str(l+1)]=parameters[”b”+str(l+1)]-learning_rate*grads[”db”+str(l+1)]
return parameters
# #测试
# print(”------------------------------------测试update_parameters_with_gd-------------------------------”)
# parameters,grads,learning_rate=testCase.update_parameters_with_gd_test_case()
# parameters=update_parameters_with_gd(parameters,grads,learning_rate)
#
# print(”W1=”+str(parameters[”W1”]))
# print(”b1=”+str(parameters[”b1”]))
# print(”W2=”+str(parameters[”W2”]))
# print(”b2=”+str(parameters[”b2”]))
# #随机梯度下降
# X=data_input
# Y=labels
# parameters=initialize_parameters(layers_dims)
# for i in range(0,num_iterations):
# #前向传布
# A,cache=forward_propagation(X,parameters)
# #计算损掉
# cost=copmute_cost(A,Y)
# #反向传布
# grads=backward_propagation(X,Y,cache)
# #更新参数
# parameters=update_parameters(parameters,grads)
#
# #随机梯度下降
# X=data_input
# Y=labels
# parameters=initialize_parameters(layers_dims)
# for i in (0,num_iteration):
# for j inm:
# #前向传布
# A,cache=forward_propagation(X,parameters)
# #计算成本
# cost=compute_cost(A,Y)
# #后向传布
# grads=backward_propagation(X,Y,cache)
# #更新参数
# parameters=update_parameters(parameters,grads)
#mini_batch
# #第一个mini_batch
# first_mini_batch_X=shuffled_X[:,0:mini_batch_size]
# #第二个mini-batch
# second_mini_batch_X=shuffled_X[:,mini_batch_size:2*mini_batch_size]
def random_mini_batches(X,Y,mini_batch_size=64,seed=0):
”””
从(X,Y)中创建一个随机的mini_batch列表
:param X: 输入数据,维度为(输入节点数,样本的数量)
:param Y: 对应的是X的标签,(蓝,红),样本的数量
:param mini_batch_size: 每个mini-batch的样本数量
:return:
mini-batchs-一个同步列表,维度为(mini_batch_X,mini_batch_Y)
”””
np.random.seed(seed) #指定随机种子
m=X.shape
mini_batchs=[]
#第一步打乱挨次
permutation=list(np.random.permutation(m)) #它会返回一个长为m的随机数,且里面的数是从0到m-1
shuffled_X=X[:,permutation] #将每一列的数据按permutation的挨次从头摆列
shuffled_Y=Y[:,permutation].reshape((1,m))
#第二步,分割
num_complete_minibatches=math.floor(m/mini_batch_size) #把你的训练集分割成多少份,如果是99.99,则返回值是99,剩下的0.99会被舍弃
for k in range(0,num_complete_minibatches+1):
mini_batch_X=shuffled_X[:,k*mini_batch_size:(k+1)*mini_batch_size]
mini_batch_Y=shuffled_Y[:,k*mini_batch_size:(k+1)*mini_batch_size]
# print(”mini_batch_X.shape”,mini_batch_X.shape)
mini_batch = (mini_batch_X, mini_batch_Y)
mini_batchs.append(mini_batch)
#如果训练集的大小刚好是mini_batch_size的整数倍,那么这里已经措置完。
#如果训练集的大小不是mini_batch_size的整数倍,那么最后必定会剩下一些。
if m % mini_batch_size!=0:
#获取最后残剩的部门
mini_batch_X=shuffled_X[:,mini_batch_size*num_complete_minibatches:]
mini_batch_Y=shuffled_Y[:,mini_batch_size*num_complete_minibatches:]
mini_batch=(mini_batch_X,mini_batch_Y)
mini_batchs.append(mini_batch)
return mini_batchs
# print(”-------------测试random_mini_batches------------------------------------------------”)
# X_aasess,Y_assess,mini_batch_size=testCase.random_mini_batches_test_case()
# print(X_aasess.shape)
# print(mini_batch_size)
# mini_batchs=random_mini_batches(X_aasess,Y_assess,mini_batch_size)
#
# print(”第1个mini_batch_X的维度为:”,mini_batchs.shape)
# print(”第1个min_batch_Y的维度为”,mini_batchs.shape)
# print(”第2个mini_batch_X的维度为:”, mini_batchs.shape)
# print(”第2个min_batch_Y的维度为”, mini_batchs.shape)
# print(”第3个mini_batch_X的维度为:”, mini_batchs.shape)
# print(”第3个min_batch_Y的维度为”, mini_batchs.shape)
#包含动量的梯度下降
def initialze_velocity(parameters):
”””
初始化速度,velocity 是一个字典
-keys:”dw1”,”db1”,....,”dWL”,”dbL”
-values:与相应的梯度/参数维度不异的值为0的矩阵
参数:
:parameters -一个字典,包含了以下参数
:parameter[”W”+str(1)]=W1
:parameter[”b”+str(1)]=b1
返回:
v- 一个字典变量,包含了 以下参数
v[”dw”+str(1)]=dw1的速度
v[”db”+str(1)]=db1的速度
”””
L=len(parameters)//2
v={}
for l in range(L):
v[”dW”+str(l+1)]=np.zeros_like(parameters[”W”+str(l+1)])
v[”db”+str(l+1)]=np.zeros_like(parameters[”b”+str(l+1)])
return v
#测试一下
#
# print(”------------------测试initialize_velocity---------------------------”)
# parameters=testCase.initialize_velocity_test_case()
# v=initialze_velocity(parameters)
#
# print('v[”dW1”]=' +str(v[”dW1”]))
# print('v[”db1”]=' +str(v[”db1”]))
# print('v[”dW2”]=' +str(v[”dW2”]))
# print('v[”db2”]='+str(v[”db2”]))
def update_parameters_with_momentun(parameters,grads,v,beta,learning_rate):
”””
使用动量更新参数
:param parameters: 一个字典的变量,包含了 以下字段:
:parameter[”W”+str(1)]=W1
parameter[”b”+str(1)]=b1
:param grads: 一个包含梯度值的字典变量,具有以下字段
grads[”dW”+str(l)]=dW1
grads[”db”+str(l)]=db1
:param v: 包含当前速度的字典变量,具有以下字段:
v[”dW”+str(l)]=...
v[”db”+str(l)]=
:param beta: 超参数,动量,实数
:param learning_rate: 学习率,实数
:return:
:parameter 更新后的参数字典
v-包含了更新后的速度变量
”””
L=len(parameters)//2
for l inrange(L):
#计算速度
v[”dW”+str(l+1)]=beta*v[”dW”+str(l+1)]+(1-beta)*grads[”dW”+str(l+1)]
v[”db” + str(l + 1)] = beta * v[”db” + str(l + 1)] + (1 - beta) * grads[”db” + str(l + 1)]
#更新参数
parameters[”W”+str(l+1)]=parameters[”W”+str(l+1)]-learning_rate*v['dW'+str(l+1)]
parameters[”b” + str(l + 1)] = parameters[”b” + str(l + 1)] - learning_rate * v['db' + str(l + 1)]
returnparameters,v
#测试
# print(”------------------测试update_parametes_with_momentun-------------------------”)
# parameters,grads,v=testCase.update_parameters_with_momentum_test_case()
# update_parameters_with_momentun(parameters,grads,v,beta=0.9,learning_rate=0.01)
# print(”W1=”+str(parameters[”W1”]))
# print(”b1=”+str(parameters[”b1”]))
#
# print(”W2=”+str(parameters[”W2”]))
# print(”b2=”+str(parameters[”b2”]))
#
# print('v[”dW1”]='+str(v[”dW1”]))
# print('v[”db1”]='+str(v[”db1”]))
#
# print('v[”dW2”]='+str(v[”dW2”]))
# print('v[”db2”]='+str(v[”db2”]))
#Adam算法
def initialize_adam(parameters):
”””
初始化v和s,它们都是字典类型的变量,都包含了以下字段
-keys:”dW1”,”db1“,...,”dWL”,”dbL”
:param paramters: 包含了以下参数的字典变量
:parameter[”W”+str(l)]=Wl
:parameter[”b”+str(l)]=bl
:return:
v -包含梯度的指数加权平均值,字段如下
V[”dW”+str(l)]=
v[”db”+str(l)]=
s- 包含平方梯度的指数加权平均值,字段如下
s[”dW”+str(l)]=
s[”db”+str(l)]=
”””
L=len(parameters)//2
v={}
s={}
for l inrange(L):
v[”dW”+str(l+1)]=np.zeros_like(parameters[”W”+str(l+1)])
v[”db” + str(l + 1)] = np.zeros_like(parameters[”b” + str(l + 1)])
s[”dW” + str(l + 1)] = np.zeros_like(parameters[”W” + str(l + 1)])
s[”db” + str(l + 1)] = np.zeros_like(parameters[”b” + str(l + 1)])
return (v,s)
# print(”--------------------------测试initalize_adam-----------------------------------”)
# parameters=testCase.initialize_adam_test_case()
# v,s=initialize_adam(parameters)
# print('v[”dW1”]='+str(v[”dW1”]))
# print('v[”db1”]='+str(v[”db1”]))
# print('v[”dW2”]='+str(v[”dW2”]))
# print('v[”db2”]='+str(v[”db2”]))
#
# print('s[”dW1”]='+str(s[”dW1”]))
# print('s[”db1”]='+str(s[”db1”]))
# print('s[”dW2”]='+str(s[”dW2”]))
# print('s[”db2”]='+str(s[”db2”]))
def upadate_parameters_with_adam(parameters,grads,v,s,t,learning_rate=0.01,beta1=0.9,beta2=0.999,epsilon=1e-8):
”””
使用Adam更新参数
:param parameters:
:parameter['W'+str(l)]=Wl
:parameter['B'+str(l)]=b1
:param grads: 包含了梯度值的字典,有以下key值
grads['dW'+str(l)]=dW1
grads['db'+str(l)]=db1
:param v: Adam变量,第一个梯度的移动平均值,是一个字典类型的变量
:param s: Adam变量,平方梯度的移动平均值,是一个字典类型的变量
:param t: 当前迭代的次数
:param learning_rate: 学习率
:param beta1: 动量,超参数,用于第一阶段,使用的曲线Y值不从0开始
:param beta2: RMSprop的一个参数,超参数
:param epsilon: 防止除0操作(分母为0)
:return:
:parameters-更新后的参数
v 第一个梯度的移动平均值,是一个字典类型的变量
s 平均梯度的移动平均值,是一个字典类型的变量
”””
L=len(parameters)//2
v_corrected={}
s_corrected={}
for l in range(L):
#梯度的移动平均值,输入v,grads,beta1,输出:”V“
v[”dW” + str(l+1)]=beta1 * v[”dW”+ str(l+1)]+(1-beta1)* grads[”dW”+str(l+1)]
v[”db”+str(l+1)] = beta1 * v[”db”+str(l+1)] +(1-beta1)*grads[”db”+str(l + 1)]
#计算第一阶段的偏差修成后的估计值,输入”v,beta1,t”,输出:”v_corrected”
v_corrected[”dW”+str(l+1)]=v[”dW”+str(l+1)]/(1-np.power(beta1,t))
v_corrected[”db” + str(l + 1)] = v[”db” + str(l + 1)] / (1 - np.power(beta1, t))
#计算平方梯度的移动平均值,输入:”s,grads,beta2“,输出”s“
s[”dW”+str(l+1)]=beta2*s[”dW”+str(l+1)]+(1-beta2)*np.square(grads[”dW”+str(l+1)])
s[”db” + str(l + 1)] = beta2 * s[”db” + str(l + 1)] + (1 - beta2) * np.square(grads[”db” + str(l + 1)])
#计算第二极端的偏差修正后的估计值,输入:”s,grads,beta2,t”,输出s_corrected
s_corrected[”dW”+str(l+1)]=s[”dW”+str(l+1)]/(1-np.power(beta2,t))
s_corrected[”db” + str(l + 1)] = s[”db” + str(l + 1)] / (1 - np.power(beta2, t))
#更新参数,输入”parameters,learning_rate,v_corrected,s_corrected,eplsion”,输出“parameters”
parameters[”W”+str(l+1)]=parameters[”W”+str(l+1)]-learning_rate*(v_corrected[”dW”+str(l+1)]/np.sqrt(s_corrected[”dW”+str(l+1)]+epsilon))
parameters[”W” + str(l + 1)] = parameters[”W” + str(l + 1)] - learning_rate * (
v_corrected[”dW” + str(l + 1)] / np.sqrt(s_corrected[”dW” + str(l + 1)] + epsilon))
return (parameters,v,s)
# #测试一下
# print(”---------------------------------------测试update_with_parameters_with_adam----------------------------------------”)
# parameters,grads,v,s=testCase.update_parameters_with_adam_test_case()
# upadate_parameters_with_adam(parameters,grads,v,s,t=2)
# print(”W1=”+str(parameters[”W1”]))
# print(”b1=”+str(parameters[”b1”]))
#
# print(”W2=”+str(parameters[”W2”]))
# print(”b2=”+str(parameters[”b2”]))
#
# print('v[”dW1”]='+str(v[”dW1”]))
# print('v[”db1”]='+str(v[”db1”]))
# print('v[”dW2”]='+str(v[”dW2”]))
# print('v[”db2”]='+str(v[”db2”]))
#
# print('s[”dW1”]='+str(s[”dW1”]))
# print('s[”db1”]='+str(s[”db1”]))
# print('s[”dW2”]='+str(s[”dW2”]))
# print('s[”db2”]='+str(s[”db2”]))
#
def model(X,Y,layers_dims,optimizer,learning_rate=0.0007,mini_batch_size=64,beta=0.9,beta1=0.9,beta2=0.999,epsilon=1e-8,num_epochs=10000,print_cost=True,is_plot=True):
”””
可以运行在分歧优化器模式下的3层神经网络
参数
:param X: 输入数据,维度为(2,输入的数据集里面样本数量)
:param Y: 与X对应的标签
:param layers_dims: 包含层数和节点数量的列表
:param optimizer: 字符串类型的参数,用于选择优化类型,[”gd”|”momentum”|”adam”]
:param learning_rate: 学习率
:param mini_batch_size: 每个小批量数据集的大小
:param beta: 用于动量优化的一个超参数
:param beta1: 用于计算梯度后的指数衰减的估计的超参数
:param beta2: 用于计算梯度后的指数衰减的估计的超参数
:param epsilon: 用于Adam中避免除0的超参数,一般不更改
:param num_epochs:整个训练集的遍历次数
:param print_cost:是否打印误差值,每遍历1000次数据集打印一次,但是每100次记录一个误差值,又称每1000次代打印一次
:param is_plot:是否绘制出曲线图
:return:
:parameters -包含了学习后的参数
”””
L=len(layers_dims)
costs=[]
t=0 #每学习完一个mini_batch就增加1
seed=10 #随机种子
#初始化参数
parameters=opt_utils.initialize_parameters(layers_dims)
#选择优化器
if optimizer=='gd':
pass
elif optimizer==”momentum”:
v=initialze_velocity(parameters) #使用动量
elif optimizer ==”adam”:
v,s=initialize_adam(parameters) #使用Adam优化
else:
print(”optimizer 参数错误,法式退出”)
exit(1)
#开始学习
for i in range(num_epochs):
#定义随机minibatches,我们在每次遍历数据集之后增加种子以从头摆列数据集
seed=seed+1
minibatches=random_mini_batches(X,Y,mini_batch_size,seed)
for minibatch in minibatches:
#选择一个minibatch
(minibatch_X,minibatch_Y)=minibatch
#前向传布
A3,cache=opt_utils.forward_propagation(minibatch_X,parameters)
#计算误差
cost=opt_utils.compute_cost(A3,minibatch_Y)
#反向传布
grads=opt_utils.backward_propagation(minibatch_X,minibatch_Y,cache)
#更新参数
if optimizer ==”gd”:
parameters=update_parameters_with_gd(parameters,grads,learning_rate)
elif optimizer==”momentum”:
parameters,v=update_parameters_with_momentun(parameters,grads,v,beta,learning_rate)
elif optimizer==”adam”:
t=t+1
parameters,v,s=upadate_parameters_with_adam(parameters,grads,v,s,t,learning_rate,beta1,beta2,epsilon)
#记录误差值
if i % 100 == 0:
costs.append(cost)
#是否打印误差值
if print_cost and i % 1000 == 0:
# print(i)
print(”第”+str(i)+”次遍历整个数据集,当前误差值:”+str(cost))
#是否绘制曲线图
if is_plot:
plt.plot(costs)
plt.ylabel('cost')
plt.xlabel('epochs(per 100)')
plt.title(”Learning rate=”+str(learning_rate))
plt.show()
return parameters
train_X,train_Y=opt_utils.load_dataset(is_plot=False)
#梯度下降测试
# layers_dims=,5,2,1]
# parameters=model(train_X,train_Y,layers_dims,optimizer=”gd”,is_plot=True)
# #
# #我们来绘制分类情况
# #预测
# predictions=opt_utils.predict(train_X,train_Y,parameters)
# # print(predictions)
# plt.title(”Model with Gradient Decent optimization”)
# axes=plt.gca()
# axes.set_xlim([-1.5,2.5])
# axes.set_ylim([-1,1.5])
# opt_utils.plot_decision_boundary(lambda x:opt_utils.predict_dec(parameters,x.T),train_X,train_Y)
#使用动量梯度下降
# layers_dims=,5,2,1]
# parameters=model(train_X,train_Y,layers_dims,optimizer=”momentum”,is_plot=True)
#
# #我们来绘制分类情况
#预测
# predictions=opt_utils.predict(train_X,train_Y,parameters)
# # print(predictions)
# plt.title(”Model with Gradient Decent optimization”)
# axes=plt.gca()
# axes.set_xlim([-1.5,2.5])
# axes.set_ylim([-1,1.5])
# opt_utils.plot_decision_boundary(lambda x:opt_utils.predict_dec(parameters,x.T),train_X,train_Y)
#Adam优化后的梯度下降
layers_dims=,5,2,1]
parameters=model(train_X,train_Y,layers_dims,optimizer=”adam”,is_plot=True)
页:
[1]