最近有个做设备振动数据预测的朋友问我,说想用机器学习做时间序列预测,但一上来就整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:i+window-1)']; Y = [Y; data(i+window)]; end %% 3. 划分训练集和测试集(保持时间顺序,禁止乱序打散) train_ratio = 0.8; split = floor(size(X,1) * train_ratio); Xtrain = X(1:split, :); Ytrain = Y(1:split, :); Xtest = X(split+1:end, :); Ytest = Y(split+1: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(t+1) = Y_pred_diff(t+1) + Y_true(t)
注意,这里要用真实值去加,而不是用预测值去加。如果用预测值累加,误差会逐点累积,一段时间的预测会完全漂移。这个细节我在项目里踩过,第一次做逆差分时用了预测值累加,预测了10个点,误差涨了5倍。后来改为每算一步,都用上一步的真实观测作为基准,才把误差压制住。
2.4 多步预测的策略
上面的代码是单步预测(用5个点预测下1个点)。实际业务里经常要预测未来7天、未来30天,那就得多步预测。多步预测有三种常见策略:
- Recursive策略:预测出第t+1步后,把预测值当真实值拼进输入窗口,继续预测t+2。优点是简单,缺点是误差逐点累积。
- Direct策略:训练7个模型,分别预测t+1、t+2...t+7。缺点是要维护多个模型,优点是每步的误差独立。
- 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天。注意这里我没有用随机划分,因为时间序列预测的核心逻辑是“用已知预测未知”,如果随机抽,模型见过未来的信息,测试指标再漂亮也是自欺欺人。
数值结果如下:
| 指标 | 只做滑动窗口 | 加入日期+节假日特征 |
|---|---|---|
| MAE | 27.5 kW | 17.8 kW |
| RMSE | 35.2 kW | 23.1 kW |
| MAPE | 16.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(window+1:end), ... monthday_feature(window+1:end), ... holiday_feature(window+1:end)]; X_train_feat = [X_train_origin, X_feat(1:split, :)]; X_test_feat = [X_test_origin, X_feat(split+1: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:i+window-1)']; Y = [Y; data(i+window)]; end mdl = fitlm(X, Y); Ypred = predict(mdl, X(end-window+1:end, :)); % 注意:预测时需要最后window个点 end调用方式是:[forecast_val, model] = lr_forecast(load_data, 7, 1)。这种封装方式最大的好处是:后续换数据、调窗口,只需改参数,不用动主程序。
提醒:函数里
predict的输入是X(end-window+1: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,因为它的参数更少,过拟合风险更低。
另一个思路是使用“滚动交叉验证”:不划分一次训练/测试,而是从第window+1个点开始,逐步向前滚动训练,每次只预测下一步,最后把所有测试预测汇总。这种方法的优点是把少量数据用到极致。我写过一个快速实现:
preds = zeros(N-window-train_len, 1); for t = train_len+1 : N-window mdl_tmp = fitlm(X(1:t, :), Y(1:t)); preds(t - train_len) = predict(mdl_tmp, X(t+1, :)); 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正则化,减少window |
| MAPE异常大 | 真实值接近0 | 改SMAPE或直接看RMSE |
| 预测滞后严重 | 窗口太短,模型只学到短期惯性 | 增大window,加入自相关分析 |
| 多步预测漂移 | 递归累积误差 | 改用Direct策略或混合模型 |
| 新数据预测效果暴跌 | 数据分布漂移 | 重训练模型,或加周期性特征 |
这张表我建议打印出来贴在工位上。做时间序列预测,日常遇到的80%问题都逃不出上面这几类,每一条背后都是我实际踩过的坑和对应的有效解法。
最后再分享一段我的真实体会
做了几年时间序列项目,我最深的感受是:模型复杂度永远应该和可用数据量、可解释需求、业务容忍度匹配。线性回归做时间序列预测,精度上不是天花板,但它稳定、透明、迭代快,永远是项目启动阶段应该先做的第一个模型。不少论文里的新方法,本质上也是在LR的框架上叠了很多模块,底层的可解释逻辑并没有变。
如果你正准备用MATLAB实现线性回归时间序列预测,建议从一段没有额外特征的滑动窗口代码跑通,再逐步加入日期特征、差分、正则化。不要一上来就追求把所有技巧叠上去,那只会让你分不清哪个调整起了作用。先复现,再改进,最后封装成函数,这才是能把模型真正用起来的路径。