BEAST变点检测与时间序列分解:MATLAB贝叶斯建模实战
2026/9/16 15:41:28 网站建设 项目流程

简介:BEAST是一种用于变点检测与时间序列分解的贝叶斯集成算法,本资源提供其完整MATLAB实现及附带案例数据,适用于信号处理、环境监测、金融数据分析等科研与教学场景,尤其适合计算机、电子信息工程、数学等专业学生用于课程设计、期末大作业或毕业设计。压缩包内共266个文件,大小约6.14MB,主体为C语言核心源码(124个.c与79个.h)及MATLAB脚本(11个.m),并包含Python辅助脚本、预编译MEX文件、示例数据(.mat/.csv)、结果图像(.png)和说明文档(.md/.txt),结构完整,便于在MATLAB 2014/2019a/2021a等版本下直接运行。全部代码采用参数化编程,参数可方便更改,注释清晰,逻辑易懂,适合二次开发和学习算法细节。资源包附赠可直接运行的案例数据与运行结果,遇到问题还可私信作者,目前已有437人学习使用,能有效帮助读者快速上手贝叶斯变点检测与分解技术。

1. 为什么变点检测与时间序列分解会同时出现在BEAST里

BEAST把变点检测和时间序列分解放进了同一个贝叶斯框架:它不只告诉你"第几个时间点发生了突变",还把趋势、季节分量一起解出来,并且给出每个变点位置的后验概率。对于水位、流量、NDVI、传感器温度这类带周期又偶发跳变的序列,BEAST比STL加人工判定省事得多。

它和"选一个最优模型"的思路不同。BEAST在趋势、季节项的组合空间里做贝叶斯模型平均,把可能的分段方式按后验概率加权,而不是押注在单一最优分段上。低信噪比场景下不容易因为一次过拟合把变点定错。适合从数据处理到环境遥感的技术人员,拿到MATLAB源码包后,半小时能跑出第一张带概率的变点图。

2. 贝叶斯数学基础与BEAST的建模思路

2.1 变点的贝叶斯定义:把"何时变化"写成随机变量

传统变点检测的第一步通常是用滑动窗口计算统计量,比如CUSUM和Mann-Whitney,再靠阈值决定"变没变"。BEAST不这么做。时间轴被划分成若干连续段,每段内部趋势和季节项参数共享,段与段之间允许参数跳变,而跳变位置被看成未知随机变量,进入同一个似然函数。换句话说,变点不是"先检验后确认"的结果,而是模型的一部分。

设观测序列 y=[y_1,...,y_n],把分段数量 K 和断点位置 τ=[τ_1,...,τ_{K-1}] 作为参数。给定分段方式以后,每段用一个低阶多项式叠加季节项解释数据,于是整个序列的生成式可以写成 y_t = f_trend(t) + f_seasonal(t) + ε_t,其中 ε_t 通常假设为独立正态误差。BEAST的后验计算目标就是 P(K, τ, θ|y),θ 包含段内回归系数和季节基系数。

落实到MATLAB的beast函数时,你对这段"模型空间"的控制集中在这几个参数上:season决定季节基函数,trend决定段内趋势形态,start决定时间轴起点。需要理解的是,BEAST不是先做STL分解再对残差找变点,而是在一个预测密度里同时处理均值跳变和周期波动。正因为如此,真实变点如果恰好落在季节峰值附近,它不会因为STL残差被季节边缘污染而误判。

2.2 层级先验与分段约束

分段数量K本身不能随意增长,否则每个点都自成一派,模型立刻过拟合。BEAST给K设了层级先验,常见做法是用泊松或几何分布控制期望段数,再通过MCMC采样估计后验。段数越多,先验惩罚越重,数据证据不足的候选段会被压掉。这个特性让BEAST在变点密集和稀疏场景之间不需要手动切换阈值。

在MATLAB包中,常见参数和字段的对应关系如表2-1。

表2-1 BEAST常用参数及先验含义

参数作用常见设置
season季节项的基函数形式harmonic适合平滑周期,dummy适合固定月份效应
trend段内趋势形态linear适合漂移序列,constant适合均值平稳
mcmc.samplesMCMC采样次数日常调试用500,出结果建议至少2000
start时间轴起始值用于把索引映射成日期,不改变检测结果
print是否打印MCMC日志第一次跑打开,批量运行关掉

这里需要注意:mcmc.samples不一定是所有版本都叫这个名字,MATLAB版里可能写作mcmc。samples,R版Rbeast里也有类似设计。拿到源码包后先运行一次帮助命令,比如help beast,核对参数名和默认值,再开始批次实验。

2.3 贝叶斯模型平均与后验加权

最大后验估计选后验概率最大的一组断点,看似合理,但变点检测的不确定性往往不是集中在某一个断点序列上。某段30个点里可能有5个候选变点位置后验概率都接近,MAP只取其中一组,下游置信区间会被严重低估。BEAST输出中每个时间点的突变概率,是对所有包含这个点的分段模型的后验概率求和,这就是贝叶斯模型平均的思想。

这样的边际化能直接生成"每个时刻发生突变的概率曲线"。你不必再回答"到底哪里断了",而是回答"在给定数据、给定先验下,这个位置有多大概率是断点"。这也是BEAST和启发式方法在习惯上的最大不同:它天然输出不确定性,而不是0/1判别。在MATLAB里,这套逻辑体现在out.changepoint.pos和out.changepoint.prob等输出字段上。一个最简单的参数设置脚本长这样:

% 以键值对方式构造BEAST调用 % y是单列数值向量,顺序代表时间顺序 out = beast(y, ... 'season', 'harmonic', ... % 季节项用谐波基 'trend', 'linear', ... % 段内趋势假设为线性 'start', 1, ... % 时间轴从1开始 'print', true); % 打开MCMC进度输出

这段脚本的逻辑是先把模型空间固定到"线性趋势加谐波季节",然后交给MCMC去探索可能的分段方式。season取harmonic的代价是额外估计谐波系数,但好处是季节形态可以连续变化,适合日尺度温度、流量等信号;dummy适用于有明显月份含义的月度数据。trend取linear时,BEAST会在段内拟合斜率和截距,若你确认数据没有趋势,改成constant可以缩小解空间、加快收敛。

3. 用MATLAB跑通BEAST的最小流程与结果解读

3.1 把代码包放进MATLAB搜索路径

假设你手上是以"附MATLAB代码.zip"结尾的源码包。解压后不要只是把文件夹拖到当前工作目录,因为一旦切换目录或清空临时路径,所有函数都会找不到。常见的做法是把解压目录放到一个长期固定的toolbox位置,再用addpath把整个目录树加进MATLAB搜索路径,最后savepath持久化保存。

addpath(genpath('D:/tools/beast')); savepath;

如果addpath成功了但savepath报权限错误,说明MATLAB安装目录不可写;此时可以把包放到userpath目录,或改用pathtool手动添加并保存。较新的MATLAB版本对模糊路径更敏感,解压后先确认根目录下有beast.m,而不是只有一层嵌套的beast/beast.m,否则genpath会把大量无关文件夹也加进路径,出现同名函数遮蔽。

3.2 最小脚本:读CSV、调用beast、画趋势带

接下来用一个可以直接复现的最小脚本。假设CSV文件water_quality.csv只有一列数据,没有表头,顺序即时间顺序。目标是把序列读进来,交给beast做变点检测与分解,然后画出趋势与不确定性带。

% 读入单列数值序列,跳过头行 raw = readmatrix('water_quality.csv'); y = raw(:,1); % 运行BEAST,模型固定为线性趋势加谐波季节 out = beast(y, 'season','harmonic', 'start', 1, 'print', true); % 提取趋势均值与标准差 trend_mean = out.trend.t; trend_sd = out.trend.sd; % 画观测值、趋势线和约95%区间 plot(y, 'Color', [0.7 0.7 0.7]); hold on; plot(trend_mean, 'b', 'LineWidth', 1.5); plot(trend_mean + 1.96*trend_sd, 'k--'); plot(trend_mean - 1.96*trend_sd, 'k--'); legend({'观测','趋势均值','95%区间'} , 'Location', 'best');

代码中有几个参数需要注意:readmatrix是较新MATLAB版本里推荐的读取函数,旧版可以换成csvread;beast的返回体out是结构体,trend字段下挂t和sd,分别存放趋势后验均值和标准差。1.96倍标准差是正态近似下的95%区间,虽然BEAST的后验分布不一定严格正态,但工程上用来观察不确定性级别是足够的。

如果你要处理多列数据,比如多个传感器断面,不要直接动画循环里对每一列调用beast,而是先做一次缺失值检查。BEAST对NaN处理能力有限,常见做法是用线性插值补全后再入模型,否则MCMC采样器会在似然计算阶段报错或输出全零概率。

3.3 看懂out结构里的趋势、季节、变点字段

第一次跑完BEAST,最常见的困惑是:明明返回的是一个out,为什么里面有trend、season、changepoint这么多子结构?因为BEAST把整个生成过程拆成了趋势、季节、突变三个部分分别做后验汇总。你需要关心的关键字段如表3-1。

表3-1 BEAST输出字段与用途

字段含义直接用途
out.trend.t趋势项后验均值提取长期变化曲线
out.trend.sd趋势项后验标准差画不确定性区间
out.season.t季节项后验均值做季节基线或异常扣除
out.season.sd季节项标准差季节强度波动分析
out.changepoint.pos候选变点位置索引输出突变时间点
out.changepoint.prob每个位置的突变概率阈值筛选与报表

在解读之前,先明确一点:out.trend.t的长度和输入y一致,out.changepoint.pos则是稀疏的列表,只记录模型认为具备一定后验质量的断点位置。概率字段prob有时会和pos等长,给出每个变点的概率;有时通道里还包含每个时间点的边际突变概率,具体以你拿到源码包的帮助为准。

如果某次运行完全没有变点,先别怀疑数据,看看是否把season设成了harmonic而数据周期强度过高。BEAST会把一部分突变信号吸收进季节项。更合理的操作是先跑一个不带季节项的版本(season设为none或constant),定位突变,再放开季节项做完整分解。

4. 变点定位与季节性分解的实战操作

4.1 把变点位置和概率导出成表格

拿到out以后,第一件事不是看那张花花绿绿的图,而是把变点位置和概率导出来,方便对照业务时间线。下面这段代码做一个简单过滤:只保留后验概率不低于0.5的变点,并写入CSV。

idx = out.changepoint.pos(:); prob = out.changepoint.prob(:); keep = prob >= 0.5; cpt = idx(keep); cpt_prob = prob(keep); % 组装成表格并写盘 T = table(cpt, cpt_prob, 'VariableNames', {'TimeIndex','Prob'}); writetable(T, 'candidate_changepoints.csv');

这段代码本身不复杂,但有两个工程点:第一,pos和prob可能一个是行向量一个是列向量,统一用(:)转成列向量,避免writetable因为维度不匹配闹脾气;第二,0.5的过滤阈值只适合初步筛选,如果业务对漏报警敏感,就要降到0.3再配合人工复核。

4.2 概率阈值怎么定:从0.5到0.8的工程经验

BEAST的突变概率是后验边际概率,含义是"在所有MCMC保留样本中,该位置被当作断点的比例"。概率1表示100%,0.5表示只有一半样本认为该处断点,非常不稳定。工程上可以分成四个档位使用,如表4-1。

表4-1 突变概率档位建议

概率区间判读建议适用场景
>= 0.8强变点,报告可直接引用水质超标、设备故障定位
0.5~0.8候选变点,需要第二个方法交叉验证流程初筛
0.3~0.5弱信号,结合业务上下文判断缓慢漂移引起的偶发变化
<0.3基本忽略,不进入报表平稳时段

这里要强调,概率不是频率学派的p值。p值假设变点不存在,计算观测到当前数据的概率;BEAST的概率直接给出"存在变点"的后验支持度。两者数值不能互相换算,也不要在报告里写成"显著性0.6"之类的混合表述。

当你发现概率曲线出现连续双峰,即两个相邻时间点概率都超0.5,而中间点概率低,这往往意味着真变点位于两点之间。可以取两个峰值的中间索引作为实际断点,或者用概率做加权平均sum(idx .* prob)/sum(prob)得到亚采样精度的时间估计。

4.3 和PELT、STL搭配使用的时机

BEAST并不是唯一合理的变点检测方法。PELT在分段常数场景下更快,还能保证全局最优;STL擅长趋势-季节分解但不直接产生变点。BEAST的不可替代性在于把三者放在同一个贝叶斯模型里,并且用模型平均输出不确定性。

运算开销方面,BEAST的MCMC使它在几千个点以内体验很好,到了几万点,每次运行可能要以分钟计。常见做法是先对整条序列用PELT或差分阈值跑快速扫描,把候选区间筛出来,再用BEAST对每个区间做精细分解。这样既拿到全序列变点候选,又不必在所有平稳段浪费马尔可夫链。

如果BEAST自己输出的概率图出现"锯齿"形状,比如连续十几个点都有0.2左右的概率,大概率不是真的频繁变点,而是季节谐波数量不够,周期残差被吸收进了断点。此时应优先增加谐波项数量,而不是提高分段先验强度。

5. 调参与验证:让BEAST结果平滑且可复现的技巧

5.1 先放大采样量,再调分段先验

MCMC后验估计的噪声会直接体现在变点概率上。同一个数据集跑两次,如果概率曲线差异很大,先加采样量。常见的做法是把mcmc.samples从默认值提高到2000,再看日志里的接受率。接受率长期低于20%,说明随机游走的提案步长太小,马尔可夫链移动缓慢;高于70%,说明步长太大,后验分布根本没被充分探索。拿到源码包后,先在短序列上调清这两组数值,再上全量数据,能省下大量回头排查时间。

5.2 固定随机种子,让实验对比公平

BEAST的MCMC需要随机数,不同次运行天然有波动。在调用beast前执行rng(2024),可以保证同一份数据、同一组参数下结果完全一致。批量调参时建议给每个实验分配独立种子,并把参数和种子一起记录到日志,这样后续复现时不用猜当初怎么跑的。如果使用Codex这样的工具辅助生成MATLAB脚本,也要注意让它保留rng这一行,否则自动跑批时会引入层层随机差异。

5.3 用趋势加季节与原始数据的残差做自检

把out.trend.t和out.season.t相加得到模型重建序列,再计算resid = y - (trend + season)。残差还应基本平稳:如果里面仍有明显周期波纹,说明季节谐波数量偏少;如果出现连续单调的v形轨迹,说明趋势分段受先验影响太大,需要放宽分段约束或者改用更灵活的段内阶数。检验完残差后,把rng('shuffle')换成显式种子rng(20240401),也是出图时保持可复现的常规操作。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询