基于粒子群优化BP神经网络(PSO-BP)的时间序列预测python代码 优化算法用于寻找BP...
遇到预测结果成直线的情况,大概率是网络没训练起来,检查参数初始化范围。先导入包,这时候你会纠结用pytorch还是keras,但咱们先用最原始的numpy实现BP,这样粒子群优化过程更可控。(用于训练的光伏特征数据集csv文件,包括气象和环境变量等信息,5min采样精度,数据集部分也可以替换成自己的数据)BP网络结构别整太复杂,三明治结构就够用。可视化部分包括pso适应度函数变化,真实值和预测值对
基于粒子群优化BP神经网络(PSO-BP)的时间序列预测python代码 优化算法用于寻找BP神经网络的网络权重和偏置的初始值,以此优化神经网络性能。 可视化部分包括pso适应度函数变化,真实值和预测值对比图,绝对误差和相对误差可视化等。 (用于训练的光伏特征数据集csv文件,包括气象和环境变量等信息,5min采样精度,数据集部分也可以替换成自己的数据)
直接开撸代码。先导入包,这时候你会纠结用pytorch还是keras,但咱们先用最原始的numpy实现BP,这样粒子群优化过程更可控。准备数据阶段,先搞个滑窗函数处理时间序列:
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
def sliding_window(data, seq_length):
x, y = [], []
for i in range(len(data)-seq_length-1):
window = data[i:(i+seq_length)]
target = data[i+seq_length]
x.append(window)
y.append(target)
return np.array(x), np.array(y)
scaler = MinMaxScaler()
scaled_data = scaler.fit_transform(df[['power']].values)
X, y = sliding_window(scaled_data, seq_length=12) # 用1小时数据预测下个5分钟
BP网络结构别整太复杂,三明治结构就够用。注意这里把权重和偏置展平成一维数组,方便PSO操作:
class BPNet:
def __init__(self, input_size, hidden_size):
self.w1 = np.random.randn(input_size, hidden_size)
self.b1 = np.zeros(hidden_size)
self.w2 = np.random.randn(hidden_size, 1)
self.b2 = np.zeros(1)
def forward(self, x):
self.h = np.tanh(x @ self.w1 + self.b1)
return self.h @ self.w2 + self.b2
def set_params(self, params):
# 把PSO找到的参数塞回网络
w1_size = self.w1.size
self.w1 = params[:w1_size].reshape(self.w1.shape)
params = params[w1_size:]
self.b1 = params[:self.b1.size]
params = params[self.b1.size:]
self.w2 = params[:self.w2.size].reshape(self.w2.shape)
self.b2 = params[-self.b2.size:]
重点来了,粒子群算法怎么和BP结合?把网络参数当作粒子位置。适应度函数用训练误差:
def fitness(params, X_train, y_train):
net = BPNet(12, 8) # 输入12步,隐层8节点
net.set_params(params)
pred = np.array([net.forward(x) for x in X_train])
return np.mean((pred - y_train)**2) # MSE作为适应度值
class PSO:
def __init__(self, n_particles, dim, bounds, X_train, y_train):
self.particles = np.random.uniform(bounds[0], bounds[1], (n_particles, dim))
self.velocities = np.zeros((n_particles, dim))
self.best_pos = self.particles.copy()
self.best_scores = np.array([fitness(p, X_train, y_train) for p in self.particles])
self.global_best_pos = self.best_pos[np.argmin(self.best_scores)]
def update(self, w=0.7, c1=1.5, c2=1.5):
r1, r2 = np.random.rand(2)
self.velocities = w * self.velocities + c1*r1*(self.best_pos - self.particles) + c2*r2*(self.global_best_pos - self.particles)
self.particles += self.velocities
# 更新个体最优
for i in range(len(self.particles)):
current_score = fitness(self.particles[i], X_train, y_train)
if current_score < self.best_scores[i]:
self.best_scores[i] = current_score
self.best_pos[i] = self.particles[i]
# 更新全局最优
if np.min(self.best_scores) < fitness(self.global_best_pos, X_train, y_train):
self.global_best_pos = self.best_pos[np.argmin(self.best_scores)]
训练环节记得把数据拆分成训练集和验证集。PSO优化完参数后,再用这些参数初始化BP网络进行微调:
# PSO参数寻优
param_dim = 12*8 + 8 + 8*1 + 1 # 计算总参数数量
pso = PSO(n_particles=30, dim=param_dim, bounds=(-1,1), X_train=X_train, y_train=y_train)
for epoch in range(50):
pso.update()
print(f"Epoch {epoch} Best MSE: {np.min(pso.best_scores):.4f}")
# 用最优参数初始化网络
best_net = BPNet(12,8)
best_net.set_params(pso.global_best_pos)
# 传统BP训练做微调
optimizer = torch.optim.Adam([torch.tensor(best_net.w1),...], lr=0.01)
# ...正常训练过程...
可视化部分别用plt默认样式,换成seaborn风格更专业。误差图建议用双坐标轴展示绝对和相对误差:
plt.figure(figsize=(12,6))
plt.plot(test_y, label='True', marker='o', markersize=3)
plt.plot(predictions, label='Pred', linestyle='--')
plt.title('5分钟粒度光伏功率预测')
plt.legend()
plt.grid(alpha=0.3)
# 误差分析
abs_error = np.abs(test_y - predictions)
rel_error = abs_error / (test_y + 1e-6) # 防止除零
plt.figure(figsize=(10,4))
plt.plot(abs_error, label='绝对误差', color='#FF6B6B')
plt.ylabel('绝对误差(kW)')
plt.twinx()
plt.plot(rel_error*100, label='相对误差', color='#4ECDC4', linestyle=':')
plt.ylabel('相对误差(%)')
plt.title(f'平均相对误差: {np.mean(rel_error)*100:.2f}%')
实际跑代码时注意几个坑:数据标准化要统一用训练集的scaler,别在测试集上重新fit;粒子群搜索空间别设太大,容易梯度爆炸;隐层激活函数用tanh比sigmoid更适合回归问题。遇到预测结果成直线的情况,大概率是网络没训练起来,检查参数初始化范围。

更多推荐
所有评论(0)