神经网络和深度学习(2)——从理论到实现:从零开始搭建L层神经网络

it2024-11-23  21

说明:本次博文训练所用的数据集依然是之前的用Logistics回归识别猫文章的数据集 数据集请参见:https://blog.csdn.net/weixin_44586473/article/details/97634016

1.导入必要的库

这个就不多说啦,直接上代码:

import numpy as np import h5py import lr_utils

2.定义激活函数

我们打算在所有的隐藏层都使用Relu()函数,最后的输出层使用sigmoid()函数 下面我们来看看这两个激活函数的图像及其导数图像: sigmoid函数的表达式是这样的: f ( Z ) = 1 1 + e ( − Z ) f(Z) = \frac{1}{1 + e^{(-Z)}} f(Z)=1+e(Z)1 Relu函数的表达式是这样的: { Z = Z ( Z > 0 ) Z = 0 ( Z ≤ 0 ) \begin{cases} Z = Z (Z > 0)\\ Z = 0 (Z ≤ 0) \end{cases} {Z=Z(Z>0)Z=0(Z0)

def sigmoid(Z): cache = Z #这里我们要将传入的Z作为cache保存下来,以后的步骤用得上 A = 1 / (1 + np.exp(-Z) return A,cache #cache也要作为返回值和A一起返回 def Relu(Z): cache = Z A = np.maximum(0,Z) #Relu函数的定义式 return A, cache

3.初始化参数Parameters

这里我们需要初始化的参数是 W [ l ] ⃗ , b [ l ] ⃗ \vec{W^{[l]}}, \vec{b^{[l]}} W[l] ,b[l] ,也就是每一层的W和b的矩阵 可是我们一开始并不知道我们要建立多少层,所以这里可以传入一个layer_dims的一个元组,我们初始化的W1,b1,W2,b2…可以保存在一个字典里面方便后续的计算过程中随时取用 同时,还需要提醒一件事情,我们在这个程序中,用大写的W矩阵表示w的转置构成的矩阵,因此 W [ l ] ⃗ \vec{W^{[l]}} W[l] 的维度就应该是[ n [ l ] , n [ l − 1 ] n^{[l]}, n^{[l-1]} n[l],n[l1]],而 b [ l ] ⃗ \vec{b^{[l]}} b[l] 的维度应该是[ n [ l ] n^{[l]} n[l],1]

def initializer_W_b(layer_dims): L = len(layer_dims) #这里我们通过len方法获取了神经网络的层数 parameters = {} #构造一个字典 for l in range(1,L): parameters["W"+str(l)] = np.random.randn(layer_dims[l],layer_dims[l-1]) / np.sqrt(layer_dims[l - 1]) parameters["b"+str(l)] = np.zeros((layer_dims[l],1)) return parameters

4.计算L层模型的前向传播

这里我们需要三个函数:1.对于一层而言的线性计算函数 -> 2.对于一层而言的前向传播 -> 3.L层模型的前向传播 如果大家想更深度地了解深层神经网络的工作过程:请看这一篇文章: https://blog.csdn.net/weixin_44586473/article/details/98058551 神经网络和深度学习(1)——深度神经网络的工作模式及其计算步骤的详细分析 但是为了便于大家理解我程序的写法,我贴一张图来仔细讲解: 对于某一层的前向传播,它分成了两个步骤:1.线性计算 2.激活 线性计算的公式中,我们要以Z作为返回值,而缓存A_prev, W, b,作为linear_cache和Z一起返回 由于激活部分需要传入Z,可是我们在反向传播过程中还需要用到它,所以在激活这一步里面,我们将Z作为activation_cache和A一起返回

def linear_forward(A, W, b): Z = np.dot(W, A) + b cache = (A, W, b) return Z, cache #对于一层而言的向前传播 def linear_activation_forward(A_prev, W, b, activation_function): if activation_function == "sigmoid": Z,linear_cache = linear_forward(A_prev, W, b) A,activation_cache = sigmoid(Z) elif activation_function == "Relu": Z,linear_cache = linear_forward(A_prev, W, b) A,activation_cache = Relu(Z) cache = (linear_cache, activation_cache) return A,cache def L_model_forward(X,parameters): caches = [] #因为每一层的前向传播都会产生一组(linear_cache,activation_cache)所以我们要用一个列表存放这些cache L = len(parameters) // 2 A = X for l in range(1,L): A_prev = A A, cache = linear_activation_forward(A_prev, parameters["W"+str(l)], parameters["b"+str(l)], "Relu") caches.append(cache) #将每一次计算产生的cache放入列表中 AL,cache = linear_activation_forward(A, parameters["W"+str(L)], parameters["b"+str(L)], "sigmoid") caches.append(cache) return AL,caches

5.计算代价

这里我们用的loss function是交叉熵损失函数 cost function就是将所有损失函数加起来再除以样本数量

def compute_cost(AL, Y): m = Y.shape[1] cost = (-1 / m) * np.sum(Y * np.log(AL) + (1 - Y) * (np.log(1 - AL))) cost = np.squeeze(cost) return cost

6. L层模型的反向传播

对于一层而言,反向传播的计算我们首先是要通过激活函数的导数计算出dZ,再通过dZ,还有linear_cache计算dW, db, dA_prev 那么首先,我们就要先定义激活函数的导数(注意:一般在这一步我们就可以把dZ求出来了,也就是说,这个函数是把激活函数求导和求dZ合并起来了)

def sigmoid_backward(dA, cache): #这里接受的cache其实是activation_cache Z = cache s = 1 / (1 + np.exp(-Z)) dZ = dA * s * (1 - s) return dZ def Relu_backward(dA, cache): Z = cache dZ = np.array(dA, copy=True) dZ[Z <= 0] = 0 #当Z <= 0时根据定义,那么dZ就等于0return dZ

然后我们来写线性反向传播(也就是传入dZ, linear_cache来计算dW, db, dA_prev的函数)

def linear_backward(dZ, cache): A_prev, W, b = cache m = A_prev.shape[1] #下面的计算就是按照公式走一遍 dW = (1 / m) * np.dot(dZ, A_prev.T) db = np.sum(dZ, axis = 1, keepdims = True) dA_prev = np.dot(W.T, dZ) return dA_prev, dW, db

把以上两步合并起来,就得到了一层的反向传播:

def linear_activation_backward(dA, cache, activation_function): linear_cache, activation_cache = cache if activation_function == "sigmoid": dZ = sigmoid_backward(dA, activation_cache) dA_prev,dW,db = linear_backward(dZ, linear_cache) elif activation_function == "Relu": dZ = Relu_backward(dA, activation_cache) dA_prev, dW, db = linear_backward(dZ,linear_cache) return dA_prev, dW, db

对于L层模型的反向传播,我们要传入的值就是AL, Y, caches了

def L_model_backward(AL, Y, caches): m = Y.shape[1] grads = {} L = len(caches) Y = Y.reshape(AL.shape) dAL = - (np.divide(Y, AL) - np.divide(1 - Y, 1 - AL)) current_cache = caches[L-1] grads["dA"+str(L)], grads["dW"+str(L)],grads["db"+str(L)] = linear_activation_backward(dAL, current_cache,"sigmoid") for l in reversed(range(L-1)): #不包括L-1 current_cache = caches[l] dA_prev, dW, db = linear_activation_backward(grads["dA"+str(l+2)], current_cache,"Relu") grads["dA"+str(l+1)] = dA_prev grads["dW"+str(l+1)] = dW grads["db"+str(l+1)] = db return grads

7.优化

这一步我们目前使用梯度下降法来优化代价函数 对于L层神经网络,它传入的应该是parameters这个字典,而非单个的W和b

def optimize_W_b(parameters, grads, learning_rate, num_iteration): 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

8.整合以上步骤并构建L层模型

def construct_L_model(X, Y, layer_dims, learning_rate, num_iteration): np.random.seed(1) parameters = initializer_W_b(layer_dims) costs = [] for i in range(num_iteration): AL, caches = L_model_forward(X,parameters) cost = compute_cost(AL, Y) if i % 100 == 0: costs.append(cost) print("第" + str(i) + "次迭代,成本值为:" + str(cost)) grads = L_model_backward(AL, Y, caches) parameters = optimize_W_b(parameters,grads,learning_rate, num_iteration) return parameters

9.数据集的导入以及参数的设定

这次的程序我们使用的数据集还是识别猫的那个 数据集的下载可以参见:https://blog.csdn.net/weixin_44586473/article/details/97634016

train_set_x_orig , train_set_y , test_set_x_orig , test_set_y , classes = lr_utils.load_dataset() train_x_flatten = train_set_x_orig.reshape(train_set_x_orig.shape[0], -1).T test_x_flatten = test_set_x_orig.reshape(test_set_x_orig.shape[0], -1).T train_x = train_x_flatten / 255 train_y = train_set_y test_x = test_x_flatten / 255 test_y = test_set_y #这里我们想构建一个三层神经网络: n_0 = 12288 n_1 = 7 n_2 = 5 n_3 = 1 layers_dims = (n_0,n_1,n_2,n_3)

10.调用函数并且Run

parameters = construct_L_model(train_x, train_y, (n_0,n_1,n_2,n_3), 0.005, 2000)

来看看结果: 第0次迭代,成本值为:0.6852813242158298 第100次迭代,成本值为:0.6201423211366498 第200次迭代,成本值为:0.5869193642660885 第300次迭代,成本值为:0.5433327058657983 第400次迭代,成本值为:0.49226714962476176 第500次迭代,成本值为:0.4469935057153989 第600次迭代,成本值为:0.407569413709912 第700次迭代,成本值为:0.37475107864805757 第800次迭代,成本值为:0.3437921663548236 第900次迭代,成本值为:0.3034880228855411 第1000次迭代,成本值为:0.2728375526744332 第1100次迭代,成本值为:0.254386082727315 第1200次迭代,成本值为:0.17260105549237426 第1300次迭代,成本值为:0.1399147875686999 第1400次迭代,成本值为:0.11877724511451739 第1500次迭代,成本值为:0.08814005795718635 第1600次迭代,成本值为:0.07016071658215332 第1700次迭代,成本值为:0.055644228599396266 第1800次迭代,成本值为:0.04660550031643402 第1900次迭代,成本值为:0.03962861417415093

当然,光是搭建神经网络还不够,因为它不一定能够完美完成我们需要的任务,我们还需要花费时间对他进行改善,那么在下一期,博主就会学习改善深层神经网络,到时候也会把学习心得发上博文

最新回复(0)