神经网络matlab的使用

来源:互联网 发布:mac破解软件 哪个网站 编辑:程序博客网 时间:2024/06/07 15:09

来自人工智能贴吧,利_刃。仅供收藏。

关于Matlab的入门教程,参看这个帖子:tieba#baidu#com/p/2945924081



例1:我们都知道,面积=长*宽,假如我们有一组数测量据如下:

我们利用这组数据来训练神经网络。(在Matlab中输入以下的代码,按回车即可执行)


p = [2 5; 3 6; 12 2; 1 6; 9 2; 8 12; 4 7; 7 9]'; % 特征数据X1,X2
t = [10 18 24 6 18 96 28 63]; % 样本值
net = newff(p, t, 20); % 创建一个BP神经网络 ff=FeedForward
net = train(net, p, t); % 用p,t数据来训练这个网络


出现如下的信息,根据蓝线的显示,可以看出最后收敛时,误差已小于10^-20。



你也许会问,计算机难道这样就能学会乘法规则吗?不用背乘法口诀表了?先随便选几个数字,试试看:


s = [3 7; 6 9; 4 5; 5 7]'; % 准备一组新的数据用于测试
y = sim(net, s) % 模拟一下,看看效果
% 结果是:25.1029 61.5882 29.5848 37.5879


看到了吧,预测结果和实际结果还是有差距的。不过从中也能看出,预测的数据不是瞎蒙的,至少还是有那么一点靠谱。如果训练集中的数据再多一些的话,预测的准确率还会大幅度提高。


你测试的结果也许和我的不同,这是因为初始化的权重参数是随机的,可能会陷入局部最优解,所以有时预测的结果会很不理想。

例2:下面测试一下拟合正弦曲线,这次我们随机生成一些点来做样本。


p = rand(1,50)*7 % 生成1行50个0~7之间的随机数
t = sin(p) % 计算正弦曲线
s = [0:0.1:7]; % 生成0~7的一组数据,间隔0.1,用于模拟测试
plot(p, t, 'x') % 画散点图



net = newff(p, t, 20); % 创建神经网络
net = train(net, p, t); % 开始训练


y = sim(net, s); % 模拟
plot(s, y, 'x') % 画散点图


从图中看出,这次的预测结果显然是不理想的,我们需要设置一些参数来调整。


下面的设置是一种标准的批量梯度下降法的配置。


% 创建3层神经网络 [隐藏层10个节点->logsig, 输出层1个节点->purelin] traingd代表梯度下降法
net = newff(p, t, 10, {'logsig' 'purelin'}, 'traingd'); % 10不能写成[10 1]


% 设置训练参数
net.trainparam.show = 50; % 显示训练结果(训练50次显示一次)
net.trainparam.epochs = 500; % 总训练次数
net.trainparam.goal = 0.01; % 训练目标:误差<0.01
net.trainParam.lr = 0.01; % 学习率(learning rate)


net = train(net, p, t); % 开始训练


注意:newff的第三个参数10不能写成[10 1],否则就是4层网络,两个隐藏层,分别是10个和1个节点。这个很容易弄错。(输出层的节点数程序会自动根据t的维度自动判断,所以不用指定)



y = sim(net, s); % 模拟
plot(s, y, 'x') % 画散点图


这时的效果显然更差了。




把精度调高一点看看。训练次数加到9999,误差<0.001;学习率调到0.06,希望能加快点速度。


% 创建2层神经网络 [隐藏层10个节点->logsig, 输出层1个节点->purelin] traingd代表梯度下降法
net = newff(p, t, 10, {'logsig' 'purelin'}, 'traingd');


% 设置训练参数
net.trainparam.show = 50; % 每间隔50次显示一次训练结果
net.trainparam.epochs = 9999; % 总训练次数
net.trainparam.goal = 0.001; % 训练目标:误差<0.001
net.trainParam.lr = 0.06; % 学习率(learning rate)


net = train(net, p, t); % 开始训练


标准的批量梯度下降法的速度确实够慢,这次计算花了一分多钟。




y = sim(net, s); % 模拟
plot(s, y, 'x') % 画散点图


效果比上次稍好一点。不过这条曲线显得坑坑洼洼的很难看,这是一种过拟合(Overfitting)现象,与之相反的是欠拟合(Underfitting)

先来解决速度问题,把traingd改为trainlm即可。trainlm使用LM算法,是介于牛顿法和梯度下降法之间的一种非线性优化方法,不但会加快训练速度,还会减小陷入局部最小值的可能性,是Matlab的默认值。



net = newff(p, t, 10, {'logsig' 'purelin'}, 'trainlm');
... 后面的代码不变


这个速度比较惊叹了,1秒钟之内完成,只做了6轮计算,效果也好了一些。不过,LM算法也有弱点,它占用的内存非常大,所以没把其它算法给淘汰掉。
 


下面解决过拟合问题,把隐藏层的节点数目设少一点就行了。


net = newff(p, t, 3, {'logsig' 'purelin'}, 'trainlm');
... 后面的代码不变


这回终于达到满意的效果了。(有时会出现局部最优解,可以多试几次)

如果节点数目太少,会出现欠拟合的情况。




关于隐藏层的节点个数,一般是要凭感觉去调的。如果训练集的维数比较多,调节起来比较耗时间,这时可以根据经验公式上下浮动地去调整。
下面给出几个经验公式供参考:

如果把输出层改为logsig激活会是什么样子呢?


net = newff(p, t, 3, {'logsig' 'logsig'}); % 创建神经网络
net = train(net, p, t); % 开始训练
y = sim(net, s); % 模拟
plot(s, y, 'x') % 画散点图

可以看出,-1~0范围之间的点都变为0了。使用logsig输出时要想得到完整数值范围的效果,必须先对数据进行归一化才行。



 


归一化(Normalization),也叫标准化,就是把一堆数字按比例缩放到0~1或-1~1的范围。
虽然用Purelin输出可以不必归一化,但归一化能在一定程度上加快收敛速度,因此被许多教程定为训练前的必须步骤。


公式为:归一值 = (当前值x-最小值min)/(最大值max-最小值min)
如果限定了范围,公式为:y = (ymax-ymin)*(x-xmin)/(xmax-xmin) + ymin;
0.1~0.9的范围:(0.9-0.1)*(x-min)/(max-min)*(0.9-0.1)+0.1
把5, 2, 6, 3这四个数归一化:
 


Matlab的归一化命令为:mapminmax
注:网上的不少教程里用premnmx命令来归一化,要注意Matlab版本R2007b和R2008b,premnmx在处理单列数据时有bug,Matlab已给出了警告,R2009a版才修正。因此推荐使用mapminmax。mapminmax的输入输出值和premnmx是行列颠倒的,使用时要注意代码中是否添加转置符号。


a = [5, 2, 6, 3];


b = mapminmax(a, 0, 1) % 归一化到0~1之间
% b = 0.7500 0 1.0000 0.2500


c = mapminmax(a) % 归一化到-1~1之间
% c = 0.5000 -1.0000 1.0000 -0.5000

反归一化(Denormalization)就是按归一化时的比例还原数值。


a = [5, 2, 6, 3];
[c,PS] = mapminmax(a); % PS记录归一化时的比例
mapminmax('reverse', c, PS) % 利用PS反归一化
% ans = 5 2 6 3

神经网络的归一化(0~1范围)代码:


p = rand(1,50)*7; % 特征数据
t = sin(p); % 样本值
s = [0:0.1:7]; % 测试数据


[pn, ps] = mapminmax(p, 0, 1); % 特征数据归一化
[tn, ts] = mapminmax(t, 0, 1); % 样本值归一化
sn = mapminmax('apply', s, ps); % 测试数据,按ps比例缩放


net = newff(pn, tn, [5 1], {'logsig' 'logsig'}); % 创建神经网络
net = train(net, pn, tn); % 开始训练


yn = sim(net, sn); % 模拟
y = mapminmax('reverse', yn, ts); % 按ps的比例还原
plot(s, y, 'x') % 画散点图



神经网络工具箱还有一个UI图形操作界面,执行nntool就可以打开。我觉得不如写代码方便,所以不怎么用。我提供一个相关的教程链接,有兴趣的可以看一下:matlab神经网络工具箱创建神经网络 - http://blog.新浪.com.cn/s/blog_8684880b0100vxtv.html (新浪替换成sina)

关于Sigmoid的由来,中文的网站上很少有提及的。下面简单讲一下,希望能给大家拓展一下思路。


PS: 这里的公式我都给出了求解过程,但如今这个年头,用手工解题的人越来越少了,一般的方程用软件来解就行了。
例如解Sigmoid微分方程,可以用Matlab去解:



dsolve('Dx=x*(1-x)')
% ans = 1/(1+exp(-t)*C1) 



如果想得到求解的步骤或更详细的信息,推荐使用Wolfram:http://www.wolframalpha.com
在Wolfram的搜索框输入 x'=x(1-x) 即可。

logsig


Sigmoid函数(S形函数,Logistic Function)是受统计学模型的启发而产生的激活函数。
基于生物学的神经元激活函数是这样的:





参看:http://eprints.pascal-network.org/archive/00008596/01/glorot11a.pdf


实践证明了基于统计学的Sigmoid函数激活效果要比基于生物学的模型好,而且计算起来很方便,所以说不能以机器和人的相似度为标准来判断AI算法的好坏。
Sigmoid函数原先是个描述人口增长的数学模型,1838提出,给出的是导数形式(概率密度)。人口增长规律:起初阶段大致是指数增长;然后逐渐开始变得饱和,增长变慢;达到成熟时几乎停止增长;整个过程形如一条S型曲线。

导数的形式知道了,那么它的原函数是什么样子呢?已知导数求原函数,用统计学的话来讲,即根据概率密度函数(PDF)求累积分布函数(CDF),不定积分(Indefinite Integral)就是专门用来做这个的工具。
根据不定积分的知识可知,由于常数项是可变的,所以存在无数个原函数的可能。让我们先用图解法看一下:既然导数是函数曲线的斜率,那么可以把一定数值范围内的斜率,都画成一根根的短斜线,组成斜率场(Slope Fields, Direction Fields),然后根据这些斜线的走势,画出积分曲线。
Matlab可以用quiver命令来画斜率场。



从上图中可以看出,在y轴的0~1之间是个分水岭,0和1处的方向趋于水平。下面放大0~1的范围看看是什么样子的。

看到了吧,我们要的Logistic Sigmoid就在这里呢。


下面给出符号求解的过程:

tansig


双曲正切函数(双极S形函数, tanh, Hyperbolic Tangent),读tanch,18世纪就已经出现了。它的定义是:tanh(x)=sinh(x)/cosh(x),可以由著名的欧拉公式(Euler's formula)推导出来。
用tanh作激活函数,收敛比较快,效果比Logistic函数还要好。
欧拉公式: i是虚数(Imaginary Number)单位,它的定义是: (即i^2 = -1)
题外话:根据上面的公式变换,可以得出史上最美的数学公式: ,数学中最神秘的5个符号e、i、π、1和0,全包含在里面了。







求tanh的导数:




logsig和tansig的关系:


0 0
原创粉丝点击