其中有很多内容参考博客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之间的调节常数;
隐藏层神经元的个数大大影响最后的预测效果,最好的方式根据经验公式选择一个参考值,然后再不断调整。