BP神经网络

序言:现如今深度学习如日中天,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(-2.0, 2.0)
103        # 初始化偏置
104        self.input_correction = make_matrix(self.input_n, self.hidden_n)
105        self.output_correction = make_matrix(self.hidden_n, self.output_n)
106
107
108    def predict(self, inputs):  #inputs是x的一个值
109        # 激活输入层
110        for i in range(self.input_n - 1):
111            self.input_cells[i] = inputs[i]
112        # 激活隐层
113        for j in range(self.hidden_n):
114            total = 0.0
115            for i in range(self.input_n):
116                total += self.input_cells[i] * self.input_weights[i][j]
117            self.hidden_cells[j] = sigmoid(total)
118        # 激活输出层
119        for k in range(self.output_n):
120            total = 0.0
121            for j in range(self.hidden_n):
122                total += self.hidden_cells[j] * self.output_weights[j][k]
123            self.output_cells[k] = sigmoid(total)
124        return self.output_cells[:]
125
126
127    def back_propagate(self, case, label, learn, correct):
128        # 反向传播
129        self.predict(case)
130        # print self.predict(case)
131        # 求输出误差
132        output_deltas = [0.0] * self.output_n
133        for o in range(self.output_n):
134            error = label[o] - self.output_cells[o]
135            output_deltas[o] = sigmoid_derivative(self.output_cells[o]) * error
136        # print output_deltas[0]
137        # 求隐层误差
138        hidden_deltas = [0.0] * self.hidden_n
139        for h in range(self.hidden_n):
140            error = 0.0
141            for o in range(self.output_n):
142                error += output_deltas[o] * self.output_weights[h][o]
143            hidden_deltas[h] = sigmoid_derivative(self.hidden_cells[h]) * error
144        # 更新输出权重
145        for h in range(self.hidden_n):
146            for o in range(self.output_n):
147                change = output_deltas[o] * self.hidden_cells[h]
148                self.output_weights[h][o] += learn * change + correct * self.output_correction[h][o]
149                self.output_correction[h][o] = change
150        # 更新输入权重
151        for i in range(self.input_n):
152            for h in range(self.hidden_n):
153                change = hidden_deltas[h] * self.input_cells[i]
154                self.input_weights[i][h] += learn * change + correct * self.input_correction[i][h]
155                self.input_correction[i][h] = change
156        # 求全局误差
157        error = 0.0
158        for o in range(len(label)):
159            error += 0.5 * (label[o] - self.output_cells[o]) ** 2
160        return error
161
162
163    def train(self, cases, labels, limit=10000, learn=0.05, correct=0.1):
164        #训练神经网络
165        for j in range(limit):
166            error = 0.0
167            for i in range(len(cases)):
168                label = labels[i]
169                case = cases[i]
170                error += self.back_propagate(case, label, learn, correct)
171
172    def fit(self,x_test):#离散预测函数用于输出数据
173        y_pre_1d = []
174        for case in x_test:
175            y_pred = self.predict(case)
176            for i in range(len(y_pred)):
177                if (y_pred[i] == max(y_pred)):
178                    y_pred[i] = 1
179                else: y_pred[i] = 0
180            y_pre_1d.append(y_pred)
181        return inv_label_tr(np.array(y_pre_1d))
182
183
184    def fit2(self,x_test):#连续预测函数用于画图
185        y_pre_1d = []
186        for case in x_test:
187            w = np.array([0,1,2])
188            y_pred = self.predict(case)
189            y_pre_1d.append(np.array(y_pred).dot(w.T))
190        return np.array(y_pre_1d)
191
192
193if __name__ == '__main__':#主函数
194    nn = BPNeuralNetwork()
195    nn.setup(2, 5, 3)#初始化
196    # nn.train(x_train, y_train, 100000, 0.05, 0.1)#训练 
197    nn.train(x_train, y_train, 100, 0.05, 0.1)#训练 
198
199    y_pre_1d = nn.fit(x_test)#测试
200    y_test_1d = inv_label_tr(y_test)
201    print accuracy_score(y_pre_1d,y_test_1d)#打印测试精度
202
203    #画图
204    mpl.rcParams['font.sans-serif'] = [u'SimHei']
205    mpl.rcParams['axes.unicode_minus'] = False
206    cm_light = mpl.colors.ListedColormap(['#FFA0A0', '#A0FFA0', '#A0A0FF'])
207    cm_dark = mpl.colors.ListedColormap(['#AAAAFF', '#FFAAAA','#AAFFAA'])
208
209    x1_min, x1_max = x[:, 0].min(), x[:, 0].max()  # 第0列的范围
210    x2_min, x2_max = x[:, 1].min(), x[:, 1].max()  # 第1列的范围
211    x1, x2 = np.mgrid[x1_min:x1_max:200j, x2_min:x2_max:200j] # 生成网格采样点
212
213    grid_test = np.stack((x1.flat, x2.flat), axis=1)  # 测试点
214    grid_hat = nn.fit2(grid_test)#预测结果
215    grid_hat = grid_hat.reshape(x1.shape)  # 使之与输入的形状相同
216    plt.pcolormesh(x1, x2, grid_hat, cmap=cm_light)
217    plt.scatter(x[:, 0], x[:, 1], c=y, edgecolors='k', s=50, cmap=cm_dark)
218    # plt.title(u'BPNN二特征分类', fontsize=15)
219    plt.title(u'BPNNClassfy', fontsize=15)
220    plt.show()
221print grid_hat.shape

(3)BP神经网络分类结果

为了方便作图,对数据集的维度进行了降维处理,先采用PCA(主成分分析法)将训练数据降为二维,然后采用BP网络进行分类,分类结果如下图所示:

图4 BP神经网络分类效果图

从图4清晰看出,整体的分类效果还算不错,截至当前,机器学习经典分类器已经介绍了两个,后续会陆续的推出SVM、贝叶斯、决策树、随机森林等经典算法,小伙伴们多多支持。

 

 

posted @ 2019-02-23 15:18  大数据技术宅  阅读(1387)  评论(0编辑  收藏  举报