这是我的第422篇原创文章。

一、引言

        将传统机器学习方法(随机森林)与深度学习模型(GRU)结合进行时间序列预测。一般有以下两种结合的思路:

1. 并联结构(Ensemble)

  • 随机森林和GRU分别预测,然后合并:

其中  是一个加权系数(可调超参数,也可以学习得到)。

2. 二阶段结构(预训练 + Fine-tune)

  • 第一阶段:训练随机森林预测 
  • 第二阶段:将  加入 GRU 输入,GRU fine-tune 预测。

    如果你时间序列数据中存在复杂的非线性结构 + 时序依赖,而且希望提升模型的泛化能力抗噪性;那么这个“随机森林 + GRU 的混合模型”确实是一个非常值得尝试的结构。本文通过一个具体的案例来实现第二种融合,大致步骤如下:

    • 读取时间序列数据,并通过滑动窗口构造特征

    • 使用随机森林模型进行初步预测

    • 并将其输出作为额外特征加入 GRU 模型输入中

    • 可视化分析原始序列、预测效果和训练损失

二、实现过程

2.1 读取数据

核心代码:

data = pd.read_csv('data.csv')
# 将日期列转换为日期时间类型
data['Month'] = pd.to_datetime(data['Month'])
# 将日期列设置为索引
data.set_index('Month', inplace=True)
series = np.array(data['Passengers'])
series  = (series - series.min()) / (series.max() - series.min())
time = np.arange(len(series))

2.2 构造滑动窗口特征

核心代码:

def create_windows(data, window_size):
    X, y = [], []
    for i in range(len(data) - window_size):
        X.append(data[i:i+window_size])
        y.append(data[i+window_size])
    return np.array(X), np.array(y)
window_size = 1
X, y = create_windows(series, window_size)

2.3 划分训练和测试集

核心代码:

split_idx = int(0.8 * len(X))
X_train, X_test = X[:split_idx], X[split_idx:]
y_train, y_test = y[:split_idx], y[split_idx:]

2.4 训练随机森林并提取预测值

核心代码:

rf = RandomForestRegressor(n_estimators=100, random_state=42)
rf.fit(X_train, y_train)
rf_train_pred = rf.predict(X_train)
rf_test_pred = rf.predict(X_test)

2.5 将 RF 输出拼接入 GRU 输入特征

核心代码:

X_train_gru = np.hstack([X_train, rf_train_pred.reshape(-1,1)])
X_test_gru = np.hstack([X_test, rf_test_pred.reshape(-1,1)])

2.6 PyTorch GRU 模型定义

核心代码:

class GRUModel(nn.Module):
    def __init__(self, input_size, hidden_size=32, num_layers=1):
        super(GRUModel, self).__init__()
        self.gru = nn.GRU(input_size, hidden_size, num_layers, batch_first=True)
        self.fc = nn.Linear(hidden_size, 1)
    def forward(self, x):
        out, _ = self.gru(x)
        out = self.fc(out[:, -1, :])
        return out
train_dataset = to_tensor_dataset(X_train_gru, y_train)
test_dataset = to_tensor_dataset(X_test_gru, y_test)
train_loader = DataLoader(train_dataset, batch_size=32, shuffle=False)
test_loader = DataLoader(test_dataset, batch_size=32, shuffle=False)

2.7 训练 GRU

核心代码:

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = GRUModel(input_size=window_size+1).to(device)
criterion = nn.MSELoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.01)
train_losses = []
for epoch in range(20):
    model.train()
    epoch_loss = 0
    for X_batch, y_batch in train_loader:
        X_batch, y_batch = X_batch.to(device), y_batch.to(device)
        optimizer.zero_grad()
        outputs = model(X_batch)
        loss = criterion(outputs, y_batch)
        loss.backward()
        optimizer.step()
        epoch_loss += loss.item() * X_batch.size(0)
    train_losses.append(epoch_loss / len(train_loader.dataset))

2.8 测试并预测

核心代码:

model.eval()
with torch.no_grad():
    test_preds = []
    test_truth = []
    for X_batch, y_batch in test_loader:
        X_batch = X_batch.to(device)
        preds = model(X_batch).cpu().numpy().flatten()
        test_preds.extend(preds)
        test_truth.extend(y_batch.numpy().flatten())

2.9 画图展示

图1,原始时间序列与训练/测试分割:

plt.figure(figsize=(10,4))
plt.plot(time, series, color='magenta', label='原始序列')
print(split_idx+window_size)
plt.axvline(x=split_idx+window_size, color='cyan', linestyle='--', label='训练/测试分界')
plt.title('图1:原始时间序列与训练测试分割')
plt.legend()
plt.show()

结果:

图片

图2,随机森林在测试集上的表现:

plt.figure(figsize=(10,4))
plt.plot(range(len(y_test)), y_test, color='orange', label='真实值 y_test')
plt.plot(range(len(rf_test_pred)), rf_test_pred, color='blue', linestyle='--', label='RF 预测')
plt.title('图2:随机森林在测试集上的表现')
plt.legend()
plt.show()

结果:

图片

图3,RU 训练损失曲线:

plt.figure(figsize=(10,4))
plt.plot(range(1, len(train_losses)+1), train_losses, color='red', marker='o')
plt.title('图3:GRU 训练损失曲线')
plt.xlabel('Epoch')
plt.ylabel('MSE Loss')
plt.show()

结果:

图片

图4,混合模型在测试集上的预测表现:

plt.figure(figsize=(10,4))
plt.plot(range(len(test_truth)), test_truth, color='green', label='真实值')
plt.plot(range(len(test_preds)), test_preds, color='purple', linestyle='--', label='混合模型预测')
plt.title('图4:混合模型在测试集上的预测表现')
plt.legend()
plt.show()

结果:

图片

输出评价指标:

mse = mean_squared_error(test_truth, test_preds)
print(f"混合模型测试集 MSE: {mse:.4f}")

结果:

图片

作者简介:

读研期间发表6篇SCI数据挖掘相关论文,现在某研究院从事数据算法相关科研工作,结合自身科研实践经历不定期分享关于Python、机器学习、深度学习、人工智能系列基础知识与应用案例。致力于只做原创,以最简单的方式理解和学习,关注我一起交流成长。需要数据集和源码的小伙伴可以关注底部公众号添加作者微信。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐