Beamformer 溯源实战:空域滤波器
给大脑里每个位置装一台"定向麦克风":只听这里,压制别处。 Beamformer 输出的虚拟电极,也是模块 6 连接分析最好的起点。
🎯 本节学习目标
- 用"定向麦克风"类比解释 beamformer:对每个体素构造"只听这里"的空域滤波器
- 理解 LCMV 权重的直觉:约束该源无衰减通过 + 最小化输出总功率
- 记住 MNE 与 beamformer 在相关源、深度偏置、时频适用性上的关键差异
- 独立完成 FieldTrip LCMV 实战:协方差估计 →
ft_sourceanalysis→ 虚拟电极 - 会用虚拟电极衔接模块 6 的连接分析,理解"先源后连"的价值
- 识别三大常见坑:协方差病态、参考依赖、相关源的信号抵消
一、Beamformer 直觉:给每个体素装一台"定向麦克风"
还是用"听声"类比:会议室里有几十个人同时说话,你手里有几十个话筒(电极),每个话筒录到的都是所有人的混音。 定向麦克风的思路是:把所有话筒的信号按不同权重加起来,让某个方向的声音原样通过、其他方向被抵消。
Beamformer(空域滤波器)对大脑做同样的事:对每个候选体素 v,从数据里学出一组权重 w_v, 使得 w_v 加权后的传感器信号"只反映体素 v 的活动"。这组权重与传感器信号的乘积, 就是该体素的虚拟电极(virtual electrode)——仿佛你把一根电极直接插进了那个位置。
空域滤波器 = 一组跨电极的加权系数(一个通道数维度的向量),作用在头皮数据上得到单一位置的活动估计。每个体素各有一套权重,所以全脑扫描要算几千套滤波器;算好后,任何数据(试次、连续段)都能"过一遍"这些滤波器得到虚拟电极。
关键点在于:权重完全从数据协方差矩阵里学来——哪些电极组合是噪声、哪些组合像信号, 不是人为规定,而是数据自己教给算法的。这也是 beamformer 家族名称的由来: 用波束(beam)的指向性"扫描"全脑。
二、LCMV:协方差驱动的空域滤波器
LCMV(Linearly Constrained Minimum Variance,线性约束最小方差)是最常用的 beamformer 变体。它的滤波器要同时满足两条:
- 约束:该位置的单位源必须"原样通过"——滤波器不能扭曲目标位置的信号幅度;
- 目标:输出总功率最小——在满足约束的前提下,把"其他位置 + 噪声"的贡献压到最低。
直觉版公式(不必推导):w = C⁻¹k / (kᵀC⁻¹k)。
其中 C 是数据协方差矩阵("各电极如何一起波动"的统计,来自你的试次),
k 是该体素的前置场列向量("这个位置的源长什么样")。
一句话:用噪声结构的逆,去乘上这个位置的特征,再做归一。
分母的归一化正是为了满足上面第 1 条约束。
两个必懂的延伸:
- 正则化:C 求逆在病态时会爆炸(见第六节),所以实践里总是加
lambda正则化,FieldTrip 写作cfg.lcmv.lambda = '5%'(按协方差迹的百分比自动定); - 深度偏置:beamformer 也有(浅部"更容易通过"),可用
cfg.lcmv.weightnorm = 'unitnoisegain'等权重归一化缓解。
FieldTrip 里在 ft_timelockanalysis 时设 cfg.covariance='yes' 即可顺手得到(timelock.cov)。窗口有两种选法:用全试次全时段(信号+噪声都进协方差,适合诱发响应),或只用刺激前基线(更"纯噪声",适合做噪声归一化的场合)。窗口选择会改变结果,要报告。
三、MNE vs Beamformer:什么时候用哪个
| 维度 | MNE(最小范数) | Beamformer(LCMV) |
|---|---|---|
| 求解思路 | 全局最优解:总能量最小的解 | 逐点滤波:每个体素独立一套空域滤波器 |
| 对相关源 | 不假设源独立,双侧同步激活可以处理 | 高度相关的源会互相抵消(假阴性风险,见第六节) |
| 深度偏置 | 明显,偏爱浅部源 | 存在但机制不同,可用权重归一化缓解 |
| 时频/诱导响应 | 为锁时平均设计,做时频不自然 | 强项:虚拟电极可做任意时频分析与无基线的诱导响应 |
| 输出形式 | 每个时间点的全脑激活图 | 每个体素的"虚拟电极"时序 |
| 数据需求 | ERP 平均即可起步 | 需要足够多的试次/时长估计稳定协方差 |
| 典型用途 | ERP 成分定位、组级激活图 | 时频定位、诱发/诱导响应、源空间连接分析 |
四、FieldTrip 实战:协方差 → LCMV → 虚拟电极
头模型与前置场直接复用 7.2 存好的那套。先算全脑激活图,再在感兴趣位置提取虚拟电极:
% ---- 第 1 步:估计数据协方差矩阵(beamformer 的燃料)----
cfg = [];
cfg.trials = 'all';
cfg.channel = 'EEG';
cfg.covariance = 'yes'; % 让 ft_timelockanalysis 顺手算协方差
cfg.covariancewindow = 'all'; % 也可 [-inf 0]:只用刺激前基线
cfg.demean = 'yes'; % 协方差对慢漂移极其敏感
timelock = ft_timelockanalysis(cfg, data); % timelock.cov 即协方差矩阵
% ---- 第 2 步:全脑 LCMV 激活图 ----
cfg = [];
cfg.method = 'lcmv';
cfg.sourcemodel = leadfield; % 复用 7.2 的头模型 + 前置场
cfg.headmodel = headmodel;
cfg.elec = timelock.elec;
cfg.lcmv.lambda = '5%'; % 协方差正则化:防病态(见第六节)
source = ft_sourceanalysis(cfg, timelock);
% source.avg.pow 是全脑激活图,用 7.2 学的方法定位感兴趣的位置
% ---- 第 3 步:在两个种子点提取虚拟电极 ----
% peakA / peakB:第 2 步激活图上选定的体素编号(或解剖先验位置)
seedpos = source.pos([peakA, peakB], :); % 2x3 坐标
cfg = [];
cfg.method = 'lcmv';
cfg.sourcemodel.pos = seedpos; % 只含 2 个种子点的"迷你源模型"
cfg.headmodel = headmodel;
cfg.elec = timelock.elec;
cfg.lcmv.keepfilter = 'yes'; % 保留滤波器,下一步要用
cfg.lcmv.fixedori = 'yes'; % 每个种子点只留一条主朝向时序
cfg.lcmv.lambda = '5%';
seed = ft_sourceanalysis(cfg, timelock); % 在平均数据上算好滤波器
% ---- 第 4 步:把滤波器应用到每个试次 → 分试次虚拟电极 ----
cfg = [];
cfg.method = 'lcmv';
cfg.sourcemodel.pos = seedpos;
cfg.sourcemodel.filter = seed.avg.filter; % 复用刚算好的滤波器(官方推荐写法)
cfg.headmodel = headmodel;
cfg.elec = timelock.elec;
cfg.keeptrials = 'yes'; % 保留每试次结果
vsource = ft_sourceanalysis(cfg, data); % 输入分试次数据
% vsource.trial{n}.mom 为 [2 x 时间]:两条虚拟电极在每个试次的时序
五、虚拟电极与连接分析:呼应模块 6.3
模块 6.3 结尾我们留了一个伏笔:头皮电极之间的"连接"有很大一部分是容积传导的假象—— 同一个源传到两个电极,它们当然高度相关。正确的思路是先源后连: 先把源定位做好,再在源与源之间算连接。虚拟电极正是这条路的入场券。
% ---- 第 5 步:虚拟电极进入模块 6 的连接分析 ----
% 先把虚拟电极包装回 FieldTrip 的 raw 数据结构
vdata = [];
vdata.label = {'seedA', 'seedB'};
vdata.fsample = data.fsample;
for n = 1:numel(vsource.trial)
vdata.trial{n} = vsource.trial(n).mom; % [2 x 时间]
vdata.time{n} = data.time{n}; % 时间轴与原试次一致
end
% 然后走模块 6.3 的老路:时频 → 连接
cfg = [];
cfg.method = 'mtmfft';
cfg.output = 'fourier';
cfg.keeptrials = 'yes';
cfg.foilim = [8 13]; % 例:alpha 频段
vfreq = ft_freqanalysis(cfg, vdata);
cfg = [];
cfg.method = 'wpli';
vconn = ft_connectivityanalysis(cfg, vfreq); % 两个源之间的 wPLI
源空间的连接避开了大部分容积传导假象,但空间泄露还在:相邻源的活动经逆解后仍会互相"沾染"(7.1 的分辨率矩阵里那张脸)。模块 6.4 讲过的对策——只报告不同半球/相距较远的源对、做泄露校正、用 wPLI 这类对零相位差不敏感的指标——在这里同样适用。
六、常见坑:病态协方差、参考依赖、信号抵消
-
协方差矩阵病态:通道多、数据少时,协方差矩阵接近奇异,求逆数值爆炸,表现为激活图上出现离谱的尖峰或全 0。
- 判据:条件数(
cond(timelock.cov))过大(经验上超过 1e12 很危险); - 对策:增加试次/时长;提高正则化(
cfg.lcmv.lambda从 '5%' 升到 '10%');检查是否有坏通道混进协方差。
- 判据:条件数(
- 参考依赖:beamformer 的滤波器建立在协方差上,而 EEG 的协方差依赖参考电极选择——换参考等于换数据。 传感器水平相对宽容的参考选择问题,在 beamformer 这里会被放大。用平均参考(模块 2.2)是最稳妥的做法,且不同被试、不同条件要用同一参考。
-
相关源的"信号抵消":beamformer 的目标函数假设"别处的活动都是要压制的干扰"。
当两个源高度相关(如双侧镜像放电)时,它们的线性组合会被滤波器当成"公共干扰"一起压制——真实存在的双侧激活在图上消失。
- 自查办法:与 MNE 结果交叉对比(MNE 不做独立性假设);分别用单侧数据重算协方差看激活是否"回来";
- 综述与癫痫文献里这是 beamformer 最著名的失败模式,报告阴性结论前务必排除。
beamformer 是"数据驱动"的方法——好处是少假设,代价是结果质量直接取决于协方差质量。跑完先做一次"体检":条件数、激活图形态、与 MNE 的一致性。三关都过,再往下走统计。
📌 本节小结
- Beamformer 每个体素一套滤波器:权重完全由数据协方差决定,输出是"虚拟电极"时序
- LCMV = "目标源无衰减通过"的约束 + "输出功率最小"的目标;lambda 正则化防协方差病态
- MNE 适合 ERP 的逐时间点激活图;beamformer 的强项是虚拟电极、时频与诱导响应
- 相关源是 beamformer 的命门:双侧同步激活可能被"信号抵消"成假阴性
- 先源后连:虚拟电极之间的连接不再受容积传导假连接干扰,但空间泄露仍要防
✏️ 课后练习
- 用同一份 ERP 数据分别跑 MNE(7.2)与 LCMV(本课),对比两者激活图的峰值位置与空间形态差异,并写一段解释。
- 故意把 cfg.lcmv.lambda 设为 0(不正则化)再跑一次,观察是否报错或结果异常;用
cond(timelock.cov)记下协方差的条件数。 - 提取两个感兴趣体素的虚拟电极,走模块 6 的流程计算两源之间的 wPLI,并与头皮电极对上的同指标比较大小并解释原因。