阶段四 · 连接与溯源 · 模块 7 · 第 20 课 / 共 28 课

Beamformer 溯源实战:空域滤波器

给大脑里每个位置装一台"定向麦克风":只听这里,压制别处。 Beamformer 输出的虚拟电极,也是模块 6 连接分析最好的起点。

🕐 60 分钟🎯 难度:高阶✅ 前置:7.2

🎯 本节学习目标

  • 用"定向麦克风"类比解释 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 变体。它的滤波器要同时满足两条:

  1. 约束:该位置的单位源必须"原样通过"——滤波器不能扭曲目标位置的信号幅度;
  2. 目标:输出总功率最小——在满足约束的前提下,把"其他位置 + 噪声"的贡献压到最低。

直觉版公式(不必推导):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 存好的那套。先算全脑激活图,再在感兴趣位置提取虚拟电极:

Matlab
% ---- 第 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 学的方法定位感兴趣的位置
Matlab
% ---- 第 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 结尾我们留了一个伏笔:头皮电极之间的"连接"有很大一部分是容积传导的假象—— 同一个源传到两个电极,它们当然高度相关。正确的思路是先源后连: 先把源定位做好,再在源与源之间算连接。虚拟电极正是这条路的入场券。

Matlab
% ---- 第 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 这类对零相位差不敏感的指标——在这里同样适用。

六、常见坑:病态协方差、参考依赖、信号抵消

  1. 协方差矩阵病态:通道多、数据少时,协方差矩阵接近奇异,求逆数值爆炸,表现为激活图上出现离谱的尖峰或全 0。
    • 判据:条件数(cond(timelock.cov))过大(经验上超过 1e12 很危险);
    • 对策:增加试次/时长;提高正则化(cfg.lcmv.lambda 从 '5%' 升到 '10%');检查是否有坏通道混进协方差。
  2. 参考依赖:beamformer 的滤波器建立在协方差上,而 EEG 的协方差依赖参考电极选择——换参考等于换数据。 传感器水平相对宽容的参考选择问题,在 beamformer 这里会被放大。用平均参考(模块 2.2)是最稳妥的做法,且不同被试、不同条件要用同一参考。
  3. 相关源的"信号抵消":beamformer 的目标函数假设"别处的活动都是要压制的干扰"。 当两个源高度相关(如双侧镜像放电)时,它们的线性组合会被滤波器当成"公共干扰"一起压制——真实存在的双侧激活在图上消失。
    • 自查办法:与 MNE 结果交叉对比(MNE 不做独立性假设);分别用单侧数据重算协方差看激活是否"回来";
    • 综述与癫痫文献里这是 beamformer 最著名的失败模式,报告阴性结论前务必排除。
⚠️ 三坑共用的一条纪律

beamformer 是"数据驱动"的方法——好处是少假设,代价是结果质量直接取决于协方差质量。跑完先做一次"体检":条件数、激活图形态、与 MNE 的一致性。三关都过,再往下走统计。

📌 本节小结

  • Beamformer 每个体素一套滤波器:权重完全由数据协方差决定,输出是"虚拟电极"时序
  • LCMV = "目标源无衰减通过"的约束 + "输出功率最小"的目标;lambda 正则化防协方差病态
  • MNE 适合 ERP 的逐时间点激活图;beamformer 的强项是虚拟电极、时频与诱导响应
  • 相关源是 beamformer 的命门:双侧同步激活可能被"信号抵消"成假阴性
  • 先源后连:虚拟电极之间的连接不再受容积传导假连接干扰,但空间泄露仍要防

✏️ 课后练习

  1. 用同一份 ERP 数据分别跑 MNE(7.2)与 LCMV(本课),对比两者激活图的峰值位置与空间形态差异,并写一段解释。
  2. 故意把 cfg.lcmv.lambda 设为 0(不正则化)再跑一次,观察是否报错或结果异常;用 cond(timelock.cov) 记下协方差的条件数。
  3. 提取两个感兴趣体素的虚拟电极,走模块 6 的流程计算两源之间的 wPLI,并与头皮电极对上的同指标比较大小并解释原因。