基于模拟退火算法优化BP神经网络的MATLAB时间序列预测模型实现
基于模拟退火算法优化BP神经网络(SA-BP)的时间序列预测 SA-BP时间序列 matlab代码 暂无Matlab版本要求 -- 推荐 2018B 版本及以上
模拟退火算法(SA)和BP神经网络结合来做时间序列预测,这个思路其实挺有意思的。BP网络容易陷进局部最优,而模拟退火偏偏擅长跳出局部坑,两个凑一块儿,算是互补短板了。
我们先看看BP网络的基础结构。时间序列预测,简单说就是用过去一段数据预测下一个点。假设我们用过去10个数据点预测第11个,输入层节点数就是10,输出层就是1。中间隐藏层节点数可以调,一般先试个经验值。
% 简单的BP网络结构初始化 inputSize = 10; hiddenSize = 15; outputSize = 1; % 初始化权重和偏置 W1 = randn(inputSize, hiddenSize) * 0.1; b1 = zeros(1, hiddenSize); W2 = randn(hiddenSize, outputSize) * 0.1; b2 = zeros(1, outputSize);这段代码就是建个两层网络,权重用随机小值初始化,偏置先设零。注意这里权重乘了0.1,是为了避免初始值太大导致梯度问题。
但BP这么训练下去,权重更新很容易卡住。这时候模拟退火就该上场了。模拟退火的核心是:有时候接受一个更差的解,反而可能找到更好的全局最优。把它用在BP上,其实就是用SA来优化网络的权重和偏置。
% 模拟退火参数设置 T_init = 100; % 初始温度 T_min = 1e-3; % 最低温度 alpha = 0.95; % 降温系数 max_iter = 500; % 迭代次数 % 初始解(即初始权重) current_solution = [W1(:); b1(:); W2(:); b2(:)]; current_cost = compute_cost(current_solution, train_data); for iter = 1:max_iter T = T_init * alpha^(iter-1); if T < T_min break; end % 生成新解 - 在当前位置加个小扰动 new_solution = current_solution + randn(size(current_solution)) * 0.1; new_cost = compute_cost(new_solution, train_data); % 判断是否接受新解 delta_cost = new_cost - current_cost; if delta_cost < 0 || rand() < exp(-delta_cost / T) current_solution = new_solution; current_cost = new_cost; end end这里compute_cost是计算预测误差的函数,比如用均方误差。温度T随着迭代下降,接受差解的概率也越来越小。开始时候温度高,系统可以大胆跳来跳去;温度降下来后,就逐渐收敛到某个最优解附近。
把SA和BP结合,通常有两种做法:一是用SA直接优化网络权重,像上面那样;二是用SA优化BP的训练参数,比如学习率、动量因子这些。第一种更彻底,但计算量大;第二种轻量些,效果也不错。
实际用的时候,数据预处理很重要。时间序列最好做归一化,不然梯度计算会出问题。
% 数据归一化到[0,1] data_min = min(original_data); data_max = max(original_data); normalized_data = (original_data - data_min) / (data_max - data_min); % 构造输入输出对 seq_len = 10; X = []; Y = []; for i = 1:length(normalized_data)-seq_len X = [X; normalized_data(i:i+seq_len-1)]; Y = [Y; normalized_data(i+seq_len)]; end归一化后数据都在0到1之间,网络训练起来稳定多了。注意预测完后还要反归一化,才能得到真实值。
基于模拟退火算法优化BP神经网络(SA-BP)的时间序列预测 SA-BP时间序列 matlab代码 暂无Matlab版本要求 -- 推荐 2018B 版本及以上
训练过程中可以观察误差变化,看看SA是不是真的帮上忙了。有时候会发现,单纯BP训练误差下降一会儿就停了,而SA-BP还能继续往下走,这就是跳出局部最优的效果。
% 训练过程记录误差 errors = zeros(max_iter, 1); for iter = 1:max_iter % ... SA优化过程 ... errors(iter) = current_cost; % 每50轮显示一次进度 if mod(iter, 50) == 0 fprintf('迭代 %d, 温度 %.4f, 误差 %.6f\n', iter, T, current_cost); end end % 画误差下降曲线 figure; plot(errors); xlabel('迭代次数'); ylabel('均方误差'); title('SA-BP训练误差曲线');误差曲线如果呈现前期快速下降、后期缓慢收敛的状态,基本就对了。如果曲线还有大幅波动,可能是温度下降太快,或者扰动幅度太大,需要调整参数。
预测的时候,用优化好的权重做前向传播就行。注意测试数据也要用同样的方式归一化。
% 前向传播预测 function pred = forward(net_params, X) % 从参数中恢复网络结构 W1 = reshape(net_params(1:inputSize*hiddenSize), inputSize, hiddenSize); offset = inputSize*hiddenSize; b1 = reshape(net_params(offset+1:offset+hiddenSize), 1, hiddenSize); offset = offset + hiddenSize; W2 = reshape(net_params(offset+1:offset+hiddenSize*outputSize), hiddenSize, outputSize); offset = offset + hiddenSize*outputSize; b2 = reshape(net_params(offset+1:end), 1, outputSize); % 隐藏层计算 hidden = tanh(X * W1 + b1); % 输出层计算 pred = hidden * W2 + b2; end这里用了tanh作为激活函数,也可以换成sigmoid或ReLU,看具体数据特性。
最后说几个实践中的小技巧:一是SA的初始温度别设太高,否则前期完全随机游走,浪费时间;二是降温速度可以动态调整,误差下降慢的时候降温慢点;三是可以结合早停策略,连续若干轮误差不下降就终止训练。
这种SA-BP混合方法在处理有明显周期或趋势的时间序列时效果不错,比如电力负荷预测、股票价格预测这些。但对于完全随机游走的数据,什么算法都难有太好效果,毕竟不可预测的部分就是噪声。
代码实现时注意把SA和BP模块解耦,这样方便单独调试。也可以尝试用SA优化网络结构,比如隐藏层节点数,说不定有意外收获。
