一、深层神经网络的优势
即便是单层的神经网络只要隐藏层有足够多的神经单元,神经网络的宽度足够大,深层神经网络复杂度就足够高,就能拟合任意复杂的函数。但是并不是神经网络越宽越好,尤其是在计算及视觉领域,,例如:第一层是边缘信息;第二层是局部特征,神经网络越深,提取的特征越复杂,从模糊到详细、从局部到整体可见隐藏层越多能够提取的特征就越丰富、越复杂,模型的准确率就会越高,此时包含多隐藏层的深层神经网络往往性能更好。
但是在实际应用中,尽量选择层数较少的神经网络,才能有效避免发生过拟合,对于较为复杂的问题,再考虑使用深层神经网络模型。
二、符号标记
- 神经网络是L层:L是隐藏层和输出层层数之和,即隐藏层为L-1层。
- 用上标l表示当前层,l=1,2,…,L。
- n [ l ] n^{[l]} n[l]表示第l层包含的神经元个数。
- 输入层X用 A [ 0 ] A^{[0]} A[0]表示,其维度是( n [ 0 ] n^{[0]} n[0],m),其中 n [ 0 ] = n x n^{[0]}=n_x n[0]=nx表示输入层特征数目,m表示样本个数。
- 输出层用 A [ L ] A^{[L]} A[L]表示。
- 对于第l层神经元,神经元个数为
n
[
l
]
n^{[l]}
n[l],各符号标记含义如下:
- W [ l ] W^{[l]} W[l]表示该层权重参数,维度为 ( n [ l ] , n [ l − 1 ] ) (n^{[l]},n^{[l-1]}) (n[l],n[l−1]);
- b [ l ] b^{[l]} b[l]表示该层偏置参数,维度为 ( n [ l ] , 1 ) (n^{[l]},1) (n[l],1);
- Z [ l ] Z^{[l]} Z[l]表示该层线性输出,维度为 ( n [ l ] , 1 ) (n^{[l]},1) (n[l],1);
- A [ l ] A^{[l]} A[l]表示该层非线性输出,维度为 ( n [ l ] , 1 ) (n^{[l]},1) (n[l],1)。
三、前向传播与反向传播
-
前向传播:
{ Z [ l ] = W [ l ] A [ l − 1 ] + b [ l ] A [ l ] = g ( Z [ l ] ) \{{Z^{[l]}=W{[l]}A^{[l-1]}+b^{[l]} \atop A^{[l]}=g(Z^{[l]})} {A[l]=g(Z[l])Z[l]=W[l]A[l−1]+b[l]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ba7mPw44-1635233129590)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\image-20211021152209322.png)] -
反向传播:
{ d Z [ l ] = d A [ l ] ∗ g ‘ ( Z [ l ] ) d W [ l ] = 1 m d Z [ l ] ⋅ A [ l − 1 ] T d b [ l ] = 1 m ∑ d Z [ l ] d A [ l − 1 ] = W [ l ] T ⋅ d Z [ l ] \{{{dZ^{[l]}=dA^{[l]}*g`(Z^{[l]}) \atop dW^{[l]}=\frac{1}{m}dZ^{[l]}·A^{[l-1]T}} \atop {db^{[l]}=\frac{1}{m}\sum dZ^{[l]} \atop dA^{[l-1]}=W^{[l]T}·dZ^{[l]}}} {dA[l−1]=W[l]T⋅dZ[l]db[l]=m1∑dZ[l]dW[l]=m1dZ[l]⋅A[l−1]TdZ[l]=dA[l]∗g‘(Z[l])
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-C1TE5uvT-1635233129600)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\image-20211021152223131.png)] -
L层神经网络数据流图:通过此图我们可以清晰的掌握神经网络的前向传播和反向传播过程。注意第一层神经网络的反向传播没有输出 d A [ 0 ] dA^{[0]} dA[0],因为没有后续数据流,不影响计算参数 d W [ 1 ] 、 d b [ 1 ] dW^{[1]}、db^{[1]} dW[1]、db[1]。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Gwy7vfbq-1635233129607)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\image-20211021152141448.png)]
-
输出层L中 d A dA dA的计算公式由交叉熵损失可得:
d A [ L ] = 1 − Y 1 − A [ L ] − Y A [ L ] dA^{[L]}=\frac{1-Y}{1-A^{[L]}}-\frac{Y}{A^{[L]}} dA[L]=1−A[L]1−Y−A[L]Y -
公式总结:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qG6ub8NI-1635233129615)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\image-20211021152117477.png)]
四、多分类函数Softmax
-
基本原理:多分类与二分类(Sigmoid函数)的主要区别就在于神经网络输出层神经元的个数,而分类模型的输出层只有一个神经元,而多分类模型的输出层有多个神经元,需要使用Softmax函数来处理。Softmax函数将 Z [ L ] Z^{[L]} Z[L]映射到预测概率(区间[0,1])上,将其看成概率,各神经元输出概率之和为1,通过比较各概率值的大小,概率值较大的神经元对应的类别则为预测值。
A [ L ] = e Z [ L ] ∑ e Z i [ L ] A^{[L]}=\frac{e^{Z^{[L]}}}{\sum e^{Z^{[L]}_i}} A[L]=∑eZi[L]eZ[L] -
Softmax损失函数:因为不是二分问题,因此不能再套用交叉熵损失函数了,我们希望正确类别对应的概率值 A i [ L ] ( i = 1 , 2 , … , C , C 为 总 的 类 别 个 数 ) A^{[L]}_i(i=1,2,…,C,C为总的类别个数) Ai[L](i=1,2,…,C,C为总的类别个数),越大越好,这样错误越小,损失也就越小。_
L = − ∑ y i l n a i [ L ] L=-\sum y_iln a^{[L]}_i L=−∑yilnai[L]
y向量中只有正确的类别对应位置为1,其他全为0。上式还可以写成(其中k为正确类别对应的神经元,这样也可以将概率值越大转化为损失越小):
− l o g a k [ L ] -log a^{[L]}_k −logak[L] -
对Softmax函数求导:根据上述损失表达式,计算损失函数对 z [ L ] z^{[L]} z[L]的梯度 d z [ L ] dz^{[L]} dz[L]。
L 对 z i [ L ] 的 偏 导 数 为 : ∂ L ∂ z i [ L ] = ∑ j ( ∂ L j ∂ a j [ L ] ∂ a j [ L ] ∂ z i [ L ] ) L对z^{[L]}_i的偏导数为:\frac{∂L}{∂z^{[L]}_i}=\sum_j (\frac{∂L_j}{∂a^{[L]}_j} \frac{∂a^{[L]}_j}{∂z^{[L]}_i}) L对zi[L]的偏导数为:∂zi[L]∂L=j∑(∂aj[L]∂Lj∂zi[L]∂aj[L])
注意,再S0ftmax函数计算公式中分母包含了所有神经元的输出,因此不等于i的其他输出里也包含了 z i z_i zi,所有的 a j [ L ] a^{[L]}_j aj[L]都要纳入计算范围。后面的计算也需要分为i=j和i!=j两种情况求导。
第 一 部 分 : ∂ L j ∂ a j [ L ] = ∂ ( − y j l n a j [ L ] ) ∂ a j [ L ] = − y i a j [ L ] 第一部分:\frac{∂L_j}{∂a^{[L]}_j}=\frac{∂(-y_jln a^{[L]}_j)}{∂a^{[L]}_j}=-\frac{y_i}{a^{[L]}_j} 第一部分:∂aj[L]∂Lj=∂aj[L]∂(−yjlnaj[L])=−aj[L]yi第 二 部 分 : { ∂ a j [ L ] ∂ z i [ L ] = ∂ a i [ L ] ∂ z i [ L ] = ∂ ( e Z i [ L ] ∑ k e Z k [ L ] ) ∂ z i [ L ] = ∑ k e Z k [ L ] e Z i [ L ] − ( e Z i [ L ] ) 2 ( ∑ k e Z k [ L ] ) 2 = ( e Z i [ L ] ∑ k e Z k [ L ] ) ( 1 − e Z i [ L ] ∑ k e Z k [ L ] ) = a i [ L ] ( 1 − a i [ L ] ) , i = j ∂ a j [ L ] ∂ z i [ L ] = ∂ ( e Z j [ L ] ∑ k e Z k [ L ] ) ∂ z i [ L ] = − e Z j [ L ] ( 1 ∑ k e Z k [ L ] ) 2 e Z i [ L ] = − a i [ L ] a j [ L ] , i ! = j 第二部分:\{{\frac{∂a^{[L]}_j}{∂z^{[L]}_i}=\frac{∂a^{[L]}_i}{∂z^{[L]}_i}=\frac{∂(\frac{e^{Z^{[L]}_i}}{\sum_k e^{Z^{[L]}_k}})}{∂z^{[L]}_i}=\frac{\sum_k e^{Z^{[L]}_k}e^{Z^{[L]}_i}-(e^{Z^{[L]}_i})^2}{(\sum_k e^{Z^{[L]}_k})^2}=(\frac{e^{Z^{[L]}_i}}{\sum_k e^{Z^{[L]}_k}})(1-\frac{e^{Z^{[L]}_i}}{\sum_k e^{Z^{[L]}_k}})=a^{[L]}_i(1-a^{[L]}_i),i=j\atop \frac{∂a^{[L]}_j}{∂z^{[L]}_i}=\frac{∂(\frac{e^{Z^{[L]}_j}}{\sum_k e^{Z^{[L]}_k}})}{∂z^{[L]}_i}=-e^{Z^{[L]}_j}(\frac{1}{\sum_k e^{Z^{[L]}_k}})^2e^{Z^{[L]}_i}=-a^{[L]}_ia^{[L]}_j,i!=j} 第二部分:{∂zi[L]∂aj[L]=∂zi[L]∂(∑keZk[L]eZj[L])=−eZj[L](∑keZk[L]1)2eZi[L]=−ai[L]aj[L],i!=j∂zi[L]∂aj[L]=∂zi[L]∂ai[L]=∂zi[L]∂(∑keZk[L]eZi[L])=(∑keZk[L])2∑keZk[L]eZi[L]−(eZi[L])2=(∑keZk[L]eZi[L])(1−∑keZk[L]eZi[L])=ai[L](1−ai[L]),i=j
将上述两部分组合起来得到:
∂ L ∂ z i [ L ] = ∑ j ( ∂ L j ∂ a j [ L ] ∂ a j [ L ] ∂ z i [ L ] ) = ∑ i = j ( ∂ L j ∂ a j [ L ] ∂ a j [ L ] ∂ z i [ L ] ) + ∑ i ! = j ( ∂ L j ∂ a j [ L ] ∂ a j [ L ] ∂ z i [ L ] ) = ∑ i = j ( − y i a j [ L ] ) ( a i [ L ] ( 1 − a i [ L ] ) ) + ∑ i ! = j ( − y i a j [ L ] ) ( − a i [ L ] a j [ L ] ) = ∑ i = j ( a i [ L ] y i − y i ) + ∑ i ! = j a i [ L ] y j = a i [ L ] ∑ j y j − y i \frac{∂L}{∂z^{[L]}_i}=\sum_j (\frac{∂L_j}{∂a^{[L]}_j} \frac{∂a^{[L]}_j}{∂z^{[L]}_i})\frac{}{}=\sum_{i=j}(\frac{∂L_j}{∂a^{[L]}_j} \frac{∂a^{[L]}_j}{∂z^{[L]}_i})+\sum_{i!=j}(\frac{∂L_j}{∂a^{[L]}_j} \frac{∂a^{[L]}_j}{∂z^{[L]}_i})=\sum_{i=j}(-\frac{y_i}{a^{[L]}_j})(a^{[L]}_i(1-a^{[L]}_i))+\sum_{i!=j}(-\frac{y_i}{a^{[L]}_j})(-a^{[L]}_ia^{[L]}_j)=\sum_{i=j}(a^{[L]}_iy_i-y_i)+\sum_{i!=j}a^{[L]}_iy_j=a^{[L]}_i\sum_jy_j-y_i ∂zi[L]∂L=j∑(∂aj[L]∂Lj∂zi[L]∂aj[L])=i=j∑(∂aj[L]∂Lj∂zi[L]∂aj[L])+i!=j∑(∂aj[L]∂Lj∂zi[L]∂aj[L])=i=j∑(−aj[L]yi)(ai[L](1−ai[L]))+i!=j∑(−aj[L]yi)(−ai[L]aj[L])=i=j∑(ai[L]yi−yi)+i!=j∑ai[L]yj=ai[L]j∑yj−yi
因为y中只有 y i y_i yi为1,其他 y j y_j yj都为0.进一步简化得到:
∂ L ∂ z i [ L ] = a i [ L ] − y i 如 果 是 m 个 样 本 则 可 以 写 成 ∂ L ∂ Z [ L ] = A [ L ] − Y \frac{∂L}{∂z^{[L]}_i}=a^{[L]}_i-y_i如果是m个样本则可以写成\frac{∂L}{∂Z^{[L]}}=A^{[L]}-Y ∂zi[L]∂L=ai[L]−yi如果是m个样本则可以写成∂Z[L]∂L=A[L]−Y
经过推到我们发现,多分类函数Softmax的梯度 d Z [ L ] dZ^{[L]} dZ[L]与而分类函数Sigmoid函数的梯度是完全一样的,因此我们可以继续进行神经网络的反向传播梯度计算了。
五、深层神经网络的Python实现
将会解决一个图像二分类问题,可能前馈神经网络效果并不够好,可以对算法进行进一步优化,也可以采用后面讲解的卷积神经网络加以解决。
-
准备数据:首先需要将图片导入,还需要将图片矩阵平铺式展开,并且将图片所有像素归一化至[0,1]区间。
#训练集,500张图片,250张猫0,250张狗1 file='F:dataset/train/*.jpg' coll=io.ImageCollection(file) X_train=np.asarray(coll) Y_train=np.hstack((np.ones((1,250)),np.zeros((1,250)))) #测试集,200张图,100猫0,100狗1 file='F:dataset/test/*.jpg' coll=io.ImageCollection(file) X_test=np.asarray(coll) Y_test=np.hstack((np.ones((1,100)),np.zeros((1,100)))) #获取数量 m_train=X_train.shape[0] #训练样本数量 m_test=X_test.shape[0] #测试样本数量 w, h, d=X_train.shape[1],X_train.shape[2],X_train.shape[3] #图片的维度64,64,3(彩色图片) #神经网络的输入层是一维向量,此三通道矩阵是三维矩阵,需要将图片平铺式展开为一维向量 #一般还要将图片所有像素归一化至[0,1]区间,也就是把像素值除以255 X_train=X_train.reshape(m_train,-1).T X_test=X_test.reshape(m_test,-1).T X_train=X_train/255 X_test=X_test/255
-
参数初始化:深层神经网络需要一个神经网络总层数长度的列表,存储神经网络各层神经元个数,包括输入层,layer_dims=[2,3,1]表示输入层有两个神经元,隐藏层只有一层,有三个神经元,输出层有一个神经元。各层(所以要加上str())参数被存储在字典中。
def initialize_parameters(layer_dims): """ 函数输入: layer_dims:列表,神经网络各层神经元个数,包括输入层 函数输出: parameters:存储参数的字典 """ np.random.seed(5) parameters={} L=len(layer_dims) #神经网络的层数,包含输入层 for l in range(1,L): parameters['W'+str(l)]=np.random.randn(layer_dims[l], layer_dims[l-1])*0.1 parameters['b'+str(l)]=np.zeros((layer_dims[l],1)) return parameters
-
前向传播:浅层神经网络中隐藏层的激活函数一般选择tanh函数,深层神经网络隐藏层的激活函数一般选择ReLU函数。
#ReLU函数的定义 def relu(Z): """ 函数输入: Z:激活函数的输入,神经元线性输出 函数输出: A:激活函数的输出,神经元非线性输出 """ A=np.maximum(0,Z) return A #二分类问题,输出层的激活函数采用Sigmoid函数 def sigmoid(Z): """ 函数输入: Z:激活函数输入,神经元线性输出 函数输出: A:激活函数输出,神经元非线性输出 """ A=1/(1+np.exp(-Z)) return A #单层前向传播函数,计算该层网络的输出A def single_layer_forward(A_prev, W, b, activation): """ 函数输入: A_prev:该层网络的输入,上一层网络的输出 W:该层网络的权重参数 b:该层网络的偏置参数 activation:该层网络使用的激活函数 函数输出: A:该层网络输出 cache:存储所有的中间变量A_prev, W, b, Z """ Z=np.dow(W,A_prev)+b #线性输出 if activation=="sigmoid": A=sigmoid(Z) elif activation=="relu": A=relu(Z) cache= (A_prev, W, b, Z) return A, cache #整个网络的前向传播函数 def forward_propagation(X, parameters): """ 函数输入: X:神经网络输入 parameters:该层网络的权重 函数输出: A:该层网络的输出 caches:存储各层网络所有的中间变量 """ caches=[] A=X L=len(parameters)//2 #因为参数有W,b #前L-1层使用ReLU函数 for l in range(1,L): A_prev=A A,cache=single_layer_forward(A_prev, parameters['W'+str(l)], parameters['b'+str(l)], "relu") caches.append(cache) #第L层使用Sigmoid函数 AL,cache=single_layer_forward(A, parameters['W'+str(L)], parameters['b'+str(L)], "sigmoid") caches.append(cache) return AL,caches
-
交叉熵损失:需要计算AL与Y之间的交叉熵。
def compute_cost(AL,Y): """ 函数输入: AL:神经网络输出层输出 Y:神经网络真实标签 函数输出: cost:交叉熵损失 """ m=AL.shape[1] cross_entropy=-(Y*np.log(AL)+(1-Y)*np.log(1-AL)) cost=1.0/m*np.sum(cross_entropy) return cost
-
反向传播:
def relu_backward(dA, Z): #relu函数的求导函数 """ 函数输入: dA:A的梯度 Z:神经网络线性输出 函数输出: dZ:Z的梯度 """ dZ=np.array(dA, copy=True) dZ[Z<=0]=0 return dZ def sigmoid_backward(dA, Z): #sigmoid函数的求导函数 """ 函数输入: dA:A的梯度 Z:神经网络线性输出 函数输出: dZ:Z的梯度 """ s=1/(1+np.exp(-Z)) dZ=dA*s*(1-s) return dZ #单层神经网络的反向传播函数 def single_layer_backward(dA, cache, activation): """ 函数输入: dA:A的梯度 cache:存储所有的中间变量A_prev,W,b,Z activation:选择的激活函数 函数输出: dA_prev:上一层A_prev的梯度 dW:参数W的梯度 db:参数b的梯度 """ A_prev, W, b, Z=cache if activation=="relu": dZ=relu_backward(dA,Z) elif activation=="sigmoid": dZ=sigmoid_backward(dA,Z) m=dA.shape[1] dW=1/m*np.dot(dZ,A_prev.T) db=1/m*np.sum(dZ,axis=1,keepdims=True) dA_prev=np.dot(W.T,dZ) return dA_prev,dW,db #整个神经网络的反向传播函数 def backward_propagation(AL, Y, caches): """ 函数输入: AL:神经网络输出层输出 cache:存储所有的中间变量A_prev,W,b,Z Y:神经网络真实标签 函数输出: grads:所有参数梯度 """ grads={} L=len(caches) #神经网络层数 m=AL.shape[1] #样本个数 #AL值 dAL=-(np.divide(Y, AL)-np.divide(1-Y, 1-AL)) #第L层,激活函数是Sigmoid current_cache=caches[L-1] grads["dA"+str(L-1)],grads["dW"+str(L)],grads["db"+str(L)]=single_layer_backward( dAL, current_cache,activation="sigmoid") #前L-1层,激活函数是ReLU for l in reversed(range(L-1)): current_cache=caches[l] dA_prev_temp,dW_temp,db_temp=single_layer_backward(grads["dA"+str(l+1)], current_cache,activation="relu") grads["dA"+str(l)]=dA_prev_temp grads["dW"+str(l+1)]=dW_temp grads["db"+str(l+1)]=db_temp return grads
-
更新参数:使用梯度下降算法的公式对神经网络各层参数进行更新。
def update_parameters(parameters, grads, learning_rate=0.1): """ 函数输入: parameters:网络参数 grads:神经网络参数梯度 函数输出: parameters:网络参数 """ L=len(parameters)//2 for l in range(L): parameters['W'+str(l+1)]-=learning_rate*grads['dW'+str(l+1)] parameters['b'+str(l+1)]-=learning_rate*grads['db'+str(l+1)] return parameters
-
构建整个神经网络:将上述模块整合起来,构建神经网络模型。
def nn_model(X, Y, layers_dims, learning_rate=0.01,num_iterations=3000): """ 函数输入: X:神经网络输入 Y:样本真实标签 layers_dim:列表,神经网络各层神经元个数,包含输入层和输出层 num_iterations:训练次数 learning_rate:学习率 函数输出: parameters:训练完成后的网络参数 """ np.random.seed(1) costs=[] #参数初始化 parameters=initialize_parameters(layers_dims) #迭代训练 for i in range(0,num_iterations): #正向传播 AL,caches=forward_propagation(X,parameters) #计算损失函数 cost=compute_cost(AL,Y) #反向传播 grads=backward_propagation(AL, Y, caches) #更新参数 parameters = update_parameters(parameters, grads, learning_rate) #绘制cost趋势图 plt.plot(np.squeeze(cost)) plt.ylabel('cost') plt.xlabel('迭代训练次数') plt.title("学习率为"+str(learning_rate)) plt.show() return parameters #定义整个神经网络的预测函数 def predict(X,parameters): """ 函数输入: X:神经网络输入 parameters:训练完成后的网络参数 函数输出: Y_pred:预测样本标签 """ #L层模型前向传播 AL,caches =forward_propagation(X,parameters) #预测标签 Y_pred=np.zeros((1,X.shape[1])) Y_pred[AL>0.5]=1 return Y_pred
-
训练与预测:构建一个五层神经网络,迭代训练次数为2000次,学习率设为0.02。
#构建神经网络 layers_dims=[12288,200,100,20,6,1] #5层神经网络 parameters=nn_model(X_train,Y_train,layers_dims, num_iterations=2000, learning_rate=0.02) #使用该模型对测试集进行预测 Y_test_prev=predict(X_test,parameters) acc_test=np.mean(Y_test_pred==Y_test) print(acc_test)
-
全部代码:
需要安一下skimage:
配置清华镜像源:
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/ conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/
然后安一下conda install opencv和conda install scikit-image
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
import skimage.io as io
#plt.rcParams['font.sans-serif']=['SimHei']
#plt.rcParams['axes.unicode_minus']=False
#训练集,500张图片,250张猫0,250张狗1
file=r'F:/dataset/train/*.jpg'
coll=io.ImageCollection(file)
#查看图片io.imshow(coll[10])
X_train=np.asarray(coll)
Y_train=np.hstack((np.ones((1,250)),np.zeros((1,250))))
#测试集,200张图,100猫0,100狗1
file='F:/dataset/test/*.jpg'
coll=io.ImageCollection(file)
X_test=np.asarray(coll)
Y_test=np.hstack((np.ones((1,100)),np.zeros((1,100))))
#获取数量
m_train=X_train.shape[0] #训练样本数量
m_test=X_test.shape[0] #测试样本数量
w, h, d=X_train.shape[1],X_train.shape[2],X_train.shape[3] #图片的维度64,64,3(彩色图片)
#神经网络的输入层是一维向量,此三通道矩阵是三维矩阵,需要将图片平铺式展开为一维向量
#一般还要将图片所有像素归一化至[0,1]区间,也就是把像素值除以255
X_train=X_train.reshape(m_train,-1).T
X_test=X_test.reshape(m_test,-1).T
#查看维度print(X_train.shape)
X_train=X_train/255
X_test=X_test/255
def initialize_parameters(layer_dims):
"""
函数输入:
layer_dims:列表,神经网络各层神经元个数,包括输入层
函数输出:
parameters:存储参数的字典
"""
np.random.seed(5)
parameters={}
L=len(layer_dims) #神经网络的层数,包含输入层
for l in range(1,L):
parameters['W'+str(l)]=np.random.randn(layer_dims[l],layer_dims[l-1])*0.1
parameters['b'+str(l)]=np.zeros((layer_dims[l],1))
return parameters
#ReLU函数的定义
def relu(Z):
"""
函数输入:
Z:激活函数的输入,神经元线性输出
函数输出:
A:激活函数的输出,神经元非线性输出
"""
A=np.maximum(0,Z)
return A
#二分类问题,输出层的激活函数采用Sigmoid函数
def sigmoid(Z):
"""
函数输入:
Z:激活函数输入,神经元线性输出
函数输出:
A:激活函数输出,神经元非线性输出
"""
A=1/(1+np.exp(-Z))
return A
#单层前向传播函数,计算该层网络的输出A
def single_layer_forward(A_prev, W, b, activation):
"""
函数输入:
A_prev:该层网络的输入,上一层网络的输出
W:该层网络的权重参数
b:该层网络的偏置参数
activation:该层网络使用的激活函数
函数输出:
A:该层网络输出
cache:存储所有的中间变量A_prev, W, b, Z
"""
#W_mat=np.mat(W)
#A_prev_mat=np.mat(A_prev)
#print("A"+str(A_prev.shape)+str(type(A_prev)))
#print("W"+str(W.shape)+str(type(W)))
Z=np.dot(W,A_prev)+b #线性输出
#print(W.shape)
#print(A_prev.shape)
if activation == "sigmoid":
A=sigmoid(Z)
elif activation == "relu":
A=relu(Z)
cache= (A_prev, W, b, Z)
return A, cache
#整个网络的前向传播函数
def forward_propagation(X, parameters):
"""
函数输入:
X:神经网络输入
parameters:该层网络的权重
函数输出:
A:该层网络的输出
caches:存储各层网络所有的中间变量
"""
caches=[]
A=X
L=len(parameters)//2 #因为参数有W,b
#前L-1层使用ReLU函数
for l in range(1,L):
A_prev=A
A,cache=single_layer_forward(A_prev,parameters['W'+str(l)],parameters['b'+str(l)],"relu")
caches.append(cache)
#第L层使用Sigmoid函数
AL,cache=single_layer_forward(A,parameters['W'+str(L)],parameters['b'+str(L)],"sigmoid")
caches.append(cache)
return AL,caches
def compute_cost(AL,Y):
"""
函数输入:
AL:神经网络输出层输出
Y:神经网络真实标签
函数输出:
cost:交叉熵损失
"""
m=AL.shape[1]
cross_entropy=-(Y*np.log(AL)+(1-Y)*np.log(1-AL))
cost=1.0/m*np.sum(cross_entropy)
return cost
def relu_backward(dA, Z): #relu函数的求导函数
"""
函数输入:
dA:A的梯度
Z:神经网络线性输出
函数输出:
dZ:Z的梯度
"""
dZ=np.array(dA, copy=True)
dZ[Z<=0]=0
return dZ
def sigmoid_backward(dA, Z): #sigmoid函数的求导函数
"""
函数输入:
dA:A的梯度
Z:神经网络线性输出
函数输出:
dZ:Z的梯度
"""
s=1/(1+np.exp(-Z))
dZ=dA*s*(1-s)
return dZ
#单层神经网络的反向传播函数
def single_layer_backward(dA, cache, activation):
"""
函数输入:
dA:A的梯度
cache:存储所有的中间变量A_prev,W,b,Z
activation:选择的激活函数
函数输出:
dA_prev:上一层A_prev的梯度
dW:参数W的梯度
db:参数b的梯度
"""
A_prev, W, b, Z=cache
if activation=="relu":
dZ=relu_backward(dA,Z)
elif activation=="sigmoid":
dZ=sigmoid_backward(dA,Z)
m=dA.shape[1]
dW=1/m*np.dot(dZ,A_prev.T)
db=1/m*np.sum(dZ,axis=1,keepdims=True)
dA_prev=np.dot(W.T,dZ)
return dA_prev,dW,db
#整个神经网络的反向传播函数
def backward_propagation(AL, Y, caches):
"""
函数输入:
AL:神经网络输出层输出
cache:存储所有的中间变量A_prev,W,b,Z
Y:神经网络真实标签
函数输出:
grads:所有参数梯度
"""
grads={}
L=len(caches) #神经网络层数
m=AL.shape[1] #样本个数
#AL值
dAL=-(np.divide(Y, AL)-np.divide(1-Y, 1-AL))
#第L层,激活函数是Sigmoid
current_cache=caches[L-1]
grads["dA"+str(L-1)],grads["dW"+str(L)],grads["db"+str(L)]=single_layer_backward(dAL, current_cache,activation="sigmoid")
#前L-1层,激活函数是ReLU
for l in reversed(range(L-1)):
current_cache=caches[l]
dA_prev_temp,dW_temp,db_temp=single_layer_backward(grads["dA"+str(l+1)],current_cache,activation="relu")
grads["dA"+str(l)]=dA_prev_temp
grads["dW"+str(l+1)]=dW_temp
grads["db"+str(l+1)]=db_temp
return grads
def update_parameters(parameters, grads, learning_rate=0.1):
"""
函数输入:
parameters:网络参数
grads:神经网络参数梯度
函数输出:
parameters:网络参数
"""
L=len(parameters)//2
for l in range(L):
parameters['W'+str(l+1)]-=learning_rate*grads["dW"+str(l+1)]
parameters['b'+str(l+1)]-=learning_rate*grads["db"+str(l+1)]
return parameters
def nn_model(X, Y, layers_dims, learning_rate=0.01,num_iterations=3000):
"""
函数输入:
X:神经网络输入
Y:样本真实标签
layers_dim:列表,神经网络各层神经元个数,包含输入层和输出层
num_iterations:训练次数
learning_rate:学习率
函数输出:
parameters:训练完成后的网络参数
"""
np.random.seed(1)
costs=[]
#参数初始化
parameters=initialize_parameters(layers_dims)
#迭代训练
for i in range(0,num_iterations):
#正向传播
AL,caches=forward_propagation(X,parameters)
#计算损失函数
cost=compute_cost(AL,Y)
#反向传播
grads=backward_propagation(AL, Y, caches)
#更新参数
parameters = update_parameters(parameters, grads, learning_rate)
# 每迭代 100 次,打印 cost
if (i+1) % 100 == 0:
print ("Cost after iteration %i: %f" %(i+1, cost))
#绘制cost趋势图
plt.plot(np.squeeze(cost))
plt.ylabel('cost')
plt.xlabel('迭代训练次数')
plt.title("学习率为"+str(learning_rate))
plt.show()
return parameters
#定义整个神经网络的预测函数
def predict(X,parameters):
"""
函数输入:
X:神经网络输入
parameters:训练完成后的网络参数
函数输出:
Y_pred:预测样本标签
"""
#L层模型前向传播
AL,caches =forward_propagation(X,parameters)
#预测标签
Y_pred=np.zeros((1,X.shape[1]))
Y_pred[AL>0.5]=1
return Y_pred
#构建神经网络
layers_dims=[12288,200,100,20,6,1] #5层神经网络
parameters=nn_model(X_train,Y_train,layers_dims,num_iterations=2000,learning_rate=0.02)
#使用该模型对测试集进行预测
Y_test_prev=predict(X_test,parameters)
acc_test=np.mean(Y_test_pred==Y_test)
print(acc_test)