1.编程题目理解
反向传播(英语:Backpropagation,缩写为BP)是“误差反向传播”的简称,是一种与最优化方法(如梯度下降法)结合使用的,用来训练人工神经网络的常见方法。该方法对网络中所有权重计算损失函数的梯度。这个梯度会反馈给最优化方法,用来更新权值以最小化损失函数。
反向传播要求有对每个输入值想得到的已知输出,来计算损失函数梯度。因此,它通常被认为是一种监督式学习方法,虽然它也用在一些无监督网络(如自动编码器)中。它是多层前馈网络的Delta规则的推广,可以用链式法则对每层迭代计算梯度。反向传播要求人工神经元(或“节点”)的激励函数可微。
2.BP算法原理阐释
BP算法由信号的正向传播和误差的反向传播两个过程组成。
正向传播时,输入样本从输入层进入网络,经隐层逐层传递至输出层,如果输出层的实际输出与期望输出(导师信号)不同,则转至误差反向传播;如果输出层的实际输出与期望输出(导师信号)相同,结束学习算法。
反向传播时,将输出误差(期望输出与实际输出之差)按原通路反传计算,通过隐层反向,直至输入层,在反传过程中将误差分摊给各层的各个单元,获得各层各单元的误差信号,并将其作为修正各单元权值的根据。这一计算过程使用梯度下降法完成,在不停地调整各层神经元的权值和阈值后,使误差信号减小到最低限度。
权值和阈值不断调整的过程,就是网络的学习与训练过程,经过信号正向传播与误差反向传播,权值和阈值的调整反复进行,一直进行到预先设定的学习训练次数,或输出误差减小到允许的程度。
3.算法设计思路
1.初始化网络权值和神经元的阈值(最简单的办法就是随机初始化)
2.前向传播:按照公式一层一层的计算隐层神经元和输出层神经元的输入和输出。
3.后向传播:根据公式修正权值和阈值
直到满足终止条件。
4.测试结果及分析
标准BP前期下降的非常快,到后期比较平缓。累计BP下降速率比较平缓。
可以看出当累计误差下降导一定程度时候,累计BP比标准BP要慢。比如停止条件为loss<=0.01时,效果如下。
5.核心代码
#标准BP算法学习
def train_standard_BP(features,labels,lr):
net=Net()
epoch=0
loss=1
all_loss=[]
while loss>0.1:#停止条件
for i in range(features.shape[1]):
X=features[:,i]
Y=labels[0,i]
net.forward(X.reshape(-1,1))
net.standard_BP(Y,lr)
output=net.forward(features)
loss=0.5*((output-labels)**2).sum()
epoch+=1
all_loss.append(loss)
print("标准BP","学习率:",lr,"\n终止epoch:",epoch,"loss: ",loss)
plt.xlabel("epoch")
plt.ylabel("loss")
plt.title('标准BP')
plt.plot(all_loss)
plt.show()
#累计BP算法学习
def train_accumulate_BP(features,labels,lr=0.2):
net=Net()
epoch=0
loss=1
all_loss=[]
while loss>0.1:#停止条件
output=net.forward(features)
net.accumulate_BP(labels,lr)
loss=0.5*((output-labels)**2).sum()/labels.shape[1]
epoch+=1
all_loss.append(loss)
print("累积BP","学习率:",lr,"\n终止epoch:",epoch,"loss: ",loss)
plt.xlabel("epoch")
plt.ylabel("loss")
plt.plot(all_loss)
plt.title("累计BP")
plt.show()
6.主要解决问题和收获
主要了解了BP算法是如何实现的;单隐层神经网络的结构;对神经网络的认识有一个初步的了解。
7.参考来源s
西瓜书第五章笔记及答案——神经网络_菜要多训练的博客-CSDN博客
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
plt.rcParams['axes.unicode_minus']=False #用来正常显示负号
#西瓜数据集 每一列为一条数据
features=np.array([
[1,2,2,1,0,1,2,2,2,1,0,0,1,0,2,0,1],
[2,2,2,2,2,1,1,1,1,0,0,2,1,1,1,2,2],
[1,0,1,0,1,1,1,1,0,2,2,1,1,0,1,1,0],
[0,0,0,0,0,0,1,0,1,0,2,2,1,1,0,2,1],
[2,2,2,2,2,1,1,1,1,0,0,0,2,2,1,0,1],
[1,1,1,1,1,0,0,1,1,0,1,0,1,1,0,1,1],
[0.697,0.774,0.634,0.608,0.556,0.403,0.481,0.437,0.666,0.243,0.245,0.343,0.639,0.657,0.360,0.593,0.719],
[0.460,0.376,0.264,0.318,0.215,0.237,0.149,0.211,0.091,0.267,0.057,0.099,0.161,0.198,0.370,0.042,0.103]
])
labels=np.array([
[1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0]
])
def sigmoid(X):
return 1./(1+np.exp(-X))
class Net():
def __init__(self,num_input=8,num_hidden=10,num_output=1):
#隐含层和输出层的权重和偏置
self.W1=np.random.randn(num_hidden,num_input)
self.b1=np.zeros(num_hidden).reshape(-1,1)
self.W2=np.random.randn(num_output,num_hidden)
self.b2=np.zeros(num_output).reshape(-1,1)
#隐含层和输出层的输出
self.o1=np.zeros(num_hidden).reshape(-1,1)
self.o2=np.zeros(num_output).reshape(-1,1)
#梯度存储变量
self.do2=np.zeros(self.o2.shape)
self.dW2=np.zeros(self.W2.shape)
self.db2=np.zeros(self.b2.shape)
self.do1=np.zeros(self.o1.shape)
self.dW1=np.zeros(self.W1.shape)
self.db1=np.zeros(self.b1.shape)
def forward(self,X):#前向传播
if X.shape[0] != self.W1.shape[1]:
print("输入数据格式错误!")
return
self.input=X
#使用sigmoid函数为激活函数
self.o1=sigmoid(np.matmul(self.W1,self.input)+self.b1)
self.o2=sigmoid(np.matmul(self.W2,self.o1)+self.b2)
return self.o2
def standard_BP(self,label,lr=0.2):#标准BP 使用均方误差为损失函数
#求梯度
self.do2=self.o2-label
self.dW2=np.matmul(self.do2*self.o2*(1-self.o2),self.o1.reshape(1,-1))
self.db2=self.do2*self.o2*(1-self.o2)
self.do1=np.matmul(self.W2.transpose(),self.do2*self.o2*(1-self.o2))
self.dW1=np.matmul(self.do1*self.o1*(1-self.o1),self.input.reshape(1,-1))
self.db1=self.do1*self.o1*(1-self.o1)
#更新参数
self.W2-=self.dW2*lr
self.b2-=self.db2*lr
self.W1-=self.dW1*lr
self.b1-=self.db1*lr
def accumulate_BP(self,labels,lr=0.2):#累积BP 使用均方误差为损失函数
num=labels.shape[1]#样本数量
#求梯度
self.do2=(self.o2-labels)/num
self.dW2=np.matmul(self.do2*self.o2*(1-self.o2),self.o1.transpose())
self.db2=(self.do2*self.o2*(1-self.o2)).sum(axis=1).reshape(-1,1)
self.do1=np.matmul(self.W2.transpose(),self.do2*self.o2*(1-self.o2))
self.dW1=np.matmul(self.do1*self.o1*(1-self.o1),self.input.transpose())
self.db1=(self.do1*self.o1*(1-self.o1)).sum(axis=1).reshape(-1,1)
#更新参数
self.W2-=self.dW2*lr
self.b2-=self.db2*lr
self.W1-=self.dW1*lr
self.b1-=self.db1*lr
def train_standard_BP(features,labels,lr):
net=Net()
epoch=0
loss=1
all_loss=[]
while loss>0.01:#停止条件
for i in range(features.shape[1]):
X=features[:,i]
Y=labels[0,i]
net.forward(X.reshape(-1,1))
net.standard_BP(Y,lr)
output=net.forward(features)
loss=0.5*((output-labels)**2).sum()
epoch+=1
all_loss.append(loss)
print("标准BP","学习率:",lr,"\n终止epoch:",epoch,"loss: ",loss)
plt.xlabel("epoch")
plt.ylabel("loss")
plt.title('标准BP')
plt.plot(all_loss)
plt.show()
def train_accumulate_BP(features,labels,lr=0.2):
net=Net()
epoch=0
loss=1
all_loss=[]
while loss>0.01:#停止条件
output=net.forward(features)
net.accumulate_BP(labels,lr)
loss=0.5*((output-labels)**2).sum()/labels.shape[1]
epoch+=1
all_loss.append(loss)
print("累积BP","学习率:",lr,"\n终止epoch:",epoch,"loss: ",loss)
plt.xlabel("epoch")
plt.ylabel("loss")
plt.plot(all_loss)
plt.title("累计BP")
plt.show()
train_standard_BP(features,labels,lr=0.2)
train_accumulate_BP(features,labels,lr=0.2)