《量化投资:以MATLAB为工具》

MATLAB技术论坛

 找回密码
 注册账号
查看: 2038|回复: 28
收起左侧

[提问] 利用BP网络来进行预测,结果差距过大

[复制链接]
发表于 2017-8-15 14:16:11 | 显示全部楼层 |阅读模式
自己利用BP神经网络来进行预测,但最后输出的结果跟实际结果有好几个数量级的差别,小白不是太懂应该怎么查找问题,请教大家?
该BP网络有8个输入,1个输出,设置了3个隐含层,训练之后,误差在27左右,但实际输出之后,输出结果比实际结果小了好多数量级,想请问大家,可能是因为什么缘故?
答主初学,对具体的神经网络也不是太了解,希望大家见谅
发表于 2017-8-16 20:24:16 | 显示全部楼层
这是结果,你再好好看看吧
图片1.jpg
回复 支持 1 反对 0

使用道具 举报

 楼主| 发表于 2017-8-16 14:44:57 | 显示全部楼层
WJB986555360 发表于 2017-8-16 13:09
你验证的代码也一起发出来看下,我们再一起检查下原来的程序

全部代码如下:
%script:main_batch.m
%批量方式训练BP网络,实现函数计算
clear all
clc
%读入数据
xlsfile = 'shiyanshuju.xls';
[data,Acdi] = getdata('shiyanshuju.xls');
%%数据划分
[ train_data,train_ans,test_data,test_ans ] = divide( data,Acdi );
%%设置参数
rng('default')
rng(0)
nTrainNum = 100;    %100个训练样本
nSampDim = 8;       %样本是8维的

%%网络
net.nIn=8;          %八个输入节点
net.nHidden = 3;    %隐含层节点数目的确定是一个问题,需要仔细考虑,目前按照之前文献中的学习将其设置为3
net.nOut=1;         %一个输出节点
w = 2*(rand(net.nHidden , net.nIn)-1/2);    %在最开始随机确定一个输入层到隐含层的权值矩阵,3X8
b = 2*(rand(net.nHidden , 1)-1/2);          %在最开始随机确定一个输入层到隐含层的阈值矩阵,3X1
net.w1 = [w,b];                             %输入层至隐含层的权值矩阵,3X9
W = 2*(rand(net.nOut , net.nHidden)-1/2);   %在最开始随机确定一个隐含层到输出层的权值矩阵,1X3
B = 2*(rand(net.nOut , 1)-1/2);             %在最开始随机确定一个隐含层到输出层的阈值矩阵,1X1
net.w2 = [W,B];                             %隐含层至输出层的权值矩阵,1X4

%在此不进行归一化,因为提前对数据进行了标准化处理
%归一化的方法待定,如何进行反归一
%%训练数据归一化       %归一化需要多学习、了解,为什么需要归一化,什么情况需要归一化,如何避免可能出现的问题
%%mm = mean(train_data);      %求得训练数据的平均值
%将均值进行平移
%%for i=1:8
    %%train_s(:,i) = train_data(:,i)-mm(i);   %train_s保存平移后的数据
%%end
%方差标准化
%%ml(1) = std(train_s(:,1));
%%ml(2) = std(train_s(:,2));
%%ml(3) = std(train_s(:,3));
%%ml(3) = std(train_s(:,4));
%%ml(5) = std(train_s(:,5));
%%ml(6) = std(train_s(:,6));
%%ml(7) = std(train_s(:,7));
%%ml(8) = std(train_s(:,8));
%%for i=1:8
    %%train_s(:,i)=train_s(:,i)/ml(i);
%%end

%%训练
%因为将阈值也作为一个权值,则默认其对应的那个输入始终为1,则在此添加一个1X100的矩阵,即ones(1,nTrainNum)
SampInEx = [train_data',;ones(1,nTrainNum)];    %在此用train_data代替train_s,因为我们提前在表格中进行了预处理,即归一化,9X100
expectedOut = train_ans';    %1X100
eb = 0.01;    %误差容限
eta = 0.6;      %学习率
mc = 0.8;       %动量因子
maxiter = 2000; %最大迭代次数
iteration = 0;  %迭代次数
errRec = zeros(1,maxiter);    %1X2000
outRec = zeros(nTrainNum,maxiter);    %1X2000
%记录
NET=[];

sse_rec=0;

%开始迭代
for i=1:maxiter
    hid_input = net.w1 * SampInEx;                %3X100
    hid_out = logsig(hid_input);                  %3X100  
    ou_input1 = [hid_out;ones(1,nTrainNum)];      %4X100,在这增加了一个1X100且都为1的输入以匹配将阈值归到权值矩阵的矩阵系数
    ou_input2 = net.w2 * ou_input1;               %1X100
    out_out =logsig(ou_input2);                   %1X100
    %out_Rec(:,i) = out_out;                       %记录每次的输出
    err = expectedOut-out_out;
    sse = sumsqr(err);
    errRec(i) = sse;                    %记录每次的错误
    fprintf('第%d次迭代  误差: %f\n',i,sse);
    iteration = iteration+1;
   
    %if sse>sse_rec
        %break;
    %end
   
    sse_rec=sse;
   
    %判断是否收敛
    if sse<=eb
        break;
    end
   
    %误差反向传播
    %隐含层与输出层之间的局部梯度度
    DELTA = err.*dlogsig(ou_input2,out_out);   %dlogsig(ou_input2,out_out)为1X100,最后结果也为1X100
    %输入层与隐含层之间的局部梯度
    delta = net.w2(:,1:end-1)'*DELTA.*dlogsig(hid_input,hid_out);    %3X100
    %权值修改量
    dWEX=DELTA*ou_input1';      %在此ou_input1即为权值修正公式中隐含层y
    dwex=delta*SampInEx';       %在此SampInEx即为权值修正公式中输入层x
    %修改权值,如果不是第一次修改,则使用动量因子
    if i==1
        net.w2 = net.w2+eta*dWEX;      %在此eta即为权值修正公式中的n
        net.w1 = net.w1+eta*dwex;
    else
        %在此使用了附加动量法
        net.w2 = net.w2+(1-mc)*eta*dWEX+mc*dWEXOld;
        net.w1 = net.w1+(1-mc)*eta*dwex+mc*dwexOld;
    end
   
    %记录上一次的权值修改量
    dWEXOld = dWEX;
    dwexOld = dwex;
end

%在此记录一下训练数据的输出
out_record=out_out';

%测试
%测试数据归一化
%for i=1:8
    %test_s(:,i) = test_data(:,i)-mm(i); %这里为什么用mm(i)需要思考
%end
%for i=1:8
    %test_s(:,i) = test_data(:,i)/ml(i);
%end

%计算测试输出
InEx = [test_data';ones(1,300-nTrainNum)];   %在此用test_data代替test_s,因为我们提前在表格中进行了预处理,即归一化
hid_input = net.w1*InEx;
hid_out = logsig(hid_input);  %output of the hidden layer nodes  
ou_input1 = [hid_out;ones(1,300-nTrainNum)];
ou_input2 = net.w2*ou_input1;
out_out = logsig(ou_input2);  
out_out1 = out_out;

%反归一化
%%因为本实例中输出本身即为0-1,并没有进行归一化,故在此不需要进行归一化,计算输出即为实际输出

%取整
%在这书上的例程可能不适用,因此将其设为了注释,并给出了自己的写法将其进行取整分类
%out_out = (out_out<0.5) = 0;
%out_out = (out_out>=0.5) = 1;
%out_int = zeros(size(out_out));
%for i=1:9
    %if out_out(i)>=0.5
        %out_int(i)=1;
    %end
%end
%out_out = out_int;

%按照书上实例显示正确率的地方,这个很尴尬,在计算答案而不是分类的情况下,正确率为0的情况有很大几率出现
%%rate = sum(out_out==test_ans)/length(out_out);

%%显示训练样本,因为有8个输入,所以以列表形式展出
train_show = train_data;
train_sequence_num = length(train_show);
fprintf('---------------------------------------------------------------训练数据----------------------------------------------------------------------------\n');
fprintf('   日照百分率    太阳高度角正弦值  太阳赤纬角余弦值   纬度余弦值        相对湿度        平均温度        饱和蒸气压        含湿量       日平均晴空指数\n');
for i=1:train_sequence_num
    fprintf('  %9d    %9d    %9d    %9d    %d    %1d    %9d    %9d    %9d    %9d\n',train_show(i,1),train_show(i,2),...
    train_show(i,3),train_show(i,4),train_show(i,5),train_show(i,6),train_show(i,7),train_show(i,8),train_ans(i),out_record(i));
end

%------------------------------------------------------------------------------------------------------------------
%%显示结果,选择不同输入进行搭配
test_show = test_data;
test_ans_show=out_out1';
test_sequence_num = length(test_data);
fprintf('---------------------------------------------------------------测试数据----------------------------------------------------------------------------\n');
fprintf('   日照百分率    太阳高度角正弦值  太阳赤纬角余弦值   纬度余弦值        相对湿度        平均温度        饱和蒸气压        含湿量       日平均晴空指数\n');
for i=1:test_sequence_num
    fprintf('  %9d    %9d    %9d    %9d    %d    %1d    %9d    %9d    %9d\n',test_show(i,1),...
    test_show(i,2),test_show(i,3),test_show(i,4),test_show(i,5),test_show(i,6),test_show(i,7),test_show(i,8),test_ans_show(i));
end
%------------------------------------------------------------------------------------------------------------------

fprintf('最终迭代次数\n   %d\n',iteration);

%在此计算正确率
Total_num=length(data);    %数据总数
test_num=Total_num-nTrainNum;
right_num=0;
for i=1:test_num
    if (test_ans_show(i)-test_ans(i)) < 0
        right_num=right_num+1;
    end
end
right_rate=100*right_num/test_num;
fprintf('正确率为%2d%%\n',right_rate);

我添加了一些验证条件之后发现,第一次反而是误差最小的,也就是说随着迭代次数的增加,我的误差越来越大,感觉像是沿着梯度的反方向增加,但是我看了原理,对照着代码查了一遍,有一句不太理解,其他的感觉都正确,我把那句单列出来,你看看,有没有什么不对的地方

%输入层与隐含层之间的局部梯度
    delta = net.w2(:,1:end-1)'*DELTA.*dlogsig(hid_input,hid_out);
这句中的net.w2(:,1:end-1)我不理解,在我的推导中,这个式子应该是一个数,按照matlab的特性,应该是一列矩阵,我不明白为什么会是这样写,这样写有什么含义,是要去掉阈值吗?
回复 支持 反对

使用道具 举报

 楼主| 发表于 2017-8-15 14:29:47 | 显示全部楼层
%script:main_batch.m
%批量方式训练BP网络,实现函数计算
clear all
clc
%读入数据
xlsfile = 'shiyanshuju.xls';
[data,Acdi] = getdata('shiyanshuju.xls');
%%数据划分
[ train_data,train_ans,test_data,test_ans ] = divide( data,Acdi );
%%设置参数
rng('default')
rng(0)
nTrainNum = 100;    %100个训练样本
nSampDim = 8;       %样本是8维的

%%网络
net.nIn=8;          %八个输入节点
net.nHidden = 3;    %隐含层节点数目的确定是一个问题,需要仔细考虑,目前按照之前文献中的学习将其设置为3
net.nOut=1;         %一个输出节点
w = 2*(rand(net.nHidden , net.nIn)-1/2);    %在最开始随机确定一个输入层到隐含层的权值矩阵,3X8
b = 2*(rand(net.nHidden , 1)-1/2);          %在最开始随机确定一个输入层到隐含层的阈值矩阵,3X1
net.w1 = [w,b];                             %输入层至隐含层的权值矩阵,3X9
W = 2*(rand(net.nOut , net.nHidden)-1/2);   %在最开始随机确定一个隐含层到输出层的权值矩阵,1X3
B = 2*(rand(net.nOut , 1)-1/2);             %在最开始随机确定一个隐含层到输出层的阈值矩阵,1X1
net.w2 = [W,B];                             %隐含层至输出层的权值矩阵,1X4

%在此不进行归一化,因为提前对数据进行了标准化处理
%归一化的方法待定,如何进行反归一
%%训练数据归一化       %归一化需要多学习、了解,为什么需要归一化,什么情况需要归一化,如何避免可能出现的问题
%%mm = mean(train_data);      %求得训练数据的平均值
%将均值进行平移
%%for i=1:8
    %%train_s(:,i) = train_data(:,i)-mm(i);   %train_s保存平移后的数据
%%end
%方差标准化
%%ml(1) = std(train_s(:,1));
%%ml(2) = std(train_s(:,2));
%%ml(3) = std(train_s(:,3));
%%ml(3) = std(train_s(:,4));
%%ml(5) = std(train_s(:,5));
%%ml(6) = std(train_s(:,6));
%%ml(7) = std(train_s(:,7));
%%ml(8) = std(train_s(:,8));
%%for i=1:8
    %%train_s(:,i)=train_s(:,i)/ml(i);
%%end

%%训练
%因为将阈值也作为一个权值,则默认其对应的那个输入始终为1,则在此添加一个1X100的矩阵,即ones(1,nTrainNum)
SampInEx = [train_data',;ones(1,nTrainNum)];    %在此用train_data代替train_s,因为我们提前在表格中进行了预处理,即归一化,9X100
expectedOut = train_ans;    %100X1
eb = 0.01;      %误差容限
eta = 0.6;      %学习率
mc = 0.8;       %动量因子
maxiter = 2000; %最大迭代次数
iteration = 0;  %迭代次数
errRec = zeros(1,maxiter);    %1X2000
outRec = zeros(1,maxiter);    %1X2000
%记录
NET=[];

%开始迭代
for i=1:maxiter
    hid_input = net.w1 * SampInEx;                %3X100
    hid_out = logsig(hid_input);                  %3X100  
    ou_input1 = [hid_out;ones(1,nTrainNum)];      %4X100
    ou_input2 = net.w2 * ou_input1;               %1X100
    out_out =logsig(ou_input2);                   %1X100
    out_Rec(:,i) = out_out;                       %记录每次的输出
    err = expectedOut'-out_out;
    sse = sumsqr(err);
    errRec(i) = sse;                    %记录每次的错误
    fprintf('第%d次迭代  误差: %f\n',i,sse);
    iteration = iteration+1;
   
    %判断是否收敛
    if sse<=eb
        break;
    end
   
    %误差反向传播
    %隐含层与输出层之间的局部梯度度
    DELTA = err.*dlogsig(ou_input2,out_out);
    %输入层与隐含层之间的局部梯度
    delta = net.w2(:,1:end-1)'*DELTA.*dlogsig(hid_input,hid_out);
    %权值修改量
    dWEX=DELTA*ou_input1';
    dwex=delta*SampInEx';
    %修改权值,如果不是第一次修改,则使用动量因子
    if i==1
        net.w2 = net.w2+eta*dWEX;
        net.w1 = net.w1+eta*dwex;
    else
        net.w2 = net.w2+(1-mc)*eta*dWEX+mc*dWEXOld;
        net.w1 = net.w1+(1-mc)*eta*dwex+mc*dwexOld;
    end
   
    %记录上一次的权值修改量
    dWEXOld = dWEX;
    dwexOld = dwex;
end

%测试
%测试数据归一化
%for i=1:8
    %test_s(:,i) = test_data(:,i)-mm(i); %这里为什么用mm(i)需要思考
%end
%for i=1:8
    %test_s(:,i) = test_data(:,i)/ml(i);
%end

%计算测试输出
InEx = [test_data';ones(1,300-nTrainNum)];   %在此用test_data代替test_s,因为我们提前在表格中进行了预处理,即归一化
hid_input = net.w1*InEx;
hid_out = logsig(hid_input);  %output of the hidden layer nodes  
ou_input1 = [hid_out;ones(1,300-nTrainNum)];
ou_input2 = net.w2*ou_input1;
out_out = logsig(ou_input2);  
out_out1 = out_out;

%反归一化
%%因为本实例中输出本身即为0-1,并没有进行归一化,故在此不需要进行归一化,计算输出即为实际输出

%取整
%在这书上的例程可能不适用,因此将其设为了注释,并给出了自己的写法将其进行取整分类
%out_out = (out_out<0.5) = 0;
%out_out = (out_out>=0.5) = 1;
%out_int = zeros(size(out_out));
%for i=1:9
    %if out_out(i)>=0.5
        %out_int(i)=1;
    %end
%end
%out_out = out_int;

%按照书上实例显示正确率的地方,这个很尴尬,在计算答案而不是分类的情况下,正确率为0的情况有很大几率出现
%%rate = sum(out_out==test_ans)/length(out_out);

%%显示训练样本,因为有8个输入,所以以列表形式展出
train_show = train_data;
train_sequence_num = length(train_show);
fprintf('---------------------------------------------------------------训练数据----------------------------------------------------------------------------\n');
fprintf('   日照百分率    太阳高度角正弦值  太阳赤纬角余弦值   纬度余弦值        相对湿度        平均温度        饱和蒸气压        含湿量       日平均晴空指数\n');
for i=1:train_sequence_num
    fprintf('  %9d    %9d    %9d    %9d    %d    %1d    %9d    %9d    %9d\n',train_show(i,1),...
    train_show(i,2),train_show(i,3),train_show(i,4),train_show(i,5),train_show(i,6),train_show(i,7),train_show(i,8),train_ans(i));
end

%------------------------------------------------------------------------------------------------------------------
%%显示结果,选择不同输入进行搭配
test_show = test_data;
test_ans_show=out_out1';
test_sequence_num = length(test_data);
fprintf('---------------------------------------------------------------测试数据----------------------------------------------------------------------------\n');
fprintf('   日照百分率    太阳高度角正弦值  太阳赤纬角余弦值   纬度余弦值        相对湿度        平均温度        饱和蒸气压        含湿量       日平均晴空指数\n');
for i=1:test_sequence_num
    fprintf('  %9d    %9d    %9d    %9d    %d    %1d    %9d    %9d    %9d\n',test_show(i,1),...
    test_show(i,2),test_show(i,3),test_show(i,4),test_show(i,5),test_show(i,6),test_show(i,7),test_show(i,8),test_ans_show(i));
end
%------------------------------------------------------------------------------------------------------------------

fprintf('最终迭代次数\n   %d\n',iteration);

%在此计算正确率
NUM_M=length(test_data);
right_num=0;
for i=1:(NUM_M-nTrainNum)
    if (test_ans_show(i)-test_ans(i))<=100
        right_num=right_num+1;
    end
end
right_rate=100*right_num/NUM_M;
fprintf('正确数为%d\n',right_num);
fprintf('正确率为%2d%%\n',right_rate);

回复 支持 反对

使用道具 举报

发表于 2017-8-15 14:24:54 | 显示全部楼层
把你的代码发出来看下
回复 支持 反对

使用道具 举报

 楼主| 发表于 2017-8-15 14:34:16 | 显示全部楼层
WJB986555360 发表于 2017-8-15 14:24
把你的代码发出来看下

代码发出来,你看一下,我是照着一本书上的例程做的
回复 支持 反对

使用道具 举报

 楼主| 发表于 2017-8-15 14:36:18 | 显示全部楼层
cdvftb 发表于 2017-8-15 14:34
代码发出来,你看一下,我是照着一本书上的例程做的

它原来的代码是做识别的,我现在想把它做成预测,里边修改权值那一部分我没看懂,照搬过来的,是不是那出了问题
回复 支持 反对

使用道具 举报

发表于 2017-8-15 15:13:26 | 显示全部楼层
回复 支持 反对

使用道具 举报

 楼主| 发表于 2017-8-15 15:29:28 | 显示全部楼层
WJB986555360 发表于 2017-8-15 15:13
你的数据文件呢。xls格式

.xls文件我没上传,这个是主脚本,还有两个函数也没上传,因为那两个函数就是提取数据的,这个不用看数据吧,我提前把数据进行了预处理,将其处理在区间0-1之间,我想了解的是假如说结果相差过大,应该考虑哪方面出问题了啊
回复 支持 反对

使用道具 举报

发表于 2017-8-15 15:42:38 | 显示全部楼层
看了下你的程序,应该问题不大,你的这个程序是BP算法原始算法,你要懂BP算法的原理才能知道他为什么这么写,建议你先直接用MATLAB自带BP工具箱先去验证你的结果,如果自带工具箱解出来没问题再来好好检查你的这个程序
回复 支持 反对

使用道具 举报

 楼主| 发表于 2017-8-15 18:05:43 | 显示全部楼层
WJB986555360 发表于 2017-8-15 15:42
看了下你的程序,应该问题不大,你的这个程序是BP算法原始算法,你要懂BP算法的原理才能知道他为什么这么写 ...

,才刚开始学,想着直接做出程序来,所以并没有仔细看数学推导过程,现在发现有些问题必须懂原理,计划好好看原理了
回复 支持 反对

使用道具 举报

 楼主| 发表于 2017-8-16 11:41:27 | 显示全部楼层
WJB986555360 发表于 2017-8-15 15:42
看了下你的程序,应该问题不大,你的这个程序是BP算法原始算法,你要懂BP算法的原理才能知道他为什么这么写 ...

我通过Matlab自带的工具箱验证了,工具箱验证的话结果准确度很高,现在正在检查程序,原理也看了,对着公式在改,希望一起探讨
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册账号

本版积分规则

QQ|网站地图|MATLAB技术论坛|Simulink仿真论坛 ( 蜀ICP备19014457号 

GMT+8, 2020-7-14 23:19 , Processed in 0.056364 second(s), 12 queries , Gzip On, MemCached On.

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表