序言:现如今深度学习如日中天,CNN擅长图像分类,RNN擅长语音识别,GAN用于生成一些有意思的图片,YOLO、SSD等改进的算法层出不穷,又有几个人能静下心,仔细研读BP神经网络呢。笔者认为,BP神经网络是一切神经网络之根本,就算谈不上根本,BP神经网络的前向传递,后向调参数的思想也影响了几乎所有神经网络模型的计算过程。鉴于此,本篇文章笔者就认真的谈一谈BP神经网络,尽量写的言简意赅,努力做到不误人子弟。
行文思路
神经网络基本概念BP神经网络推理 (1)前向传递 (2)损失函数 (3)梯度下降 (4)反向传播BP神经网络分类举例 (1)数据集 (2)BP神经网络模型的完整代码(3)BP神经网络分类结果
神经网络基本概念
神经网络:这个名词的解释要分成两部分神经+网络。(1)神经这个词出现在生物学中,神经元受到刺激,电位发生变化,达到一定阈值就会将电信号继续传递下去,最终传到大脑做出反应(哈哈,笔者上高中那会儿残存的一些记忆)。(2)网络这个词,不是指我们生活中的互联网的网络,而是数据结构中定义的网状结构。结合在一起解释为,人们构建了一个模型,原理类似于神经元模型,形状像一张网,所以起名为神经网络。图1简单展示了一张神经网络的图。
图1 神经网络结构图
深度神经网络:神经网络中隐藏层超过两层的神经网络被称为深度神经网络。
机器学习:机器学习是一种能够赋予机器学习的能力以此让它完成直接编程无法完成的功能的方法。但从实践的意义上来说,机器学习是一种通过利用数据,训练模型,然后使用模型进行预测的方法。
深度学习:这个名词听起来很高大上,让人摸不着头脑,但是笔者将起放开来读,读者就一目了然了。深度学习就是“基于深度神经网络模型的机器学习”。
上边几个概念搞清楚,以后就不会轻易的被一些概念名词糊弄住。
BP神经网络推理
BP(back propagation)神经网络是1986年由Rumelhart和McClelland提出的概念,是一种按照误差逆向传播算法训练的多层前馈神经网络。主要包含前向传递和后向调参两个过程,下边就涉及到的几个概念进行简要讨论。
(1)前向传递
图2 前向传递过程
图2描述的是前向传递的过程,上图中包含三层,分别是输入层、隐藏层和输出层,输入的数据包含两个维度,输出数据也包含两个维度,一般情况下,输出的维度就是分类的类别数。
(2)损失函数
前向传递到达输出层,就计算出了模型的预测值,那么如何计算预测值与真实值之间的偏离程度呢,就需要用到损失函数来衡量。下边介绍几个常见的损失函数:
0-1损失函数:如果预测值与真实值一样,损失值为0,相反则损失之为1。表示成公式如下:
对值损失函数:损失值等于预测值与真实值之差的绝对值。表示成公式如下:
均方差损失函数:损失值与真实值之差取平方,再除以样本数。表示成公式如下:
(3)梯度下降
当有了若干输入-输出对,也选择了评估模型输出结果与真实结果差值的损失函数之后,如何来调整神经网络中的参数,来提高神经网络的精度呢?比较经典的方法是采用梯度下降法来对神经网络中的参数进行优化,下文简要介绍一下梯度下降。梯度下降法的迭代公式如下(公式1):
其中和bi是要优化的神经网络中的参数,
表示损失函数,
是损失函数对
的偏导数,
是损失函数对bi的偏导数,a学习率。
最简单的情况,就是输入一个样本,根据上边公式调整一下参数,直到满足某种条件中止。也可以对一批样本进行处理,设置一个固定大小的batch_size,将batch_size个样本的值相加取平均后,再更新参数。
(4)反向传播
通过上边几步的讨论,第一步前向传递得到模型预测结果,第二步根据预测结果计算预测误差(通过损失函数),第三步通过梯度下降等最优化方法调整网络中参数。当网络中有多层的时候,如何方便的求出损失函数对每个参数的导数呢?一个简单有效的方法就是反向传播。反向传播的迭代公式如下(公式2):
公式2含义跟公式1含义基本一样,l表示的是神经网络中的第几层,表示连接l的第j个节点到l+1层的第i个节点的权值,
表示第l+1层的第i个节点的偏置项的值。
从公式2中可以看出,只要得到了和
的值,就可以根据梯度下降逐层更新神经网络的权值了。为了方便计算,引入了一个叫“残差”
的值,其定义是损失函数对l层的第i个神经元
的偏导数。用公式表示为:
根据链式求导法则:
是前向传递时候计算出来的,因此要通过公式2去逐层更新参数的重担就落在了残差
身上。反向调参的整体流程如图3所示:
图3 反向调参流程图
文章写到这个地方笔者也比较累了,在此再专门的捋一下BP网络计算的思路:
▪ 给定一个样本(x,y),首先通过前向计算,逐层计算,直到输出。
▪ 一层一层的计算残差.
▪ 根据残差值计算损失值对权值的导数,然后跟新权值。
BP神经网络分类举例
(1)数据集
本文采用的是iris数据集,包含150个样本。每个样本包含四个特征和一个样本的类别信息,该数据集是一个150行5列的二维表(关注公众号,回复“BP数据”,即可获取训练数据集)。
(2)BP神经网络模型的完整代码
1# _*_ coding:utf-8 _*_
2#https://blog.csdn.net/li_huifei/article/details/78675032
3import math
4import random
5from sklearn.decomposition import PCA
6from sklearn.model_selection import train_test_split
7import matplotlib.pyplot as plt
8import matplotlib as mpl
9import numpy as np
10from sklearn.metrics import accuracy_score
11
12def trtype(s):#定义类别转换函数
13 types = {'Iris-setosa': 0, 'Iris-versicolor': 1, 'Iris-virginica': 2}
14 return types[s]
15
16# data = np.loadtxt('iris.data',delimiter=',',converters={4:trtype})#读入数据,第五列转换为类别012
17data = np.loadtxt('data.txt',delimiter=',',converters={4:trtype})#读入数据,第五列转换为类别012
18
19x,y = np.split(data,(4,),axis=1)#切分data和label
20
21pca=PCA(n_components=2) #主成分分析
22x=pca.fit_transform(x) #为方便绘图,对x进行PCA降维至二维
23#划分测试集和训练集
24def label_tr(y):#标签转换,将一维标签转换为三维
25 l = {0:[1,0,0],1:[0,1,0],2:[0,0,1]}
26 ys = []
27 for i in range(len(y)):
28 ys.append(l[int(y[i])])
29 return np.array(ys)
30
31def inv_label_tr(y_1d):#标签转换逆过程
32 y_pres = []
33 for i in range(y_1d.shape[0]):
34 for j in range(3):
35 if (y_1d[i][j]==1):
36 y_lable = j
37 y_pres.append(y_lable)
38
39 return np.array(y_pres)
40
41y = label_tr(y)
42
43#划分数据(将数据划分成训练集和测试集)
44x_train, x_test, y_train, y_test = train_test_split(x, y, random_state=1, train_size=0.6)
45
46# print y_train
47
48random.seed(0)
49
50
51def rand(a, b):#随机数函数
52 return (b - a) * random.random() + a
53
54
55def make_matrix(m, n, fill=0.0):#矩阵生成函数
56 mat = []
57 for i in range(m):
58 mat.append([fill] * n)
59 return mat
60
61
62def sigmoid(x):#激活函数
63 return 1.0 / (1.0 + math.exp(-x))
64
65
66def sigmoid_derivative(x):#激活函数求导
67 return x * (1 - x)
68
69
70class BPNeuralNetwork:#BP神经网络类
71 def __init__(self):#初始化
72 self.input_n = 0
73 self.hidden_n = 0
74 self.output_n = 0
75 self.input_cells = []
76 self.hidden_cells = []
77 self.output_cells = []
78 self.input_weights = []
79 self.output_weights = []
80 self.input_correction = []
81 self.output_correction = []
82
83
84 def setup(self, ni, nh, no):
85 #初始化输入、隐层、输出元数
86 self.input_n = ni + 1
87 self.hidden_n = nh
88 self.output_n = no
89 # 初始化神经元
90 self.input_cells = [1.0] * self.input_n
91 self.hidden_cells = [1.0] * self.hidden_n
92 self.output_cells = [1.0] * self.output_n
93 # 初始化权重矩阵
94 self.input_weights = make_matrix(self.input_n, self.hidden_n)
95 self.output_weights = make_matrix(self.hidden_n, self.output_n)
96 # 初始化权重
97 for i in range(self.input_n):
98 for h in range(self.hidden_n):
99 self.input_weights[i][h] = rand(-0.2, 0.2)
100 for h in range(self.hidden_n):
101 for o in range(self.output_n):
102 self.output_weights[h][o] = rand(