前一篇文章中我们提到单层感知机网络被局限在解决传统的是非问题,当面对异或这类问题时则显得无能为力,为了解决这一问题,人们通过增加网络层数来提升神经网络解决问题的能力。这就是我们今天要讲到的多层感知机以及用来在多层感知机中用来实现权重迭代的方法—BP(Back propagation)迭代方法。实际上两者混在一起也就是我们常说的BP神经网络。
多层感知机(multi layer perception, MLP )
多层感知机就是含有至少一个隐藏层的由全连接层组成的神经网络,且在整个神经网络中除了输入层之外,每一层都经过激活函数进行非线性变换。多层感知机的层数和各隐藏层中隐藏单元个数都是超参数。参考下图的单隐藏层的MLP结构:
![](https://i-blog.csdnimg.cn/blog_migrate/51114c1bac4de8a5bc6dcdc5505eecae.png)
反响传播算法(Back propagation)
反响传播算法的基本思想是,利用一次前向过程计算得到的误差,反向传播到每一层的权重上,并实现权重的逐步更新,通过足够多的更新步骤后得到一个较优权重使最终得到的误差能够满足运算标准。
一种最常用的误差反向传递方法是梯度下降法:即通过计算后一层到前一层的运算梯度,使得权重的变化量正比于该梯度的值,以此得到每一层权重的误差。其比例系数就是常说的学习率。
BP 神经网络的相关推导网络上已经有很多,例如戳这,可以详细的了解其推导过程。
下面附上用BP网络实现手写数字识别的代码
#三层神经网络,含有一个隐藏层
#结果大约在84%左右
import math
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
#读入数据
dataset = pd.read_csv('train_data.csv',header=None)
#数据归一化
dataset /=256.0
dataset = dataset.values
#print (type(dataset))
dataset_label=pd.read_csv('train_data_labels.csv',header=None)
dataset_label = dataset_label.values
#神经网络配置
dataset_num = len(dataset)
input_num =dataset.shape[1] #确定输入节点数
out_num = 10
hid_num = 12 #隐藏层节点数
#初始化输入权重矩阵
w12 = 0.2*np.random.random((input_num,hid_num)) - 0.1
# 初始化隐层权矩阵
w23 = 0.2*np.random.random((hid_num, out_num))- 0.1
print(w12.shape)
hid_offset = np.zeros(hid_num) # 隐层偏置向量
out_offset = np.zeros(out_num) # 输出层偏置向量
input_learnrate = 0.15 # 输入层权值学习率
hid_learnrate = 0.15 # 隐层学权值习率
err_th = 0.01 # 学习误差门限
#print(dataset.loc[1:2].shape)
#定义激活函数
def get_act(x):
act_vec = []
for i in x:
act_vec.append(1/(1+math.exp(-i)))
act_vec = np.array(act_vec)
return act_vec
def get_err(e):
return 0.5*np.dot(e,e)
# 训练——可使用err_th与get_err() 配合,提前结束训练过程
for count in range (0,dataset_num):
t_label = np.zeros(out_num)
t_label[dataset_label[count]] = 1
#前向过程
hid_value = np.dot(dataset[count],w12)
hid_act = get_act(hid_value)
out_value = np.dot(hid_act,w23)
out_act = get_act(out_value)
#反向过程
e = t_label - out_act
out_delta = e*out_act*(1-out_act)
hid_delta = hid_act * (1-hid_act)*np.dot(w23,out_delta)
for i in range(0,out_num):
#更新隐藏层到输出层权向量
w23[:,i] += hid_learnrate * out_delta[i] * hid_act
for i in range (0,hid_num):
#更新输入层到隐藏层权重
w12[:,i] += input_learnrate * hid_delta[i] * dataset[count]
out_offset += hid_learnrate * out_delta
hid_offset += input_learnrate * hid_delta
#测试
test_data = pd.read_csv('test_data.csv',header=None)
test_data /=256.0
test_data = test_data.values
test_label = pd.read_csv('test_data_label.csv',header=None)
test_label =test_label.values
right = np.zeros(10)
numbers = np.zeros(10)
# 统计测试数据中各个数字的数目
print(test_label.shape)
for i in test_label:
numbers[i] += 1
for count in range(len(test_data)):
hid_value = np.dot(test_data[count],w12) + hid_offset
hid_act = get_act(hid_value)
out_value = np.dot(hid_act,w23) + out_offset
out_act = get_act(out_value)
if np.argmax(out_act) == test_label[count]:
right[test_label[count]] += 1
print (right)
print (numbers)
result = right/numbers
sum = right.sum()
print (result)
print (sum/len(test_data))
本文代码和数据可以在我的github主页上找到。