时间序列分析实战:从ARIMA到LSTM的完整建模流程
1. 从“预测明天”到“理解过去”时间序列分析为何如此重要在数据科学和数学建模的广阔天地里时间序列分析是一个既基础又充满魅力的领域。它处理的不是一堆静态的、彼此独立的数据点而是那些按时间顺序排列的观测值——比如过去十年的每日气温、过去五年的月度销售额、过去一年的每分钟股票价格甚至是过去一周每小时的服务器负载。这些数据天然地带有“时间戳”而时间戳之间往往存在着某种依赖关系比如今天的股价会影响明天的上个月的销量会影响这个月的。这种依赖关系正是时间序列分析所要捕捉和利用的核心。很多人初次接触时间序列会立刻联想到“预测未来”。没错预测是它最闪亮的应用之一。无论是电商平台预测下个季度的商品需求以优化库存还是能源公司预测未来几天的电力负荷以调度发电背后都离不开时间序列模型。但它的价值远不止于此。更深入地看时间序列分析是“理解过去”的利器。通过分析历史数据的波动模式、周期性、趋势性我们可以回答一系列关键问题我们的业务是否存在明显的季节性规律最近的增长是趋势性的还是偶然的数据中是否存在某种我们尚未察觉的周期性循环这些洞见对于诊断业务问题、评估策略效果、甚至发现潜在的系统性风险如经济周期、设备故障周期至关重要。因此无论你是数据分析师、量化研究员、运维工程师还是对数据驱动决策感兴趣的任何角色掌握时间序列分析的基本思想和工具都意味着你多了一把打开“时序数据”宝库的钥匙。它让你不仅能描述“发生了什么”更能尝试解释“为什么会发生”并基于此对“将要发生什么”做出有依据的推断。接下来的内容我将以一个从业者的视角结合常见的实战场景为你拆解时间序列分析的核心流程、关键模型和那些“教科书上不会写”的实操细节。2. 时间序列分析的基石核心概念与数据预处理在动手建模之前我们必须打好地基。这个地基就是对时间序列数据特性的深刻理解以及如何将原始数据“清洗”成适合分析的格式。跳过这一步后续的模型很可能建立在流沙之上。2.1 理解时间序列的“三要素”趋势、季节性与残差任何一个时间序列数据理论上都可以被分解为三个主要成分的组合趋势指数据在长期内呈现的持续向上或向下的运动。它反映了事物发展的基本方向。例如一家处于成长期的科技公司其用户数的月活数据很可能呈现一个向上的长期趋势。季节性指数据在固定时间间隔如一年、一月、一周、一天内出现的规律性、重复性的波动。例如冰淇淋的销量在夏季会周期性升高在冬季会周期性降低电商网站的流量在周末和工作日会呈现不同的模式。残差有时也称为“不规则波动”或“噪声”。这是剔除趋势和季节性成分后剩下的、无法用规律解释的部分。它可能由随机事件、测量误差或未被模型捕捉的短期因素引起。一个理想的模型其残差应该是类似于白噪声的随机序列。这种分解思想非常重要。在建模前我们可以通过可视化或统计方法如移动平均法、STL分解初步判断序列是否包含明显的趋势和季节性这直接决定了后续模型的选择。例如一个没有明显趋势和季节性的平稳序列可能适合简单的自回归模型而一个同时包含强劲趋势和复杂季节性的序列则需要更强大的模型如SARIMA或Prophet来处理。2.2 平稳性时间序列建模的“入场券”平稳性是时间序列分析中一个核心且必须理解的概念。简单来说一个平稳的时间序列其统计特性如均值、方差不随时间变化。这意味着序列没有趋势其波动幅度大致恒定。注意这里说的“平稳”通常指“弱平稳”或“宽平稳”即均值、方差恒定且任意两个时间点之间的协方差只与它们的时间间隔有关而与具体的时间点无关。这是大多数经典时间序列模型如ARIMA的基本假设。为什么平稳性如此重要因为大多数经典时间序列模型如AR、MA、ARIMA都建立在数据平稳的假设之上。如果直接将一个非平稳序列例如有明显上升趋势的股票价格喂给这些模型模型会错误地将趋势当作可预测的模式来学习导致预测结果严重失真甚至得出毫无意义的结论。检验平稳性的常用方法有看图法直接绘制时序图。如果序列围绕一个常数均值波动且波动范围大致恒定则初步判断为平稳。统计检验最常用的是单位根检验如ADF检验。其原假设是“序列存在单位根即非平稳”。如果检验得到的p值小于显著性水平如0.05我们就有足够的证据拒绝原假设认为序列是平稳的。如果序列不平稳怎么办常见的处理方法是差分。一阶差分就是用当前时刻的值减去前一时刻的值Y_t X_t - X_{t-1}。差分可以有效地消除趋势和部分季节性。对于有季节性趋势的数据还可以进行季节性差分如月度数据做12步差分。通常经过一阶或二阶差分后序列就能变得平稳。2.3 数据预处理实战以销售数据为例假设我们有一份某零售商品过去三年的日度销售额数据sales_data.csv。在进行分析前我们需要完成以下预处理步骤import pandas as pd import numpy as np import matplotlib.pyplot as plt from statsmodels.tsa.stattools import adfuller from statsmodels.graphics.tsaplots import plot_acf, plot_pacf # 1. 加载与检查数据 df pd.read_csv(sales_data.csv, parse_dates[date], index_coldate) print(df.head()) print(df.info()) # 检查缺失值 print(f缺失值数量: {df[sales].isnull().sum()}) # 处理缺失值对于时间序列常用前后插值或线性插值 df[sales] df[sales].interpolate(methodlinear) # 2. 初步可视化观察三要素 plt.figure(figsize(14, 6)) plt.plot(df.index, df[sales], labelDaily Sales) plt.title(Raw Time Series Data) plt.xlabel(Date) plt.ylabel(Sales) plt.legend() plt.grid(True) plt.show() # 3. 平稳性检验 (ADF Test) result adfuller(df[sales]) print(ADF Statistic: %f % result[0]) print(p-value: %f % result[1]) print(Critical Values:) for key, value in result[4].items(): print(\t%s: %.3f % (key, value)) # 如果p-value 0.05则认为序列非平稳需要差分 if result[1] 0.05: print(序列非平稳进行一阶差分。) df[sales_diff] df[sales].diff().dropna() # 再次检验差分后序列的平稳性 result_diff adfuller(df[sales_diff].dropna()) print(差分后序列 p-value: %f % result_diff[1]) else: print(序列平稳可以直接用于建模。)这段代码完成了数据加载、缺失值处理、初步可视化和平稳性检验。如果发现序列非平稳我们创建了一个一阶差分序列sales_diff。这里的一个关键经验是差分虽然能平稳序列但也会损失一部分原始信息并且使序列的解释性变差我们不再预测销售额本身而是预测销售额的变化量。因此在模型预测后通常需要进行差分的逆操作积分来还原到原始尺度。这步操作在后续建模中必须牢记。3. 经典模型ARIMA原理、定阶与实战拟合当我们的时间序列数据是平稳的或者通过差分变得平稳后ARIMA模型家族就成为了一个强大而经典的选择。理解ARIMA是进入时间序列建模殿堂的关键一步。3.1 ARIMA模型的三位一体AR, I, MAARIMA模型实际上是三个部分的组合AR自回归模型。它用历史值来预测当前值。模型阶数p表示用过去p个时间点的值来回归当前值。公式可以简化为Y_t c φ1*Y_{t-1} φ2*Y_{t-2} ... φp*Y_{t-p} ε_t。φ是自回归系数ε_t是误差。I差分。为了使序列平稳而进行的差分操作阶数为d。d1表示进行了一阶差分。MA移动平均模型。它用历史预测误差来预测当前值。模型阶数q表示考虑过去q个时间点的预测误差。公式简化为Y_t c ε_t θ1*ε_{t-1} θ2*ε_{t-2} ... θq*ε_{t-q}。θ是移动平均系数。所以一个ARIMA(p, d, q)模型就是先对原始序列做d阶差分使其平稳然后对一个结合了p阶自回归和q阶移动平均的模型进行拟合。3.2 如何确定p, d, qACF与PACF图解读确定ARIMA模型的三个超参数(p, d, q)是建模的核心难点通常依赖自相关函数图和偏自相关函数图。自相关函数图展示时间序列与其自身滞后版本之间的相关性。它同时包含了直接和间接的相关性。偏自相关函数图在控制了中间滞后项的影响后展示时间序列与其某个滞后版本之间的直接相关性。如何看图定阶经验法则确定d通过ADF检验确定使序列平稳所需的差分阶数。通常d为0, 1或2。确定q观察平稳序列的ACF图。如果ACF图在滞后q阶后突然截尾即之后的值都落在置信区间内像被砍断一样而在此前有显著超出置信区间的尖峰那么q可以初步定为该截尾处。ACF的截尾阶数通常提示MA的阶数。确定p观察平稳序列的PACF图。如果PACF图在滞后p阶后突然截尾那么p可以初步定为该截尾处。PACF的截尾阶数通常提示AR的阶数。# 绘制平稳序列或差分后序列的ACF和PACF图 fig, axes plt.subplots(1, 2, figsize(14, 4)) plot_acf(df[sales_diff].dropna(), lags40, axaxes[0]) # 假设我们使用差分后的序列 plot_pacf(df[sales_diff].dropna(), lags40, axaxes[1], methodywm) plt.show()实操心得看图定阶是一门“艺术”对于混合模型AR和MA特征都不明显或季节性数据ACF/PACF图可能会很复杂难以清晰判断。这时更可靠的方法是网格搜索配合信息准则。我们遍历一组可能的(p, d, q)组合为每个组合拟合一个ARIMA模型然后计算AIC或BIC信息准则。AIC/BIC值越小说明模型在拟合优度和复杂度之间取得了更好的平衡。我们选择AIC最小的那组参数。3.3 模型拟合、诊断与预测确定了(p, d, q)后我们就可以拟合模型并进行预测了。from statsmodels.tsa.arima.model import ARIMA from sklearn.metrics import mean_absolute_error, mean_squared_error import warnings warnings.filterwarnings(ignore) # 忽略一些模型警告 # 假设通过看图或网格搜索我们确定了 (p2, d1, q1) model ARIMA(df[sales], order(2, 1, 1)) # 注意这里传入的是原始序列d1已在模型内部处理 model_fit model.fit() print(model_fit.summary()) # 模型诊断检查残差 residuals model_fit.resid fig, axes plt.subplots(2, 2, figsize(12, 8)) axes[0, 0].plot(residuals) axes[0, 0].set_title(Residuals over Time) axes[0, 0].axhline(y0, colorr, linestyle--) plot_acf(residuals, lags40, axaxes[0, 1]) axes[1, 0].hist(residuals, bins30, edgecolorblack) axes[1, 0].set_title(Histogram of Residuals) # Q-Q图检查是否近似正态分布 from scipy import stats stats.probplot(residuals.dropna(), distnorm, plotaxes[1, 1]) axes[1, 1].set_title(Q-Q Plot) plt.tight_layout() plt.show() # 残差检验理想情况下残差应该是白噪声无自相关 from statsmodels.stats.diagnostic import acorr_ljungbox lb_test acorr_ljungbox(residuals.dropna(), lags[10], return_dfTrue) print(fLjung-Box检验 p-value: {lb_test[lb_pvalue].values[0]}) # 如果p-value 0.05不能拒绝残差是白噪声的原假设说明模型拟合较好。 # 进行样本外预测例如预测未来7天 forecast_steps 7 forecast model_fit.forecast(stepsforecast_steps) forecast_index pd.date_range(startdf.index[-1] pd.Timedelta(days1), periodsforecast_steps, freqD) forecast_series pd.Series(forecast, indexforecast_index) # 可视化预测结果 plt.figure(figsize(12, 6)) plt.plot(df.index[-60:], df[sales][-60:], labelHistorical Data) plt.plot(forecast_series.index, forecast_series.values, labelForecast, colorred, markero) plt.fill_between(forecast_series.index, forecast_series - 1.96*np.std(residuals), # 近似95%置信区间 forecast_series 1.96*np.std(residuals), colorred, alpha0.2, label95% Confidence Interval) plt.title(ARIMA Model Forecast) plt.xlabel(Date) plt.ylabel(Sales) plt.legend() plt.grid(True) plt.show()关键经验模型拟合后的诊断至关重要。我们需要确保残差近似为白噪声无自相关、均值为0、方差恒定。如果残差的ACF图还有显著的自相关或者Ljung-Box检验的p值很小说明模型没有完全捕捉数据中的模式可能需要调整(p, d, q)或考虑更复杂的模型如SARIMA。此外永远要给出预测的置信区间这比一个孤立的预测值更有信息量它反映了预测的不确定性。4. 应对复杂模式季节性模型SARIMA与 Prophet现实世界的数据很少是简单的非季节性ARIMA能完美处理的。月度数据有年季节性周度数据有周季节性日度数据可能有“小时”季节性。这时我们需要引入季节性成分。4.1 SARIMA为ARIMA加上季节的翅膀SARIMA模型全称季节性差分自回归移动平均模型记为SARIMA(p, d, q)(P, D, Q, s)。它在ARIMA的基础上增加了一组季节性的参数(P, D, Q, s)。s季节周期。月度数据s12季度数据s4周度数据s7如果以周为频率日度数据若以天为周期则s1若以周为周期则s7。P, D, Q分别对应季节性部分的AR阶数、差分阶数、MA阶数含义与非季节性参数p, d, q类似。SARIMA模型可以同时捕捉非季节性的短期依赖和季节性的长期循环模式。建模流程与ARIMA类似但参数搜索空间更大多了4个参数通常更依赖网格搜索和AIC准则。from statsmodels.tsa.statespace.sarimax import SARIMAX import itertools # 定义参数搜索范围范围不宜过大否则计算量爆炸 p d q range(0, 3) # 非季节性参数 P D Q range(0, 2) # 季节性参数 s 12 # 假设是月度数据季节周期为12 pdq list(itertools.product(p, d, q)) seasonal_pdq list(itertools.product(P, D, Q, [s])) best_aic float(inf) best_order None best_seasonal_order None warnings.filterwarnings(ignore) # 网格搜索这是一个计算密集型操作实际中应对数据切片或使用更智能的搜索 for param in pdq: for param_seasonal in seasonal_pdq: try: mod SARIMAX(df[sales], orderparam, seasonal_orderparam_seasonal, enforce_stationarityFalse, enforce_invertibilityFalse) results mod.fit(dispFalse) if results.aic best_aic: best_aic results.aic best_order param best_seasonal_order param_seasonal except: continue print(fBest SARIMA{best_order}x{best_seasonal_order} - AIC:{best_aic}) # 用最优参数拟合最终模型 final_model SARIMAX(df[sales], orderbest_order, seasonal_orderbest_seasonal_order, enforce_stationarityFalse, enforce_invertibilityFalse) final_model_fit final_model.fit(dispFalse) print(final_model_fit.summary())4.2 Facebook Prophet面向业务分析师的“黑盒”利器对于不熟悉统计细节或需要快速处理具有强季节性、节假日效应以及存在缺失值和异常值的数据的分析师来说Facebook开源的Prophet是一个极佳的选择。它将时间序列分解为趋势、季节性和节假日三个主要成分并使用加性模型进行拟合。其最大优点是全自动、对缺失值和异常值稳健并且内置了对节假日效应的建模。from prophet import Prophet # Prophet要求数据框有两列ds (日期) 和 y (数值) df_prophet df.reset_index()[[date, sales]].rename(columns{date: ds, sales: y}) # 创建并拟合模型 model_prophet Prophet( yearly_seasonalityTrue, # 默认开启年季节性 weekly_seasonalityTrue, # 默认开启周季节性如果数据频率天 daily_seasonalityFalse, # 日度数据可考虑开启 seasonality_modeadditive # 或 multiplicative取决于季节性幅度是否随趋势变化 ) # 如果业务有特殊节假日可以在这里添加 # holidays pd.DataFrame({ # holiday: special_sale, # ds: pd.to_datetime([2023-11-11, 2023-12-12]), # lower_window: -2, # 节前2天 # upper_window: 1, # 节后1天 # }) # model_prophet.add_country_holidays(country_nameCN) # 或添加特定国家节假日 # model_prophet.fit(df_prophet) model_prophet.fit(df_prophet) # 构建未来时间框架并预测 future model_prophet.make_future_dataframe(periods30, freqD) # 预测未来30天 forecast_prophet model_prophet.predict(future) # 可视化预测结果 fig1 model_prophet.plot(forecast_prophet) fig2 model_prophet.plot_components(forecast_prophet) # 分解趋势、季节性和节假日成分Prophet的优缺点优点易用性极高自动处理季节性和节假日对缺失值和异常值不敏感预测结果带有不确定性区间可视化非常友好。缺点相对于传统统计模型它是一个“黑盒”可解释性较弱模型内部的参数调整空间相对较小对于某些特定模式如长周期、复杂自相关可能不如精心调参的SARIMA。它更适合作为快速基线模型或业务探索工具。选择建议如果你的数据季节性明显且包含节假日追求快速出结果和易解释性通过成分分解图Prophet是首选。如果你需要对模型有极强的控制力进行严格的统计推断或者数据模式更偏向于短期的自相关那么SARIMA/ARIMA可能更合适。5. 模型评估、对比与生产化考量拟合了多个模型后我们如何判断哪个更好模型上线前还需要考虑什么这是从“实验”走向“应用”的关键一步。5.1 模型评估不止于训练集误差评估时间序列模型绝不能只看它在训练集上的拟合误差如MSE、MAE因为很容易过拟合。更可靠的评估方法是时间序列交叉验证。一种经典的方法是滚动预测假设我们有T个时间点的数据。用前t个数据点训练模型t远小于T。预测未来h步h是预测步长。计算这h步预测值与真实值的误差。将训练窗口向后移动一步或h步重复步骤1-3直到用完所有数据。将所有滚动窗口产生的预测误差汇总如计算平均MAE、平均RMSE作为模型的泛化性能指标。from sklearn.metrics import mean_absolute_error, mean_squared_error def rolling_forecast_evaluation(data, train_size, forecast_horizon, order): 简单的滚动预测评估函数 data: 时间序列数据 (Pandas Series) train_size: 初始训练集大小 forecast_horizon: 每次预测的步长 order: ARIMA模型的 (p,d,q) 参数 total_steps len(data) - train_size forecasts [] actuals [] for i in range(0, total_steps, forecast_horizon): train data[:train_size i] # 为了演示这里简化处理实际中每次循环都应重新拟合模型 # 注意对于大数据集频繁拟合ARIMA可能很慢 model ARIMA(train, orderorder) model_fit model.fit() # 预测未来 forecast_horizon 步 fc model_fit.forecast(stepsmin(forecast_horizon, len(data) - len(train))) forecasts.extend(fc) actuals.extend(data[len(train): len(train) len(fc)].values) # 计算评估指标 mae mean_absolute_error(actuals, forecasts) rmse np.sqrt(mean_squared_error(actuals, forecasts)) mape np.mean(np.abs((np.array(actuals) - np.array(forecasts)) / np.array(actuals))) * 100 return mae, rmse, mape, forecasts, actuals # 示例使用最后20%的数据作为测试期进行滚动评估假设数据足够长 train_len int(len(df) * 0.8) mae_arima, rmse_arima, mape_arima, fc_arima, act_arima rolling_forecast_evaluation( df[sales], train_len, forecast_horizon7, order(2,1,1) ) print(fARIMA模型滚动评估 - MAE: {mae_arima:.2f}, RMSE: {rmse_arima:.2f}, MAPE: {mape_arima:.2f}%)对于Prophet可以使用其内置的cross_validation和performance_metrics函数进行类似的交叉验证。5.2 模型对比与选择拿到多个模型的评估指标后如何选择首要看泛化误差在滚动交叉验证中MAE、RMSE、MAPE更低的模型通常泛化能力更好。MAPE平均绝对百分比误差因其无量纲特性便于在不同量级的序列间比较。考虑复杂性与稳定性在误差相近的情况下选择更简单的模型参数更少。简单模型往往更稳健过拟合风险小也更容易解释和部署。检查残差确保最终选定模型的残差近似白噪声。这是模型“充分性”的统计检验。业务可解释性有时一个误差略高但能清晰解释趋势、季节性和节假日效应的模型如Prophet比一个误差略低但像个黑盒的复杂模型如某些深度学习模型更受业务方欢迎。5.3 生产化部署的注意事项当模型准备投入生产环境用于定期自动预测时有几个坑需要提前避开模型重训练频率数据模式会随时间漂移。是每天、每周还是每月重新训练一次模型这需要权衡计算成本和模型性能衰减速度。可以设置一个监控指标如最近一次预测的误差当误差持续超过阈值时触发重训练。预测结果的监控与报警上线后不能放任不管。需要监控预测值是否出现极端异常如远超历史范围以及预测值与实际值的偏差是否持续过大。建立报警机制。特征工程与外部变量在实际业务中纯时间序列模型可能不够。例如预测销售额时促销活动、天气、竞争对手动态等都是重要因素。考虑使用带外生变量的ARIMA或Prophet的附加回归量功能将这些因素纳入模型。代码与环境的封装生产环境的Python版本、库版本必须与开发环境一致。最好将模型训练和预测逻辑封装成独立的服务或脚本并做好日志记录和异常处理。6. 超越传统机器学习与深度学习方法初探当传统统计方法遇到非常复杂、高维或非线性的时间序列模式时机器学习和深度学习方法提供了新的可能性。它们不依赖于严格的平稳性假设并能自动学习特征。6.1 特征工程将时间序列转化为监督学习问题要使用机器学习模型如随机森林、XGBoost首先需要将时间序列数据转化为标准的特征-目标格式。核心思想是使用滞后特征。def create_lag_features(data, series_column, max_lag, forecast_horizon1): 为时间序列创建滞后特征 data: 包含时间序列的DataFrame series_column: 时间序列列名 max_lag: 创建的最大滞后阶数 forecast_horizon: 预测未来多少步 (thorizon) df data.copy() # 创建滞后特征 for lag in range(1, max_lag 1): df[flag_{lag}] df[series_column].shift(lag) # 创建目标变量 (未来第horizon步的值) df[target] df[series_column].shift(-forecast_horizon) # 删除因shift产生的NaN行 df.dropna(inplaceTrue) return df # 示例创建过去7天的滞后特征预测明天 df_ml create_lag_features(df[[sales]], sales, max_lag7, forecast_horizon1) print(df_ml.head()) # 划分特征X和目标y X df_ml.drop(target, axis1) y df_ml[target] # 可以进一步添加其他特征如星期几、是否节假日、月份等 df_ml[day_of_week] df_ml.index.dayofweek df_ml[is_weekend] df_ml[day_of_week].apply(lambda x: 1 if x 5 else 0) df_ml[month] df_ml.index.month6.2 使用树模型进行预测有了特征后就可以使用任何回归模型进行预测了。这里以LightGBM为例它处理表格数据效率很高。import lightgbm as lgb from sklearn.model_selection import TimeSeriesSplit from sklearn.metrics import mean_absolute_error # 使用时间序列交叉验证划分不能随机打乱 tscv TimeSeriesSplit(n_splits5) maes [] for train_index, test_index in tscv.split(X): X_train, X_test X.iloc[train_index], X.iloc[test_index] y_train, y_test y.iloc[train_index], y.iloc[test_index] model_lgb lgb.LGBMRegressor(n_estimators100, random_state42) model_lgb.fit(X_train, y_train) y_pred model_lgb.predict(X_test) mae mean_absolute_error(y_test, y_pred) maes.append(mae) print(fFold MAE: {mae:.2f}) print(f平均MAE: {np.mean(maes):.2f}) # 特征重要性分析 feature_importance pd.DataFrame({ feature: X.columns, importance: model_lgb.feature_importances_ }).sort_values(importance, ascendingFalse) print(feature_importance)机器学习方法的优缺点优点能方便地融入外部特征对数据分布假设少能捕捉复杂非线性关系许多模型如树模型自带特征重要性分析。缺点需要精心设计特征尤其是滞后阶数的选择对纯粹的长期时间依赖捕捉可能不如ARIMA类模型直接模型解释性相对统计模型稍弱。6.3 深度学习方法RNN与LSTM对于序列数据循环神经网络天然适配。LSTM作为RNN的变体通过门控机制缓解了长期依赖问题在时间序列预测中应用广泛。import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import DataLoader, TensorDataset from sklearn.preprocessing import MinMaxScaler # 数据准备与标准化 scaler MinMaxScaler() scaled_data scaler.fit_transform(df[[sales]].values) # 创建序列样本函数 def create_sequences(data, seq_length, pred_length1): xs, ys [], [] for i in range(len(data) - seq_length - pred_length 1): x data[i:(i seq_length)] y data[i seq_length: i seq_length pred_length] xs.append(x) ys.append(y) return np.array(xs), np.array(ys) seq_length 30 # 用过去30天预测 pred_length 7 # 预测未来7天 X_seq, y_seq create_sequences(scaled_data, seq_length, pred_length) # 划分训练集和测试集按时间顺序 split_idx int(0.8 * len(X_seq)) X_train, X_test X_seq[:split_idx], X_seq[split_idx:] y_train, y_test y_seq[:split_idx], y_seq[split_idx:] # 转换为PyTorch张量 X_train_t torch.FloatTensor(X_train).squeeze(-1) # (样本数, 序列长度) y_train_t torch.FloatTensor(y_train).squeeze(-1) # (样本数, 预测长度) X_test_t torch.FloatTensor(X_test).squeeze(-1) y_test_t torch.FloatTensor(y_test).squeeze(-1) # 定义LSTM模型 class SalesLSTM(nn.Module): def __init__(self, input_size1, hidden_size50, num_layers2, output_size7): super().__init__() self.lstm nn.LSTM(input_size, hidden_size, num_layers, batch_firstTrue, dropout0.2) self.linear nn.Linear(hidden_size, output_size) def forward(self, x): # x shape: (batch, seq_len, input_size) lstm_out, _ self.lstm(x) # 只取最后一个时间步的输出 last_time_step lstm_out[:, -1, :] predictions self.linear(last_time_step) return predictions model SalesLSTM(input_size1, hidden_size50, num_layers2, output_sizepred_length) criterion nn.MSELoss() optimizer optim.Adam(model.parameters(), lr0.001) # 训练模型简化版未包含验证集和早停 num_epochs 100 train_dataset TensorDataset(X_train_t.unsqueeze(-1), y_train_t) # 增加一个特征维度 train_loader DataLoader(train_dataset, batch_size32, shuffleTrue) # 时间序列通常不shuffle这里仅为示例 for epoch in range(num_epochs): model.train() total_loss 0 for batch_x, batch_y in train_loader: optimizer.zero_grad() outputs model(batch_x) loss criterion(outputs, batch_y) loss.backward() optimizer.step() total_loss loss.item() if (epoch1) % 20 0: print(fEpoch [{epoch1}/{num_epochs}], Loss: {total_loss/len(train_loader):.6f}) # 评估模型 model.eval() with torch.no_grad(): test_predictions model(X_test_t.unsqueeze(-1)) test_predictions scaler.inverse_transform(test_predictions.numpy()) y_test_actual scaler.inverse_transform(y_test_t.numpy()) test_mae mean_absolute_error(y_test_actual, test_predictions) print(fLSTM在测试集上的MAE: {test_mae:.2f})深度学习方法的优缺点优点理论上能自动学习最有效的特征表示对极其复杂的非线性模式捕捉能力强适合处理海量高维时间序列数据。缺点需要大量的数据训练成本高模型是黑盒难以解释超参数多且调优复杂对序列长度、缺失值等比较敏感。选择策略在实际项目中我通常会建立一个从简到繁的模型流水线进行实验先尝试Prophet作为快速基线再用SARIMA进行严格的统计建模和诊断接着用LightGBM/XGBoost尝试融入更多特征最后如果数据量充足且问题足够复杂才会考虑LSTM等深度学习模型。最终的选择永远是业务效果、可解释性、维护成本和计算资源的平衡。