粒子群优化回声状态网络(PSO-ESN)MATLAB 实现

使用粒子群优化(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 直接运行

  1. 将代码保存为 .m 文件
  2. 运行 pso_esn_main.m
  3. 程序会自动生成 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

五、应用场景

应用 说明
时间序列预测 金融数据、气象数据、能源需求预测
系统辨识 非线性动态系统建模
异常检测 基于预测误差的异常识别
控制应用 作为预测控制器中的模型