资讯详情

用Logistic回归做时间序列预测的Matlab实战指南

📅 2026/9/14 15:28:59 | 华诺云谱 👁 阅读
用Logistic回归做时间序列预测的Matlab实战指南
把“Logistic回归”用到“时间序列预测”上这组合听起来有点冷门但实际做下来你会发现它是个非常实用的工具。尤其适合那些样本量不大、不需要特别复杂非线性关系、但需要快速给出概率判断的场景。我自己在好几个项目里用过这个思路包括设备故障预警和流量异常检测效果都挺稳。这篇就完整讲一遍模型原理怎么理解、为什么它能做时间序列预测、Matlab里怎么一步步写出来以及实操中那些文档里不会写的坑。1. 这个模型到底是什么线性二项分布回归的通俗拆解1.1 名字里的三层意思先把这个吓人的名字拆开。所谓“线性二项分布回归模型”在统计学里更常见的叫法是“二项逻辑回归Binomial Logistic Regression”在机器学习圈子里就叫“逻辑回归分类器Logistic Regression Classifier”。名字里的三个词分别对应三个核心设计线性模型的预测核心是一个线性组合也就是 \eta \beta_0 \beta_1 x_1 \beta_2 x_2 ... \beta_k x_k。这和线性回归里做的事一样只是这个线性组合的结果不会直接作为预测值而是会经过一次“链接函数”的转换。二项分布模型假设我们要预测的目标变量因变量服从二项分布。最简单的二项分布就是伯努利分布即结果只有两个可能事件发生记为1或不发生记为0。比如明天涨不涨、设备坏不坏、用户买不买。回归虽然最终常常拿来做分类判断但模型本身输出的其实是一个概率值本质上还是回归在实际操作层面这个模型的输出是一个0到1之间的概率然后我们再根据概率去做判断。1.2 关键公式Logit函数与Sigmoid函数逻辑回归的核心转换链路是这样的p P(Y 1 | X) 1 / (1 exp(-(\beta_0 \beta_1 x_1 ... \beta_k x_k)))简单说先把特征做线性加权求和得到一个取值在负无穷到正无穷的分数然后塞进Sigmoid函数里把分数压缩到0到1的区间。这个Sigmoid函数有一个对称性质输入为0时输出0.5输入越大越接近1越小越接近0。反过来看如果把公式变形就能得到Logit函数log(p / (1 - p)) \beta_0 \beta_1 x_1 ... \beta_k x_k这个 p/(1-p) 在统计学里叫“优势比Odds”对它取自然对数得到的结果就叫“对数优势比”。也就是说逻辑回归本质上是在用线性回归去拟合对数优势比所以它才被归入“广义线性模型”。1.3 为什么它能用于时间序列预测这个问题是理解整篇文章的关键。传统时间序列预测方法如移动平均、指数平滑、ARIMA都是在直接预测“下一个数值是多少”。而逻辑回归预测的是“下一个时间点处于某种状态的概率”。举个例子你想预测未来某时刻的股价是涨还是跌或者预测设备在未来24小时内是否会发生故障这些问题的答案都是二元的。你真正关心的是概率以及基于概率的决策。时间序列的历史数据滞后值、移动平均、差分等可以作为特征 X未来的状态涨/跌、故障/正常可以作为标签 Y然后训练逻辑回归。这就是一个“把序列预测问题转化成分类问题”的思路。这种思路有个巨大的优势不要求数据平稳性不要求特征和标签之间是线性关系实际上逻辑回归本身就是非线性映射也不要求误差服从正态分布。对于很多工程场景来说这比传统时序模型更贴近真实业务需求。2. Matlab实现前的准备工具箱选择与核心思路2.1 需要的工具箱与基础函数Matlab里实现逻辑回归有好几条路使用 Statistics and Machine Learning Toolbox 中的 fitglm 函数指定 Distribution 参数为 binomialLink 参数为 logit。这是最直接、最贴近统计建模思路的方法。使用 fitclogistic 或 fitclinear 函数这是机器学习视角的分类器接口。自己写梯度下降或牛顿迭代完全手工实现。我个人最推荐 fitglm原因有三个第一它是广义线性模型的统一接口训练结果返回一个 GeneralizedLinearModel 对象后续 predict、coefCI系数置信区间、devianceTest偏差检验等操作全都在这个对象上完成非常顺手第二它对惩罚项、权重、偏移量等高级参数的支持比分类器接口更完善第三输出结果可以直接给出系数表、p值、AIC等统计量方便判断每个特征是否显著。2.2 时间序列数据结构的转化用逻辑回归做时间序列预测核心操作是构造“特征矩阵 X”和“标签向量 y”。假设原始序列长度为 N我们要用过去 L 个时刻的值预测第 L1 时刻的二分类状态。那么特征矩阵的每一行就是某个时刻往前数 L 个历史值标签就是那个时刻的状态值。这种结构在机器学习里叫“滑动窗口”在时间序列领域叫“滞后特征”。具体来说假设原始序列为 y_1, y_2, ..., y_N设定滞后阶数 L第 t 个训练样本的特征X(t) [y_{t-L}, y_{t-L1}, ..., y_{t-1}]第 t 个训练样本的标签y(t) I(y_t threshold) 或其他二值化规则这里有个特别容易犯的错误标签不能和特征同时使用 t 时刻的信息否则就造成了“数据泄漏”。特征必须严格使用 t 时刻之前的信息标签使用 t 时刻及以后的信息。这是所有基于分类器做预测的模型都必须遵守的铁律。2.3 案例设计预测“未来趋势方向”为了让代码可复现、可理解我设计了这样一个案例生成一条模拟时间序列比如带趋势和周期成分的正弦波加噪声。任务预测下一天相对于今天是上涨还是下跌二分类。特征过去3天的观测值以及它们的差分变化量。数据划分前80%训练后20%测试。这个案例覆盖了从数据生成、特征提取、模型训练、预测评估到可视化呈现的完整流程针对性强又不失一般性。如果你实际应用中有真实数据直接把“数据生成”这一段替换成 load 你自己的数据就行。3. 手把手写代码完整流程实现3.1 模拟时间序列数据先造一份可以用来测试的数据。% 设置随机种子保证可复现 rng(42); % 生成模拟时间序列趋势 周期 噪声 t (1:500); trend 0.01 * t; seasonal 2 * sin(2 * pi * t / 30); noise 0.5 * randn(500, 1); data trend seasonal noise; % 将其二值化与前一天相比上涨记为1下跌记为0 direction [0; double(diff(data) 0)]; figure; subplot(2,1,1); plot(t, data); title(模拟原始时间序列); xlabel(时间); ylabel(数值); subplot(2,1,2); stairs(t, direction); title(方向标签1上涨0下跌); xlabel(时间); ylabel(方向);这段代码里的 direction 就是我们要预测的标签。需要注意第一天的方向没有历史可以用来比较所以置为0。实际数据中也一样构造标签时会出现缺失值通常直接删掉或填0。3.2 构造滞后特征矩阵下面进入核心环节把原始序列变成“特征标签”的机器学习格式。lag 3; % 用过去3天的数据预测明天 n length(data); % 初始化特征矩阵和标签 X zeros(n - lag, lag); y direction(lag 1:end); % 填充特征矩阵每一行是 t-lag 到 t-1 的数据 for i 1:lag X(:, i) data(i:n - lag i - 1); end % 同时构造差分特征变化幅度 diffX diff(data); XDiff zeros(size(X)); for i 1:lag XDiff(:, i) diffX(i:n - lag i - 1); end % 合并原始值特征和差分特征 X [X, XDiff]; % 特征命名 varNames {lag1, lag2, lag3, diff1, diff2, diff3};这一段的原理一定要理解透。假设原始数据是 1, 3, 5, 8, 11lag3那么特征矩阵的第一行是 [1, 3, 5]对应标签是第4个点的方向第二行是 [3, 5, 8]对应标签是第5个点的方向依此类推。每一行都代表着“拿最近的三个历史点看下一个点是涨还是跌”。我强烈建议加上差分特征而不是只用原始值。原因在于逻辑回归拟合的是目标状态的“对数优势比”而优势比的本质是“变化倾向”。差分特征天然表达了变化量和变化率和这种建模目标的契合度更高。你可以在实际数据上对比一下加了差分特征后预测准确率通常会有明显提升。3.3 划分训练集和测试集时间序列数据和普通横截面数据不同不能随机打乱再划分必须严格按时间顺序划分否则会用到“未来信息”导致评估结果虚高。% 按时间顺序划分训练集和测试集 trainRatio 0.8; numTrain floor(trainRatio * size(X, 1)); XTrain X(1:numTrain, :); yTrain y(1:numTrain); XTest X(numTrain 1:end, :); yTest y(numTrain 1:end);这里踩过的坑分享给你刚开始做的时候我图省事用过随机划分的 crossval结果测试集准确率高达95%但是放到滚动预测里一塌糊涂。后来才意识到随机划分把未来数据混进了训练集模型提前“看见了答案”。时间序列必须老老实实按时间切分或者用带时间感知的交叉验证方法。3.4 训练Logistic回归模型核心代码只需一行% 训练逻辑回归模型二项分布 logit链接 mdl fitglm(XTrain, yTrain, ... Distribution, binomial, ... Link, logit, ... VarNames, [varNames, direction]); % 查看模型摘要 disp(mdl);fitglm 默认会给每个特征加一个截距项intercept这个截距在时间序列预测中通常保留因为序列的方向变化往往存在系统性偏差。模型摘要会输出每个系数的估计值、标准误差、t统计量和p值。重点关注 p 值那一列。如果某个滞后特征的 p 值大于0.05说明这个特征在统计意义上不显著可以考虑删掉它重新建模。这一步虽然简单但是能有效降低过拟合风险尤其在特征数量较多的场景下。3.5 预测与评估模型训练好了接下来是预测和评估。逻辑回归的 predict 函数返回的是概率值不是直接的类别。实际业务里概率值往往比类别更有价值。% 在测试集上预测 probTest predict(mdl, XTest); predClass double(probTest 0.5); % 混淆矩阵与准确率 confmat confusionmat(yTest, predClass); accuracy sum(diag(confmat)) / sum(confmat(:)); fprintf(测试集准确率: %.2f%%\n, accuracy * 100); % 计算AUC [~, ~, ~, auc] perfcurve(yTest, probTest, 1); fprintf(AUC: %.3f\n, auc); % 可视化预测概率 figure; plot(t(numTrain lag 1:end), probTest, o-); hold on; plot(t(numTrain lag 1:end), yTest, s-); legend(预测上涨概率, 实际方向); xlabel(时间); ylabel(概率/方向); title(测试集预测概率与真实方向对比);这里有个细节要强调划线时横坐标的索引是从 numTrain lag 1 开始的。为什么因为 yTest 的第一行对应着第 numTrain lag 1 个原始样本点前面构造滞后特征时丢掉了前 lag 个点。索引错位这个错误我见过很多次包括我自己早期也错过。3.6 滚动预测多步未来的方法如果你不只预测下一步而是要预测未来 h 步那需要用滚动预测的方法。这是所有监督学习时序模型都会遇到的场景。h 10; % 预测未来10步 lastFeatures XTest(end, :); futureProbs zeros(h, 1); for step 1:h % 用当前特征预测下一步概率 prob predict(mdl, lastFeatures); futureProbs(step) prob; % 构造下一步的特征 % 将预测的上涨/下跌方向作为新的差分特征的一部分 % 具体构造方式根据特征定义而定 % 这里以最简方式示例将预测概率的类别作为新值 newVal double(prob 0.5); % 更新特征向量需要根据你的特征构造方式调整 lastFeatures updateFeatures(lastFeatures, newVal); end % 绘图展示未来走势概率 figure; plot(1:h, futureProbs, o-); xlabel(未来步数); ylabel(预测上涨概率); title(未来10步上涨概率滚动预测);滚动预测的更新逻辑一定要和你之前构造特征的方式完全一致这是一个极易出错的地方。我的建议是如果你在构造特征时写了一个叫做 buildFeatures(data, t) 的函数那么在滚动预测中也调用同一个函数保证特征口径一致而不是手动去挪向量。4. 模型评价与特征调优的实操技巧4.1 不止看准确率结合业务场景选指标在时间序列分类场景里准确率这个指标经常具有欺骗性。比如序列整体上涨的日子占70%那么就算模型把所有日子都预测为“上涨”准确率也有70%。但这样的模型没有实际价值。所以要根据业务目标选择指标如果你更关心“上涨”是否预测准看灵敏度Sensitivity/Recall。如果你更关心“下跌”是否被误判成“上涨”看特异性Specificity。如果你想综合衡量排序能力看AUC。如果你想评估概率预测的校准度可以用对数损失Log Loss。% 快速计算多个指标 TP confmat(2,2); TN confmat(1,1); FP confmat(1,2); FN confmat(2,1); sensitivity TP / (TP FN); specificity TN / (TN FP); precision TP / (TP FP); f1 2 * precision * sensitivity / (precision sensitivity); fprintf(灵敏度: %.3f\n, sensitivity); fprintf(特异性: %.3f\n, specificity); fprintf(精确率: %.3f\n, precision); fprintf(F1分数: %.3f\n, f1);这些指标在 Matlab 的 Classification Learner App 里也都直接可视化。但脚本方式更适合批量实验和复盘。4.2 特征工程让Logistic回归更“能打”逻辑回归本身是个线性模型它的表达能力上限由特征质量决定。想让它在时间序列预测里有更好表现特征工程可以这么做第一加入“统计特征”而不是只用原始滞后值。比如过去 L 日的均值、标准差、最大值、最小值、斜率通过线性拟合得到。这些统计量能捕捉序列局部形态对方向预测帮助很大。第二加入“日历特征”。如果数据带有明显的周周期性比如工作日/周末差异、节假日效应就把星期几、是否节假日做成哑变量直接放进模型。逻辑回归对哑变量的支持非常自然系数直接反映了某个日期类型相对于参照类别的影响。第三加入“外部特征”。比如预测电价时把天气预报温度放进去预测流量时把促销活动标记放进去。业务时序预测里外部特征往往是提升模型效果的主力。举个例子预测股票指数方向时只靠历史价格很难有稳定收益。但如果加入成交量特征、波动率特征模型会好很多这背后的逻辑是市场动能与波动聚集效应。% 计算过去5日波动率标准差作为额外特征 vol5 movstd(data, 5); vol5(1:4) NaN; % 前4天无完整窗口 % 然后放入特征矩阵注意对齐索引4.3 样本不平衡方向性数据的常见问题时间序列的二分类标签经常是不平衡的。比如故障数据里正常样本可能有几千条故障样本只有几十条。直接用原始样本训练逻辑回归会倾向于把所有样本都预测为多数类。处理办法有三种常用手段调整分类阈值。默认用0.5做阈值但如果多数类是1应该提高阈值如果多数类是0应降低阈值。可以通过查看训练集上的概率分布来确定阈值。给少数类加权。在 fitglm 中可以通过Weights参数给少数类样本更高权重。计算方式少数类样本权重 总样本数 / (2 * 少数类样本数)。重采样。对少数类做SMOTE或者对多数类做下采样。但时间序列数据做重采样要非常小心不能打乱时间顺序。% 计算权重 n1 sum(yTrain 1); n0 sum(yTrain 0); w0 length(yTrain) / (2 * n0); w1 length(yTrain) / (2 * n1); weights zeros(length(yTrain), 1); weights(yTrain 0) w0; weights(yTrain 1) w1; % 带权训练 mdlWeighted fitglm(XTrain, yTrain, ... Distribution, binomial, ... Link, logit, ... Weights, weights);4.4 滞后阶数的选择方法滞后阶数 L 是这类模型的超参数选多少有讲究。太小信息不足太大维度膨胀、噪声累加。一个实用的做法是看自相关函数ACF和偏自相关函数PACF图。如果 PACF 在第 k 阶后截尾后面没有显著的尖峰那 L 大致取 k 就够了。Matlab 里用 autocorr 和 parcorr 两个函数直接出图figure; subplot(2,1,1); autocorr(data); title(自相关图); subplot(2,1,2); parcorr(data); title(偏自相关图);更稳妥的办法在一个候选集合里比如 L 1, 2, 3, 5, 7分别训练模型比较 AIC 或交叉验证准确率选最优。AIC 可以在 mdl.ModelCriterion.AIC 里直接读取这个逻辑在Matlab里大概几行就能搞定。5. 常见问题与排查技巧实录5.1 收敛警告及处理方法运行 fitglm 时如果看到警告Warning: Iteration limit reached.说明模型在迭代中没有收敛。这在时间序列场景里不算罕见尤其是特征之间存在严重的多重共线性比如 lag1 和 lag2 高度相关。某个特征取值区间特别大导致梯度爆炸。训练数据发生“完全分离”现象即某个特征组合下标签完全区分。解决方法是先检查特征相关性删掉冗余特征再对特征做标准化注意是数值稳定性意义上的标准化不改变逻辑回归的可解释性必要时提高迭代上限或者换用更稳健的优化算法。% 检查特征相关性 corrMatrix corr(XTrain); figure; imagesc(corrMatrix); colorbar; title(特征相关矩阵);5.2 预测概率全在0.5附近如果你发现 predict 输出的一堆概率都接近0.5缺乏区分度这通常说明特征和标签之间的关联很弱。这种情况我认为比“过拟合”更难办因为过拟合还能通过正则化挽救而“欠拟合”意味着当前特征体系不足以解释标签的变化。对策是回到特征工程环节。检查是不是差分方向定义得不好是不是滞后阶数选得太短是不是缺少关键外部因子。另外可以审视一下标签的定义。以涨跌方向为例如果收盘价波动很小甚至收平那这种“微小涨跌”和“显著涨跌”是两种完全不同的状态可以考虑加一个过滤条件只有当涨跌幅超过一定阈值时才标记为1否则剔除或标记为0。这样标签的噪声会小很多模型也更容易学到规律。5.3 训练集和测试集表现差距过大这是过拟合的典型症状。时间序列数据有自相关性如果滞后特征过多、训练轮数过久模型就容易记住训练集中的局部震荡模式。我的建议是不要一上来就追求高准确率先做baseline对比。最简单的baseline是“用昨天的方向预测今天”如果逻辑回归比这个benchmark只高一点点说明模型学到的信号还不够强需要继续做特征工程而非调参。另一个有效手段是添加正则化。Matlab的 fitglm 默认不带惩罚项。如果要加L2正则化可以用 fitclinear 并设置正则化强度或者用 fitglm 配合 Weights 等参数手工模拟。不过要提醒一句正则化强度太大也会把有效信号压没要结合交叉验证来调。5.4 系数符号和直觉不符有时候模型训练完你发现某个滞后特征的系数是负的可是直觉上这个特征应该是正向影响。不要急着改数据。先检查这个特征和标签之间的单变量关系有可能该特征与其他特征存在交互作用使得在多元模型中的符号反转。这种“辛普森悖论”在时序特征里经常出现。另一个更简单的可能性滞后阶数选得不对。比如 lag1 的差分项系数为负但 lag3 的差分项系数为正说明序列在短期有均值回归特性涨多了就跌但中期有动量效应趋势延续。这其实是符合很多金融时间序列的经验规律的。5.5 关于Matlab版本的兼容性fitglm 在 R2013b 及以后版本都可用R2018a 之后对广义线性模型对象的可视化方法更完善。如果你的版本比较旧建议至少升级到 R2016a 以上。目前 R2023b、R2024a 都在正常迭代官方文档对 fitglm 的说明也很细致直接在命令行输入 doc fitglm 就能看到完整解释。新版本里还有 fitglm 的增强版本 fitglm(...,CategoricalVars,...) 等参数处理分类变量非常方便。软件的获取建议通过学校或公司的正版授权渠道不要用来路不明的安装包。6. 方法对比与适用场景6.1 与ARIMA、LSTM的对比看到这里你可能会想既然有ARIMA、有LSTM这么“高级”的模型为什么还要用逻辑回归我的看法是没有免费的午餐但也没有必要顿顿吃大餐。ARIMA 的优势在于对单变量线性时间序列的精确建模但它要求数据平稳性、需要识别阶数、对非线性关系无能为力LSTM 擅长捕捉长期依赖和非线性关系但它需要大量数据、调参复杂、可解释性差动不动就要GPU。而逻辑回归的定位正好在两者中间它对数据量要求低数百个样本就能训练、计算速度极快、结果可以直接用系数解释、对特征与标签之间的非线性关系有天然适应能力因为sigmoid本身就是非线性函数。如果数据只有几十到几百个点特征维度不过几十维我强烈建议先用逻辑回归做个baseline再决定是否有必要上更复杂的模型。很多场景下这个baseline已经足够好甚至好于调参不到位的神经网络模型。6.2 适合与不适合的预测场景适合逻辑回归做时间序列预测的场景包括预测二分类状态涨/跌、故障/正常、入侵/正常。预测某个连续值是否超过阈值的概率。需要概率输出而非点预测的决策场景。特征维度中等少于100维样本量不是特别大。业务上需要解释“为什么预测为1”系数就是理由。不适合的场景包括必须预测精确数值比如明天销售额具体是多少这与逻辑回归的目标输出形式不匹配。存在复杂的长期依赖关系比如自然语言文本序列LSTM等循环网络更适合。数据量极大且模式高度非线性这时集成学习或深度模型往往更好。如果你拿不准自己的场景适不适合我的建议是先画一条预测概率曲线跟实际标签对比目测一下。如果概率曲线很少有超过0.7或者低于0.3的值说明模型区分度不足可能需要换方案。6.3 与其他分类器的实际对比经验我在一次设备故障预测项目中对比过逻辑回归、随机森林和XGBoost三种方法。样本量大约2000条时序特征类别不平衡比约1:10。逻辑回归训练时间不到1秒AUC约0.82。随机森林训练时间约5秒AUC约0.84。XGBoost训练时间约30秒未精细调参AUC约0.85。从AUC角度看复杂的模型确实有微弱优势但时间成本和调试成本高了不少。而在资源有限、需要频繁重训练的产线场景里逻辑回归的稳定性和可解释性反而成了最大优势。某个特征的系数是正还是负直接影响运维人员对预警规则的理解和信任度这一点是黑盒模型难以替代的。7. 个人经验与进阶扩展方向代码跑通只是第一步。实际项目里我现在会建议你在基础流程上至少做两件事第一把阈值选择这步做得更精细一些。默认0.5只是最小化分类错误的阈值但不一定是最符合业务利益的阈值。你可以把 predict 输出的概率和实际标签放进 cost matrix用最小化业务成本的方式选阈值。比如故障漏报的代价是停机损失而误报的代价只是人工检查那阈值就应该调低宁可多报也不能漏报。Matlab 里可以用 perfcurve 输出不同阈值下的灵敏度和特异性然后用业务权重加权计算最优阈值。第二考虑模型更新策略。时间序列的分布特征会随时间变化一个今年表现很好的模型明年可能就失效了。建议不要训练完就不管而是设计一个滚动重训练的机制比如每30天用最近90天的数据重新训练一次。Matlab 里可以用 datastore 管理增量数据脚本定时跑一遍即可。最后说一下扩展方向。如果你是做金融方向的可以在逻辑回归基础上叠加一个收益预测模型用连续回归去预测涨跌幅度然后用分类模型判断方向两者结合就是个简单的“方向幅度”双层预测框架。如果你是做工业设备健康管理的可以把逻辑回归作为初筛器筛出高风险的样本后再用随机森林或深度学习进行精细判断这样兼顾了效率和精度。曾有一次我为了做这个方向的研究把整个逻辑回归的迭代过程自己手写了一遍收获非常明显手动实现了梯度下降法之后你才会真正理解 loss 函数的梯度、学习率选择、特征缩放的意义也才能在 fitglm 报错时一眼看出问题出在哪个环节而不是对着报错信息发呆。如果你想把这条路走实强烈建议你手动实现一次代码不复杂但理解深度会完全不一样。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。