超实用!Informer-LSTM时序预测+SHAP可解释性分析,手把手教你打造高精度模型
精准捕捉长短期依赖,让黑箱模型不再神秘!
在时间序列预测领域,长序列预测一直是个挑战。今天,我要向大家介绍一个强大的混合模型——Informer-LSTM,并结合SHAP进行可解释性分析,让你的预测模型既精准又透明!
一、为什么选择Informer-LSTM?
传统的时序预测模型在处理长序列时往往力不从心。RNN系列模型受限于梯度消失问题,Transformer虽然强大但计算复杂度呈平方增长。
Informer-LSTM混合模型巧妙地结合了两者优势:
- Informer模块:负责捕捉宏观的长期依赖
- LSTM模块:精细处理局部的短期模式
二、核心原理详解
1. Informer模块:高效处理长序列
ProbSparse自注意力机制
传统自注意力机制的复杂度是O(L²),当序列长度L=10000时,计算量巨大。ProbSparse注意力通过稀疏性评估,只计算重要分数,将复杂度优化到O(L log L),轻松处理超长序列。
自注意力蒸馏
在每层注意力模块后,将序列长度裁剪一半,像金字塔一样浓缩关键信息,既减少计算量又提炼出最重要的长期依赖特征。
生成式解码器
传统模型需要逐步预测,而Informer的解码器能一次性生成所有未来时间点的预测值,极大提升效率。
2. LSTM模块:精准捕捉短期动态
LSTM通过精巧的“门控”机制(输入门、遗忘门、输出门)克服了传统RNN的梯度消失问题,特别擅长捕捉时间序列中复杂的短期动态和局部模式。
三、实战:完整实现流程
步骤1:环境配置与数据准备
importnumpyasnpimportpandasaspdimporttorchimporttorch.nnasnnfromtorch.utils.dataimportDataLoader,TensorDatasetfromsklearn.preprocessingimportMinMaxScalerfromsklearn.metricsimportmean_squared_error,mean_absolute_error,r2_scoreimportmatplotlib.pyplotaspltimportshap# 设置随机种子torch.manual_seed(42)np.random.seed(42)步骤2:构建Informer-LSTM模型
classProbSparseAttention(nn.Module):"""ProbSparse自注意力机制"""def__init__(self,d_model,n_heads):super().__init__()self.d_model=d_model self.n_heads=n_heads self.scale=(d_model//n_heads)**-0.5defforward(self,Q,K,V):# 简化的ProbSparse实现batch_size,seq_len,_=Q.shape# 采样部分关键查询sample_len=min(seq_len,int(np.log(seq_len)))idx=torch.randperm(seq_len)[:sample_len]Q_sample=Q[:,idx,:]# 计算稀疏注意力分数scores=torch.matmul(Q_sample,K.transpose(-2,-1))*self.scale attn=torch.softmax(scores,dim=-1)# 只保留重要部分output=torch.matmul(attn,V)returnoutputclassInformerLSTM(nn.Module):"""Informer-LSTM混合模型"""def__init__(self,input_size,hidden_size=64,num_layers=2,d_model=128,n_heads=8,output_size=1):super().__init__()# Informer模块self.informer_attention=ProbSparseAttention(d_model,n_heads)self.informer_fc=nn.Linear(input_size,d_model)# LSTM模块self.lstm=nn.LSTM(input_size=d_model,hidden_size=hidden_size,num_layers=num_layers,batch_first=True,dropout=0.2)# 输出层self.fc=nn.Sequential(nn.Linear(hidden_size,32),nn.ReLU(),nn.Dropout(0.1),nn.Linear(32,output_size))defforward(self,x):# Informer路径x_informer=self.informer_fc(x)attn_output=self.informer_attention(x_informer,x_informer,x_informer)# LSTM路径lstm_output,_=self.lstm(attn_output)# 取最后一个时间步的输出output=self.fc(lstm_output[:,-1,:])returnoutput步骤3:模型训练与评估
deftrain_model(model,train_loader,val_loader,epochs=100,lr=0.001):"""训练模型并记录指标"""criterion=nn.MSELoss()optimizer=torch.optim.Adam(model.parameters(),lr=lr)train_losses,val_losses,r2_scores=[],[],[]best_val_loss=float('inf')forepochinrange(epochs):# 训练阶段model.train()train_loss=0forX_batch,y_batchintrain_loader:optimizer.zero_grad()y_pred=model(X_batch)loss=criterion(y_pred,y_batch)loss.backward()optimizer.step()train_loss+=loss.item()# 验证阶段model.eval()val_loss=0all_preds,all_true=[],[]withtorch.no_grad():forX_batch,y_batchinval_loader:y_pred=model(X_batch)val_loss+=criterion(y_pred,y_batch).item()all_preds.extend(y_pred.numpy())all_true.extend(y_batch.numpy())# 计算R2r2=r2_score(all_true,all_preds)train_losses.append(train_loss/len(train_loader))val_losses.append(val_loss/len(val_loader))r2_scores.append(r2)# 保存最佳模型ifval_loss<best_val_loss:best_val_loss=val_loss torch.save(model.state_dict(),'best_informer_lstm.pth')if(epoch+1)%10==0:print(f'Epoch [{epoch+1}/{epochs}], 'f'Train Loss:{train_losses[-1]:.4f}, 'f'Val Loss:{val_losses[-1]:.4f}, 'f'R2:{r2:.4f}')returntrain_losses,val_losses,r2_scores步骤4:SHAP可解释性分析
defshap_analysis(model,X_background,X_explain,feature_names):"""进行SHAP分析并可视化"""# 创建解释器explainer=shap.DeepExplainer(model,X_background)# 计算SHAP值shap_values=explainer.shap_values(X_explain)# 绘制各种可视化图表plt.figure(figsize=(12,8))# 1. 蜂巢图shap.plots.beeswarm(shap_values,max_display=10,show=False)plt.title('SHAP蜂巢图 - 特征重要性分布')plt.tight_layout()plt.savefig('shap_beeswarm.png',dpi=300,bbox_inches='tight')plt.show()# 2. 瀑布图(单个预测)shap.plots.waterfall(shap_values[0],max_display=10,show=False)plt.title('SHAP瀑布图 - 单个预测解释')plt.tight_layout()plt.savefig('shap_waterfall.png',dpi=300,bbox_inches='tight')plt.show()# 3. 力图shap.initjs()shap.force_plot(explainer.expected_value,shap_values,X_explain,feature_names=feature_names,matplotlib=True,show=False)plt.title('SHAP力图 - 特征贡献可视化')plt.tight_layout()plt.savefig('shap_force.png',dpi=300,bbox_inches='tight')plt.show()# 4. 决策图shap.decision_plot(explainer.expected_value,shap_values,feature_names,show=False)plt.title('SHAP决策图 - 预测决策路径')plt.tight_layout()plt.savefig('shap_decision.png',dpi=300,bbox_inches='tight')plt.show()# 5. 热力图shap.plots.heatmap(shap_values,max_display=10,show=False)plt.title('SHAP热力图 - 特征影响模式')plt.tight_layout()plt.savefig('shap_heatmap.png',dpi=300,bbox_inches='tight')plt.show()# 6. 柱状图(特征重要性)shap.summary_plot(shap_values,X_explain,feature_names=feature_names,plot_type="bar",show=False)plt.title('SHAP柱状图 - 特征重要性排序')plt.tight_layout()plt.savefig('shap_bar.png',dpi=300,bbox_inches='tight')plt.show()returnshap_values四、完整案例演示
# 生成示例数据集defgenerate_sample_data(n_samples=5000):"""生成示例时间序列数据"""t=np.arange(n_samples)# 趋势 + 季节 + 噪声trend=0.01*t seasonal=np.sin(2*np.pi*t/50)+0.5*np.sin(2*np.pi*t/20)noise=np.random.randn(n_samples)*0.1# 构建特征X=np.column_stack([trend+seasonal+noise,np.roll(trend,1)+seasonal,np.sin(2*np.pi*t/30),np.cos(2*np.pi*t/30),np.random.randn(n_samples)*0.2])y=trend+seasonal+noisereturnX,y# 准备数据lookback=20# 时间步长X,y=generate_sample_data()X_scaled,y_scaled,scaler_X,scaler_y=prepare_data(X,y)# 创建序列数据X_seq,y_seq=create_sequences(X_scaled,y_scaled,lookback)# 划分数据集train_size=int(0.7*len(X_seq))val_size=int(0.15*len(X_seq))test_size=len(X_seq)-train_size-val_size train_dataset=TensorDataset(X_seq[:train_size],y_seq[:train_size])val_dataset=TensorDataset(X_seq[train_size:train_size+val_size],y_seq[train_size:train_size+val_size])test_dataset=TensorDataset(X_seq[train_size+val_size:],y_seq[train_size+val_size:])# 训练模型model=InformerLSTM(input_size=X.shape[1],output_size=1)train_loader=DataLoader(train_dataset,batch_size=64,shuffle=True)val_loader=DataLoader(val_dataset,batch_size=64)train_losses,val_losses,r2_scores=train_model(model,train_loader,val_loader)# 评估模型model.load_state_dict(torch.load('best_informer_lstm.pth'))test_loader=DataLoader(test_dataset,batch_size=64)y_pred,y_true=evaluate_model(model,test_loader)# 计算评价指标mse=mean_squared_error(y_true,y_pred)rmse=np.sqrt(mse)mae=mean_absolute_error(y_true,y_pred)r2=r2_score(y_true,y_pred)mape=np.mean(np.abs((y_true-y_pred)/y_true))*100print(f"MSE:{mse:.4f}")print(f"RMSE:{rmse:.4f}")print(f"MAE:{mae:.4f}")print(f"R2:{r2:.4f}")print(f"MAPE:{mape:.2f}%")五、结果可视化
defplot_results(y_true,y_pred,train_losses,val_losses,r2_scores):"""绘制所有结果图表"""fig,axes=plt.subplots(2,3,figsize=(15,10))# 损失曲线axes[0,0].plot(train_losses,label='Train Loss')axes[0,0].plot(val_losses,label='Val Loss')axes[0,0].set_xlabel('Epoch')axes[0,0].set_ylabel('Loss')axes[0,0].set_title('训练和验证损失曲线')axes[0,0].legend()axes[0,0].grid(True)# R2曲线axes[0,1].plot(r2_scores)axes[0,1].set_xlabel('Epoch')axes[0,1].set_ylabel('R2 Score')axes[0,1].set_title('R2评分随epoch变化')axes[0,1].grid(True)# 回归散点图axes[0,2].scatter(y_true,y_pred,alpha=0.5)axes[0,2].plot([y_true.min(),y_true.max()],[y_true.min(),y_true.max()],'r--',lw=2)axes[0,2].set_xlabel('True Values')axes[0,2].set_ylabel('Predictions')axes[0,2].set_title(f'回归散点图 (R2={r2_score(y_true,y_pred):.3f})')axes[0,2].grid(True)# 残差图residuals=y_true-y_pred axes[1,0].scatter(y_pred,residuals,alpha=0.5)axes[1,0].axhline(y=0,color='r',linestyle='--')axes[1,0].set_xlabel('Predicted Values')axes[1,0].set_ylabel('Residuals')axes[1,0].set_title('残差图')axes[1,0].grid(True)# 时序对比图axes[1,1].plot(y_true[:200],label='True',alpha=0.7)axes[1,1].plot(y_pred[:200],label='Predicted',alpha=0.7)axes[1,1].set_xlabel('Time')axes[1,1].set_ylabel('Value')axes[1,1].set_title('时序预测对比图(前200个样本)')axes[1,1].legend()axes[1,1].grid(True)# 误差分布图axes[1,2].hist(residuals,bins=50,edgecolor='black',alpha=0.7)axes[1,2].set_xlabel('Residuals')axes[1,2].set_ylabel('Frequency')axes[1,2].set_title('残差分布直方图')axes[1,2].grid(True)plt.tight_layout()plt.savefig('all_results.png',dpi=300,bbox_inches='tight')plt.show()六、总结与建议
Informer-LSTM混合模型在时序预测任务中表现出色:
- 优势:既能捕捉长期依赖,又能关注短期模式
- 适用场景:长序列预测、多变量时间序列、金融数据预测等
- 可解释性:通过SHAP分析,让模型决策透明可信
使用建议:
- 根据数据特征调整lookback参数
- 尝试不同的隐藏层维度和层数
- 使用早停法避免过拟合
- 结合交叉验证选择最优超参数
完整代码和数据集已准备好,支持PyCharm和Jupyter Notebook使用,包含详细的代码注释。欢迎留言交流!
本文代码使用PyTorch实现,案例数据集可直接运行。