赞
踩
有什么写的不对的地方请大家在评论区指出来,我会及时改正,或是有什么疑问,我也会及时解答
网络结构是输入层->隐藏层->隐藏层->···->隐藏层->输出层,在每一层中,我会首先计算Z = np.dot(W,A) + b,这叫做【linear_forward】,然后再计算relu(Z) 或者 sigmoid(Z),这叫做【linear_activation_forward】,合并起来就是这一层的计算方法,所以每一层的计算都有这两个步骤,计算误差,然后就开始反向传播(计算出每一层w的导数),最后更新每一层参数信息
你也可以参照下图:
根据上的步骤我们先要把网络架构出来隐藏层层数,宽度,向前传输的公式是Z = np.dot(W,A) + b,我们根据这个公式定义矩阵和W,b,由公式np.dot(W,A) 得到W的形状就必须为(n,X.shape[0])n为该层的宽度,得到的节点结果就是(n,X.shape[1]),矩阵相加第一个维度要必须一样,所以b的形状就为(n,1),得到结果Z,Z经过激活函数变化后不会改变形状依然是(n,Xshape[1]),下一层隐藏层W形状就为(m,n)m为该层的宽度,b的形状为(m,1),依次类推,最后一层要和y的形状一样所以最后一个隐藏层的形状要为(Y.shape[0],上一层宽度),b的形状(Yshape[0],1),知道这样的规律后我们就可以写神经网络了
矩阵乘积如下图
首先,我们创建一个列表来定义深度和宽度,列表的长度就是深度,列表里面的每一个值就是我们要定义的宽度
layers_dims = [X.shape[0],3,4,5,Y.shape[0]]
这个列表的意思就是 有3个隐藏层,3,4,5分别代表每一个隐藏层的宽度,下面就开始初始化我们每一层的W和b值
- def initialize_parameters_deep(layers_dims):
- """
- 此函数是为了初始化多层网络参数而使用的函数。
- 参数:
- layers_dims - 包含我们网络中每个图层的节点数量的列表
- 返回:
- parameters - 包含参数“W1”,“b1”,...,“WL”,“bL”的字典:
- Wi - 权重矩阵,维度为(layers_dims [i],layers_dims [i-1])
- bi - 偏向量,维度为(layers_dims [i],1)
- """
- import numpy as np
- parameters={}
- L=len(layers_dims)
- for i in range(1,L):
- parameters["W"+str(i)]=np.random.normal(size=(layers_dims[i],layers_dims[i-1]))
- parameters["b"+str(i)]=np.zeros((layers_dims[i],1))
-
- return parameters
测试一下打印一下W,b的形状看一下对不对
- X = np.array([[1,2,3,4],[5,6,7,8]])
- Y = np.array([[0,1]])
- layers_dims = [X.shape[0],3,4,5,Y.shape[0]]
- parameters = initialize_parameters_deep(layers_dims)
- L = len(layers_dims)
- for i in range(1,L):
- print("W",i,"shape:",parameters["W"+str(i)].shape)
- print("b",i,"shape:",parameters["b"+str(i)].shape)
-
- 打印结果为:
- W1 shape: (3, 2)
- b1 shape: (3, 1)
- W2 shape: (4, 3)
- b2 shape: (4, 1)
- W3 shape: (5, 4)
- b3 shape: (5, 1)
- W4 shape: (1, 5)
- b4 shape: (1, 1)
layers_dims大家可以自己改变数值,和长度
向前传播我们从公式下手比较容易 每一层的的计算公式是一样的 Z= np.dot(W,A_prev) + b A是前一层的值,W,b是对应当前层的值,A=激活函数(Z) 计算出来的A就是当前层的值,根据公式写代码
激活函数公式入下图
- def sigmoid(Z):
- return 1/(1+np.exp(-Z))
-
-
- def linear_activation_forward(A_prev,W,b,activation):
- """
- 实现LINEAR-> ACTIVATION 这一层的前向传播
- 参数:
- A_prev - 来自上一层(或输入层)的激活,维度为(上一层的节点数量,示例数)
- W - 权重矩阵,numpy数组,维度为(当前层的节点数量,前一层的大小)
- b - 偏向量,numpy阵列,维度为(当前层的节点数量,1)
- activation - 选择在此层中使用的激活函数名,字符串类型,【"sigmoid" | "relu"】
- 返回:
- A - 激活函数的输出,也称为激活后的值
- """
- Z = np.dot(W,A_prev)+b
- if activation == "relu":
- A = np.where(Z>0,Z,0)
- elif activation == "sigmoid":
- A = sigmoid(Z)
-
- return A
公式定义完了,就可以计算每一层的值一步一步的向前传播了,因为后面要向后传播求导,所以这里求出来的每一层的A我们都要记录一下
- def L_model_forward(X,parameters):
- """
- 实现[LINEAR-> RELU] *(L-1) - > LINEAR-> SIGMOID计算前向传播,也就是多层网络的前向传播,为后面每一层都执行LINEAR和ACTIVATION
- 参数:
- X - 数据,numpy数组,维度为(输入节点数量,示例数)
- parameters - initialize_parameters_deep()的输出
- 返回:
- AL - 最后的激活值
- caches - 包含参数“A1”,“A2”,...,“AL”,”的字典
- """
- A = X
- cache={}
- L = len(parameters) // 2
- for i in range(1,L):
- A = linear_activation_forward(A,parameters["W"+str(i)],parameters["b"+str(i)],"relu")
- cache["A"+str(i)]=A
- A = linear_activation_forward(A,parameters["W"+str(L)],parameters["b"+str(L)],"sigmoid")
- cache["A"+str(L)]=A
- return cache
我们已经把这模型的前向传播部分完成了,我们需要计算成本(误差),以确定它到底有没有在学习,成本的计算公式如下:
代码如下
- def compute_cost(AL,Y):
- """
- 成本函数。
- 参数:
- AL - 与标签预测相对应的概率向量,维度为(1,示例数量)
- Y - 标签向量(例如:如果不是猫,则为0,如果是猫则为1),维度为(1,数量)
- 返回:
- cost - 交叉熵成本
- """
- m = Y.shape[1]
- cost = -np.sum(np.multiply(np.log(AL),Y) + np.multiply(np.log(1 - AL), 1 - Y)) / m
-
- cost = np.squeeze(cost)
-
- return cost
反向传播用于计算相对于参数的损失函数的梯度,下面就是求导了,我们一个一个的来求导,先是cost的导数:
激活函数的导数 :
由上面的推导可得第一个倒数信息dZ:
之前记录 的每个节点A值现在就要都用上了
A=WA(i-1)-b
dW=dZ*A
db=dZ
- def linear_activation_backward(X,parameters,cache):
- """
- 实现LINEAR-> ACTIVATION层的后向传播。
- 参数:
- cache - 我们存储的用于有效计算反向传播的值的元组(值为linear_cache,activation_cache)
- activation - 要在此层中使用的激活函数名,字符串类型,【"sigmoid" | "relu"】
- 返回:
- dA_prev - 相对于激活(前一层i-1)的成本梯度值,与A_prev维度相同
- dW - 相对于W(当前层i)的成本梯度值,与W的维度相同
- db - 相对于b(当前层i)的成本梯度值,与b的维度相同
- """
- grads={}
- L=len(cache)
- dZ = cache["A"+str(L)] - Y
- for i in range(1,L):
- dW = (1 / m) * (np.dot(dZ, cache["A"+str(L-i)].T))
- db = (1 / m) * (np.sum(dZ, axis=1, keepdims=True))
- dZ =(np.dot(parameters["W"+str(L-i+1)].T,dZ))*np.where(cache["A"+str(L-i)] > 0, 1, 0)
- grads["dW"+str(L-i)]=dW
- grads["db"+str(L-i)]=db
- dW = (1 / m) * (np.dot(dZ, X.T))
- db = (1 / m) * (np.sum(dZ, axis=1, keepdims=True))
- grads["dW"+str(L-i-1)]=dW
- grads["db"+str(L-i-1)]=db
-
- return grads
我们把向前向后传播都完成了,现在我们就开始更新参数
- def update_parameters(parameters, grads, learning_rate):
- """
- 使用梯度下降更新参数
- 参数:
- parameters - 包含你的参数的字典
- grads - 包含梯度值的字典,是L_model_backward的输出
- 返回:
- parameters - 包含更新参数的字典
- 参数[“W”+ str(i)] = ...
- 参数[“b”+ str(i)] = ...
- """
- L = len(parameters) // 2 #整除
- for i in range(L):
- parameters["W" + str(i + 1)] = parameters["W" + str(i + 1)] - learning_rate * grads["dW" + str(i)]
- parameters["b" + str(i + 1)] = parameters["b" + str(i + 1)] - learning_rate * grads["db" + str(i )]
-
- return parameters
我把它封装成了一个class
- #!usr/bin/env python
- #-*- coding:utf-8 _*-
-
- import numpy as np
-
-
- class Net(object):
-
- def __init__(self, X, Y, layers_dims, learning_rate, num_iterations, num_display, num_learn_rate=None,
- learn_decline_rate=0.1,lambd=0.001):
-
- """
- X - 训练参数
- Y - 目标值
- layers_dims - 包含我们网络中每个图层的节点数量的列表 ,例如 [X.shape[0],3,5,Y.shape[0]] 两个隐藏层 宽度分别是3,5
- learning_rate - 学习率
- num_iterations - 迭代次数
- num_display - 每迭代多少次显示cost
- num_learn_rate - 每迭代多少次下降学习,默认None,不下降
- learn_decline_rate - 学习率下降比例 默认0.1
-
- lambd - 正则权重,默认0.001
- parameters - 包含参数“W1”,“b1”,...,“WL”,“bL”的字典:
- Wi - 权重矩阵,维度为(layers_dims [i],layers_dims [i-1])
- bi - 偏向量,维度为(layers_dims [i],1)
- caches - 包含参数 "A1","A2",...,"AL"的字典
- Ai - 每个节点值
- grads - 包含参数“dW1”,“db1”,...,“dWL”,“dbL”的字典:
- dWi - 相对于Wi的成本梯度值,与Wi的维度相同
- dbi - 相对于bi的成本梯度值,与bi的维度相同
- L - 网络结构的深度
- """
- self.X = X
- self.Y = Y
- self.m = self.Y.shape[1]
- self.learning_rate = learning_rate
- self.layers_dims = layers_dims
- self.L = len(self.layers_dims)
- self.num_iterations = num_iterations
- self.num_display = num_display
- self.num_learn_rate = num_learn_rate
- self.learn_decline_rate = learn_decline_rate
- self.parameters = {}
- self.cache = {}
- self.grads = {}
- self.cost = 0.0
- self.lambd = lambd
-
- def softmax(self,Z):
- Z = Z - np.max(Z)
- exp_Z = np.exp(Z)
- softmax_Z = exp_Z / np.sum(exp_Z)
- return softmax_Z
-
- def sigmoid(self, Z):
- return np.float32(1 / (1 + np.exp(-Z)))
-
- def initialize_parameters_deep(self):
- """
- 此函数是为了初始化多层网络参数而使用的函数。
- """
-
- for i in range(1, self.L):
- self.parameters["W" + str(i)] =np.float32 (np.random.normal(size=(self.layers_dims[i], self.layers_dims[i - 1])))
- self.parameters["b" + str(i)] =np.float32 (np.zeros((self.layers_dims[i], 1)))
-
- def linear_activation_forward(self, A_prev, W, b, activation):
- """
- 实现LINEAR-> ACTIVATION 这一层的前向传播
- 参数:
- A_prev - 来自上一层(或输入层)的激活,维度为(上一层的节点数量,示例数)
- W - 权重矩阵,numpy数组,维度为(当前层的节点数量,前一层的大小)
- b - 偏向量,numpy阵列,维度为(当前层的节点数量,1)
- activation - 选择在此层中使用的激活函数名,字符串类型,【"sigmoid" | "relu"】
- 返回:
- A - 激活函数的输出,也称为激活后的值
- """
- Z = np.dot(W, A_prev) + b
- assert (Z.shape == (W.shape[0], A_prev.shape[1]))
- if activation == "relu" :
- A = np.where(Z > 0, Z, 0)
- elif activation == "sigmoid" :
- A = self.sigmoid(Z)
- elif activation == "tanh" :
- A = np.tanh(Z)
- elif activation == "softmax" :
- A = self.softmax_x(Z)
- return A
-
- def L_model_forward(self):
- """
- 实现[LINEAR-> RELU] *(L-1) - > LINEAR-> SIGMOID计算前向传播,也就是多层网络的前向传播,为后面每一层都执行LINEAR和ACTIVATION
- """
- A = self.X
- for i in range(1, self.L - 1):
- A = self.linear_activation_forward(A, self.parameters["W" + str(i)], self.parameters["b" + str(i)], "relu")
- self.cache["A" + str(i)] = np.float32(A)
- A = self.linear_activation_forward(A, self.parameters["W" + str(self.L - 1)],
- self.parameters["b" + str(self.L - 1)], "softmax")
- self.cache["A" + str(self.L - 1)] = np.float32(A)
-
- def regularization_2(self):
- L2_regularization_cost = 0.0
- for i in range(1,self.L):
- L2_regularization_cost += np.nansum(np.square(self.parameters["W"+str(i)]))
-
- return L2_regularization_cost
-
- def pre_model_forward(self, per_X):
- """
- 实现[LINEAR-> RELU] *(L-1) - > LINEAR-> SIGMOID计算前向传播,也就是多层网络的前向传播,为后面每一层都执行LINEAR和ACTIVATION
- """
- A = per_X
- for i in range(1, self.L - 1):
- A = self.linear_activation_forward(A, self.parameters["W" + str(i)], self.parameters["b" + str(i)], "relu")
- self.cache["A" + str(i)] = np.float32(A)
- A = self.linear_activation_forward(A, self.parameters["W" + str(self.L - 1)],
- self.parameters["b" + str(self.L - 1)], "sigmoid")
- self.cache["A" + str(self.L - 1)] = np.float32(A)
-
- def compute_cost(self):
- """
- 实施定义的成本函数。
- 返回:
- cost - 交叉熵成本
- """
-
- m = self.Y.shape[1]
- # self.cost = -np.sum(self.Y * np.log(self.cache["A" + str(self.L - 1)]) + (1 - self.Y) * np.log(1 - self.cache["A" + str(self.L - 1)])) / m
- self.cost = (-np.nansum(np.multiply(np.log(self.cache["A" + str(self.L - 1)]),self.Y)\
- + np.multiply(np.log(1 - self.cache["A" + str(self.L - 1)]), 1 - self.Y)) / m)
- self.cost = np.squeeze(self.cost)
- L2_regularization_cost = self.regularization_2()
- self.cost += self.lambd * L2_regularization_cost / (2*m)
- self.cost = np.float32(self.cost)
-
- def linear_activation_backward(self):
- """
- 实现LINEAR-> ACTIVATION层的后向传播。
- """
- m = self.Y.shape[1]
- dZ = self.cache["A" + str(self.L - 1)] - self.Y
- for i in range(1, self.L - 1):
- dW = (1 / self.m) * (np.dot(dZ, self.cache["A" + str(self.L - i - 1)].T)) \
- + ((self.lambd*self.parameters["W" + str(self.L - i )])/m)
- db = (1 / self.m) * (np.sum(dZ, axis=1, keepdims=True))
- dZ = (np.dot(self.parameters["W" + str(self.L - i)].T, dZ)) * np.where( \
- self.cache["A" + str(self.L - i - 1)] > 0, 1, 0)
- self.grads["dW" + str(self.L - i - 1)] = np.float32(dW)
- self.grads["db" + str(self.L - i - 1)] = np.float32(db)
- dW = (1 / self.m) * (np.dot(dZ.astype(np.float32), self.X.T)) \
- + ((self.lambd*self.parameters["W" + str(self.L - i - 1)])/m)
- db = (1 / self.m) * (np.sum(dZ.astype(np.float32), axis=1, keepdims=True))
- self.grads["dW" + str(self.L - i - 2)] = np.float32(dW)
- self.grads["db" + str(self.L - i - 2)] = np.float32(db)
-
- def update_parameters(self):
- """
- 使用梯度下降更新参数
- """
- L = len(self.parameters) // 2 # 整除
- for i in range(L):
- self.parameters["W" + str(i + 1)] = self.parameters["W" + str(i + 1)].astype(np.float32) - self.learning_rate * self.grads[
- "dW" + str(i)]
- self.parameters["b" + str(i + 1)] = self.parameters["b" + str(i + 1)].astype(np.float32) - self.learning_rate * self.grads[
- "db" + str(i)]
-
- def run(self):
-
- self.initialize_parameters_deep()
-
- for j in range(self.num_iterations + 1):
- self.L_model_forward()
- self.compute_cost()
- self.linear_activation_backward()
- self.update_parameters()
-
- if self.num_learn_rate and j % self.num_learn_rate == 0 and j:
- print("当前学习率:",self.learning_rate)
- self.learning_rate -= np.multiply(self.learning_rate , self.learn_decline_rate)
- print("下降后学习率:",self.learning_rate)
-
-
- if j % self.num_display == 0:
- print("第", j, "次迭代,cost值为:" + str(self.cost))
-
- def predict(self, per_X):
- """
- 返回
- predictions - 我们模型预测的向量
- """
- self.pre_model_forward(per_X)
- self.predictions = np.round(self.cache["A" + str(self.L - 1)])
-
- return self.predictions
有什么不对的地方还请多多指正
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。