前言
这一篇博客是在上一篇博客的基础上对网络训练使用梯度下降法进行改进,加快训练的速度,主要从两方面进行改进:(1)对数据集进行分割,以部分数据集为单位进行梯度下降;(2)分别采用momentum和Adam算法进行梯度下降,加快收敛速度。
上一篇博客:采用L2正则化和随机删除节点的L层神经网络
最后,如果有写得不对的地方,希望大家不吝赐教,谢谢!
一、主要任务:(1)对数据集进行分割 (2)采用momentum算法进行梯度下降 (3)采用Adam算法进行梯度下降
二、整个流程:构造数据集用随机数初始化w和b参数前向传播损失函数反向传播优化(分割数据梯度下降含momentum的梯度下降含Adam的梯度下降)预测模型
三、引用的包有:
import numpy as np
import matplotlib.pyplot as plt
import sklearn.datasets as sd
import math
四、需要准备的函数
激活函数:relu函数和输出层的sigmoid函数
#输出层激活函数
def sigmoid(z):
s=1/(1+np.exp(-z))
return s
#隐藏层激活函数
def relu(z):
s=np.maximum(0,z)
return s
1、构造数据集
#构造数据集
def load_datasets(is_plot=False):
np.random.seed(1)
train_x,train_y=sd.make_moons(n_samples=300,noise=.2)
if is_plot==True:
plt.scatter(train_x[:,0],train_x[:,1],c=train_y,s=40,cmap=plt.cm.Spectral)
train_x=train_x.T
train_y=train_y.reshape(1,train_y.shape[0])
print('train_x_shape:',train_x.shape)
print('train_y_shape:',train_y.shape)
return train_x,train_y
数据集大小:
train_x_shape: (2, 300)
train_y_shape: (1, 300)
2、用随机数初始化w和b参数
#用随机数初始化w和b
def init_w_b(net_dims):
L=len(net_dims)
np.random.seed(2)
parms={}
for i in range(1,L):
parms['W'+str(i)]=np.random.randn(net_dims[i],net_dims[i-1])
parms['b'+str(i)]=np.zeros(shape=(net_dims[i],1))
return parms
3、前向传播
这次网络设计的是一个三层的网络
#前向传播
def forward(X,parms):
forward_data={}
W1=parms['W1']
b1=parms['b1']
W2=parms['W2']
b2=parms['b2']
W3=parms['W3']
b3=parms['b3']
z1=np.dot(W1,X)+b1
a1=relu(z1)
z2=np.dot(W2,a1)+b2
a2=relu(z2)
z3=np.dot(W3,a2)+b3
a3=sigmoid(z3)
forward_data={'W1':W1,
'b1':b1,
'z1':z1,
'a1':a1,
'W2':W2,
'b2':b2,
'z2':z2,
'a2':a2,
'W3':W3,
'b3':b3,
'z3':z3,
'a3':a3}
return forward_data
4、损失函数
#损失函数
def cost_function(Y,forward_data):
a3=forward_data['a3']
m=Y.shape[1]
cost=(-1/m)*np.sum(Y*np.log(a3)+(1-Y)*np.log(1-a3))
return cost
5、反向传播
#反向传播
def backward(forward_data,X,Y):
m=Y.shape[1]
dZ3=forward_data['a3']-Y
dW3=(1/m)*np.dot(dZ3,forward_data['a2'].T)
db3=(1/m)*np.sum(dZ3,axis=1,keepdims=True)
dZ2=np.dot(forward_data['W3'].T,dZ3)*np.int64(forward_data['a2']>0)
dW2=(1/m)*np.dot(dZ2,forward_data['a1'].T)
db2=(1/m)*np.sum(dZ2,axis=1,keepdims=True)
dZ1=np.dot(forward_data['W2'].T,dZ2)*np.int64(forward_data['a1']>0)
dW1=(1/m)*np.dot(dZ1,X.T)
db1=(1/m)*np.sum(dZ1,axis=1,keepdims=True)
grads={'dZ3':dZ3,
'dW3':dW3,
'db3':db3,
'dZ2':dZ2,
'dW2':dW2,
'db2':db2,
'dZ1':dZ1,
'dW1':dW1,
'db1':db1}
return grads
6、优化
(1)分割数据集
1)先打乱train_x的顺序,即一共有m个数据,原始的排列是:0,1,2,,,m-1,现在随机打乱顺序,train_y对应地也会和train_x随机顺序一样。
#第一步:打乱顺序
permutation =list(np.random.permutation(m))
X_random=X[:,permutation]
Y_random=Y[:,permutation].reshape(1,m)
2)切分数据,把m个数据按照一定大小进行切分,如每一个大小为mini_batch_size=64,即一共分成(m/mini_batch_size)组,例如:
第一组:first_mini_batch=X_random[:,0:mini_batch_size]
第二组:sceond_mini_batch=X_random[:,mini_batch_size:2*mini_batch_size]
,,,
整个过程如下:
#先进行分割数据
def random_mini_batches(X,Y,mini_batches_size=64):
np.random.randn(3)
m=Y.shape[1]
mini_batches=[]
#第一步:打乱顺序
permutation =list(np.random.permutation(m))
X_random=X[:,permutation]
Y_random=Y[:,permutation].reshape(1,m)
#第二步:分割
num_competed=math.floor(m/mini_batches_size)
for i in range(num_competed):
mini_batch_x=X_random[:,i*mini_batches_size:(i+1)*mini_batches_size]
mini_batch_y=Y_random[:,i*mini_batches_size:(i+1)*mini_batches_size]
mini_batch=(mini_batch_x,mini_batch_y)
mini_batches.append(mini_batch)
if m%mini_batches_size!=0:
mini_batch_x=X_random[:,num_competed*mini_batches_size:]
mini_batch_y=Y_random[:,num_competed*mini_batches_size:]
mini_batch=(mini_batch_x,mini_batch_y)
mini_batches.append(mini_batch)
return mini_batches
(2)梯度下降
#1、梯度下降
def updata_w_b(parms,grads,learning_rate,net_dims):
L=len(net_dims)
for i in range(1,L):
parms['W'+str(i)]=parms['W'+str(i)]-learning_rate*grads['dW'+str(i)]
parms['b'+str(i)]=parms['b'+str(i)]-learning_rate*grads['db'+str(i)]
return parms
在进行momentum和Adam梯度下降时,先对算法中需要用到的v和s变量进行初始化
#初始化momentun和Adam算法中需要用到的v和s变量
def init_v_s(net_dims,parms):
L=len(net_dims)
v={}
s={}
for i in range(1,L):
v['dW'+str(i)]=np.zeros_like(parms['W'+str(i)])
v['db'+str(i)]=np.zeros_like(parms['b'+str(i)])
s['dW'+str(i)]=np.zeros_like(parms['W'+str(i)])
s['db'+str(i)]=np.zeros_like(parms['b'+str(i)])
return v,s
(3)含momentum的梯度下降
算法本身主要利用前面的梯度进行综合求和平均,来进行平滑,防止波动,每次求和个数:
其中一般
#2、包含动量的梯度下降
def update_w_b_momentum(parms,v,grads,net_dims,learning_rate,beta=0.9):
L=len(net_dims)
for i in range(1,L):
v['dW'+str(i)]=beta*v['dW'+str(i)]+(1-beta)*grads['dW'+str(i)]
v['db'+str(i)]=beta*v['db'+str(i)]+(1-beta)*grads['db'+str(i)]
parms['W'+str(i)]=parms['W'+str(i)]-learning_rate*v['dW'+str(i)]
parms['b'+str(i)]=parms['b'+str(i)]-learning_rate*v['db'+str(i)]
return parms,v
(4)含Adam算法的梯度下降
先介绍下RMSprop算法:这个算法主要是加快梯度下降的前进速度,减缓梯度左右波动的速度
其中一般
Adam算法是将momentum和RMSprop进行结合起来
偏差校正:
update:
其中一般
#3、Adam算法
def update_w_b_adam(parms,grads,v,s,t,net_dims,learning_rate,beta1=0.9,beta2=0.999,e=1e-8):
L=len(net_dims)
v_corr={}
s_corr={}
for i in range(1,L):
v_corr['dW'+str(i)]=np.zeros_like(parms['W'+str(i)])
v_corr['db'+str(i)]=np.zeros_like(parms['b'+str(i)])
s_corr['dW'+str(i)]=np.zeros_like(parms['W'+str(i)])
s_corr['db'+str(i)]=np.zeros_like(parms['b'+str(i)])
for i in range(1,L):
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)*np.square(grads['dW'+str(i)])
s['db'+str(i)]=beta2*s['db'+str(i)]+(1-beta2)*np.square(grads['db'+str(i)])
v_corr['dW'+str(i)]=v['dW'+str(i)]/(1-np.power(beta1,t))
v_corr['db'+str(i)]=v['db'+str(i)]/(1-np.power(beta1,t))
s_corr['dW'+str(i)]=s['dW'+str(i)]/(1-np.power(beta2,t))
s_corr['db'+str(i)]=s['db'+str(i)]/(1-np.power(beta2,t))
parms['W'+str(i)]=parms['W'+str(i)]-learning_rate*v_corr['dW'+str(i)]/(np.sqrt(s_corr['dW'+str(i)])+e)
parms['b'+str(i)]=parms['b'+str(i)]-learning_rate*v_corr['db'+str(i)]/(np.sqrt(s_corr['db'+str(i)])+e)
return parms,v,s
7、预测
#预测
def predict(X,parms,Y):
forward_data=forward(X,parms)
a3=forward_data['a3']
m=Y.shape[1]
y_predict=np.zeros(shape=(1,m))
for i in range(m):
if a3[0,i]>0.5:
y_predict[0,i]=1
else:
y_predict[0,i]=0
print('Accuracy:'+str(np.mean((y_predict[0,:]==Y[0,:]))))
return y_predict
8、模型
首先网络结构:net_dims={0:n,1:5,2:2,3:1},其中n=train_x.shape[0]
#模型
def mole(train_x,train_y,choice,learning_rate,mini_batches_size=64,iterations=5000,
beta=0.9,beta1=0.9,beta2=0.999,e=1e-8,print_flag=False,is_plot=False):
n=train_x.shape[0]
net_dims={0:n,1:5,2:2,3:1}
t=0
costs=[]
parms=init_w_b(net_dims)
v,s=init_v_s(net_dims,parms)
for i in range(iterations):
mini_batches=random_mini_batches(train_x,train_y,mini_batches_size)
for mini_batch in mini_batches:
#选择一个batch
(mini_batch_x,mini_batch_y)=mini_batch
#前向传播
forward_data=forward(mini_batch_x,parms)
#损失函数
cost=cost_function(mini_batch_y,forward_data)
#反向传播
grads=backward(forward_data,mini_batch_x,mini_batch_y)
#更新参数
if choice=='gd': #梯度下降
parms=updata_w_b(parms,grads,learning_rate,net_dims)
elif choice=='momentum': #包含动量的梯度下降
parms,v=update_w_b_momentum(parms,v,grads,net_dims,learning_rate,beta)
elif choice=='adam': #使用Adam的梯度下降
t=t+1
parms,v,s=update_w_b_adam(parms,grads,v,s,t,net_dims,learning_rate,beta1,beta2,e)
if i%100==0:
costs.append(cost)
if (print_flag) and (i%1000==0):
print('迭代次数:%d,损失函数:%f' % (i,cost))
if is_plot==True:
plt.plot(costs)
plt.ylabel('costs')
plt.xlabel('epochs(per 100)')
plt.title('learning_rate='+str(learning_rate))
plt.show()
return parms
九、结果
(1)梯度下降
plt.figure(1)
train_x,train_y=load_datasets(is_plot=True)
plt.figure(2)
choice='gd'
parms=mole(train_x,train_y,choice,learning_rate=0.0007,mini_batches_size=64,iterations=10000,beta=0.9,
beta1=0.9,beta2=0.999,e=1e-8,print_flag=True,is_plot=True)
y_predict=predict(train_x,parms,train_y)
迭代次数:0,损失函数:0.591491
迭代次数:1000,损失函数:0.437251
迭代次数:2000,损失函数:0.406010
迭代次数:3000,损失函数:0.466082
迭代次数:4000,损失函数:0.498481
迭代次数:5000,损失函数:0.436827
迭代次数:6000,损失函数:0.414368
迭代次数:7000,损失函数:0.364876
迭代次数:8000,损失函数:0.372451
迭代次数:9000,损失函数:0.344451
Accuracy:0.8566666666666667
图1 梯度下降
(2)含momentum的梯度下降
plt.figure(1)
train_x,train_y=load_datasets(is_plot=True)
plt.figure(2)
choice='momentum'
parms=mole(train_x,train_y,choice,learning_rate=0.0007,mini_batches_size=64,iterations=10000,beta=0.9,
beta1=0.9,beta2=0.999,e=1e-8,print_flag=True,is_plot=True)
y_predict=predict(train_x,parms,train_y)
迭代次数:0,损失函数:0.591578
迭代次数:1000,损失函数:0.437270
迭代次数:2000,损失函数:0.406018
迭代次数:3000,损失函数:0.466111
迭代次数:4000,损失函数:0.498487
迭代次数:5000,损失函数:0.436937
迭代次数:6000,损失函数:0.414402
迭代次数:7000,损失函数:0.364930
迭代次数:8000,损失函数:0.372556
迭代次数:9000,损失函数:0.344508
Accuracy:0.8566666666666667
图2 含momentum的梯度下降法
注:由于小的学习速率和小的数据集,所以导致和普通的梯度下降效果差不多。
(3)含Adam的梯度下降
plt.figure(1)
train_x,train_y=load_datasets(is_plot=True)
plt.figure(2)
choice='adam'
parms=mole(train_x,train_y,choice,learning_rate=0.0007,mini_batches_size=64,iterations=10000,beta=0.9,
beta1=0.9,beta2=0.999,e=1e-8,print_flag=True,is_plot=True)
y_predict=predict(train_x,parms,train_y)
迭代次数:0,损失函数:0.590683
迭代次数:1000,损失函数:0.143360
迭代次数:2000,损失函数:0.072868
迭代次数:3000,损失函数:0.072864
迭代次数:4000,损失函数:0.162808
迭代次数:5000,损失函数:0.042045
迭代次数:6000,损失函数:0.107865
迭代次数:7000,损失函数:0.058769
迭代次数:8000,损失函数:0.043267
迭代次数:9000,损失函数:0.101692
Accuracy:0.9666666666666667
图3 含Adam的梯度下降法