吴恩达第五集 神经网络反向传播算法推导

    xiaoxiao2022-07-07  183

    其中有很多内容参考博客https://blog.csdn.net/qq_32865355/article/details/80260212

    算法简介

    误差反向传播算法简称反向传播算法(即BP算法)。使用反向传播算法的多层感知器又称为BP神经网络。BP算法是一个迭代算法,它的基本思想为:(1)先计算每一层的状态和激活值,直到最后一层(即信号是前向传播的);(2)计算每一层的误差,误差的计算过程是从最后一层向前推进的(这就是反向传播算法名字的由来);(3)更新参数(目标是误差变小)。迭代前面两个步骤,直到满足停止准则(比如相邻两次迭代的误差的差别很小)。

    记号说明

     L表示总共的层数,比如下面这个例子当中L=3

     例子详解

    下面以三层感知器(即只含有一个隐藏层的多层感知器)为例介绍“反向传播算法(BP 算法)”。

    信息向前传播公式推导 

    误差反向推导 

    误差一般公式

    同理可得

    误差递归公式

     

     偏置参数更新

    以上总结的四个论文

    BP算法执行步骤

     python代码

    """ BP神经网络 """ import numpy as np import math # 激励函数及相应导数,后续可添加 def sigmoid(x): return 1.0 / (1 + np.exp(-x)) def diff_sigmoid(x): fval = sigmoid(x) return fval * (1 - fval) def linear(x): return x def diff_linear(x): return np.ones_like(x)#返回一个和x结构一样的矩阵,但是里面所有的元素都为1 class BP: def __init__(self, n_hidden=None, f_hidden='sigmoid', f_output='sigmoid', epsilon=1e-3, maxstep=1000, eta=0.1, alpha=0.0): self.n_input = None # 输入层神经元数目 self.n_hidden = n_hidden # 隐藏层神经元数目 self.n_output = None # 输出结果数量,也就是输出层神经元数量 self.f_hidden = f_hidden self.f_output = f_output self.epsilon = epsilon#总误差临界点 self.maxstep = maxstep self.eta = eta # 学习率,是运用梯度下降算法时,W系数的变化率 self.alpha = alpha # 动量因子,是运用梯度下降算法时,b系数的变化率 self.wih = None # 输入层到隐藏层权值矩阵 self.who = None # 隐藏层到输出层权值矩阵 self.bih = None # 输入层到隐藏层阈值,就是偏执量 self.bho = None # 隐藏层到输出层阈值,同样是偏执量 self.N = None # 样本数量 def init_param(self, X_data, y_data): # 初始化 if len(X_data.shape) == 1: # 若输入数据为一维数组,则进行转置为n维数组 X_data = np.transpose([X_data]) self.N = X_data.shape[0] # normalizer = np.linalg.norm(X_data, axis=0) # X_data = X_data / normalizer if len(y_data.shape) == 1: y_data = np.transpose([y_data]) self.n_input = X_data.shape[1]#将输入层神经元数量赋值给变量 self.n_output = y_data.shape[1]#将输出层神经元数量赋值给变量 #如果没有确定的隐藏层个数,可以用公式确定一个大概量,并且不断调整隐藏层个数,但是这里只通过公式确定,并没有不断调整的过程 if self.n_hidden is None: self.n_hidden = int(math.ceil(math.sqrt(self.n_input + self.n_output)) + 2) self.wih = np.random.rand(self.n_input, self.n_hidden) # i*h,创建一个self.n_input行,self.n_hidden列的随机矩阵,并且随机数在(0-1)之间 self.who = np.random.rand(self.n_hidden, self.n_output) # h*o self.bih = np.random.rand(self.n_hidden) # h,创建一个self.n_hidden行1列的随机矩阵 self.bho = np.random.rand(self.n_output) # o return X_data, y_data def inspirit(self, name): # 获取相应的激励函数 if name == 'sigmoid': return sigmoid elif name == 'linear': return linear else: raise ValueError('the function is not supported now') def diff_inspirit(self, name): # 获取相应的激励函数的导数 if name == 'sigmoid': return diff_sigmoid elif name == 'linear': return diff_linear#返回的都是函数 else: raise ValueError('the function is not supported now') def forward(self, X_data): # 前向传播 x_hidden_in = X_data @ self.wih + self.bih # n*h,@代表矩阵的乘法,X_data @ self.wih等价于dot(X_data,self.wih) x_hidden_out = self.inspirit(self.f_hidden)(x_hidden_in) # n*h x_output_in = x_hidden_out @ self.who + self.bho # n*o x_output_out = self.inspirit(self.f_output)(x_output_in) # n*o #x_hidden_in,x_output_in是z,x_hidden_out,x_output_out是a,只不过分别是隐藏层和输出层的a和z return x_output_out, x_output_in, x_hidden_out, x_hidden_in def fit(self, X_data, y_data): # 训练主函数 X_data, y_data = self.init_param(X_data, y_data) step = 0 # 初始化动量项 delta_wih = np.zeros_like(self.wih) delta_who = np.zeros_like(self.who) delta_bih = np.zeros_like(self.bih) delta_bho = np.zeros_like(self.bho) while step < self.maxstep: step += 1 # 向前传播 x_output_out, x_output_in, x_hidden_out, x_hidden_in = self.forward(X_data) if np.sum(abs(x_output_out - y_data)) < self.epsilon: break#当总误差小于一个规定值时也会退出循环 # 误差反向传播,依据权值逐层计算当层误差 err_output = y_data - x_output_out # n*o, 输出层上,每个神经元上的误差 delta_ho = -err_output * self.diff_inspirit(self.f_output)(x_output_in) # n*o,输出层的δ err_hidden = delta_ho @ self.who.T # n*h, 隐藏层(相当于输入层的输出),每个神经元上的误差,应用递归得到δ # 隐藏层到输出层权值及阈值更新 delta_bho = np.sum(self.eta * delta_ho + self.alpha * delta_bho, axis=0) / self.N self.bho -= delta_bho delta_who = self.eta * x_hidden_out.T @ delta_ho + self.alpha * delta_who self.who -= delta_who # 输入层到隐藏层权值及阈值的更新 delta_ih = err_hidden * self.diff_inspirit(self.f_hidden)(x_hidden_in) # n*h delta_bih = np.sum(self.eta * delta_ih + self.alpha * delta_bih, axis=0) / self.N self.bih -= delta_bih delta_wih = self.eta * X_data.T @ delta_ih + self.alpha * delta_wih self.wih -= delta_wih return def predict(self, X): # 预测 res = self.forward(X) return res[0] if __name__ == '__main__': import matplotlib.pyplot as plt N = 100 X_data = np.linspace(-1, 1, N) X_data = np.transpose([X_data]) y_data = np.exp(-X_data) * np.sin(2 * X_data) bp = BP(f_output='linear', maxstep=2000, eta=0.01, alpha=0.1) # 注意学习率若过大,将导致不能收敛 #bp = BP(maxstep=2000, eta=0.01, alpha=0.1) # 注意学习率若过大,将导致不能收敛 bp.fit(X_data, y_data) plt.plot(X_data, y_data) pred = bp.predict(X_data) plt.scatter(X_data, pred, color='r') plt.show()

    这个BP神经网络中并没有严格按照上面的推导过程来,其中有许多改进,具体可以参考https://blog.csdn.net/slx_share/article/details/80236070

    其中隐藏层的个数选择可以按照如下方公式选择:

    h为隐含层节点的数目,m和n分别是输入层和输出层节点的数目,a为1~10之间的调节常数; 

    隐藏层神经元的个数大大影响最后的预测效果,最好的方式根据经验公式选择一个参考值,然后再不断调整。 

    最新回复(0)