📢 转载信息
原文链接:https://machinelearningmastery.com/forecasting-the-future-with-tree-based-models-for-time-series/
原文作者:Jason Brownlee
本篇指南探讨了如何使用基于树的模型(如LightGBM和XGBoost)进行时间序列预测。这些模型在处理非线性数据和自动特征交互方面表现出色,是现代时间序列分析的有力工具。
本指南将介绍构建稳健特征工程策略的关键步骤,这些策略对于最大化基于树的预测模型的性能至关重要。
我们将使用LightGBM库(LGBMRegressor)来演示预测过程,因为它通常在预测任务中表现出卓越的速度和准确性。
以下是本指南将涵盖的主要步骤:
- 数据准备:为基于树的模型准备时间序列数据,包括创建滞后特征。
- 特征工程:生成用于时间序列预测的额外特征(如时间特征和滑动窗口统计量)。
- 模型训练:训练一个LightGBM模型。
- 模型评估:使用交叉验证评估模型性能。
本指南假设您已对Python、Pandas和Scikit-learn有基本了解,并已安装了必要的库。
让我们开始吧。
首先,我们需要导入所需的Python库。
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
from lightgbm import LGBMRegressor
import numpy as np
我们将使用一个简单的人造时间序列数据集,以便专注于特征工程和模型构建的流程,而不是数据探索。
1. 创建示例数据集
我们将创建一个包含100个时间步长的人造时间序列数据集,其中包含明显的趋势和一些季节性模式。
# 创建时间序列
time_steps = 100
dates = pd.to_datetime(pd.date_range(start='2023-01-01', periods=time_steps, freq='D'))
# 模拟数据:趋势 + 季节性 + 噪声
trend = np.arange(time_steps) * 0.5
seasonality = 10 * np.sin(np.arange(time_steps) * 2 * np.pi / 7) # 每周季节性
noise = np.random.randn(time_steps) * 2
data = trend + seasonality + noise
df = pd.DataFrame({'Date': dates, 'Value': data})
df = df.set_index('Date')
print(df.head())
输出应如下所示:
Value
Date
2023-01-01 2.036613
2023-01-02 1.977898
2023-01-03 2.279972
2023-01-04 2.129690
2023-01-05 3.095808
2. 特征工程:基于树的模型
与ARIMA等传统模型不同,基于树的模型不直接处理时间序列的结构。相反,它们通过特征工程来学习这些模式。这意味着我们需要手动创建能够捕获时间依赖性的特征。
对于时间序列预测,两个最关键的特征类别是滞后特征和时间特征。
滞后特征 (Lag Features)
滞后特征是先前时间步的值。例如,昨天的值(滞后1)或上周的同一天(滞后7)的值。
我们将创建一个函数来添加指定数量的滞后特征。
def create_lag_features(df, target_col, lags):
df_lags = df.copy()
for lag in lags:
df_lags[f'{target_col}_lag_{lag}'] = df_lags[target_col].shift(lag)
return df_lags
# 定义滞后阶数
lags_to_use = [1, 2, 7, 14]
df_featured = create_lag_features(df, 'Value', lags_to_use)
print(df_featured.head(15))
查看输出时,您会注意到前几行包含NaN值,因为它们没有足够过去的观察值来计算滞后值。
时间特征 (Time Features)
时间特征有助于模型捕捉日历效应(如星期几、月份、季度)。
def create_time_features(df):
df_time = df.copy()
df_time['year'] = df_time.index.year
df_time['month'] = df_time.index.month
df_time['day'] = df_time.index.day
df_time['dayofweek'] = df_time.index.dayofweek
df_time['dayofyear'] = df_time.index.dayofyear
df_time['weekofyear'] = df_time.index.isocalendar().week.astype(int)
return df_time
df_featured = create_time_features(df_featured)
print(df_featured.head())
滑动窗口统计量 (Sliding Window Statistics)
滑动窗口统计量(如均值、标准差)可以提供目标变量的局部趋势和平稳性信息。
def create_rolling_features(df, target_col, windows):
df_rolling = df.copy()
for window in windows:
df_rolling[f'{target_col}_rolling_mean_{window}'] = df_rolling[target_col].shift(1).rolling(window=window).mean()
df_rolling[f'{target_col}_rolling_std_{window}'] = df_rolling[target_col].shift(1).rolling(window=window).std()
return df_rolling
# 定义窗口大小
windows_to_use = [3, 7]
df_featured = create_rolling_features(df_featured, 'Value', windows_to_use)
print(df_featured.head(15))
3. 准备训练和测试集
在时间序列预测中,我们必须严格保持时间顺序。我们不能随机分割数据。
首先,我们需要删除由于滞后和滚动计算而产生的NaN行。
# 删除包含NaN的行(即前几行)
df_clean = df_featured.dropna()
# 定义特征 (X) 和目标 (y)
TARGET = 'Value'
FEATURES = [col for col in df_clean.columns if col != TARGET]
X = df_clean[FEATURES]
y = df_clean[TARGET]
接下来,我们将数据分割为训练集和测试集。我们将使用最后20个时间点作为测试集。
# 确定分割点
SPLIT_POINT = len(df_clean) - 20
X_train = X.iloc[:SPLIT_POINT]
y_train = y.iloc[:SPLIT_POINT]
X_test = X.iloc[SPLIT_POINT:]
y_test = y.iloc[SPLIT_POINT:]
print(f"训练集大小: {len(X_train)}")
print(f"测试集大小: {len(X_test)}")
确保我们只对测试集的目标值进行时间排序检查。
print("\n测试集时间排序:")
print(y_test.index)
LightGBM非常适合处理稀疏数据和大量特征,非常适合这种手工构造的特征集。
4. 训练 LightGBM 模型
我们将使用默认参数训练一个LGBMRegressor实例。在实际应用中,您会使用Grid Search或Optuna进行超参数调优。
# 初始化模型
model = LGBMRegressor(random_state=42, n_estimators=1000, learning_rate=0.05)
# 训练模型
model.fit(X_train, y_train,
eval_set=[(X_test, y_test)],
early_stopping_rounds=50,
verbose=False)
评估模型特征重要性
基于树的模型允许我们轻松检查哪些特征对预测最重要。
feature_importance = pd.Series(model.feature_importances_, index=FEATURES)
feature_importance.sort_values(ascending=False).head(10).plot(kind='barh', title='Top 10 Feature Importance')
从重要性图表中,我们通常会看到滞后特征和滚动特征占据主导地位,这表明它们捕获了时间序列的动态变化。
5. 进行预测和评估
现在,我们使用训练好的模型对测试集进行预测,并计算均方根误差(RMSE)。
# 进行预测
y_pred = model.predict(X_test)
# 计算RMSE
rmse = np.sqrt(mean_squared_error(y_test, y_pred))
print(f"\n测试集 RMSE: {rmse:.2f}")
对于这种人工生成、清晰结构的时间序列,基于树的模型通常能取得相当低的RMSE。
可视化预测结果
为了直观地评估模型性能,最好将预测值与实际值进行比较。
import matplotlib.pyplot as plt
plt.figure(figsize=(14, 7))
plt.plot(y_test.index, y_test, label='Actual Value', marker='o', linestyle='-', color='blue')
plt.plot(y_test.index, y_pred, label='Predicted Value (LGBM)', marker='x', linestyle='--', color='red')
plt.title('Time Series Forecasting with LightGBM')
plt.xlabel('Date')
plt.ylabel('Value')
plt.legend()
plt.grid(True)
plt.show()
从图表中可以清晰地看到,LightGBM很好地捕捉了底层趋势和周期性模式,即使是预测的滞后和滚动特征也得到了有效利用。
6. 关于单步预测的注意事项
上述示例是一个多步预测的简化版本,其中所有特征都可以在时间点t之前计算出来(因为我们使用整个历史数据集进行训练)。
对于真正的、迭代的单步预测,流程会略有不同:
- 使用仅限训练数据来训练模型。
- 在预测下一个时间步t+1时,您必须手动计算所有特征(如滞后1、滚动均值等),使用当前时间点t的已知数据。
- 预测值y_pred(t+1)然后需要被添加到历史数据集中,以便在下一步预测t+2时使用。
在真实场景中,迭代预测的误差会随着每一步的累积而增加。因此,对于长时间范围的预测,通常建议使用多输出回归或特定架构(如RNNs)。然而,对于短期的、滚动预测,基于树的方法配合强大的特征工程是极其高效的。
总结:
基于树的模型通过将时间序列转换为监督学习问题来发挥作用。成功的关键在于创建信息丰富的特征集,特别是滞后和滚动统计量,这些特征集能让模型理解时间依赖性。LightGBM和XGBoost由于其速度和处理非线性的能力,是实现这一目标的首选工具。
🚀 想要体验更好更全面的AI调用?
欢迎使用青云聚合API,约为官网价格的十分之一,支持300+全球最新模型,以及全球各种生图生视频模型,无需翻墙高速稳定,文档丰富,小白也可以简单操作。
评论区