线性回归用于时间序列预测:MATLAB实现与特征工程详解
发布时间:2026/9/28 13:28:13来源:尧图网络
最近有个做设备振动数据预测的朋友问我说想用机器学习做时间序列预测但一上来就整LSTM、Transformer这些数据量和算力都不够问我有没有简单点的起步方案。我第一个推荐的就是线性回归Linear Regression简称LR——别看它老用来做时间序列预测只要特征构造得当效果稳、速度快、可解释性强尤其适合给后续上复杂模型做一个基准线。这篇文章就完整拆解一下为什么LR能预测时间序列、特征怎么构造、MATLAB代码怎么写、有哪些坑要避开以及几个我在实测中踩过、也帮别人排过的典型问题。先给我的个人结论线性回归做时间序列预测本质是“把过去的时间点变成特征把未来的时间点变成标签然后学习两者之间的线性关系”。它不是万能的但在短期预测、趋势平稳型数据、异常检测场景下性价比极高。1. 内容整体设计与思路拆解1.1 为什么是线性回归而不是一上来就上LSTM很多初学者有个误区以为时间序列预测就必须用循环神经网络RNN、LSTM或者Transformer。实际上2020年前的大量工业场景预测任务用的还是线性模型和统计模型原因很现实数据量小LSTM需要大量序列数据才能收敛稳定你手里可能就几百个点硬上深度学习基本就是过拟合。算力有限普通办公电脑跑LSTM虽然也能跑但调参周期长试错成本高。可解释性要求高在很多生产场景里领导或者业务方会问“为什么预测值是这么多”线性回归的系数可以直接解释成“每个历史时刻的影响权重”这是黑盒模型给不了的。基准线价值做任何预测项目第一步都是建立一个简单可复现的基线模型。LR做出来的精度就是后续所有复杂模型的“及格线”。如果LSTM连LR都打不过那它在这个问题上就不值得上线。我用过几个月LSTM处理轴承温度预测效果确实比LR好一点但也就好5%左右耗时却是LR的几十倍。后来我把LR模型换成带滞后特征的线性回归配合简单调参性能差距缩小到了2%以内。这就是我说的“性价比”。1.2 滑动窗口把时间序列“翻译”成回归问题时间序列本身是一堆按时间排列的点x1, x2, x3 ... xn。线性回归不会“看”时间它只处理“特征-标签”的映射。所以第一步就是做一个转换叫滑动窗口Sliding Window法。假设我们有100天的销售量数据想用前3天预测第4天那么样本1特征[x1, x2, x3]标签x4样本2特征[x2, x3, x4]标签x5样本3特征[x3, x4, x5]标签x6...样本97特征[x97, x98, x99]标签x100这样100个时间点就能构造出97个训练样本。窗口大小就是“用几天预测下一天”。这个值没有固定标准我一般先用自相关函数ACF图看哪个滞后阶数的相关性最高然后定为窗口长度。实操中窗口取3~10比较常见取太大容易引入噪声取太小会丢失长期趋势。1.3 除了历史值还要不要加其他特征这里有个关键点LR对特征非常敏感。只喂滞后值是最基础的方案但如果你想进一步提升效果可以考虑加入以下特征时间索引特征比如第几个采样点、星期几、月份、是否为节假日。线性回归能学到一个线性趋势时间索引可以帮助捕捉“整体上升/下降”的趋势。差分序列如果原序列不平稳做一个一阶差分x_t - x_{t-1}用差分后的序列做预测再逆变换回来效果通常比直接预测原值更稳。滚动统计特征过去N天的均值、标准差、最大值、最小值。这些特征能帮模型捕捉波动性在设备监测类数据里特别有用。外部变量比如温度、湿度、促销活动等。LR对这种“外生变量”的支持非常直接这比LSTM处理外生变量要简单很多。我在一次用电负荷预测里只加了“星期几”和“是否工作日”两个特征MAPE平均绝对百分比误差就下降了将近三成。所以永远不要闷头只喂原始数据特征工程的价值在LR身上体现得最淋漓尽致。2. 核心细节解析与实操要点2.1 数据预处理先把“脏东西”清掉时间序列数据最常见的三个脏问题缺失值、异常值、量纲不统一。缺失值的处理我推荐用前向填充forward fill也就是用上一个有效值补上。不要用全局均值填充因为时间序列有顺序性拿未来的均值填过去会泄漏未来信息导致模型表现虚高。如果你用插值法比如线性插值或样条插值也可以但要控制插值窗口避免过长。异常值要分两种情况真异常传感器故障、录入错误和数据本身的真实波动。前者可以剔除或用中位数替换后者千万别动否则等于人为改写了规律。我见过一个案例有人把“双十一”当异常值剔掉了结果模型把大促的冲量全部归为噪声预测彻底失真。量纲问题主要是针对多特征场景。如果特征里有“温度”这种几十量级的也有“销量”这种几千量级的LR的损失函数会被大数值特征主导梯度更新的方向基本被它带偏。标准化手段很简单用z-score减去均值除以标准差把每个特征拉到同一量级这才是后续结果可复现的前提。2.2 MATLAB的代码结构与核心函数先给出一份标准可跑的MATLAB代码。这套代码的设计思路是加载数据 - 构造滑动窗口样本 - 划分训练集测试集 - 训练回归模型 - 预测 - 评估指标。% LR时间序列预测 基础示例 % 适用数据格式data为列向量每行一个时间点的观测值 %% 1. 加载数据示例生成一个带趋势和噪声的序列 data sin(0.1*(1:200)) 0.01*(1:200) 0.2*randn(200,1); N length(data); window 5; % 滑动窗口大小用前5个点预测第6个点 %% 2. 构造特征矩阵X和标签向量Y X []; Y []; for i 1:N-window X [X; data(i:iwindow-1)]; Y [Y; data(iwindow)]; end %% 3. 划分训练集和测试集保持时间顺序禁止乱序打散 train_ratio 0.8; split floor(size(X,1) * train_ratio); Xtrain X(1:split, :); Ytrain Y(1:split, :); Xtest X(split1:end, :); Ytest Y(split1:end, :); %% 4. 线性回归训练fitlm自带统计工具箱没有的话用regress替代 mdl fitlm(Xtrain, Ytrain); %% 5. 测试集预测与误差评估 Ypred predict(mdl, Xtest); MAE mean(abs(Ypred - Ytest)); RMSE sqrt(mean((Ypred - Ytest).^2)); MAPE mean(abs((Ytest - Ypred)./Ytest)) * 100; fprintf(MAE: %.4f\nRMSE: %.4f\nMAPE: %.2f%%\n, MAE, RMSE, MAPE); %% 6. 可视化 figure; plot(Ytest, b-); hold on; plot(Ypred, r--); legend(真实值, 预测值); title(LR时间序列预测结果);这里要注意几个关键点train_ratio切分时不能随机抽样必须按时间顺序前80%训练、后20%测试。一旦打乱就会用到未来数据训练测试结果毫无意义。fitlm是统计工具箱的函数如果没有统计工具箱可以用regress函数用法类似只是返回参数结构不同。预测结果默认是连续数值如果你的业务场景是分类比如上涨/下跌需要在输出后加阈值转换LR回归本身输出的是连续值。2.3 逆差分预测值如何还原成原始尺度前面提到可以用差分序列建模如果用了差分预测出来的值就是一阶差分值也就是“下一时刻相对当前时刻的变化量”。还原公式很简单Y_pred_original(t1) Y_pred_diff(t1) Y_true(t)注意这里要用真实值去加而不是用预测值去加。如果用预测值累加误差会逐点累积一段时间的预测会完全漂移。这个细节我在项目里踩过第一次做逆差分时用了预测值累加预测了10个点误差涨了5倍。后来改为每算一步都用上一步的真实观测作为基准才把误差压制住。2.4 多步预测的策略上面的代码是单步预测用5个点预测下1个点。实际业务里经常要预测未来7天、未来30天那就得多步预测。多步预测有三种常见策略Recursive策略预测出第t1步后把预测值当真实值拼进输入窗口继续预测t2。优点是简单缺点是误差逐点累积。Direct策略训练7个模型分别预测t1、t2...t7。缺点是要维护多个模型优点是每步的误差独立。Direct-Recursive混合前几步用Direct后几步用Recursive。我常用这种前3步用Direct保证短期精度后面用Recursive降低成本。如果你的场景预测时域不跨太长我更推荐Direct策略尤其是训练数据充足的情况下。误差独立性在多步预测里的价值非常大。3. 实操过程与核心环节实现3.1 一次用电负荷预测的完整复盘去年有一个小项目要我预测某楼宇的每日用电峰值数据大约360个点。我先看了原始序列的ACF图前7个滞后项的相关系数都很高说明用前7天预测第8天是合理的于是窗口定7。特征方面我加了三个额外特征星期几编码为1~7、当月第几天、节假日标志0/1。用电数据里有明显的周周期性不加日期特征的时候误差一直在16%左右浮动加了之后MAPE直接降到9.3%。训练集我取前300天测试集取后60天。注意这里我没有用随机划分因为时间序列预测的核心逻辑是“用已知预测未知”如果随机抽模型见过未来的信息测试指标再漂亮也是自欺欺人。数值结果如下指标只做滑动窗口加入日期节假日特征MAE27.5 kW17.8 kWRMSE35.2 kW23.1 kWMAPE16.3%9.3%这个表很能说明问题LR模型的天花板不在算法本身而在特征构造。同样的模型特征改好了误差能掉四十个百分点。3.2 MATLAB中自定义特征如何嵌入上面这张表的特征如果不用excel预加工直接在MATLAB里做也可以。时间特征可以这样生成% data是原始用电序列dates是对应的日期向量datetime类型 weekday_feature weekday(dates); % 得到1-7的值 monthday_feature day(dates); % 当月第几天 holiday_feature zeros(size(dates); % 需要手动标记节假日1为节假日 %% 合并成特征矩阵 % 注意特征要和滑动窗口的样本对齐取每行样本对应日期的特征 X_feat [weekday_feature(window1:end), ... monthday_feature(window1:end), ... holiday_feature(window1:end)]; X_train_feat [X_train_origin, X_feat(1:split, :)]; X_test_feat [X_test_origin, X_feat(split1:end, :)];这里有一点要特别提醒额外特征的对齐位置必须是“预测目标日期”的特征不是“窗口第1天”的特征。很多人在这里弄错把输入窗口起始日的星期几当成标签日的星期几喂给模型误差虽然也能跑出来但逻辑上是不对的实际部署时一换数据就露馅。3.3 模型评估的指标选择与结果解读线性回归做时间序列预测常用指标就是MAE、RMSE、MAPE三个。它们各有偏向MAE没有平方项不容易被个别极端值放大适合稳定观测场景。RMSE因为平方的存在对离群点非常敏感如果预测里出现一两个极端偏差RMSE会急剧上升这正好帮你发现模型在某些片段完全失效。MAPE是百分比指标适合和业务方沟通。但注意如果真实值里有接近0的数值MAPE会被除零导致的异常大值污染。遇到这种数据我一般改用SMAPE或者直接放弃MAPE。还有一点实战心得好模型不只是误差低还要误差稳定。我习惯看误差序列的滚动均值图——如果误差在后半段急剧升高说明模型在测试集上存在衰减多半是数据快超出训练分布了。这时你就算调参也没用需要用模型更新策略或者重训练策略来适应新分布。3.4 把模型部署成函数易复用的小技巧如果你要用这个模型预测多组数据每次都把训练脚本复制一遍很蠢。建议把核心逻辑封装成函数输入一行数据就输出预测结果。function [Ypred, mdl] lr_forecast(data, window, horizon) % 简化版单步预测为主horizon未实现递归 X []; Y []; N length(data); for i 1:N-window X [X; data(i:iwindow-1)]; Y [Y; data(iwindow)]; end mdl fitlm(X, Y); Ypred predict(mdl, X(end-window1:end, :)); % 注意预测时需要最后window个点 end调用方式是[forecast_val, model] lr_forecast(load_data, 7, 1)。这种封装方式最大的好处是后续换数据、调窗口只需改参数不用动主程序。提醒函数里predict的输入是X(end-window1:end, :)这表示用最后window个观测样本作为特征。所以长度一定是window不是window-1或者别的。4. 常见问题与排查技巧实录4.1 为什么我的预测曲线是一条直线这个问题我回答过不下十次。现象是预测的后半段曲线平得像用尺子画的几乎没有跟随真实波动。出现这种情况通常有两个原因输入特征只有窗口内的滞后值而窗口内的这些值在测试阶段已经用预测值填充预测值一旦稳定后续的输入也是接近稳定的输出自然趋近直线。特征中时间索引权重太大模型学到的主要是“趋势”忽略了局部波动。解决办法是检查特征构成看回归系数。如果时间索引的系数远大于滞后值系数说明趋势项主导了预测可以尝试去掉时间索引或者把时间索引排序后做离散化分桶。4.2 训练集误差很低测试集误差炸裂典型的过拟合。线性回归虽然模型简单但特征多了以后一样会过拟合尤其是特征之间存在多重共线性时。比如你的窗口是10历史10个点的数据通常高度自相关特征之间的相关性很强。排查方法看训练集和测试集误差差距如果测试是训练的3倍以上基本就是过拟合。解法有三个加正则化用ridge回归或者lasso在MATLAB里对应ridge和lasso函数比fitlm多一个正则化参数。降窗口尺寸把window从10降到5减少特征维度。去掉高相关特征先用corrcoef计算特征相关系数把相关系数超过0.9的特征删掉一个。代码层面用lasso只需要把fitlm替换掉其他流程不变。[B, FitInfo] lasso(Xtrain, Ytrain, CV, 10); idx FitInfo.Index1SE; % 最精简的模型 Ypred Xtest * B(:,idx) FitInfo.Intercept(idx);4.3 真实值里有跃变模型完全跟不上这类场景常见于“促销日”“服务器故障日”“突发流量日”。模型学的历史正常规律遇到异常跳变自然跟不上。我处理这类问题的思路是分层建模先把数据按“正常日”和“事件日”分组分别训练两个LR模型。预测时先用一个轻量分类器判断明天是不是事件日再走对应的预测模型。虽然维护成本高了一点但精度提升是压倒性的。我在广告点击量预测里分层后RMSE下降了22%。如果不想维护两个模型也可以在特征里加“事件标识符”把事件日当特征喂进去让模型自己学权重。不过线性模型对这种0-1特征的处理比较粗糙如果事件类型很多还是分层更干净。4.4 数据量太少怎么办如果只有30~50个点训练LR会很不稳定。一个相对靠谱的做法是使用简单移动平均SMA作为基准然后看LR是否显著优于它。如果差异不大就用SMA因为它的参数更少过拟合风险更低。另一个思路是使用“滚动交叉验证”不划分一次训练/测试而是从第window1个点开始逐步向前滚动训练每次只预测下一步最后把所有测试预测汇总。这种方法的优点是把少量数据用到极致。我写过一个快速实现preds zeros(N-window-train_len, 1); for t train_len1 : N-window mdl_tmp fitlm(X(1:t, :), Y(1:t)); preds(t - train_len) predict(mdl_tmp, X(t1, :)); end对比一次性划分滚动交叉验证更接近真实业务里“每天用新数据重训练模型”的节奏误差也更真实不容易乐观。4.5 MATLAB运行报错fitlm输入维度不一致新手最常见的报错就是特征矩阵和标签向量的行数对不上。检查三处构造X和Y时的for循环边界i到N-window说明X有N-window行Y也有N-window行如果多了一行少了一行基本就是边界写错。额外特征合并的时候X_feat行数必须等于X行数别把原始数据的行数当成特征矩阵的行数。predict的输入必须是至少一个样本的特征向量列数必须和训练时X的列数完全一致。4.6 常见问题速查表为了方便以后查阅我整理了一张速查表现象原因快速解决预测为直线特征趋势项权重过大或有预测值递归污染去掉时间索引改用差分序列训练好测试差特征维度过高或共线性用lasso正则化减少windowMAPE异常大真实值接近0改SMAPE或直接看RMSE预测滞后严重窗口太短模型只学到短期惯性增大window加入自相关分析多步预测漂移递归累积误差改用Direct策略或混合模型新数据预测效果暴跌数据分布漂移重训练模型或加周期性特征这张表我建议打印出来贴在工位上。做时间序列预测日常遇到的80%问题都逃不出上面这几类每一条背后都是我实际踩过的坑和对应的有效解法。最后再分享一段我的真实体会做了几年时间序列项目我最深的感受是模型复杂度永远应该和可用数据量、可解释需求、业务容忍度匹配。线性回归做时间序列预测精度上不是天花板但它稳定、透明、迭代快永远是项目启动阶段应该先做的第一个模型。不少论文里的新方法本质上也是在LR的框架上叠了很多模块底层的可解释逻辑并没有变。如果你正准备用MATLAB实现线性回归时间序列预测建议从一段没有额外特征的滑动窗口代码跑通再逐步加入日期特征、差分、正则化。不要一上来就追求把所有技巧叠上去那只会让你分不清哪个调整起了作用。先复现再改进最后封装成函数这才是能把模型真正用起来的路径。
网站建设高端定制企业官网