BP神经网络
1.BP神经网络介绍及发展背景
从前面介绍的感知器学习规则来看,其权值的调整 取决于期望输出与实际输出之差: $ {\Delta w_i = \eta(t-y)x_i}$
但是对于各个隐藏层的节点来说,不存在已知的期望输出,因而该学习规则不能用于隐藏层的权值调整。
BP 算法的基本思想是,学习过程由信号的正向传播和误差的反向传播两个过程组成。
正向传播时,把样本的特征从输入层进行输入,信号经过各个隐藏层逐层处理后,最后从 输出层传出。对于网络的实际输出与期望输出之间的误差,把误差信号从最后一层逐层反传,从而获得各个层的误差学习信号,再根据误差学习信号来修正各个层神经元的权值。
2.代价函数
cost或loss表示
最简单常见的一个代价函数是均方差(Mean-Square Error, MSE)代价函数,也称为二次代价函数:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3Qvy6oS3-1631831470720)(attachment:image-3.png)]
3.梯度下降法
3.1 梯度下降法介绍
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-skjkJR9P-1631831470723)(attachment:image-4.png)]
3.2 梯度下降法二维例子
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OjXJ1qwg-1631831470724)(attachment:image-5.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OMio5kVl-1631831470725)(attachment:image-6.png)]
3.3 梯度下降法三维例子
4.Delta学习规则
5.常用激活函数讲解
5.1 Sigmoid函数
5.2 Tanh函数
5.3 Softsign函数
5.4 ReLU函数
6.BP网络模型和公式推导
6.1 BP网络模型
6.2 BP算法推导
6.3 BP算法推导补充说明
7.BP算法推导结论总结
8.梯度消失与梯度爆炸
8.1 梯度消失
8.2 梯度爆炸
如果学习信号乘以一个大于 1 的数,那么δ就会变大。学习信号从输出层一层一层向前反向传播的时候,每传播一层学习信号就会变大一点,经过多层传播后,学习信号就会接近于无穷大,从而使得权值调整接近于无穷大。接近于无穷大那就意味着该层的参数,处于 一种极不稳定的状态,那么网络就不能正常工作了。学习信号随着网络传播逐渐增大的问题也被称为梯度爆炸(Exploding Gradient)的问题。
8.3 使用ReLU函数解决梯度消失和梯度爆炸的问题
导数为 1 是一个很好的特性,不会使得学习信号越来越小,也不会让学习信号越来越大,可以让学习信号比较稳定地从后向前传 播。解决了梯度消失和梯度爆炸的问题,同时计算方便,可以加速网络的训练。 ReLU 函数还有一个优点,它是一个非线性的激活函数,可以用来处理非线性问题
在神经网络 中,信号是冗余的,也就是说其实网络最后在做预测的时候并不需要从前面传过来的所有的信号,实际上只需要一部分的信号,网络就可以进行预测。并且使用部分信号来进行预测与使用全部信号来进行预测得到的结果相差不大
9.使用BP神经网络解决异或问题
10.分类模型评估方法
10.1 准确率/精确率/召回率/F1值
-
准确率(accuracy) = 所有预测正确的结果除以所有结果。TP+TN/TP+TN+FP+FN
-
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MlSy45YK-1631831470726)(attachment:image-7.png)]
-
召回率(recall) = TP/(TP + FN) 描述的是模型对于正例–恐怖分子的召回能力,找出恐怖分子的能力
-
精确率(precision) = TP/(TP + FP) 描述的是模型对于正例-恐怖分子的判断能力,找的是否精准。
-
在实际应用中,最理想的情况是精确率和召回率都比较高,不过一般来说,很难得到精确率和召回率都很高的结果。很多时候是提高了精确率,召回率就会降低;提高召回率,精确率 就会降低。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9LMeVbyX-1631831470727)(attachment:image-8.png)] -
F1 值综合了 P 和 R 的结果,可用于综合评价分类结果的质量。准确率,召回率,精确率,F1 值都是在 0-1 之间,并且都是越大越好。
10.2 混淆矩阵
11.独热编码
12.BP神经网络完成手写数字识别
从最基层实现一个BP神经网络的算法,函数都需要自己构造,代码如下
13.Sklearn 手写数字识别
利用sklearn包调用封装好的函数来实现相同的分类,使用 scikit-learn 模块。scikit-learn 是一个常用的 python 模型,里面封装了大量机器学习算法,其中就包括 BP 神经网络。代码如下
#BP神经网络解决异或问题
import numpy as np
import matplotlib.pyplot as plt
#输入数据
X = np.array([[0,0],
[0,1],
[1,0],
[1,1]])
#标签
T = np.array([[0],
[1],
[1],
[0]])
#定义一个2层的神经网络:2-10-1
#输入层2个神经元,隐藏层10个神经元,输出层1个神经元
#输入层到隐藏层的权值初始化,2行10列
W1 = np.random.random([2,10])
#隐藏层到输出层的权值初始化,10行1列
W2 = np.random.random([10,1])
#初始化偏置值,偏置值的初始化一般可以取0,或者一个比较小的常数,如0.1
#隐藏层的10个神经元偏置
b1 = np.zeros([10])
#输出层的一个神经元偏置
b2 = np.zeros([1])
#学习率设置
lr = 0.1
#定义训练周期数
epochs = 100001
#定义测试周期数
test = 5000
#定义sigmoid函数
def sigmoid(x):
return 1/(1+np.exp(-x))
#定义sigmoid 函数导数
def dsigmoid(x):
return x*(1-x)
#更新权值和偏置值
def update():
global X,T,W1,W2,lr,b1,b2
#隐藏层输出
L1 = sigmoid(np.dot(X,W1)+b1)
#输出层输出
L2 = sigmoid(np.dot(L1,W2)+b2)
#求输出层的学习信号
delta_L2 = (T - L2)*dsigmoid(L2)
#隐藏层的学习信号
delta_L1 = delta_L2.dot(W2.T)*dsigmoid(L1)
#求隐藏层到输出层的权值改变
#由于一次计算了多个样本,所以需要求平均
delta_W2 = lr*L1.T.dot(delta_L2)/X.shape[0]
#输入层到隐藏层的权值改变
#由于一次计算了多个样本,所以需要求平均
delta_W1 = lr * X.T.dot(delta_L1)/X.shape[0]
#更新均值
W2 = W2+delta_W2
W1 = W1+delta_W1
#改变偏置值
#由于一次计算了多个样本,所以需要求平均
b2 = b2 + lr*np.mean(delta_L2,axis=0)
b1 = b1 + lr*np.mean(delta_L1,axis=0)
#定义空list用与保存loss
loss = []
#训练模型
for i in range(epochs):
#更新权值
update()
#每训练5000次计算一次loss值
if i%test == 0:
#隐藏层输出
L1 = sigmoid(np.dot(X,W1)+b1)
#输入层输出
L2 = sigmoid(np.dot(L1,W2)+b2)
#计算loss值
print('epochs:',i,'loss',np.mean(np.square(T-L2)/2))
#保存loss值
loss.append(np.mean(np.square(T-L2)/2))
#画图训练周期数与loss的关系图
plt.plot(range(0,epochs,test),loss)
plt.xlabel('epochs')
plt.ylabel('loss')
plt.show()
#隐藏层输出
L1 = sigmoid(np.dot(X,W1)+b1)
#输出层输出
L2 = sigmoid(np.dot(L1,W2)+ b2)
print('output:')
print(L2)
#因为最终的分类只有0和1,所以我们可以把大于等于0.5的归为1类,
#小于0.5的值归为0类
def predict(x):
if x>=0.5:
return 1
else:
return 0
# map 会根据提供的函数对指定序列做映射
#相当于依次把L2中的值放到predict函数中计算
#然后打印出结果
print('predict:')
for i in map(predict,L2):
print(i)
运行结果:
#4.12 BP神经网络完成手写数字识别(片段1)--了解数据的相关信息
from sklearn.datasets import load_digits
import matplotlib.pyplot as plt
#载入手写数字数据
digits = load_digits()
#打印数据集的shape,行表示数据集个数,列表示每个数据的特征数
print('data shape:',digits.data.shape)
#打印数据标签的shape,数据标签的值为0-9
print('target shape:',digits.target.shape)
#准备显示第0张图片,图片为灰度图
plt.imshow(digits.images[0],cmap='gray')
#显示图片
plt.show()
运行结果:
#4.12 BP神经网络完成手写数字识别(片段2)
#导入numpy科学计算库
import numpy as np
#载入画图工具包
import matplotlib.pyplot as plt
#导入手写数字数据集
from sklearn.datasets import load_digits
#用于标签二值化处理,把标签转成独热编码one-hot的格式
from sklearn.preprocessing import LabelBinarizer
#用于把数据集拆分为训练集和测试集
from sklearn.model_selection import train_test_split
#用于评估分类结果
from sklearn.metrics import classification_report,confusion_matrix
#定义sigmoid函数
def sigmoid(x):
return 1/(1+np.exp(-x))
#定义sigmoid函数的导数
def dsigmoid(x):
return x*(1-x)
#定义神经网络分类
class NeuralNetwork:
#初始化网络,定义网络结构
#假设传入(64,100,10),说明定义:
#输入层64个神经元,隐藏层100个神经元,输出层10个神经元
def __init__(self,layers):
#权值的初始化,范围-1到1
self.W1 = np.random.random([layers[0],layers[1]])*2-1
self.W2 = np.random.random([layers[1],layers[2]])*2-1
#初始化偏置值
self.b1 = np.zeros([layers[1]])
self.b2 = np.zeros([layers[2]])
#定义空list用于保存accuracy
self.accuracy = []
#定义空list用于保存loss
self.loss = []
#训练模型
#X为数据输出
#T为数据对应的标签
#lr学习率
#steps训练次数
#batch批次大小
#使用批量随机梯度下降法,每次随机抽取一个批次的数据进行训练
def train(self,X,T,lr=0.1,steps=20000,test=5000,batch=50):
#进行steps+1次训练
for n in range(steps+1):
#随机选取一个批次数据
index = np.random.randint(0,X.shape[0],batch)
x = X[index]
#计算隐藏层输出
L1 = sigmoid(np.dot(x,self.W1)+self.b1)
#计算输出层输出
L2 = sigmoid(np.dot(L1,self.W2)+self.b2)
#求输出层的学习信号
delta_L2 = (T[index]-L2)*dsigmoid(L2)
#求隐藏层的学习信号
delta_L1 = delta_L2.dot(self.W2.T)*dsigmoid(L1)
#求隐藏层到输出层的权值改变
#由于一次计算了多个样本,所以需要求平均
self.W2 +=lr*L1.T.dot(delta_L2)/x.shape[0]
#求输出层到隐藏层的权值改变
#由于一次计算了多个样本,所以需要求平均
self.W1 += lr*x.T.dot(delta_L1)/x.shape[0]
#改变偏置值
self.b2 = self.b2 + lr*np.mean(delta_L2,axis=0)
self.b1 = self.b1 + lr*np.mean(delta_L1,axis=0)
#每训练5000次预测一次准确率
if n%test==0:
#预测测试集的预测结果
Y2 = self.predict(X_test)
#取得预测结果最大的所在的索引
#例如最大值所在的索引是3,那么预测结果就是3
predictions = np.argmax(Y2,axis=1)
#计算准确率
#np.equal(predictions,y_test)判断预测结果和真实标签是否相等,相等返回True,
#不相等返回False
#np.equal(predictions,y_test)执行后得到一个包含多个True和False的列表
#然后用np.mean对列表求平均True为1,False为0。
#例如一共有10个结果,9个True,一个False,平均后的结果为0.9,即预测的准确率为90%
acc = np.mean(np.equal(predictions,y_test))
#计算loss
l = np.mean(np.square(y_test-predictions)/2)
#保存准确率
self.accuracy.append(acc)
#保存loss值
self.loss.append(l)
#打印训练次数,准确率和loss
print('step:%d accuracy:%.3f loss:%.3f'%(n,acc,l))
#模型预测结果
def predict(self,x):
L1 = sigmoid(np.dot(x,self.W1)+self.b1) #隐层输出
L2 = sigmoid(np.dot(L1,self.W2)+self.b2) #输出层输出
return L2
#程序从这里开始运行
#定义训练次数
steps = 30001
#定义测试周期数
test = 3000
#载入数据
digits = load_digits()
#得到数据
X = digits.data
#得到标签
y = digits.target
#输入数据归一化,有助于加快训练速度
#X中原来的数值范围是0-255之间,归一化后变成0-1之间
X -= X.min()
X /= X.max()-X.min()
#分割数据1/4为测试数据,3/4为训练数据
#有1347个训练数据,450个测试数据
X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.25)
#创建网络,输入层64个神经元,隐藏层100个神经元,输出层10个神经元
nm = NeuralNetwork([64,100,10])
#标签转化为独热编码one-hot的格式
labels_train = LabelBinarizer().fit_transform(y_train)
#开始训练
print('Start training')
nm.train(X_train,labels_train,steps = steps,test=test)
#预测测试数据
predictions = nm.predict(X_test)
predictions = np.argmax(predictions,axis=1)
#对比测试数据的真实标签与网络预测结果,得到准确率,召回率和F1值
print(classification_report(y_test,predictions))
#对于测试数据的真实标签与网络预测结果,得到混淆矩阵
print(confusion_matrix(y_test,predictions))
#训练次数与loss的关系图
plt.plot(range(0,steps+1,test),nm.loss)
plt.xlabel('steps')
plt.ylabel('loss')
plt.show()
# 训练次数与 accuracy 的关系图
plt.plot(range(0,steps+1,test),nm.accuracy)
plt.xlabel('steps')
plt.ylabel('accuracy')
plt.show()
运行结果:
BP网络完成手写数字识别(使用scikit-learn中的神经网络算法)
#载入BP神经网络
from sklearn.neural_network import MLPClassifier
from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
import matplotlib.pyplot as plt
#载入数据
digits = load_digits()
#数据
x_data = digits.data
#标签
y_data = digits.target
#在X中原来的数值范围是0-225之间,归一化后变为0-1之间
x_data -= x_data.min()
x_data/=x_data.max()-x_data.min()
#分割数据1/4为测试数据,3/4为训练数据
#有1347个训练数据,450个测试数据
x_train,x_test,y_train,y_test = train_test_split(x_data,y_data,test_size=0.25)
#定义神经网络模型,模型输入神经元个数和输出神经元个数不需要设置
#hidden_layer_sizes用于设置隐藏层结构:
#比如(50)表示有1个隐藏层,隐藏层神经元个数为50
#比如(100,20)表示有2个隐藏层,第1个隐藏层有100个神经元,第2个隐藏层有20个神经元
#比如(100,20,10)表示3个隐藏层,神经元个数分别为100,20,10
#max_iter设置训练次数
mlp = MLPClassifier(hidden_layer_sizes=(100,20),max_iter=500)
#fit传入训练集数据开始训练模型
mlp.fit(x_train,y_train)
#predict用于模型预测
predictions = mlp.predict(x_test)
#标签数据和模型预测数据进行对比,计算分类评估指标
print(classification_report(y_test,predictions))
运行结果: