在前面两节课的基础上,这次作业是训练一个N层神经网络,来判断一张图片是否有猫,实现过程其实和第三周很相似,因为层数不确定,所以在向前传播和反向传播的时候会用到for循环,代码相对而言反而更精简了。贴出的代码可能和老师给的模板不一样,我没有看到老师的原版课程作业,也是在网上找的资料自己写的,但是网上的代码大部分函数封装度高,但初学为了方便,也好理解,我没有采用那种方法。
简单介绍一下模型,N层,传入每层的单元数,包括最后的输出层,这里我们是一个二分类问题,最后一层单元数就是1,隐藏层激活函数使用relu,最后输出层使用sigmoid
可能有人不好理解这个N层,和传入参数的问题,举个栗子就很好理解了。比如我们Layer维度传入的list是[4,3,2,1],这其实是一个三层神经网络,因为4是第0层,也就是输入层的特征值有4个,然后两个隐藏层,单元数分别为3,2,最后输出层
但在代码中会通过这个list来计算我们模型的层数,使用 len(layer_list)来计算,得到的结果是4,这就把第一层也算进去了,但这里得到的L=4其实不是真正意义上的我们模型的层数,这里不给它减1,而是保留4的值是因为,即使他是4,但list存储元素是从0开始的,也就是list[0]=4,list[1]=3,list[2]=2,这样子,而我们无论在计算w还是b的时候,都是(w1,b1),(w2,b2)直到3,可以发现数标是正好吻合的。
下面贴代码了:
1.导库
python">import numpy as np
import matplotlib.pyplot as plt
import h5py
2.参数初始化
python">#参数初始化,将所有w/b都封装在一个dict中
def initialize_parameters(layer_dims):
parameters = {}
L = len(layer_dims)
for i in range(1,L):
parameters['w'+ str(i)] = np.random.randn(layer_dims[i],layer_dims[i-1])*0.01
parameters['b'+ str(i)] = np.zeros((layer_dims[i],1))
assert(parameters['w'+ str(i)]).shape == (layer_dims[i],layer_dims[i-1])
assert(parameters['b'+ str(i)]).shape == (layer_dims[i],1)
return parameters
3.向前传播
python">#定义激活函数
def relu(Z):
A=(Z+abs(Z))/2
assert(A.shape == Z.shape)
return A
def sigmoid(Z):
A=1.0/(1+np.exp(-Z))
assert(A.shape == Z.shape)
return A
#向前传播
def forward_propagation(X,parameters):
#caches存储了每一层计算得到的A,Z值
caches = {}
L=len(parameters)//2
A_prev=X
for i in range(1,L):
Z=np.dot(parameters['w'+str(i)],A_prev)+parameters['b'+str(i)]
A=relu(Z)
caches['Z'+str(i)]=Z
caches['A'+str(i)]=A
#这一层计算得到的A需要保留,下一层计算Z要用
A_prev=A
#输出层的激活函数时sigmoid
Z=np.dot(parameters['w'+str(L)],A_prev)+parameters['b'+str(L)]
A=sigmoid(Z)
caches['Z'+str(L)]=Z
caches['A'+str(L)]=A
#这里多存一个X是因为反向传播的时候要用到
caches['A0'] = X
return A,caches
4.代价
python">#计算代价
def cpmpute_cost(A,Y):
m=Y.shape[1]
cost=-1/m*np.sum(np.multiply(np.log(A),Y)+np.multiply((1-Y),np.log(1-A)))
cost=np.squeeze(cost)
return cost
5.反向传播
python">#relu函数的导数
def relu_back(Z,dA):
deri = Z
deri[Z < 0]=0
deri[Z >=0]=1
return deri
#反向传播
def back_propagation(Y,caches,parameters):
#所有的dw和db
grads={}
L=len(caches)//2
m=Y.shape[1]
#AL其实就是一次迭代得到的预测值
AL=caches['A'+str(L)]
#因为sigmoid反向传和relu不同,所以单独处理
dZ=AL-Y
dW=np.dot(dZ,caches['A'+str(L-1)].T)/m
db=np.sum(dZ,axis=1,keepdims=True)/m
grads['dw'+str(L)]=dW
grads['db'+str(L)]=db
for i in reversed(range(1,L)):
dA=np.dot(parameters['w'+str(i+1)].T,dZ)
dZ=np.multiply(dA,relu_back(caches['Z'+str(i)],dA))
dW=1.0/m * np.dot(dZ,caches['A'+str(i-1)].T)
db=1.0/m * np.sum(dZ,axis=1,keepdims=True)
grads['dw'+str(i)]=dW
grads['db'+str(i)]=db
return grads
6.参数更新
python">#更新参数
def update_parameters(parameters, grads, alphs):
L = len(parameters)//2
for l in range(L):
parameters['w'+str(l+1)] = parameters['w'+str(l+1)] - alphs * grads['dw'+str(l+1)]
parameters['b'+str(l+1)] = parameters['b'+str(l+1)] - alphs * grads['db'+str(l+1)]
return parameters
7.数据(猫,也可以用其他数据集试试)
python">#处理数据
train_data = h5py.File('D:\\jupyter\\datasets\\train_catvnoncat.h5','r')
test_data = h5py.File('D:\\jupyter\\datasets\\test_catvnoncat.h5','r')
train_data_x=train_data['train_set_x'][:]
train_data_y=train_data['train_set_y'][:]
test_data_x=test_data['test_set_x'][:]
test_data_y=test_data['test_set_y'][:]
m_train=train_data_x.shape[0]
train_data_finalX=train_data_x.reshape(m_train,-1).T
m_test=test_data_x.shape[0]
test_data_finalX=test_data_x.reshape(m_test,-1).T
train_data_finalY=train_data_y.reshape(1,m_train)
test_data_finalY=test_data_y.reshape(1,m_test)
train_data_finalX=train_data_finalX/255
test_data_finalX=test_data_finalX/255
8.模型预测
python">#模型预测
def predict(X,parameters):
A2,caches=forward_propagation(X,parameters)
temp=A2.shape[1]
Y_pred=np.zeros([1,temp])
for i in range(temp):
if A2[:,i]>0.5:
Y_pred[:,i]=1
else:
Y_pred[:,i]=0
return Y_pred
#模型整合
def model(X,Y,layer_dims,iter_times,alphs,print_flag):
np.random.seed(1)
parameters=initialize_parameters(layer_dims)
for i in range(0,iter_times):
A,caches=forward_propagation(X,parameters)
cost=cpmpute_cost(A,Y)
grads=back_propagation(Y,caches,parameters)
parameters=update_parameters(parameters,grads,alphs)
if print_flag and i % 100 == 0:
print('iteration at ',i,' cost :',cost)
return parameters
最后测试一下:
python">n=train_data_finalX.shape[0]
layer_dims=[n,20,4,1]
parameters=model(train_data_finalX,train_data_finalY,layer_dims,2500,0.05,True)
y_pred_train=predict(train_data_finalX,parameters)
print('train acc is ',np.mean(y_pred_train == train_data_finalY)*100,'%')
y_pred_test=predict(test_data_finalX,parameters)
print('test acc is ',np.mean(y_pred_test == test_data_finalY)*100,'%')
得到的结果:
python">iteration at 0 cost : 0.6932015486338629
iteration at 100 cost : 0.6482987506672847
iteration at 200 cost : 0.6443527436694975
iteration at 300 cost : 0.6439059082659386
iteration at 400 cost : 0.6436651460852033
iteration at 500 cost : 0.6431109804509275
iteration at 600 cost : 0.6428896805499592
iteration at 700 cost : 0.6433981174416904
iteration at 800 cost : 0.6424129644194355
iteration at 900 cost : 0.6101151197326483
iteration at 1000 cost : 0.48396387299853605
iteration at 1100 cost : 0.42416172606012914
iteration at 1200 cost : 0.38773483207677206
iteration at 1300 cost : 0.3540606824486229
iteration at 1400 cost : 0.3387176239551042
iteration at 1500 cost : 0.3238536531634526
iteration at 1600 cost : 0.3148366753236183
iteration at 1700 cost : 0.3069509047774539
iteration at 1800 cost : 0.29751081135148866
iteration at 1900 cost : 0.28693234063058815
iteration at 2000 cost : 0.2870926250045233
iteration at 2100 cost : 0.3030038796284433
iteration at 2200 cost : 0.34263222828051587
iteration at 2300 cost : 0.23760048293266464
iteration at 2400 cost : 0.23174642600881754
train acc is 96.65071770334929 %
test acc is 74.0 %
总结:
在最开始的测试过程中,发现代价值降低到64以后,就不再变化了,或者变化微乎其微,我打印出每一次迭代中每一层的A,Z的值发现处理到第三次迭代时,最后得到的预测值几乎都一样,区别只在小数点后3位以上,把整个流程又推了一遍,发现是relu函数的导数有问题,之前用的是网上的一个版本,对照了老师讲的笔记写了这个,然后代价值就降低了
如果遇到预测值几乎都一样的情况,可以分析:
1.初始值是不是没有做预处理(均值化,归一化等)
2.传播过程某个环节出了问题,这个时候仔细推一遍