尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

SVM时间序列预测实战:从特征工程到参数调优的完整指南

SVM时间序列预测实战:从特征工程到参数调优的完整指南 简介时间序列预测是数据分析与机器学习中的核心任务旨在基于历史数据预测未来趋势。其核心原理在于挖掘数据中的时序依赖关系、周期性与趋势。支持向量机作为一种经典的监督学习算法凭借其强大的非线性处理能力和良好的泛化性在回归任务中展现出独特的技术价值。通过引入核函数技巧SVM能够将低维非线性问题映射到高维空间进行线性求解尤其适合处理具有复杂模式但数据量适中的序列数据。在应用场景上SVM可用于销量预测、设备故障预警、金融指标分析等多个领域。本文将聚焦于如何将支持向量回归应用于时间序列预测详细解析通过滑动窗口构建特征、利用RBF核函数捕捉非线性关系以及规避数据泄露等关键工程实践为读者提供一个清晰、可复现的解决方案。1. 项目概述当SVM遇上时间序列提起时间序列预测大家脑子里蹦出来的多半是ARIMA、LSTM、Transformer这些“明星选手”。确实它们在处理序列依赖关系上各有千秋。但今天我想聊点不一样的用支持向量机来做时间序列预测。乍一听你可能会觉得有点“跨界”——SVM不是主要搞分类和回归的吗怎么跟带时间戳的数据扯上关系了这正是这个项目的核心价值所在探索一种经典但常被忽视的路径。我最初接触这个想法是在处理一个工业设备故障预警的项目里。数据是典型的传感器时间序列有周期性但噪声大而且存在大量非线性的突变点。试过LSTM效果不稳定对超参数和初始化太敏感用ARIMA对非线性部分又无能为力。后来我想到SVM特别是它的变体——支持向量回归其核心思想是寻找一个最优超平面使得所有样本点到该平面的“距离”最小同时通过核函数技巧它能隐式地将数据映射到高维空间从而处理非线性关系。这不正好可以应对时间序列中的复杂模式吗这个项目的目标很明确提供一个完整的、可复现的Python代码框架教你如何将SVM有效地应用于时间序列预测任务。它特别适合以下场景你的数据量不算特别庞大SVM在大数据集上训练较慢但特征间存在复杂的非线性关系或者你希望有一个解释性相对较好、不容易过拟合的模型作为基线或补充方案。我会从最基础的数据准备、特征工程开始一步步带你构建SVM预测模型并深入剖析其中的关键参数和调优技巧最后分享我在实战中踩过的坑和总结出的经验。无论你是数据分析的新手还是想拓宽建模思路的老手相信都能从中获得启发。2. 核心思路如何让SVM“看懂”时间SVM本身并不理解“时间”这个概念。它只是一个静态的模型输入一个特征向量输出一个预测值。因此用SVM做时间序列预测最核心、也是最关键的一步就是如何将一维的时间序列数据转化为SVM能够处理的二维特征样本。这个过程我们称之为“时间序列的滑动窗口特征构建”或者更通俗地说叫“制造训练样本”。2.1 滑动窗口把时间线切成样本块想象一下你有一串按时间排列的珍珠你的时间序列数据。SVM无法直接消化整条项链。我们的策略是用一个固定长度的“窗口”在这条项链上滑动。每次窗口停下窗口内的几颗珍珠就组成一个样本的特征X而紧接着窗口后面的那颗珍珠就是我们想要预测的目标值y。举个例子假设我们有一个简单的销售额序列[100, 150, 200, 250, 300, 350]。我们设定窗口大小look_back为3。第一个样本X [100, 150, 200], y 250 用前3天的数据预测第4天第二个样本X [150, 200, 250], y 300 窗口滑动一位第三个样本X [200, 250, 300], y 350就这样我们成功地把一个长度为6的时间序列转化成了3个训练样本。窗口大小的选择至关重要它决定了模型能看到多长的历史信息来做出预测。太小了可能信息不足太大了会引入噪声、增加计算量还可能导致过拟合。通常我们需要根据数据的周期性和趋势来判断比如对于具有明显7天周期性的数据窗口大小至少应为7。注意构建完样本后必须将数据集划分为训练集和测试集。这里有个大坑绝对不能使用随机划分因为时间序列具有严格的顺序性。你必须保证测试集的时间点都在训练集之后。通常的做法是按时间顺序划分比如前80%的数据用于训练后20%用于测试。随机打乱会使得模型“窥见”未来的信息导致评估结果完全失真变得毫无意义。2.2 特征工程的扩展不止于滞后值仅仅使用原始序列的滞后值lag values作为特征是最基础的做法。为了让SVM更强大我们往往需要加入更多手工特征这尤其能发挥SVM结合核函数处理非线性关系的优势。统计特征可以计算窗口内数据的均值、标准差、最大值、最小值、斜率趋势等。例如一个窗口内数据的标准差很大可能预示着波动加剧。周期性特征对于有明显周期如小时、星期、月份的数据可以加入周期位置信息。比如将“星期几”转化为独热编码One-hot Encoding让模型学习每周不同日子的模式。交互特征与多项式特征将窗口内的滞后值进行组合如相乘、相除或计算其多项式项如某个滞后值的平方可以帮助模型捕捉更复杂的非线性关系。不过这类特征需谨慎使用最好基于业务理解否则容易导致维度灾难和过拟合。外部特征如果有影响目标序列的外部变量如天气温度影响用电量、促销活动影响销量一定要把它们作为特征加入。SVM可以很好地处理多特征输入。在我的一个电商销量预测项目中除了过去7天的销量滞后值我还加入了“是否周末”、“是否节假日”、“前一周同期的销量”以及“营销费用”作为特征。这个特征组合让SVM模型的预测准确性提升了约15%因为它更好地刻画了销量波动的驱动因素。2.3 为什么选择SVR而不是简单线性模型你可能会问既然都是做回归为什么不直接用线性回归或岭回归关键在于核函数。线性模型只能拟合线性关系而时间序列中的关系往往是非线性的。SVM通过核技巧能够在不显式计算高维坐标的情况下在高维空间中寻找线性分割或回归平面。线性核相当于没有使用核技巧退化为线性模型。适用于特征本身已足够或关系基本线性的情况。径向基函数核这是最常用、也往往最有效的核函数。它可以将样本映射到无限维空间擅长捕捉复杂的非线性模式但对参数特别是gamma非常敏感。多项式核可以显式地控制非线性程度通过degree参数。适用于特征之间存在明确的乘幂关系的情况。对于时间序列这种模式复杂的数据RBF核通常是首选。它赋予了SVM拟合复杂曲线的能力这是普通线性回归所不具备的。当然这份强大也带来了调参的复杂性。3. 实战构建从数据到预测的完整流程理论说得再多不如一行代码。接下来我们用一个模拟的、具有趋势和季节性的时间序列数据来完整走一遍流程。我会使用scikit-learn这个强大的机器学习库。3.1 环境准备与数据生成首先确保你的环境里安装了必要的库numpy,pandas,matplotlib,scikit-learn。如果没有通过pip install numpy pandas matplotlib scikit-learn安装。我们创建一个模拟数据集它包含一个长期的线性增长趋势和一个明显的季节性波动。import numpy as np import pandas as pd from sklearn.preprocessing import StandardScaler from sklearn.svm import SVR from sklearn.model_selection import TimeSeriesSplit, GridSearchCV from sklearn.metrics import mean_squared_error, mean_absolute_error import matplotlib.pyplot as plt # 设置随机种子以保证结果可复现 np.random.seed(42) # 生成时间索引假设是2020年全年的每日数据 dates pd.date_range(start2020-01-01, end2020-12-31, freqD) n_samples len(dates) # 生成合成数据趋势 季节性 噪声 trend np.linspace(10, 50, n_samples) # 线性趋势 seasonality 10 * np.sin(2 * np.pi * np.arange(n_samples) / 365) # 年周期 noise np.random.normal(0, 2, n_samples) # 随机噪声 y trend seasonality noise # 创建DataFrame df pd.DataFrame({date: dates, value: y}) df.set_index(date, inplaceTrue) print(df.head())3.2 特征工程与样本构建这是整个流程的灵魂。我们将实现一个函数不仅能创建滞后特征还能加入时间特征。def create_features(df, target_col, look_back7, forecast_horizon1): 为时间序列创建特征。 df: 包含时间序列的DataFrame target_col: 目标列名 look_back: 使用过去多少期作为特征 forecast_horizon: 预测未来多少期 df_feat df.copy() # 1. 创建滞后特征 (Lag Features) for i in range(1, look_back 1): df_feat[flag_{i}] df_feat[target_col].shift(i) # 2. 创建时间特征 (Temporal Features) df_feat[day_of_week] df_feat.index.dayofweek # 周一0, 周日6 df_feat[month] df_feat.index.month df_feat[day_of_year] df_feat.index.dayofyear # 3. 创建简单的滚动统计特征 (可选) df_feat[rolling_mean_3] df_feat[target_col].shift(1).rolling(window3).mean() df_feat[rolling_std_3] df_feat[target_col].shift(1).rolling(window3).std() # 4. 定义目标列预测未来forecast_horizon期的值 df_feat[target] df_feat[target_col].shift(-forecast_horizon) # 删除因创建滞后和滚动特征产生的NaN行以及因定义目标产生的尾部NaN df_feat.dropna(inplaceTrue) # 分离特征和目标 X df_feat.drop([target, target_col], axis1) # 注意也丢弃原始目标列因为我们用滞后项替代 y df_feat[target] return X, y # 应用函数假设我们想用过去14天的数据预测明天 look_back 14 X, y create_features(df, value, look_backlook_back, forecast_horizon1) print(f特征矩阵形状: {X.shape}) print(f目标向量形状: {y.shape}) print(X.head())3.3 数据标准化与划分SVM对特征的尺度非常敏感特别是使用RBF核时。因此标准化是必须的。同时我们要按时间顺序划分数据集。# 按时间顺序划分训练集和测试集 (例如 80%/20%) split_idx int(len(X) * 0.8) X_train, X_test X.iloc[:split_idx], X.iloc[split_idx:] y_train, y_test y.iloc[:split_idx], y.iloc[split_idx:] print(f训练集大小: {X_train.shape}, 测试集大小: {X_test.shape}) # 数据标准化使用训练集的均值和标准差来转换训练集和测试集 scaler_X StandardScaler() scaler_y StandardScaler() X_train_scaled scaler_X.fit_transform(X_train) X_test_scaled scaler_X.transform(X_test) # 重要使用相同的scaler转换测试集 y_train_scaled scaler_y.fit_transform(y_train.values.reshape(-1, 1)).ravel() # y_test暂时不缩放因为我们最终要评估原始尺度上的误差 # 注意对于SVR我们是在缩放后的y_train上进行训练。3.4 模型训练与超参数调优直接使用默认参数的SVR效果通常不会好。我们需要调优几个关键参数C正则化参数、gammaRBF核的系数、epsilonSVR的容忍度。我们将使用时间序列交叉验证和网格搜索。# 初始化SVR模型 svr SVR(kernelrbf) # 使用RBF核 # 定义参数网格 param_grid { C: [0.1, 1, 10, 100], # 正则化强度。越小模型越简单越可能欠拟合越大越可能过拟合。 gamma: [scale, auto, 0.01, 0.1, 1], # 核系数。scale1/(n_features * X.var())auto1/n_features。值越大模型越复杂。 epsilon: [0.01, 0.1, 0.2] # Epsilon-SVR的容忍度。预测值与真实值之差小于epsilon则不计算损失。 } # 使用时间序列交叉验证 # TimeSeriesSplit能确保在交叉验证中训练集始终在测试集之前 tscv TimeSeriesSplit(n_splits5) # 网格搜索 grid_search GridSearchCV(estimatorsvr, param_gridparam_grid, cvtscv, # 使用时间序列分割 scoringneg_mean_squared_error, # 用负均方误差评估网格搜索会找最大值 verbose1, n_jobs-1) print(开始网格搜索...) grid_search.fit(X_train_scaled, y_train_scaled) print(网格搜索完成) print(f最佳参数: {grid_search.best_params_}) print(f最佳交叉验证分数负MSE: {grid_search.best_score_}) # 获取最佳模型 best_svr grid_search.best_estimator_3.5 预测与结果评估用最佳模型在测试集上进行预测并将结果反标准化回原始尺度进行评估和可视化。# 使用最佳模型进行预测在缩放后的特征上 y_pred_scaled best_svr.predict(X_test_scaled) # 将预测值反标准化回原始尺度 y_pred scaler_y.inverse_transform(y_pred_scaled.reshape(-1, 1)).ravel() y_test_vals y_test.values # 测试集真实值原始尺度 # 计算评估指标 mse mean_squared_error(y_test_vals, y_pred) rmse np.sqrt(mse) mae mean_absolute_error(y_test_vals, y_pred) print(f测试集均方误差 (MSE): {mse:.4f}) print(f测试集均方根误差 (RMSE): {rmse:.4f}) print(f测试集平均绝对误差 (MAE): {mae:.4f}) # 可视化结果 plt.figure(figsize(14, 7)) plt.plot(df.index[-len(y_test):], y_test_vals, label真实值, colorblue, alpha0.7) plt.plot(df.index[-len(y_test):], y_pred, labelSVR预测值, colorred, linestyle--, alpha0.9) plt.title(时间序列预测真实值 vs SVR预测值) plt.xlabel(日期) plt.ylabel(值) plt.legend() plt.grid(True, alpha0.3) plt.tight_layout() plt.show() # 也可以绘制误差分布图 errors y_test_vals - y_pred plt.figure(figsize(10, 5)) plt.hist(errors, bins30, edgecolorblack, alpha0.7) plt.axvline(x0, colorred, linestyle--) plt.title(预测误差分布) plt.xlabel(预测误差 (真实值 - 预测值)) plt.ylabel(频次) plt.grid(True, alpha0.3) plt.show()4. 关键参数深度解析与调优心得SVM/SVR的性能极大程度上依赖于超参数的选择。如果调参不当再好的特征工程也白搭。下面我结合自己的经验深入聊聊这几个参数。4.1 正则化参数 C在“经验风险”与“模型复杂度”间走钢丝C是SVM中最重要的参数之一。你可以把它理解成模型对“犯错”的容忍度。C值很大例如1000模型会尽可能减少在训练集上的错误即经验风险最小化。这会导致决策边界非常复杂试图“穿过”每一个训练样本点附近。后果就是过拟合模型在训练集上表现极好但在未知的测试集上表现糟糕泛化能力差。C值很小例如0.01模型允许犯更多的错误。决策边界会更平滑、更简单。这可能导致欠拟合模型连训练数据中的基本模式都没学好。如何调我的策略是先用一个默认值比如1或一个中等范围如[0.1, 1, 10]进行初步网格搜索。观察模型在验证集上的表现。如果训练集得分远高于验证集得分差距大可能是过拟合需要尝试减小C。如果训练集和验证集得分都很低可能是欠拟合需要尝试增大C。在实践中对于时间序列数据由于存在噪声和不确定性C值不宜设置得过大。我经手的项目中最优C值通常在0.1到10这个区间内很少超过100。4.2 核系数 gamma决定单个样本的影响力半径gamma是RBF核特有的参数它定义了单个训练样本的影响范围。gamma值很大例如10样本的影响范围很小决策边界会变得非常曲折因为只有非常靠近的样本点才会被考虑。这同样容易导致过拟合模型会紧紧抓住每一个训练样本的细节包括噪声。gamma值很小例如0.001样本的影响范围很大决策边界会更平滑甚至趋于线性。这可能导致欠拟合模型无法捕捉数据中的复杂结构。gamma与C的联动gamma和C经常需要联合调优。一个经典的组合是大C 大gamma极度复杂的模型几乎必然过拟合。小C 小gamma非常平滑简单的模型可能欠拟合。通常我们需要寻找一个“中间地带”。scikit-learn提供了scale和auto两个智能选项作为gamma的起点它们会根据特征方差或数量自动计算一个值在很多时候效果不错可以作为网格搜索的基准。实操心得我习惯将gamma和C在网格搜索中设置为指数增长的序列例如C: [0.1, 1, 10, 100],gamma: [0.001, 0.01, 0.1, 1]。这样能高效地探索参数空间。同时务必使用时间序列交叉验证来评估参数组合普通的K折交叉验证会破坏时间结构给出过于乐观的错误估计。4.3 容忍度 epsilon控制回归的“缓冲带”epsilon定义了SVR中的“不敏感损失带”。对于每一个样本如果预测值与真实值的绝对差小于epsilon则这个样本的损失为0。这个参数让SVR专注于拟合那些误差较大的点对小的噪声不敏感从而增强了模型的鲁棒性。epsilon很大模型只关注误差非常大的点对大多数数据点“视而不见”模型会非常平滑但可能忽略重要趋势导致欠拟合。epsilon很小模型会尽力让每一个预测点都接近真实值对噪声也更敏感可能过拟合。对于时间序列预测数据通常包含测量噪声。设置一个合适的epsilon如0.1可以帮助模型忽略这些微小波动专注于学习主要的趋势和周期模式。通常我会将epsilon设置为目标变量标准差的一个小比例例如0.1倍的标准差作为搜索起点。5. 避坑指南与进阶技巧纸上得来终觉浅绝知此事要躬行。下面这些坑都是我或者我的同事实实在在踩过的希望你能绕过去。5.1 数据泄露时间序列建模的头号杀手这是新手最容易犯的致命错误我必须要用最严重的语气强调一遍在特征工程中绝对不能使用未来的信息来预测过去错误示范在计算“过去7天的滚动平均”作为特征时错误地包含了当前时刻的值。使用整个数据集包括测试集的均值和标准差进行标准化。在构建滞后特征前对整个数据集进行了差分或平滑处理。这些操作都会让模型在训练时“偷看”到测试集的信息导致评估指标虚高模型在实际部署中完全失效。正确做法滚动统计特征必须滞后如df[rolling_mean] df[value].shift(1).rolling(7).mean()。shift(1)确保了计算平均值时不会用到当前值。标准化必须基于训练集像我们代码中做的那样用fit_transform处理训练集用transform处理测试集确保尺度一致且无信息泄露。先划分再工程化一种更安全的做法是先按时间顺序划分好训练集和测试集然后只在训练集上进行需要拟合的特征工程如计算某个统计量再将拟合好的转换器应用到测试集上。对于简单的滞后特征可以在整体上做但要时刻保持警惕。5.2 特征重要性评估SVM的“黑箱”难题与树模型不同SVM没有直接提供特征重要性排序。这对于理解哪些时间滞后或特征对预测最关键造成了困难。我们可以通过一些间接方法排列重要性对某个特征的值进行随机打乱观察模型性能如RMSE下降的程度。下降越多说明该特征越重要。scikit-learn的inspection模块提供了permutation_importance函数。基于系数的解释仅限线性核如果使用线性核SVR的权重系数大小和正负可以解释特征的重要性。但对于更常用的RBF核此路不通。消融实验依次移除某个特征重新训练模型观察性能变化。变化越大特征越重要。这是最直接但计算成本较高的方法。在我的经验里对于时间序列最靠前的几个滞后项lag_1, lag_2和强周期性特征如星期几通常最重要。通过排列重要性分析你可以验证这一点并果断剔除那些贡献近乎为零的特征简化模型。5.3 多步预测策略我们的例子是预测下一步one-step ahead forecasting。但现实中我们往往需要预测未来多个时间点。有两种主要策略递归预测用模型预测t1时刻的值然后将这个预测值作为已知特征输入模型去预测t2时刻如此递归进行。这种方法误差会随着预测步长累积放大。直接多输出预测为每一个要预测的未来时刻t1, t2, ..., th训练一个独立的SVR模型。这种方法避免了误差累积但需要训练h个模型且模型之间忽略了预测值之间的相关性。多输出SVR使用支持多输出回归的SVR变体如sklearn.multioutput.MultiOutputRegressor。这是一个更优雅的解决方案可以一次性输出多个预测值。对于短期预测如未来3-5步递归法简单有效。对于长期预测建议使用直接法或多输出法。我曾在一个电力负荷预测项目中需要预测未来24小时每小时的负荷采用了直接法训练了24个模型虽然训练开销大但每个小时的预测精度独立且可控整体效果优于递归法。5.4 当SVM力不从心时SVM不是万能的在以下情况你可能需要考虑其他模型超大规模数据集SVM的训练时间复杂度通常在O(n^2)到O(n^3)之间当样本数超过数万时训练会非常缓慢。此时随机森林、梯度提升树如XGBoost、LightGBM或神经网络可能是更好的选择。超长序列依赖如果预测严重依赖于非常久远的历史信息比如上百个时间点之前SVM即使有大的滑动窗口也可能难以有效捕捉这种长期依赖。LSTM、GRU等循环神经网络或Transformer结构天生为此设计。需要概率预测SVM输出的是确定性的点预测。如果你需要预测的置信区间或概率分布那么贝叶斯方法或分位数回归森林等模型更合适。一个实用的工作流我通常将SVM作为一个强大的基线模型。在项目开始快速用SVM配合合理的特征工程跑出一个结果。这个结果可以作为衡量更复杂模型如LSTM价值的基准。如果LSTM比SVM好不了多少甚至更差那就要反思是数据问题、特征问题还是模型本身不适合而不是盲目追求复杂。6. 完整源码与扩展思考以下是整合了上述所有最佳实践的完整代码框架你可以直接复制使用并根据自己的数据修改特征工程部分。 完整版基于SVM的时间序列预测框架 作者一名从业者 说明包含数据生成、特征工程、时间序列交叉验证、网格搜索调参、评估与可视化。 import numpy as np import pandas as pd import matplotlib.pyplot as plt from sklearn.preprocessing import StandardScaler from sklearn.svm import SVR from sklearn.model_selection import TimeSeriesSplit, GridSearchCV from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score from sklearn.inspection import permutation_importance import warnings warnings.filterwarnings(ignore) # 1. 数据准备 (此处替换为你自己的数据加载) print(步骤1: 数据准备) np.random.seed(42) # 生成模拟数据 n_samples 500 trend np.linspace(0, 20, n_samples) seasonality 5 * np.sin(2 * np.pi * np.arange(n_samples) / 50) noise np.random.normal(0, 1, n_samples) y trend seasonality noise dates pd.date_range(start2023-01-01, periodsn_samples, freqD) df pd.DataFrame({date: dates, value: y}) df.set_index(date, inplaceTrue) # 2. 特征工程函数 def create_advanced_features(df, target_col, look_back14): df_feat df.copy() # 滞后特征 for i in range(1, look_back 1): df_feat[flag_{i}] df_feat[target_col].shift(i) # 时间特征 df_feat[day_of_week] df_feat.index.dayofweek df_feat[month] df_feat.index.month df_feat[day_of_year] df_feat.index.dayofyear # 可以加入更多如是否为季度末、是否为月初等 # 滚动统计特征 (务必滞后!) df_feat[rolling_mean_7] df_feat[target_col].shift(1).rolling(window7, min_periods1).mean() df_feat[rolling_std_7] df_feat[target_col].shift(1).rolling(window7, min_periods1).std() df_feat[rolling_max_7] df_feat[target_col].shift(1).rolling(window7, min_periods1).max() # 目标变量 df_feat[target] df_feat[target_col].shift(-1) # 清理NaN df_feat.dropna(inplaceTrue) X df_feat.drop([target, target_col], axis1) y df_feat[target] return X, y, df_feat look_back 21 # 使用过去21天的信息 X, y, df_feat create_advanced_features(df, value, look_backlook_back) print(f特征矩阵 X 形状: {X.shape}) print(f特征列名: {X.columns.tolist()}) # 3. 数据划分与标准化 split_idx int(len(X) * 0.75) # 75%训练25%测试 X_train, X_test X.iloc[:split_idx], X.iloc[split_idx:] y_train, y_test y.iloc[:split_idx], y.iloc[split_idx:] scaler_X StandardScaler() scaler_y StandardScaler() X_train_scaled scaler_X.fit_transform(X_train) X_test_scaled scaler_X.transform(X_test) y_train_scaled scaler_y.fit_transform(y_train.values.reshape(-1, 1)).ravel() # 4. 模型训练与超参数优化 print(\n步骤4: 模型训练与调优) base_svr SVR(kernelrbf) param_grid { C: [0.5, 1, 5, 10], gamma: [scale, 0.01, 0.05, 0.1], epsilon: [0.01, 0.05, 0.1] } tscv TimeSeriesSplit(n_splits3) # 时间序列交叉验证3折 grid_search GridSearchCV(base_svr, param_grid, cvtscv, scoringneg_mean_squared_error, verbose0, n_jobs-1) grid_search.fit(X_train_scaled, y_train_scaled) best_model grid_search.best_estimator_ print(f最佳参数: {grid_search.best_params_}) print(f最佳CV分数(-MSE): {grid_search.best_score_:.4f}) # 5. 预测与评估 y_pred_scaled best_model.predict(X_test_scaled) y_pred scaler_y.inverse_transform(y_pred_scaled.reshape(-1, 1)).ravel() y_test_vals y_test.values mse mean_squared_error(y_test_vals, y_pred) rmse np.sqrt(mse) mae mean_absolute_error(y_test_vals, y_pred) r2 r2_score(y_test_vals, y_pred) print(f\n测试集性能:) print(f MSE: {mse:.4f}) print(f RMSE: {rmse:.4f}) print(f MAE: {mae:.4f}) print(f R²: {r2:.4f}) # 6. 可视化 plt.figure(figsize(15, 10)) # 子图1: 预测对比 plt.subplot(2, 2, 1) plt.plot(df.index[-len(y_test):], y_test_vals, label真实值, linewidth2) plt.plot(df.index[-len(y_test):], y_pred, labelSVR预测, linestyle--, linewidth1.5) plt.fill_between(df.index[-len(y_test):], y_pred - rmse, y_pred rmse, alpha0.2, colorgray, label±RMSE区间) plt.title(时间序列预测对比) plt.xlabel(日期) plt.ylabel(值) plt.legend() plt.grid(True, alpha0.3) # 子图2: 残差图 plt.subplot(2, 2, 2) residuals y_test_vals - y_pred plt.scatter(y_pred, residuals, alpha0.6) plt.axhline(y0, colorr, linestyle--) plt.xlabel(预测值) plt.ylabel(残差 (真实值 - 预测值)) plt.title(残差图 (理想应随机分布在0线周围)) plt.grid(True, alpha0.3) # 子图3: 误差分布 plt.subplot(2, 2, 3) plt.hist(residuals, bins30, edgecolorblack, alpha0.7, densityTrue) plt.axvline(x0, colorred, linestyle--, linewidth2) plt.xlabel(预测误差) plt.ylabel(密度) plt.title(预测误差分布) plt.grid(True, alpha0.3) # 子图4: 特征重要性 (排列重要性) plt.subplot(2, 2, 4) # 计算排列重要性 (在测试集上) perm_importance permutation_importance(best_model, X_test_scaled, y_test_vals, n_repeats10, random_state42, n_jobs-1) sorted_idx perm_importance.importances_mean.argsort()[::-1] # 降序排列 top_n 10 # 显示前10个重要特征 top_idx sorted_idx[:top_n] plt.barh(range(top_n), perm_importance.importances_mean[top_idx]) plt.yticks(range(top_n), [X.columns[i] for i in top_idx]) plt.xlabel(排列重要性 (测试集)) plt.title(Top 10 特征重要性) plt.tight_layout() plt.show() # 7. 未来多步预测示例 (递归法) print(\n步骤7: 未来多步递归预测示例) def recursive_forecast(model, last_known_features, scaler_X, scaler_y, steps10): 递归预测未来多步 model: 训练好的SVR模型 last_known_features: 最近一个时间点的特征向量 (已缩放) steps: 要预测的未来步数 future_predictions [] current_features last_known_features.copy().reshape(1, -1) for _ in range(steps): # 预测下一步 next_pred_scaled model.predict(current_features)[0] next_pred scaler_y.inverse_transform([[next_pred_scaled]])[0][0] future_predictions.append(next_pred) # 为下一步预测更新特征 (这里需要根据你的特征工程逻辑来更新此处为简化示例) # 在实际中你需要用预测值去更新滞后特征这比较复杂通常需要重新构建特征向量。 # 此处仅作示意说明递归的思想。 # 假设我们只更新 lag_1 特征其他特征如时间特征需要根据未来日期计算 # 这是一个不完整的示例实际应用需要完整的状态更新逻辑。 # current_features[0, 0] next_pred_scaled # 更新 lag_1 # ... 更新其他滞后和滚动特征 break # 简化示例只循环一次 print(警告递归预测的特征更新逻辑需根据具体特征工程完整实现此处仅为流程示意。) return future_predictions # 获取最后一个已知时间点的特征 (测试集最后一个点) last_features_scaled X_test_scaled[-1, :] # future recursive_forecast(best_model, last_features_scaled, scaler_X, scaler_y, steps5) # print(f未来5步预测(示意): {future})这个框架提供了从数据到评估的完整管道并加入了特征重要性分析和多步预测的思考。要应用到你的实际项目最关键的是深入理解你的数据并据此设计有意义的特征。SVM是一个强大的工具但它不会自动从原始数据中提取时间特征这部分“智慧”需要由你来注入。本文还有配套的精品资源点击获取
返回列表