人工神经网络无需事先确定输入输出之间映射关系的数学方程,仅通过自身的训练,学习某种规则,在给定输入值时得到最接近期望输出值的结果。作为一种智能信息处理系统,人工神经网络实现其功能的核心是算法。BP神经网络是一种按误差反向传播(简称误差反传)训练的多层前馈网络,其算法称为BP算法,它的基本思想是梯度下降法,利用梯度搜索技术,以期使网络的实际输出值和期望输出值的误差均方差为最小。
基本BP算法包括信号的前向传播和误差的反向传播两个过程。即计算误差输出时按从输入到输出的方向进行,而调整权值和阈值则从输出到输入的方向进行。正向传播时,输入信号通过隐含层作用于输出节点,经过非线性变换,产生输出信号,若实际输出与期望输出不相符,则转入误差的反向传播过程。误差反传是将输出误差通过隐含层向输入层逐层反传,并将误差分摊给各层所有单元,以从各层获得的误差信号作为调整各单元权值的依据。通过调整输入节点与隐层节点的联接强度和隐层节点与输出节点的联接强度以及阈值,使误差沿梯度方向下降,经过反复学习训练,确定与最小误差相对应的网络参数(权值和阈值),训练即告停止。此时经过训练的神经网络即能对类似样本的输入信息,自行处理输出误差最小的经过非线形转换的信息。
BP网络是在输入层与输出层之间增加若干层(一层或多层)神经元,这些神经元称为隐单元,它们与外界没有直接的联系,但其状态的改变,则能影响输入与输出之间的关系,每一层可以有若干个节点。
BP神经网络的计算过程由正向计算过程和反向计算过程组成。正向传播过程,输入模式从输入层经隐单元层逐层处理,并转向输出层,每~层神经元的状态只影响下一层神经元的状态。如果在输出层不能得到期望的输出,则转入反向传播,将误差信号沿原来的连接通路返回,通过修改各神经元的权值,使得误差信号最小。
1.网络状态初始化
2.前向计算过程
对于每个训练样本,BP算法先将输入样例提供给输入神经元,然后逐层的将信号向前传播,直到产生输出层的结果,然后计算输出层的误差,再将误差逆向传播到隐层神经元,然后根据神经元的误差来对连接权值和与之进行调整优化,知道训练达到很小的误差值或者迭代到一定的次数。
最后通过实验可知,在mnist数据集上利用BP神经网络识别的正确率大致在87.78%左右。
从数学角度看,BP算法为一种局部搜索的优化方法,但它要解决的问题为求解复杂非线性函数的全局极值,因此,算法很有可能陷入局部极值,使训练失败;
网络的逼近、推广能力同学习样本的典型性密切相关,而从问题中选取典型样本实例组成训练集是一个很困难的问题。
网络的预测能力(也称泛化能力、推广能力)与训练能力(也称逼近能力、学习能力)的矛盾。一般情况下,训练能力差时,预测能力也差,并且一定程度上,随训练能力地提高,预测能力也提高。但这种趋势有一个极限,当达到此极限时,随训练能力的提高,预测能力反而下降,即出现所谓“过拟合”现象。此时,网络学习了过多的样本细节,而不能反映样本内含的规律。
这个也是这题比较担心的问题。我们能想到的是给他添加误差。
那么这个BP是不是前馈的呢,答案是肯定的。
计算的时候,这就是前馈了,也就是信号从输入到得到输出的过程是向前传递的。那么反馈又是哪里来的呢?在你的网络没有训练好的时候,输出肯定和你想象的不一样,这个时候求偏差,并且把偏差一级一级向前传递,逐层得到,这就是反馈。反馈是用来求偏导数的,偏导数是用来作梯度下降的,梯度下降是为了求得代价函数的极小值,使得期望和输出之间的误差尽可能的减小。你也可以自己定义一个代价函数,没准就不需要反馈了呢
根据这幅图,我们可以开始准备理论知识然后敲代码了。
理论知识等汤布院男爵有空了再写吧。
在《MATLAB在数学建模中的应用》卓金武第二版的134页的这道题,我以它为例子:
给定某地区20年的数据,6列,21行,第一列值为年份,第二列为人数,第三列为机动车数量,第四列为公路面积,第五列为公路客运量,第六列为公路货运量,这20年是1990年到2009年,现在给我们2010和2011年,第二、三和四列的数据,让我们用BP网络预测该地区2010年和2011年公路的客运量和公路货运量,也就是第五和六列的值。
这个是书上给的matlab代码片段:
p=[sqrs;sqjdcs;sqglmj]; % 输入数据矩阵
t=[glkyl;glhyl]; % 目标数据矩阵
[SamIn,minp,maxp,tn,mint,maxt]=premnmx(p,t); % 原始样本对(输入和输出)初始化
rand('state',sum(100*clock)); % 依据系统时钟种子产生随机数
NoiseVar=0.01; % 噪声强度为0.01(添加噪声的目的是为了防止网络过度拟合)
Noise=NoiseVar*randn(2,SamNum); % 生成噪声
SamOut=tn+Noise; % 将噪声添加到输出样本上
TestSamIn=SamIn; % 这里取输入样本与测试样本相同,因为样本容量偏少
TestSanOut=SamOut; % 也取输出样本与测试样本相同
MaxEpochs=50000; % 最多训练次数为50000
lr=0.035; % 学习速率为0.035
E0=0.65*10^(-3); % 目标误差为0.65*10^(-3)
W1=0.5*rand(HiddenUnitNum,InDim)-0.1;% 初始化输入层与隐含层之间的权值
B1=0.5*rand(HiddenUnitNum,1)-0.1;% 初始化输入层与隐含层之间的权值
W2=0.5*rand(OutDim,HiddenUnitNum)-0.1;% 初始化输出层与隐含层之间的权值
B2=0.5*rand(OutDim,1)-0.1;% 初始化输出层与隐含层之间的权值
最近在苦逼的学习python,因此来练习下:
首先是相关参数的设定:(读入数据)
fname = "predict.xlsx"
bk = xlrd.open_workbook(fname)
# print(bk.nsheets)
shxrange = range(bk.nsheets)
之后时看看画图的效果如何:
fig1 = pl.figure(figsize=(20,10))
ax = pl.subplot(511)
xmajorLocator = MultipleLocator(1)
ax.xaxis.set_major_locator(xmajorLocator)
pl.plot(year, sqrs, 'o-', label=u"线条", color="r")
bx = pl.subplot(512)
xmajorLocator = MultipleLocator(1)
bx.xaxis.set_major_locator(xmajorLocator)
pl.plot(year, sqjdcs, 'o-', label=u"线条", color="y")
cx = pl.subplot(513)
xmajorLocator = MultipleLocator(1)
cx.xaxis.set_major_locator(xmajorLocator)
pl.plot(year, sqglmj, 'o-', label=u"线条", color="b")
dx = pl.subplot(514)
xmajorLocator = MultipleLocator(1)
dx.xaxis.set_major_locator(xmajorLocator)
pl.plot(year, glkyl, 'o-', label=u"线条", color="g")
ex = pl.subplot(515)
xmajorLocator = MultipleLocator(1)
ex.xaxis.set_major_locator(xmajorLocator)
pl.plot(year, glhyl, 'o-', label=u"线条")
pl.savefig('simulation.png',dpi=500,bbox_inches='tight')
pl.show()
图片展示如下:
我们这样大概能知道是个什么样的趋势
然后开始进行数据预处理,包括合并必须列、归一化和为了防止过拟合进行加噪音。代码此处略去。
之后就是训练部分,由于有成型的MATLAB代码,依葫芦画瓢:
w1 = 0.5*np.random.rand(hiddenunitnum, indim)-0.1
b1 = 0.5*np.random.rand(hiddenunitnum, 1)-0.1
w2 = 0.5*np.random.rand(outdim, hiddenunitnum)-0.1
b2 = 0.5*np.random.rand(outdim, 1)-0.1
errhistory = []
for i in range(maxepochs):
hiddenout = logsig((np.dot(w1, sampleinnorm).transpose()+b1.transpose())).transpose()
networkout = (np.dot(w2,hiddenout).transpose()+b2.transpose()).transpose()
err = sampleoutnorm - networkout
sse = sum(sum(err**2))
errhistory.append(sse)
if sse < errorfinal:
break
delta2 = err
delta1 = np.dot(w2.transpose(), delta2)*hiddenout*(1-hiddenout)
dw2 = np.dot(delta2, hiddenout.transpose())
db2 = np.dot(delta2, np.ones((samnum, 1)))
dw1 = np.dot(delta1, sampleinnorm.transpose())
db1 = np.dot(delta1, np.ones((samnum, 1)))
w2 += learnrate*dw2
b2 += learnrate*db2
w1 += learnrate*dw1
b1 += learnrate*db1
这个时候整个网络已经搭建完毕并且会在这个核心部分进行迭代。
最后用测试的两个年份的数据进行预测:
# 利用训练好的网络进行预测
pnew1 = [[73.39, 75.55], [3.9635, 4.0975], [0.9880, 1.0268]]
pnew = np.mat(pnew1)
这个是数据,之后时经典的三段式:
归一化
隐藏层
输出层
得到的预测结果还是很理想的:
本代码是在linux环境下进行配置改造之后编写的,有些放到Windows下会有不兼容,在此不再误人子弟啦~