赞
踩
来自人工智能贴吧,利_刃。仅供收藏。
关于Matlab的入门教程,参看这个帖子:tieba#baidu#com/p/2945924081
你测试的结果也许和我的不同,这是因为初始化的权重参数是随机的,可能会陷入局部最优解,所以有时预测的结果会很不理想。
例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的关系:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。