使用粒子群优化(PSO)自动优化回声状态网络(ESN)的关键超参数,实现时间序列预测任务。
一、算法原理
1.1 回声状态网络(ESN)
ESN 是一种特殊的递归神经网络,包含:
- 输入层 :WinW_{in}Win(随机固定)
- 储备池 :WresW_{res}Wres(随机稀疏连接,谱半径控制稳定性)
- 输出层 :WoutW_{out}Wout(唯一需要训练的权重,通过线性回归求解)
1.2 关键超参数
ESN 性能高度依赖以下超参数:
| 超参数 | 符号 | 作用 |
|---|---|---|
| 储备池规模 | NNN | 储备池神经元数量 |
| 谱半径 | ρ\rhoρ | 控制储备池动力学的稳定性 |
| 输入缩放 | α\alphaα | 输入信号的缩放因子 |
| 泄漏率 | λ\lambdaλ | 神经元状态更新的记忆程度 |
| 稀疏度 | ddd | 储备池连接的稀疏程度 |
1.3 PSO 优化框架
使用 PSO 在超参数空间中搜索最优组合,以最小化验证集上的预测误差:
minθRMSE(ytrue,ypred) \min_{\theta} \text{RMSE}(y_{true}, y_{pred}) θminRMSE(ytrue,ypred)
其中 θ=N,ρ,α,λ,d\theta = N, \\rho, \\alpha, \\lambda, dθ=N,ρ,α,λ,d 为待优化超参数向量。
二、完整 MATLAB 代码
2.1 主程序 pso_esn_main.m
matlab
%% 粒子群优化回声状态网络(PSO-ESN)
clear; clc; close all;
%% ========== 1. 生成/加载时间序列数据 ==========
% 使用 Lorenz 混沌时间序列作为示例
data = generate_lorenz_data(1000, 0.02); % [x, y, z] 坐标
target_idx = 1; % 预测 x 坐标
% 数据预处理
[data_norm, mu, sigma] = normalize_data(data(:,target_idx));
% 划分数据集
train_ratio = 0.6;
val_ratio = 0.2;
test_ratio = 0.2;
N = length(data_norm);
train_end = round(N * train_ratio);
val_end = train_end + round(N * val_ratio);
train_data = data_norm(1:train_end);
val_data = data_norm(train_end+1:val_end);
test_data = data_norm(val_end+1:end);
fprintf('数据集划分: 训练=%d, 验证=%d, 测试=%d\n', ...
length(train_data), length(val_data), length(test_data));
%% ========== 2. PSO 参数设置 ==========
n_particles = 20; % 粒子数量
max_iter = 30; % 最大迭代次数
w_max = 0.9; w_min = 0.4; % 惯性权重范围
c1 = 2.0; c2 = 2.0; % 学习因子
% 超参数边界 [N, ρ, α, λ, d]
lb = [50, 0.1, 0.1, 0.1, 0.01]; % 下界
ub = [500, 0.99, 2.0, 1.0, 0.5]; % 上界
% 初始化粒子群
particles = zeros(n_particles, 5);
velocities = zeros(n_particles, 5);
p_best = zeros(n_particles, 5);
p_best_fitness = inf(n_particles, 1);
g_best = zeros(1, 5);
g_best_fitness = inf;
% 随机初始化粒子
for i = 1:n_particles
particles(i,:) = lb + rand(1,5) .* (ub - lb);
velocities(i,:) = zeros(1,5);
p_best(i,:) = particles(i,:);
end
%% ========== 3. PSO 优化循环 ==========
fitness_history = zeros(max_iter, 1);
for iter = 1:max_iter
fprintf('PSO 迭代 %d/%d\n', iter, max_iter);
% 更新惯性权重(线性递减)
w = w_max - (w_max - w_min) * (iter-1) / (max_iter-1);
for i = 1:n_particles
% 评估当前粒子的适应度
fitness = evaluate_esn(particles(i,:), train_data, val_data);
% 更新个体最优
if fitness < p_best_fitness(i)
p_best_fitness(i) = fitness;
p_best(i,:) = particles(i,:);
end
% 更新全局最优
if fitness < g_best_fitness
g_best_fitness = fitness;
g_best = particles(i,:);
end
end
% 更新粒子速度和位置
for i = 1:n_particles
% 速度更新
r1 = rand(1,5); r2 = rand(1,5);
velocities(i,:) = w * velocities(i,:) + ...
c1 * r1 .* (p_best(i,:) - particles(i,:)) + ...
c2 * r2 .* (g_best - particles(i,:));
% 速度限制
velocities(i,:) = max(velocities(i,:), -0.5*(ub-lb));
velocities(i,:) = min(velocities(i,:), 0.5*(ub-lb));
% 位置更新
particles(i,:) = particles(i,:) + velocities(i,:);
% 边界处理
particles(i,:) = max(particles(i,:), lb);
particles(i,:) = min(particles(i,:), ub);
end
fitness_history(iter) = g_best_fitness;
fprintf(' 最优适应度: %.6f\n', g_best_fitness);
end
%% ========== 4. 使用最优超参数训练最终模型 ==========
fprintf('\n使用最优超参数训练最终模型...\n');
fprintf('最优超参数: N=%.0f, ρ=%.3f, α=%.3f, λ=%.3f, d=%.3f\n', ...
g_best(1), g_best(2), g_best(3), g_best(4), g_best(5));
% 训练最终 ESN 模型
[esn_model, train_error] = train_esn(g_best, train_data, train_data);
% 测试集评估
test_predictions = predict_esn(esn_model, test_data, length(test_data));
% 反归一化
test_predictions = test_predictions * sigma + mu;
test_actual = test_data * sigma + mu;
% 计算性能指标
rmse = sqrt(mean((test_actual - test_predictions).^2));
mae = mean(abs(test_actual - test_predictions));
r2 = 1 - sum((test_actual - test_predictions).^2) / sum((test_actual - mean(test_actual)).^2);
fprintf('测试集性能:\n');
fprintf(' RMSE: %.6f\n', rmse);
fprintf(' MAE: %.6f\n', mae);
fprintf(' R²: %.4f\n', r2);
%% ========== 5. 结果可视化 ==========
figure('Position', [100, 100, 1400, 600]);
% PSO 收敛曲线
subplot(2,3,1);
plot(fitness_history, 'b-', 'LineWidth', 2);
xlabel('迭代次数'); ylabel('适应度 (RMSE)');
title('PSO 收敛曲线');
grid on;
% 训练集预测
subplot(2,3,2);
train_predictions = predict_esn(esn_model, train_data, length(train_data));
train_actual = train_data * sigma + mu;
train_predictions = train_predictions * sigma + mu;
plot(train_actual, 'b-', 'LineWidth', 1.5); hold on;
plot(train_predictions, 'r--', 'LineWidth', 1.5);
xlabel('时间'); ylabel('幅值');
title('训练集预测');
legend('真实值', '预测值');
grid on;
% 测试集预测
subplot(2,3,3);
plot(test_actual, 'b-', 'LineWidth', 1.5); hold on;
plot(test_predictions, 'r--', 'LineWidth', 1.5);
xlabel('时间'); ylabel('幅值');
title('测试集预测');
legend('真实值', '预测值');
grid on;
% 预测误差
subplot(2,3,4);
errors = test_actual - test_predictions;
plot(errors, 'k-', 'LineWidth', 1.5);
xlabel('时间'); ylabel('误差');
title('预测误差');
grid on;
% 误差分布
subplot(2,3,5);
histogram(errors, 30, 'Normalization', 'probability');
xlabel('误差'); ylabel('概率');
title('误差分布');
grid on;
% 超参数重要性(简单分析)
subplot(2,3,6);
hyperparams = {'储备池规模', '谱半径', '输入缩放', '泄漏率', '稀疏度'};
bar(g_best);
set(gca, 'XTick', 1:5, 'XTickLabel', hyperparams);
ylabel('最优值');
title('最优超参数');
grid on;
sgtitle('PSO 优化回声状态网络(ESN)', 'FontSize', 14, 'FontWeight', 'bold');
%% ========== 6. 保存结果 ==========
save('pso_esn_results.mat', 'esn_model', 'g_best', 'test_predictions', 'test_actual', 'rmse', 'mae', 'r2');
fprintf('结果已保存到 pso_esn_results.mat\n');
2.2 ESN 模型实现 esn_model.m
matlab
classdef ESN < handle
% 回声状态网络类
properties
N = 200; % 储备池规模
rho = 0.9; % 谱半径
alpha = 0.5; % 输入缩放
lambda = 0.8; % 泄漏率
density = 0.1; % 连接稀疏度
W_in; % 输入权重 (N×1)
W_res; % 储备池权重 (N×N)
W_out; % 输出权重 (1×N)
state; % 储备池状态
washout = 100; % 清洗期长度
end
methods
function obj = ESN(params)
% 构造函数
if nargin > 0
obj.N = round(params(1));
obj.rho = params(2);
obj.alpha = params(3);
obj.lambda = params(4);
obj.density = params(5);
end
% 初始化网络
obj.initialize_network();
end
function initialize_network(obj)
% 初始化输入权重
obj.W_in = obj.alpha * (2*rand(obj.N, 1) - 1);
% 初始化储备池权重(稀疏随机连接)
obj.W_res = sprandsym(obj.N, obj.density);
% 调整谱半径
eigvals = eig(full(obj.W_res));
max_eig = max(abs(eigvals));
if max_eig > 0
obj.W_res = obj.W_res * obj.rho / max_eig;
end
% 初始化储备池状态
obj.state = zeros(obj.N, 1);
end
function train(obj, data)
% 训练 ESN(只训练输出权重)
T = length(data);
% 收集储备池状态
states = zeros(obj.N, T);
for t = 1:T
% 更新储备池状态
u = data(t);
obj.state = (1 - obj.lambda) * obj.state + ...
obj.lambda * tanh(obj.W_in * u + obj.W_res * obj.state);
% 存储状态(跳过清洗期)
if t > obj.washout
states(:, t - obj.washout) = obj.state;
end
end
% 准备训练数据
X = states(:, 1:T-obj.washout);
Y = data(obj.washout+1:end)';
% 岭回归求解输出权重
ridge_param = 1e-6;
obj.W_out = Y * X' / (X * X' + ridge_param * eye(obj.N));
end
function predictions = predict(obj, data, steps)
% 预测未来 steps 步
predictions = zeros(steps, 1);
T = length(data);
% 用历史数据初始化状态
for t = 1:T
u = data(t);
obj.state = (1 - obj.lambda) * obj.state + ...
obj.lambda * tanh(obj.W_in * u + obj.W_res * obj.state);
end
% 预测
for t = 1:steps
% 当前输入
if t == 1
u = data(end);
else
u = predictions(t-1);
end
% 更新状态
obj.state = (1 - obj.lambda) * obj.state + ...
obj.lambda * tanh(obj.W_in * u + obj.W_res * obj.state);
% 预测输出
predictions(t) = obj.W_out * obj.state;
end
end
end
end
2.3 评估函数 evaluate_esn.m
matlab
function fitness = evaluate_esn(params, train_data, val_data)
% 评估 ESN 在给定超参数下的性能
% 返回验证集上的 RMSE
try
% 创建 ESN 模型
esn = ESN(params);
% 训练模型
esn.train(train_data);
% 验证集预测
val_predictions = esn.predict(val_data, length(val_data));
% 计算 RMSE
fitness = sqrt(mean((val_data - val_predictions).^2));
catch
% 如果参数导致数值不稳定,给予惩罚
fitness = 1e6;
end
end
2.4 辅助函数
matlab
%% 生成 Lorenz 时间序列
function data = generate_lorenz_data(N, dt)
% 生成 Lorenz 混沌时间序列
sigma = 10; rho = 28; beta = 8/3;
x0 = [1; 1; 1];
data = zeros(N, 3);
x = x0;
for i = 1:N
% Runge-Kutta 4阶积分
k1 = lorenz_deriv(x, sigma, rho, beta);
k2 = lorenz_deriv(x + dt/2*k1, sigma, rho, beta);
k3 = lorenz_deriv(x + dt/2*k2, sigma, rho, beta);
k4 = lorenz_deriv(x + dt*k3, sigma, rho, beta);
x = x + dt/6*(k1 + 2*k2 + 2*k3 + k4);
data(i,:) = x';
end
end
function dx = lorenz_deriv(x, sigma, rho, beta)
dx = zeros(3,1);
dx(1) = sigma*(x(2) - x(1));
dx(2) = x(1)*(rho - x(3)) - x(2);
dx(3) = x(1)*x(2) - beta*x(3);
end
%% 数据归一化
function [data_norm, mu, sigma] = normalize_data(data)
mu = mean(data);
sigma = std(data);
data_norm = (data - mu) / sigma;
end
%% 训练 ESN
function [esn, error] = train_esn(params, train_data, val_data)
esn = ESN(params);
esn.train(train_data);
% 计算训练误差
train_pred = esn.predict(train_data, length(train_data));
error = sqrt(mean((train_data - train_pred).^2));
end
%% 预测 ESN
function predictions = predict_esn(esn, data, steps)
predictions = esn.predict(data, steps);
end
三、运行说明
3.1 直接运行
- 将代码保存为
.m文件 - 运行
pso_esn_main.m - 程序会自动生成 Lorenz 数据并进行优化
3.2 参数调优建议
| 参数 | 建议范围 | 说明 |
|---|---|---|
n_particles |
20~50 | 粒子数量,越多搜索越全面但越慢 |
max_iter |
30~100 | 迭代次数,确保收敛 |
w_max/w_min |
0.9/0.4 | 惯性权重,平衡全局和局部搜索 |
c1/c2 |
2.0/2.0 | 学习因子,通常相等 |
3.3 预期结果
- PSO 收敛:通常在 20~30 次迭代内收敛
- 预测精度:Lorenz 时间序列的 RMSE 通常在 0.01~0.05 之间
- 超参数:最优储备池规模通常在 100~300 之间,谱半径在 0.8~0.95 之间
四、算法优化建议
4.1 改进 PSO
matlab
% 自适应惯性权重
w = w_max - (w_max - w_min) * (iter/max_iter)^2;
% 添加收缩因子
chi = 2 / (2 - c1 - c2 + sqrt((2 - c1 - c2)^2 - 4*(c1 + c2)));
velocities(i,:) = chi * velocities(i,:);
4.2 改进 ESN
matlab
% 使用岭回归的改进版本
ridge_param = 1e-8;
obj.W_out = Y * X' / (X * X' + ridge_param * eye(obj.N));
% 添加偏置项
X = [ones(1, T-obj.washout); X]; % 增加偏置行
obj.W_out = Y * X' / (X * X' + ridge_param * eye(obj.N+1));
4.3 多目标优化
matlab
% 同时优化精度和复杂度
fitness = rmse + 0.01 * params(1)/500; % 惩罚大的储备池
参考代码 利用PSO优化回声状态网络 www.youwenfan.com/contentcsw/81821.html
五、应用场景
| 应用 | 说明 |
|---|---|
| 时间序列预测 | 金融数据、气象数据、能源需求预测 |
| 系统辨识 | 非线性动态系统建模 |
| 异常检测 | 基于预测误差的异常识别 |
| 控制应用 | 作为预测控制器中的模型 |