实验内容
用BP算法训练单隐层前馈神经网络,实现对lris数据分类;数据划分:训练和测试数据划分参考网站上论文中的划分。
- 程序画出BP算法迭代过程中训练准确性曲线
- 列表给出10次独立实验平均错误率和标准差要求
- 讨论BP算法中学习系数的变化对训练错误率的影响,画出曲线
- 在权值更新公式总加入动量项重复以上实验
实验数据集
本实验以Fisher的Iris数据集作为神经网络程序的测试数据集。Iris数据集能够在http:/larchive.ics.uci.edu/ml/datasets/Iris找到。这里简要介绍一下Iris数据集:
有一批Iris花,已知这批Iris花可分为3个品种,现须要对其进行分类。不同品种的Iris花的花萼长度、花萼宽度、花瓣长度、花瓣宽度会有差异。我们现有一批已知品种的Iris花的花萼长度、花萼宽度、花瓣长度、花瓣宽度的数据。
神经网络的实现
数据预处理
在训练神经网络前一般须要对数据进行预处理,一种重要的预处理手段是归一化处理。以下简要介绍归一化处理的原理与方法。
原理
- 什么是归一化?
数据归一化,就是将数据映射到[0,1]或[-1,1]区间或更小的区间,比方(0.1,0.9) 。
- 为什么要归一化处理?
- 输入数据的单位不一样,有些数据的范围可能特别大,导致的结果是神经网络收敛慢、训练时间长。
- 数据范围大的输入在模式分类中的作用可能会偏大,而数据范围小的输入作用就可能会偏小。
- 因为神经网络输出层的激活函数的值域是有限制的,因此须要将网络训练的目标数据映射到激活函数的值域。比如神经网络的输出层若採用S形激活函数,因为S形函数的值域限制在(0,1),也就是说神经网络的输出仅仅能限制在(0,1),所以训练数据的输出就要归一化到[0,1]区间。
- S形激活函数在(0,1)区间以外区域非常平缓,区分度太小。比如S形函数f(X)在參数a=1时,f(100)与f(5)仅仅相差0.0067。
- 归一化算法
一种简单而高速的归一化算法是线性转换算法。线性转换算法常见有两种形式:
y = ( x – min )/( max – min )
当中min为x的最小值,max为x的最大值,输入向量为x,归一化后的输出向量为y 。上式将数据归一化到 [ 0 , 1 ]区间,当激活函数採用S形函数时(值域为(0,1))时这条式子适用。
y = 2 * ( x – min ) / ( max – min ) – 1
这条公式将数据归一化到 [ -1 , 1 ] 区间。当激活函数採用双极S形函数(值域为(-1,1))时这条式子适用。
MATLAB数据归一化处理函数
Matlab中归一化处理数据能够採用premnmx , postmnmx , tramnmx 这3个函数。
- premnmx
语法:[pn,minp,maxp,tn,mint,maxt] = premnmx(p,t)
參数:
pn: p矩阵按行归一化后的矩阵
minp,maxp:p矩阵每一行的最小值,最大值
tn:t矩阵按行归一化后的矩阵
mint,maxt:t矩阵每一行的最小值,最大值
作用:将矩阵p,t归一化到[-1,1] ,主要用于归一化处理训练数据集。
- tramnmx
语法:[pn] = tramnmx(p,minp,maxp)
參数:
minp,maxp:premnmx函数计算的矩阵的最小,最大值
pn:归一化后的矩阵
作用:主要用于归一化处理待分类的输入数据。
- postmnmx
语法: [p,t] = postmnmx(pn,minp,maxp,tn,mint,maxt)
參数:
minp,maxp:premnmx函数计算的p矩阵每行的最小值,最大值
mint,maxt:premnmx函数计算的t矩阵每行的最小值,最大值
作用:将矩阵pn,tn映射回归一化处理前的范围。postmnmx函数主要用于将神经网络的输出结果映射回归一化前的数据范围。
使用Matlab实现神经网络
使用Matlab建立前馈神经网络主要会使用到以下3个函数:
- newff :前馈网络创建函数
- train:训练一个神经网络
- sim :使用网络进行仿真
以下简要介绍这3个函数的使用方法。
- newff函数
- newff函数语法
newff函数參数列表有非常多的可选參数,详细能够參考Matlab的帮助文档,这里介绍newff函数的一种简单的形式。
语法:net = newff ( A, B, {C} ,‘trainFun’)
參数:
A:一个n×2的矩阵,第i行元素为输入信号xi的最小值和最大值;
B:一个k维行向量,其元素为网络中各层节点数;
C:一个k维字符串行向量,每一分量为相应层神经元的激活函数;
trainFun :为学习规则採用的训练算法。
- 经常使用的激活函数
经常使用的激活函数有:
a) 线性函数 (Linear transfer function)
f(x) = x
该函数的字符串为’purelin’。
b) 对数S形转移函数( Logarithmic sigmoid transfer function )
该函数的字符串为’logsig’。
c) 双曲正切S形函数 (Hyperbolic tangent sigmoid transfer function )
也就是上面所提到的双极S形函数。
该函数的字符串为’ tansig’。
Matlab的安装文件夹下的toolbox\nnet\nnet\nntransfer子文件夹中有全部激活函数的定义说明。
- 常见的训练函数
常见的训练函数有:
traingd :梯度下降BP训练函数(Gradient descent backpropagation)
traingdx :梯度下降自适应学习率训练函数
- 网络配置參数
一些重要的网络配置參数例如以下:
net.trainparam.goal :神经网络训练的目标误差
net.trainparam.show : 显示中间结果的周期
net.trainparam.epochs :最大迭代次数
net.trainParam.lr : 学习率
- train函数
网络训练学习函数。
语法:[ net, tr, Y1, E ] = train( net, X, Y )
參数:
X:网络实际输入
Y:网络应有输出
tr:训练跟踪信息
Y1:网络实际输出
E:误差矩阵
- sim函数
语法:Y=sim(net,X)
參数:
net:网络
X:输入给网络的K×N矩阵,当中K为网络输入个数,N为数据样本数
Y:输出矩阵Q×N,当中Q为网络输出个数。
实验过程
将数据集加载到工作区
load iris.dat;
数据预处理
- 将原始iris数据集随机抽取118行作为训练数据集。再在这118行之外抽取32行(即剩余的32行)作为测试数据集。然后分别保存在trainData1.txt和testData1.txt中。
A1=randsample(150,118,false) ;
trainData1=iris(A1,:);
A2=randsample(150,32,false) ;
testData1=iris(A2,:);
save(‘trainData1.txt’,‘trainData1’,’-ascii’),
save(‘testData1.txt’,‘testData1’,’-ascii’),
- 将训练数据每行相同格式的数据按语法读取150次,然后得到每列。
f1 f2 f3 f4是四个特征值,class是类别。
[f1,f2,f3,f4,class] = textread(‘trainData1.txt’ , ‘%f%f%f%f%f’,150);
- 特征值归一化
把输入的4个属性矩阵归一化到[-1,1],并保存到input矩阵中。
即下式为归一化公式:
[input,minI,maxI] = premnmx( [f1 , f2 , f3 , f4 ]’)
- 构造输出矩阵
s = length( class) ; %得到类别矩阵长度:118
Output =zeros(s,3); %生成s行3列的全零阵:预先分配内存空间大小效率更高
for i = 1 : s
output( i , class( i ) ) = 1 ;
End
output矩阵即为输出矩阵
例第一个样本即第一行:0 1 0 则表示属于第二类,而第二个样本即第二行: 1 0 0 则表示它属于第一类。
而 0 0 1则表示属于第三类。
创建神经网络
net = newff(minmax(input),[10 3] , { ‘logsig’ ‘logsig’ } , ‘traingdx’ ); minmax(input):设定输入特征的范围—获取4个输入信号(存储在f1 f2 f3 f4中)的最大值和最小值;
{ ‘logsig’ ‘logsig’ }:隐层和输出层的传递函数–这里隐层和输出层传递函数都为logsig(S形传输函数)
‘traingdx’:表示学习规则采用的学习方法,即学习训练函数为traingdx(梯度下降)
设置训练参数
net.trainparam.show = 50 ;% 显示中间结果的周期
net.trainparam.epochs = 500 ;%最大迭代次数(学习次数)
net.trainparam.goal = 0.01;%神经网络训练的目标误差
net.trainParam.lr = 0.01 ;%学习速率
开始训练
将原先创建的神经网络以input为输入,output为目标函数的基础上训练epochs次即500次。
net = train( net, input , output’ ) ;
这里训练就结束了,以下开始测试。
开始测试
- 读取测试数据
[t1, t2, t3, t4, c] = textread(‘testData1.txt’ , ‘%f%f%f%f%f’,150);
- 测试数据归一化
[testInput,minI,maxI] = premnmx( [t1 , t2 , t3 , t4 ]’) ;
- 仿真
Y = sim( net , testInput ) ; %其中net为训练后得到的网络
Y是最终得到的输出结果,在该样本中,哪一类的值更大,则将该样本分给哪一类。
比如说第一列:就是第一个样本:(2.9182e-05,0.322,0.9606)中0.9606更大,那么将第一个样本分到第三类;再举例说第32列,即第32个样本:(0.0028,0.9408,0.0628)中0.9408更大,那么将第32个样本分给第二类。
统计识别正确率
[s1,s2] = size(Y) ; %返回矩阵的行-s1,列-s2
hitNum = 0 ;
for i = 1 : s2
[m,Index]= max(Y(:,i)); % m返回的是Y矩阵第i列中最大的数(归为类)
if(Index ==c(i)) % index返回的是Y矩阵第i列中最大的数下标
hitNum =hitNum + 1 ;
end
end
sprintf(‘识别率是 %3.3f%%’,100 * hitNum / s2 )
最终命令窗口得到识别的正确率如上:基本稳定在100.000%
实验结果及分析
改变输出层的传递函数
- 左图输出层为s型传递函数即logsig ,右图为线性传递函数即purelin
- Performance:性能-用mse评估,进度条右边是设定的均方误差,即通过参数net.trainparam.goal设定。
- Gradient:梯度-当梯度值达到了1.00e-05时,则停止训练
- Validation check:泛化能力检查-若连续6次训练误差不降反升(误差不再减小,不会再有更好的效果了),则停止训练。
- 训练集性能
从纵坐标就可以知道,它通过均方差开衡量网络的性能。由图可看到随着迭代次数的增大,均方误差越来越小,性能越好。
总结:改变输出层传递函数对训练周期、训练性能有一定影响,本例中左图迭代到206次收敛,均方误差0.0099642;而右图迭代到492次才收敛,均方误差0.091497,超过了目标误差0.0815328;所以输出层为s型传递函数要优于线性传递函数。
改变隐层的传递函数
- 左图隐层为s型传递函数即logsig ,右图隐层为双曲正切S型传递函数即tansig
- 训练集性能
总结:改变隐层传递函数对训练的性能、训练周期都有影响,本例中左图迭代到206次收敛,均方误差0.0099642;而右图迭代到500次收敛,均方误差0.015068;所以隐层传递函数使用双曲正切s型传递函数要比S型传递函数更好一些。
改变学习率
- 左图学习率为0.01 ,右图为0.05
- 训练集性能
总结:学习速率的选取大了可能导致系统不稳定,小了会导致训练周期过长、收敛慢,达不到要求的误差。一般通过观察误差下降曲线来判断。同时,由于网络规模大小的不同,学习率选择应当针对其进行调整。
总结
我在实验中通过调整隐含层节点数,选择不通过的激活函数,设定不同的学习率,
- 隐含层节点个数
隐含层节点的个数对于识别率的影响并不大,可是节点个数过多会添加运算量,使得训练较慢。
- 激活函数的选择
激活函数不管对于识别率或收敛速度都有显著的影响。在逼近高次曲线时,S形函数精度比线性函数要高得多,但计算量也要大得多。
- 学习率的选择
学习率影响着网络收敛的速度,以及网络是否能收敛。学习率设置偏小能够保证网络收敛,可是收敛较慢。相反,学习率设置偏大则有可能使网络训练不收敛,影响识别效果。
源代码
%读取训练数据
clear
clc
load iris.dat;
%随机提取完数据并保存之后,要注释掉以下这6行代码
%因为每次训练都必须是同样的训练集和测试集,不能每训练一次随机提取一次。
%A1=randsample(150,118,false) ;
%trainData1=iris(A1,:);
%A2=randsample(150,32,false) ;
%testData1=iris(A2,:);
%save('trainData1.txt','trainData1','-ascii'),
%save('testData1.txt','testData1','-ascii'),
%f1 f2 f3 f4是四个特征值
[f1,f2,f3,f4,class] = textread('trainData1.txt' , '%f%f%f%f%f',150);
%特征值归一化
[input,minI,maxI] = premnmx( [f1 , f2 , f3 , f4 ]') ;
%构造输出矩阵
s = length( class) ;
output = zeros( s , 3 ) ;
for i = 1 : s
output( i , class( i ) ) = 1 ;
end
%创建神经网络
net = newff( minmax(input) , [10 3] , { 'tansig' 'tansig' } , 'traingdx' ) ;
%{
minmax(input):获取4个输入信号(存储在f1 f2 f3 f4中)的最大值和最小值;
[10,3]:表示使用2层网络,第一层网络节点数为10,第二层网络节点数为3;
{ 'logsig' 'purelin' }:
表示每一层相应神经元的**函数;
即:第一层神经元的**函数为logsig(对数S形转移函数),第二层为purelin(线性函数)
'traingdx':表示学习规则采用的学习方法为traingdx(梯度下降自适应学习率训练函数)
%}
%设置训练參数
net.trainparam.show = 50 ;% 显示中间结果的周期
net.trainparam.epochs = 500 ;%最大迭代次数(学习次数)
net.trainparam.goal = 0.01;%神经网络训练的目标误差
net.trainParam.lr = 0.05 ;%学习速率(Learning rate)
%开始训练
%其中input为训练集的输入信号,对应output为训练集的输出结果
net = train( net, input , output' ) ;
%================================训练完成====================================%
%% 读取测试数据并归一化
%=============================接下来进行测试=================================%
%读取测试数据
[t1, t2, t3, t4, c] = textread('testData1.txt' , '%f%f%f%f%f',150);
%測试数据归一化
[testInput,minI,maxI] = premnmx( [t1 , t2 , t3 , t4 ]') ;
%仿真
%其中net为训练后得到的网络,返回的Y为
Y = sim( net , testInput ) ;
%统计识别正确率
[s1 , s2] = size( Y ) ;
hitNum = 0 ;
for i = 1 : s2
[m , Index] = max( Y( : , i ) ) ;
if( Index == c(i) )
hitNum = hitNum + 1 ;
end
end
sprintf('识别率是 %3.3f%%',100 * hitNum / s2 )