python对BP神经网络实现
python对BP神经网络实现
一、概念理解
开始之前首先了解一下BP神经网络,BP的英文是back propagationd的意思,它是一种按误差反向传播(简称误差反传)训练的多层前馈网络,其算法称为BP算法。
它的基本思想是梯度下降法,利用梯度搜索技术,期望使网络的实际输出值和期望输出值的误差和均方差为最小。
基本BP算法包括信号的前向传播和误差的反向传播两个过程。
正向传播过程:输入信号——通过隐含层——>作用于输出节点(经过非线性变换,产生输出信号)——>验证实际输出结果是否与期望输出符合【若不符合,转入到误差的反向传播】
反向传播过程:输出误差——通过隐含层——>输入层(逐层反传,并将误差分摊给各层单元)——>获得误差信号(作为调整各单元权值的依据)——>调整输入节点与隐层节点的联接强度和隐层节点与输出
节点的联接强度以及阈值——>误差沿梯度方向下降——>经过反复学习训练——>确定与最小误差相对应的网络参数(权值和阈值)——>结束
二、python实现过程
1、前言
实现任务例子:
路运量主要包括公路客运量和公路货运量两方面。某个地区的公路运量主要与该地区的人数、机动车数量和公路面积有关,已知该地区20年(1990-2009)的公路运量相关数据如下:
(1) 人数/万人
20.55 22.44 25.37 27.13 29.45 30.10 30.96 34.06 36.42 38.09 39.13 39.99 41.93 44.59 47.30 52.89 55.73 56.76 59.17 60.63
(2) 机动车数量/万辆
0.6 0.75 0.85 0.9 1.05 1.35 1.45 1.6 1.7 1.85 2.15 2.2 2.25 2.35 2.5 2.6 2.7 2.85 2.95 3.1
(3) 公路面积/单位:万平方公里
0.09 0.11 0.11 0.14 0.20 0.23 0.23 0.32 0.32 0.34 0.36 0.36 0.38 0.49 0.56 0.59 0.59 0.67 0.69 0.79
(4) 公路客运量/万人
5126 6217 7730 9145 10460 11387 12353 15750 18304 19836 21024 19490 20433 22598 25107 33442 36836 40548 42927 43462
(5) 公路货运量(单位:万吨)
1237 1379 1385 1399 1663 1714 1834 4322 8132 8936 11099 11203 10524 11115 13320 16762 18673 20724 20803 21804
结果检测:预测2010和2011年的公路客运量和公路客运量?、
准备:
(1)、激活函数
此函数为,注意的是在机器学习中,np.exp()的参数可以是向量,当传入是向量的时候,其返回值是该向量内所有的元素值分别进行公式求值后构成的一个列表。
1 def sigmod(x):
2 return 1 / (1 + np.exp(-x))
(2)、数据的读取
需要了解xlrd函数的的使用
2、实现过程
(1)、数据读取
训练数据的读取
1 def read_xls_file(filename): #读取训练数据 2 data = xlrd.open_workbook(filename) 3 sheet1 = data.sheet_by_index(0) 4 m = sheet1.nrows 5 n = sheet1.ncols 6 # 人口数量 机动车数量 公路面积 公路客运量 公路货运量 7 pop,veh,roa,pas,fre=[],[],[],[],[] 8 for i in range(m): 9 row_data = sheet1.row_values(i) 10 if i > 0: 11 pop.append(row_data[1]) 12 veh.append(row_data[2]) 13 roa.append(row_data[3]) 14 pas.append(row_data[4]) 15 fre.append(row_data[5]) 16 dataMat = np.mat([pop,veh,roa]) 17 labels = np.mat([pas,fre]) 18 dataMat_old = dataMat 19 labels_old = labels 20 # 数据集合,标签集合,保留数据集合,保留标签集合 21 return dataMat,labels,dataMat_old,labels_old
测试数据的读取
def read_xls_testfile(filename): #读取测试数据 data = xlrd.open_workbook(filename) sheet1 = data.sheet_by_index(0) m = sheet1.nrows n = sheet1.ncols pop = [] veh = [] roa = [] for i in range(m): row_data = sheet1.row_values(i) if i > 0: pop.append(row_data[1]) veh.append(row_data[2]) roa.append(row_data[3]) dataMat = np.mat([pop,veh,roa]) return dataMat
(2)、最大最小法归一
将原始数据线性化的方法转换到[0 1]的范围,归一化公式如下:
1 def Norm(dataMat,labels): 2 dataMat_minmax = np.array([dataMat.min(axis=1).T.tolist()[0],dataMat.max(axis=1).T.tolist()[0]]).transpose() 3 dataMat_Norm = ((np.array(dataMat.T)-dataMat_minmax.transpose()[0])/(dataMat_minmax.transpose()[1]-dataMat_minmax.transpose()[0])).transpose() 4 labels_minmax = np.array([labels.min(axis=1).T.tolist()[0],labels.max(axis=1).T.tolist()[0]]).transpose() 5 labels_Norm = ((np.array(labels.T).astype(float)-labels_minmax.transpose()[0])/(labels_minmax.transpose()[1]-labels_minmax.transpose()[0])).transpose() 6 return dataMat_Norm,labels_Norm,dataMat_minmax,labels_minmax
(3)、训练模型
返回值为4个权值以及最大可迭代次数
1 def BP(sampleinnorm, sampleoutnorm,hiddenunitnum=3): 2 # 超参数 3 maxepochs = 60000 # 最大迭代次数 4 learnrate = 0.030 # 学习率 5 errorfinal = 0.65*10**(-3) # 最终迭代误差 6 indim = 3 # 输入特征维度3 7 outdim = 2 # 输出特征唯独2 8 # 隐藏层默认为3个节点,1层 9 n,m = shape(sampleinnorm) 10 w1 = 0.5*np.random.rand(hiddenunitnum,indim)-0.1 #8*3维 11 b1 = 0.5*np.random.rand(hiddenunitnum,1)-0.1 #8*1维 12 w2 = 0.5*np.random.rand(outdim,hiddenunitnum)-0.1 #2*8维 13 b2 = 0.5*np.random.rand(outdim,1)-0.1 #2*1维 14 15 errhistory = [] 16 17 for i in range(maxepochs): 18 # 激活隐藏输出层 19 hiddenout = sigmod((np.dot(w1,sampleinnorm).transpose()+b1.transpose())).transpose() 20 # 计算输出层输出 21 networkout = (np.dot(w2,hiddenout).transpose()+b2.transpose()).transpose() 22 # 计算误差 23 err = sampleoutnorm - networkout 24 # 计算代价函数(cost function)sum对数组里面的所有数据求和,变为一个实数 25 sse = sum(sum(err**2))/m 26 errhistory.append(sse) 27 if sse < errorfinal: #迭代误差 28 break 29 # 计算delta 30 delta2 = err 31 delta1 = np.dot(w2.transpose(),delta2)*hiddenout*(1-hiddenout) 32 # 计算偏置 33 dw2 = np.dot(delta2,hiddenout.transpose()) 34 db2 = 1 / 20 * np.sum(delta2, axis=1, keepdims=True) 35 36 dw1 = np.dot(delta1,sampleinnorm.transpose()) 37 db1 = 1/20*np.sum(delta1,axis=1,keepdims=True) 38 39 # 更新权值 40 w2 += learnrate*dw2 41 b2 += learnrate*db2 42 w1 += learnrate*dw1 43 b1 += learnrate*db1 44 45 return errhistory,b1,b2,w1,w2,maxepochs
(7)、曲线显示
显示内容为两个内容:1、神经网络曲线图(包括客运量预测输出、客运量真实输出、货运量预测输出、货运量真实输出)2、误差曲线图
1 def show(sampleinnorm,sampleoutminmax,sampleout,errhistory,maxepochs): # 图形显示 2 matplotlib.rcParams['font.sans-serif']=['SimHei'] # 防止中文乱码 3 hiddenout = sigmod((np.dot(w1,sampleinnorm).transpose()+b1.transpose())).transpose() 4 networkout = (np.dot(w2,hiddenout).transpose()+b2.transpose()).transpose() 5 diff = sampleoutminmax[:,1]-sampleoutminmax[:,0] 6 networkout2 = networkout 7 networkout2[0] = networkout2[0]*diff[0]+sampleoutminmax[0][0] 8 networkout2[1] = networkout2[1]*diff[1]+sampleoutminmax[1][0] 9 sampleout = np.array(sampleout) 10 11 fig,axes = plt.subplots(nrows=2,ncols=1,figsize=(12,10)) 12 line1, = axes[0].plot(networkout2[0],'k',markeredgecolor='b',marker = 'o',markersize=7) 13 line2, = axes[0].plot(sampleout[0],'r',markeredgecolor='g',marker = u'$\star$',markersize=7) 14 line3, = axes[0].plot(networkout2[1],'g',markeredgecolor='g',marker = 'o',markersize=7) 15 line4, = axes[0].plot(sampleout[1],'y',markeredgecolor='b',marker = u'$\star$',markersize=7) 16 axes[0].legend((line1,line2,line3,line4),(u'客运量预测输出',u'客运量真实输出',u'货运量预测输出',u'货运量真实输出'),loc = 'upper left') 17 axes[0].set_ylabel(u'公路客运量及货运量') 18 xticks = range(0,22,1) 19 xtickslabel = range(1990,2012,1) 20 axes[0].set_xticks(xticks) 21 axes[0].set_xticklabels(xtickslabel) 22 axes[0].set_xlabel(u'年份') 23 axes[0].set_title(u'BP神经网络') 24 25 errhistory10 = np.log10(errhistory) 26 minerr = min(errhistory10) 27 plt.plot(errhistory10) 28 axes[1]=plt.gca() 29 axes[1].set_yticks([-2,-1,0,1,2,minerr]) 30 axes[1].set_yticklabels([u'$10^{-2}$',u'$10^{-1}$',u'$1$',u'$10^{1}$',u'$10^{2}$',str(('%.4f'%np.power(10,minerr)))]) 31 axes[1].set_xlabel(u'训练次数') 32 axes[1].set_ylabel(u'误差') 33 axes[1].set_title(u'误差曲线')34 plt.show() 35 plt.close() 36 return diff, sampleoutminmax
(8)、主函数
1 if __name__ == "__main__": 2 dataMat,labels,dataMat_old,labels_old = read_xls_file('训练.xls') 3 dataMat_Norm,labels_Norm, dataMat_minmax, labels_minmax = Norm(dataMat,labels) 4 err, b1, b2, w1, w2,maxepochs = BP(dataMat_Norm,labels_Norm,6) 5 dataMat_test = read_xls_testfile('测试.xls') 6 diff, sampleoutminmax = show(dataMat_Norm,labels_minmax,labels,err,maxepochs) 7 pre(dataMat_test,dataMat_minmax,diff, sampleoutminmax ,w1,b1,w2,b2)
(9)、拓展
以上采用的是数据读取,也可以直接定义,返回的值是里面的数据以及label就可以了,可以自己实验一下。
1 # 人数(单位:万人) 2 population = [20.55, 22.44, 25.37, 27.13, 29.45, 30.10, 30.96, 34.06, 36.42, 38.09, 39.13, 39.99, 41.93, 44.59, 47.30, 3 52.89, 55.73, 56.76, 59.17, 60.63] 4 # 机动车数(单位:万辆) 5 vehicle = [0.6, 0.75, 0.85, 0.9, 1.05, 1.35, 1.45, 1.6, 1.7, 1.85, 2.15, 2.2, 2.25, 2.35, 2.5, 2.6, 2.7, 2.85, 2.95, 6 3.1] 7 # 公路面积(单位:万平方公里) 8 roadarea = [0.09, 0.11, 0.11, 0.14, 0.20, 0.23, 0.23, 0.32, 0.32, 0.34, 0.36, 0.36, 0.38, 0.49, 0.56, 0.59, 0.59, 0.67, 9 0.69, 0.79] 10 # 公路客运量(单位:万人) 11 passengertraffic = [5126, 6217, 7730, 9145, 10460, 11387, 12353, 15750, 18304, 19836, 21024, 19490, 20433, 22598, 25107, 12 33442, 36836, 40548, 42927, 43462] 13 # 公路货运量(单位:万吨) 14 freighttraffic = [1237, 1379, 1385, 1399, 1663, 1714, 1834, 4322, 8132, 8936, 11099, 11203, 10524, 11115, 13320, 16762, 15 18673, 20724, 20803, 21804] 16 17 # 数据转为矩阵 18 samplein = np.mat([population, vehicle, roadarea]) 19 sampleinminmax = np.array([samplein.min(axis=1).T.tolist()[0], samplein.max(axis=1).T.tolist()[0]]).transpose() # 3*2 20 sampleout = np.mat([passengertraffic, freighttraffic]) 21 sampleoutminmax = np.array( 22 [sampleout.min(axis=1).T.tolist()[0], sampleout.max(axis=1).T.tolist()[0]]).transpose() # 2*2
三、总结
1、整体代码
注意:记得替换掉数据集,该程序只适用于数据集保存在表格里,如果其他格式,请参考python的其他文件形式的读取
1 from numpy import * 2 from mpl_toolkits.mplot3d import Axes3D 3 import matplotlib.pyplot as plt 4 import numpy as np 5 from pylab import * 6 import xlrd 7 8 # 数据读取 9 def read_xls_file(filename): #读取训练数据 10 data = xlrd.open_workbook(filename) 11 sheet1 = data.sheet_by_index(0) 12 m = sheet1.nrows 13 n = sheet1.ncols 14 # 人口数量 机动车数量 公路面积 公路客运量 公路货运量 15 pop,veh,roa,pas,fre=[],[],[],[],[] 16 for i in range(m): 17 row_data = sheet1.row_values(i) 18 if i > 0: 19 pop.append(row_data[1]) 20 veh.append(row_data[2]) 21 roa.append(row_data[3]) 22 pas.append(row_data[4]) 23 fre.append(row_data[5]) 24 dataMat = np.mat([pop,veh,roa]) 25 labels = np.mat([pas,fre]) 26 dataMat_old = dataMat 27 labels_old = labels 28 # 数据集合,标签集合,保留数据集合,保留标签集合 29 return dataMat,labels,dataMat_old,labels_old 30 31 # 读取测试集 32 def read_xls_testfile(filename): #读取测试数据 33 data = xlrd.open_workbook(filename) 34 sheet1 = data.sheet_by_index(0) 35 m = sheet1.nrows 36 n = sheet1.ncols 37 pop = [] 38 veh = [] 39 roa = [] 40 for i in range(m): 41 row_data = sheet1.row_values(i) 42 if i > 0: 43 pop.append(row_data[1]) 44 veh.append(row_data[2]) 45 roa.append(row_data[3]) 46 47 dataMat = np.mat([pop,veh,roa]) 48 return dataMat 49 50 # 由于数据数量级相差太大,我们要先对数据进行归一化处理Min-Max归一化 51 def Norm(dataMat,labels): 52 dataMat_minmax = np.array([dataMat.min(axis=1).T.tolist()[0],dataMat.max(axis=1).T.tolist()[0]]).transpose() 53 dataMat_Norm = ((np.array(dataMat.T)-dataMat_minmax.transpose()[0])/(dataMat_minmax.transpose()[1]-dataMat_minmax.transpose()[0])).transpose() 54 labels_minmax = np.array([labels.min(axis=1).T.tolist()[0],labels.max(axis=1).T.tolist()[0]]).transpose() 55 labels_Norm = ((np.array(labels.T).astype(float)-labels_minmax.transpose()[0])/(labels_minmax.transpose()[1]-labels_minmax.transpose()[0])).transpose() 56 return dataMat_Norm,labels_Norm,dataMat_minmax,labels_minmax 57 58 # 激活函数 59 def sigmod(x): 60 return 1/(1+np.exp(-x)) 61 62 # Back Propagation 63 def BP(sampleinnorm, sampleoutnorm,hiddenunitnum=3): 64 # 超参数 65 maxepochs = 60000 # 最大迭代次数 66 learnrate = 0.030 # 学习率 67 errorfinal = 0.65*10**(-3) # 最终迭代误差 68 indim = 3 # 输入特征维度3 69 outdim = 2 # 输出特征唯独2 70 # 隐藏层默认为3个节点,1层 71 n,m = shape(sampleinnorm) 72 w1 = 0.5*np.random.rand(hiddenunitnum,indim)-0.1 #8*3维 73 b1 = 0.5*np.random.rand(hiddenunitnum,1)-0.1 #8*1维 74 w2 = 0.5*np.random.rand(outdim,hiddenunitnum)-0.1 #2*8维 75 b2 = 0.5*np.random.rand(outdim,1)-0.1 #2*1维 76 77 errhistory = [] 78 79 for i in range(maxepochs): 80 # 激活隐藏输出层 81 hiddenout = sigmod((np.dot(w1,sampleinnorm).transpose()+b1.transpose())).transpose() 82 # 计算输出层输出 83 networkout = (np.dot(w2,hiddenout).transpose()+b2.transpose()).transpose() 84 # 计算误差 85 err = sampleoutnorm - networkout 86 # 计算代价函数(cost function)sum对数组里面的所有数据求和,变为一个实数 87 sse = sum(sum(err**2))/m 88 errhistory.append(sse) 89 if sse < errorfinal: #迭代误差 90 break 91 # 计算delta 92 delta2 = err 93 delta1 = np.dot(w2.transpose(),delta2)*hiddenout*(1-hiddenout) 94 # 计算偏置 95 dw2 = np.dot(delta2,hiddenout.transpose()) 96 db2 = 1 / 20 * np.sum(delta2, axis=1, keepdims=True) 97 98 dw1 = np.dot(delta1,sampleinnorm.transpose()) 99 db1 = 1/20*np.sum(delta1,axis=1,keepdims=True) 100 101 # 更新权值 102 w2 += learnrate*dw2 103 b2 += learnrate*db2 104 w1 += learnrate*dw1 105 b1 += learnrate*db1 106 107 return errhistory,b1,b2,w1,w2,maxepochs 108 109 110 def show(sampleinnorm,sampleoutminmax,sampleout,errhistory,maxepochs): # 图形显示 111 matplotlib.rcParams['font.sans-serif']=['SimHei'] # 防止中文乱码 112 hiddenout = sigmod((np.dot(w1,sampleinnorm).transpose()+b1.transpose())).transpose() 113 networkout = (np.dot(w2,hiddenout).transpose()+b2.transpose()).transpose() 114 diff = sampleoutminmax[:,1]-sampleoutminmax[:,0] 115 networkout2 = networkout 116 networkout2[0] = networkout2[0]*diff[0]+sampleoutminmax[0][0] 117 networkout2[1] = networkout2[1]*diff[1]+sampleoutminmax[1][0] 118 sampleout = np.array(sampleout) 119 120 fig,axes = plt.subplots(nrows=2,ncols=1,figsize=(12,10)) 121 line1, = axes[0].plot(networkout2[0],'k',markeredgecolor='b',marker = 'o',markersize=7) 122 line2, = axes[0].plot(sampleout[0],'r',markeredgecolor='g',marker = u'$\star$',markersize=7) 123 line3, = axes[0].plot(networkout2[1],'g',markeredgecolor='g',marker = 'o',markersize=7) 124 line4, = axes[0].plot(sampleout[1],'y',markeredgecolor='b',marker = u'$\star$',markersize=7) 125 axes[0].legend((line1,line2,line3,line4),(u'客运量预测输出',u'客运量真实输出',u'货运量预测输出',u'货运量真实输出'),loc = 'upper left') 126 axes[0].set_ylabel(u'公路客运量及货运量') 127 xticks = range(0,22,1) 128 xtickslabel = range(1990,2012,1) 129 axes[0].set_xticks(xticks) 130 axes[0].set_xticklabels(xtickslabel) 131 axes[0].set_xlabel(u'年份') 132 axes[0].set_title(u'BP神经网络') 133 134 errhistory10 = np.log10(errhistory) 135 minerr = min(errhistory10) 136 plt.plot(errhistory10) 137 axes[1]=plt.gca() 138 axes[1].set_yticks([-2,-1,0,1,2,minerr]) 139 axes[1].set_yticklabels([u'$10^{-2}$',u'$10^{-1}$',u'$1$',u'$10^{1}$',u'$10^{2}$',str(('%.4f'%np.power(10,minerr)))]) 140 axes[1].set_xlabel(u'训练次数') 141 axes[1].set_ylabel(u'误差') 142 axes[1].set_title(u'误差曲线') 143 plt.show() 144 145 plt.close() 146 147 return diff, sampleoutminmax 148 149 def pre(dataMat,dataMat_minmax,diff,sampleoutminmax,w1,b1,w2,b2): #数值预测 150 # 归一化数据 151 dataMat_test = ((np.array(dataMat.T)-dataMat_minmax.transpose()[0])/(dataMat_minmax.transpose()[1]-dataMat_minmax.transpose()[0])).transpose() 152 # 然后计算两层的输出结果 153 # 隐藏层 154 hiddenout = sigmod((np.dot(w1,dataMat_test).transpose()+b1.transpose())).transpose() 155 # 输出层 156 networkout1 = (np.dot(w2,hiddenout).transpose()+b2.transpose()).transpose() 157 networkout = networkout1 158 # 计算结果 159 networkout[0] = networkout[0]*diff[0] + sampleoutminmax[0][0] 160 networkout[1] = networkout[1]*diff[1] + sampleoutminmax[1][0] 161 162 print("2010年预测的公路客运量为:", int(networkout[0][0]),"(万人)") 163 print("2010年预测的公路货运量为:", int(networkout[1][0]),"(万吨)") 164 print("2011年预测的公路客运量为:", int(networkout[0][1]),"(万人)") 165 print("2011年预测的公路货运量为:", int(networkout[1][1]),"(万吨)") 166 167 if __name__ == "__main__": 168 dataMat,labels,dataMat_old,labels_old = read_xls_file('训练.xls') 169 dataMat_Norm,labels_Norm, dataMat_minmax, labels_minmax = Norm(dataMat,labels) 170 err, b1, b2, w1, w2,maxepochs = BP(dataMat_Norm,labels_Norm,6) 171 dataMat_test = read_xls_testfile('测试.xls') 172 diff, sampleoutminmax = show(dataMat_Norm,labels_minmax,labels,err,maxepochs) 173 pre(dataMat_test,dataMat_minmax,diff, sampleoutminmax ,w1,b1,w2,b2)
2、结果展示
2010年预测的公路客运量为: 53980 (万人)
2010年预测的公路货运量为: 28513 (万吨)
2011年预测的公路客运量为: 55716 (万人)
2011年预测的公路货运量为: 29521 (万吨)
注:以上均为自己学习使用,希望可以帮助到你,感谢支持!!!