时间序列预测方法¶
一句话说明¶
时间序列预测用历史数据预测未来值,在生信中常见于纵向研究(患者随访数据、微生物丰度随时间变化)和信号处理(基因组测序质量监控)。
核心知识点¶
常用方法对比¶
| 方法 | 类型 | 优点 | 缺点 |
|---|---|---|---|
| ARIMA | 统计 | 可解释强,参数少 | 假设线性平稳 |
| Prophet | 统计 | 自动处理季节性 | 不适合复杂非线性 |
| LSTM | 深度学习 | 捕捉长依赖 | 数据量要求高 |
| Transformer | 深度学习 | 并行,长序列 | 计算量大 |
| TCN | 深度学习 | 因果卷积,快速 | 感受野固定 |
| TiDE | 深度学习 | 简单MLP,SOTA | 2023年新方法 |
关键概念¶
平稳性:均值和方差不随时间变化。非平稳序列需差分处理。 ACF/PACF:自相关/偏自相关函数,用于确定 ARIMA 的 p、q 参数。 滑动窗口:用过去 k 步预测未来 n 步。
生信应用场景¶
- 纵向微生物组:随访患者肠道菌群丰度变化预测
- ICU 监护:生命体征时间序列预测
- 基因表达随发育时间变化(pseudotime 分析)
- 流行病预测:传染病传播曲线
实战代码¶
import numpy as np
import pandas as pd
import torch
import torch.nn as nn
# ===== 1. ARIMA 统计方法(用 statsmodels)=====
# pip install statsmodels
from statsmodels.tsa.arima.model import ARIMA
from statsmodels.tsa.stattools import adfuller
# 模拟微生物丰度时间序列(周采样,52周)
np.random.seed(42)
microbiome_abundance = np.random.randn(52).cumsum() + 10 # 随机游走模拟
# ADF 检验平稳性
result = adfuller(microbiome_abundance)
print(f'ADF 检验 p 值: {result[1]:.4f}')
# p < 0.05: 平稳;p > 0.05: 非平稳需差分
# 拟合 ARIMA 模型
# p=2: 自回归阶数;d=1: 差分阶数;q=1: 移动平均阶数
model = ARIMA(microbiome_abundance, order=(2, 1, 1))
fitted = model.fit()
print(fitted.summary())
# 预测未来 8 周
forecast = fitted.forecast(steps=8)
print(f'未来8周预测: {forecast.round(2)}')
# ===== 2. LSTM 深度学习方法 =====
def create_sequences(data, seq_len=10, pred_len=1):
"""
把时间序列切成滑动窗口训练样本
seq_len: 用多少历史步预测
pred_len: 预测未来多少步
"""
X, y = [], []
for i in range(len(data) - seq_len - pred_len + 1):
X.append(data[i:i+seq_len]) # 输入:过去 seq_len 步
y.append(data[i+seq_len:i+seq_len+pred_len]) # 标签:未来 pred_len 步
return np.array(X), np.array(y)
class LSTMForecaster(nn.Module):
"""LSTM 时间序列预测器"""
def __init__(self, input_size=1, hidden_size=64, num_layers=2, pred_len=1):
super().__init__()
# LSTM 层:处理时间序列依赖
self.lstm = nn.LSTM(
input_size=input_size, # 每个时间步的特征维度(单变量=1)
hidden_size=hidden_size, # 隐藏状态维度
num_layers=num_layers, # 堆叠 LSTM 层数
batch_first=True, # 输入格式: [batch, seq, features]
dropout=0.2 # 层间 dropout
)
# 线性输出层:LSTM 输出 -> 预测值
self.fc = nn.Linear(hidden_size, pred_len)
def forward(self, x):
# x 形状: [batch, seq_len, input_size]
lstm_out, _ = self.lstm(x) # lstm_out: [batch, seq_len, hidden_size]
last_step = lstm_out[:, -1, :] # 取最后一步的输出 [batch, hidden_size]
prediction = self.fc(last_step) # 线性变换到预测长度 [batch, pred_len]
return prediction
# 数据准备
seq_len = 10 # 用过去 10 周
pred_len = 1 # 预测下 1 周
X, y = create_sequences(microbiome_abundance, seq_len, pred_len)
X = torch.FloatTensor(X).unsqueeze(-1) # [N, seq_len, 1]
y = torch.FloatTensor(y) # [N, pred_len]
# 数据归一化(时间序列必须!)
X_mean, X_std = X.mean(), X.std()
X = (X - X_mean) / X_std
y = (y - X_mean) / X_std
# 划分训练/测试集
train_size = int(0.8 * len(X))
X_train, X_test = X[:train_size], X[train_size:]
y_train, y_test = y[:train_size], y[train_size:]
# 训练
model = LSTMForecaster(input_size=1, hidden_size=32, num_layers=2, pred_len=pred_len)
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)
loss_fn = nn.MSELoss()
for epoch in range(100):
model.train()
pred = model(X_train)
loss = loss_fn(pred, y_train)
optimizer.zero_grad()
loss.backward()
optimizer.step()
if epoch % 20 == 0:
print(f'Epoch {epoch}, Loss: {loss.item():.4f}')
# 评估(反归一化)
model.eval()
with torch.no_grad():
pred_test = model(X_test) * X_std + X_mean # 反归一化
true_test = y_test * X_std + X_mean
mae = torch.abs(pred_test - true_test).mean()
print(f'测试 MAE: {mae.item():.4f}')
面试常问点¶
Q: ARIMA(p,d,q) 中 p、d、q 分别是什么? A: p=自回归阶数(用几步历史),d=差分次数(使序列平稳),q=移动平均阶数(使用历史误差)。
Q: 时间序列预测为什么要归一化? A: 神经网络对数值范围敏感,归一化使梯度稳定,加速收敛。注意用训练集的统计量归一化测试集(防止数据泄露)。
Q: LSTM 和 Transformer 在时间序列上哪个更好? A: 短序列 LSTM 够用;超长序列用 Transformer(自注意力);实践中简单 MLP(如 TiDE)有时效果不亚于二者。
Q: 微生物丰度时间序列有什么特殊性? A: 成分数据(各菌加和为1),高度稀疏,个体间差异大,需要 CLR 变换后再建模。
速查表¶
| 术语 | 解释 |
|---|---|
| 平稳性 | 统计特性不随时间变化 |
| 差分 | 相邻时间步做减法,消除趋势 |
| ACF | 自相关函数,q 参数参考 |
| PACF | 偏自相关函数,p 参数参考 |
| 滑动窗口 | 用固定长度历史窗口预测 |
| MAE | 平均绝对误差,评估指标 |
| RMSE | 均方根误差,对大误差敏感 |
| Prophet | Facebook 开源的时序预测库 |