尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

SPM fMRI分析实战:从预处理到组分析的报错排查全攻略

SPM fMRI分析实战:从预处理到组分析的报错排查全攻略 要说处理脑成像数据最磨人的部分不是什么高深的统计模型而是那些藏在SPM每个按钮后面的“小问题”。我最早用SPM做fMRI分析那会儿光是数据预处理就折腾了小半个月每一阶段报错都不一样网上搜到的答案又是零零散散的经常是LSTM还卡在“Normalise”这一步。这篇就当作一个实战问题台账把我在SPM里踩过的坑、查过的资料、最后怎么绕过去的按分析流程的顺序整理出来。不管是刚上手SPM的新手还是已经被预处理搞得焦头烂额的老手应该都能在这里找到对症下药的那味药。1. 环境与安装阶段还没开始分析就被绊倒1.1 SPM版本和MATLAB的兼容性SPM对MATLAB版本非常敏感。SPM12官方推荐的是R2013b到R2020a之间但实际你会发现在MATLAB R2021a以后跑SPM12会在启动时直接报出“Invalid MEX file”之类的错误。这主要是因为SPM12原生的MEX文件是用旧版C标准编译的和较新版本的MATLAB运行时库存在兼容问题。如果坚持用新版本MATLAB最简单的解决办法是重新编译MEX文件。在MATLAB命令行里进入SPM12的src目录运行cd spm12/src make编译需要提前配置好系统编译器Windows下需要安装MinGW-w64Linux下一般gcc就能满足。如果不想折腾编译器就老实用MATLAB R2020a或更早版本我个人的经验是R2019b和R2020a这个窗口内最稳定SPM的图形界面和并行计算工具箱也没有明显冲突。还有一个容易踩的坑是SPM路径下有中文或空格。我曾在一台用户名为中文的工作站上装SPM导致所有工具箱加载失败界面按钮连点都没反应。把MATLAB、SPM、数据目录全部换成纯英文路径不要放在系统盘Program Files下权限不够读写的坑更烦人装在D盘或用户根目录下的一级文件夹里最省心。1.2 启动SPM时报错的快速判断启动SPM时最常见的两类报错一是缺少必要的工具箱二是MEX文件加载失败。判断方法是直接运行which spm spm(version)如果version输出正常但图形界面打不开问题基本出在Java或GPU相关设置。可以在MATLAB启动命令中加上-softwareopengl参数例如matlab -softwareopengl因为有些集成显卡的OpenGL加速和SPM的渲染窗口冲突界面会闪退。另一类问题是旧版本残留。比如之前装过SPM8又装了SPM12两个路径混在MATLAB默认搜索路径里SPM会用旧的函数覆盖新的。处理方式是每次只保留一个SPM版本的路径其他的一律rmpath。提示建议在startup.m文件里把SPM12路径单独写死避免每次启动MATLAB后手动去Set Path里找省了很多事。1.3 SPM12与SPM8的差异提醒如果一开始学的是SPM8转SPM12后会遇到几个操作层面的变化SPM12的fMRI模型模块里“Microtime resolution”和“Microtime onset”这两个参数默认值更合理了16和1不需要像以前那样还要拿着扫描参数手册去手动改SPM12的“Segment”整合了新的DARTEL流程做VBM更容易但也意味着按钮功能变了SPM12在处理多回波、多平面加速等新型序列时默认的Slice Timing模块不再像SPM8那样“踩死”某一种切片采集顺序必须手动指定。2. 数据整理与格式转换源头错了后面全都白搭2.1 DICOM转NIfTI还要不要单独用工具很多朋友习惯先用dcm2niiMRIcron里的工具统一把DICOM转成NIfTI再放进SPM。SPM12确实自带的spm_dicom_convert也能转但实际用下来我还是推荐用dcm2niix新版叫dcm2bids也内置了这个工具转一次。原因是dcm2niix除了转换格式还会顺手帮你整理出BIDS风格的命名很多后续需要用到的元数据TR、切片数、翻转角、回波时间都会写进JSON文件里这对记录分析参数非常有帮助。转换时的关键检查项是图像方向是否统一三维图像尺寸和体素大小是否和扫描序列一致是否错位。如果在SPM里点击某个图像后Display窗口里显示的左右方向反了不要慌这是SPM遵循神经影像学惯例放射科显示习惯的结果不一定是数据错。真正需要担心的是同被试不同RUN之间维度不一致。2.2 文件名和文件夹结构的坑SPM本身对文件名不算挑剔但它有个隐性规则是“不能以数字开头”还有“不要包含括号、空格、逗号”。我的习惯是统一用这样的结构sub-01/ anat/ func/ dwi/func文件夹里放sub-01_task-rest_run-1_bold.nii sub-01_task-rest_run-1_bold.json文件名前缀一定要保留“sub-01_task-rest_run-1_bold”这种结构化信息因为后面批处理写代码时正则表达式提取条件、扫描次数都是靠这些字符规律来的。真到了组分析阶段你会发现文件名乱的数据预处理做得再漂亮也没办法顺利合并。还有一点原始DICOM文件千万不要直接放在SPM工作目录下否则SPM的依赖文件检查会让每一步都缓慢因为它会把所有文件都纳入待扫描列表。建议单独建raw和derived两个大目录前者只读不动后者专门放SPM处理后的中间产物。2.3 需要提前处理好头动大的被试吗头动是fMRI最大的噪声源之一但什么时候剔除被试很多人处理得太早或太晚。我建议在预处理完成后先看一眼头动估计出来的平移和旋转参数文件如果某个RUN的头动超过了一个体素大小比如3mm并且不是全样本一致性的这个RUN在做一阶模型之前就要认真考虑是否剔除。这个问题在“小问题合集”里几乎必有一席之地因为处理策略直接会影响后面组分析的被试数目。通常做法是在预处理后的头动参数曲线图上标注事件时间点看是否有突发性大位移1帧位移大于1.5mm。如果有可以先把该帧用ART或spm_art标记出来在一阶模型中作为regressor剔除而不是直接把整个RUN扔掉。这个思路后面还会详细展开。3. 结构像与功能像对齐看似简单实则坑最多3.1 Coregister先还是Segment先很多SPM教程里给的预处理顺序是Slice Timing → Realign → Coregister → Segment → Normalise → Smooth。但这个顺序在实际数据上会有问题如果个体的解剖结构差异很大老年人、病患、儿童用T1像作为中间配准目标很容易因为T1分割质量差而把功能像带到错误的空间去。我常用的改进顺序是先把T1像单独Segment得到灰质、白质组织概率图再在Coregister步骤中用“T1 → mean functional”的估计结果把分割得到的组织概率图一并变换到功能像空间之后再做Segmentation-based normalization。这样可以充分利用每个个体T1的结构信息比直接用SPM默认流程稳定很多尤其对临床数据。实际操作时步骤是先CoregisterEstimate用T1像作为Sourcemean functional像作为Reference然后检查配准后的边界是否贴合功能像的脑轮廓满意后再到Segment里给T1做分割但注意这时分割的是原始T1空间后面标准化可以走DARTEL也可以直接用“Normalise: Estimate Write”把分割参数用于功能像。3.2 检查配准质量的逐层翻图法配准质量怎么看SPM的Check Registration按钮可以三视图联动但很多新手只是扫一眼觉得“差不多”就过了。我的经验是逐层翻看Fused显示重点看脑沟回边界是否对齐、侧脑室和皮层表面是否有系统性偏移。如果功能像有化学位移或图像形变T1和功能像的Envelope可能对不上这时要在Coregister里改Cost function例如把互信息Mutual Information改成Normalised Mutual Information并适当增加Separation参数的范围从默认的[4 2]改为[8 4 2]。重要配准之前务必先做Realign生成mean functional image再当Reference。没有mean image直接用第一帧做Reference头动大的被试几乎必然配不准。3.3 DARTEL还是旧式NormaliseDARTEL流程比旧式“Segment → Normalise”更精细但计算量大、参数多很多朋友在“小问题”里问的正是“为什么DARTEL出来的灰质图有一半是黑的”。这通常是模板迭代时的默认参数下Initial Template空间和最终写入空间不匹配造成的。解决办法是在DARTEL流程里最后一步“Normalise to MNI”时把“Voxel size”设置成1.5并选择FWHM为估计的平滑核一般6mm以上而不是默认的0同时检查“Bounding box”是否包含整个脑部。DARTEL虽然好但对单个被试的快速分析而言旧式“Normalise”更轻量。如果只是常规fMRI组分析不是做VBM我反而觉得没必要强上DARTEL旧式配准已经够用。与其纠结方法高级不如保证所有被试的处理参数完全一致这才是统计可比性的核心。4. 广义线性模型设置从一阶到组分析前的最后关卡4.1 条件命名和Onset时间最容易出偏差的地方一阶模型中每个条件必须精确对应每个刺激出现的时间点Onset单位是秒。这个时间点从哪里来如果你的实验程序用E-Prime或Psychtoolbox输出文件里记录的通常是刺激出现的绝对时刻你需要把这些时刻统一减去第一个扫描开始的时间点再换算成秒。很多朋友在看结果发现激活差异不显著时回头查才发现Onset时间没有对齐。一个快速验证方法是在SPM的Design矩阵的“Explore”里查看设计矩阵的Columns是否和条件块匹配同时点击“Design”按钮查看“Stimulus onset”的图形化展示。如果发现刺激事件显示的时间明显和任务设计对不上多半是在写入Onset之前做过删帧或slice timing校正导致参考时间基点偏移。4.2 设计矩阵里Regressor的共线性问题当两个条件的时间序列高度相关比如Block Design的两个条件的顺序几乎互补设计矩阵会存在共线性导致统计结果极不稳定。检查SPM给出的contrast估计时如果发现某个条件参数估计值的方差极大或者设计矩阵图形中有明显的对角条纹就要考虑重新编码条件或者合并降维。处理方式在定义条件时把任务编码改成“stick function canonical HRF”方式而不是直接给一个宽大方波。或者把高度共线的两个条件合并成一个条件然后在组分析时再做差分对比。另一个实用的办法是加入Pmod参数调制比如用反应时间作为modulator虽然不能完全消除共线性但可以在设计层面捕捉到更多任务相关的变异。4.3 高通滤波截止值为什么默认为128秒还要改SPM默认把高通滤波High-pass Filter的cutoff设为128秒这个值对大多数Block设计是合适的。但对事件相关设计如果两次事件间隔超过64秒128秒的截止可能会把有用的低频信号也滤掉导致敏感度下降。从傅里叶变换的角度理解128秒高通滤波相当于只保留周期快于128秒的分量。Event-related设计中如果刺激概率低、间隔不稳定信号频谱集中在低频高通截止值需要拉得更长如256秒甚至512秒。怎么选最简单的办法是去看设计矩阵频谱SPM的“Design”窗口能显示高通滤波后的频谱如果低频那个凸起被削掉太多说明截止值选小了。4.4 序列相关校正AR(1)和预白化的细节fMRI时间序列存在显著的时序相关性SPM默认用AR(1)模型来模拟并预白化。很多人不关心这个参数但当采集TR比较长比如3秒以上或信号噪比较大时AR(1)模型会趋向于把噪声“刷”得很白结果反而是真实激活信号也被削弱了。这里我的处理经验是在一阶模型的“Estimation”里将“Serial correlations”选项从“AR(1)”改为“Fast”。Fast选项会先估计残差的自相关再使用比AR(1)更灵活的白化矩阵能明显减少假阳性。不过改完Fast以后最好对比几次统计结果确认激活图变化不大再全样本统一使用不要在同一批次里混合AR(1)和Fast否则组分析的标准差估计会出问题。4.5 多会话Multi-session如何设计跑一次实验分多个RUN时一阶模型需要把多个RUN放进同一个SPM设计矩阵。常见错误是为了省事在“Session”里分开指定多个模型文件最后用“Implicit Mask”合并这样会在组分析时丢失会话内的具体信息而且自由度计算也不对。正确做法是在一个“fMRI model specification”里使用“Multiple sessions”选择所有RUN的NIfTI文件逐个指定每个Session的条件。SPM会自动把每个Session截距单独建模同时把低通/高通滤波器分别应用到每个Session。组分析时一个被试只有一个beta文件自由度是整个所有RUN相加后再减模型参数个数统计效力明显更高。5. 组分析阶段的隐蔽大坑5.1 单样本t检验还是双样本t检验别选错了很多人在做组内对比时纠结该选“One-sample t-test”还是“Two-sample t-test”。SPM组分析里如果只是比较同一组被试在两种条件下的差异选的是“Flexible factorial”或“Full factorial”但最简单的上来就是一个“One-sample t-test”输入contrast图像做组内检验这也是我推荐的第一个检验。只有当你想比较患者组和对照组时才用“Two-sample t-test”。这里要注意的是无论哪种都必须在“Subjects”里指定好每个被试的con_*.img文件别手滑把多人文件全选进去。5.2 组分析中协变量的陷阱做组间比较或回归分析时经常要加入年龄、性别作为协变量。SPM的协变量设置菜单里默认会把协变量做“mean centering”很多新手没意识到这一点导致解释结果时方向弄反。实际上加入协变量前最好看一下各组的均值差异如果两组协变量均值本来就有显著差异那么把协变量放进模型会把组间主效应吸收掉结果变得无法解释。我对协变量的处理原则是先做描述性统计检查两组协变量的重叠性。如果年龄在两组的分布几乎不重叠比如患者组都是70对照组都是20年龄方差膨胀因子里肯定超限。这时候要么用PACE匹配后再做分组比较要么把年龄当作匹配变量逐对纳入而不是直接放进ANCOVA。这个道理同样适用于头动等质量指标。5.3 多重比较校正FDR、FWE、Voxel-level还是Cluster-levelSPM结果界面上“Threshold”和“FWE correction”几个选项让很多人迷茫。SPM默认的voxel-level FWE校正对fMRI信号来说过于严格因为fMRI信号的空间平滑性导致激活分布不符合纯体素独立检验假设。我平时会搭配使用先看voxel-level uncorrected的p0.001作为初筛然后用cluster-level FWE校正即Family Wise Error on cluster extent来决定哪些团块是显著的。实际操作中发现JD和TFCE这些现代工具可以更灵活地控制FWE但SPM里的cluster-level校正必须注意首先要设置一个cluster-forming threshold通常是p0.001再去统计团块大小。不同平滑核下团块大小分布不同最好在结果报告中同时报告平滑核FWHM和cluster size阈值这也是期刊审稿人经常关注的内容。经验响应变量不要全用刚测出来的con图像原始值可以考虑在组分析前把每个被试的con图像统一分割成标准空间后再做一次分量归一化比如整脑均值或GM mask均值归一化能有效降低全脑信号的全局强度干扰。5.4 柔性因子模型Flexible Factorial的自由度问题SPM12里“Flexible factorial”设计虽然灵活但也带来自由度的坑。它默认每个被试当作“Level”加入同时每个被试的subject factor又被当作随机效应。如果被试数量少例如每组10人以下自由度会被高估导致p值偏低、假阳性上升。这种情况我一般建议直接用“Full factorial”配“between-subject”因子并把“Variance”选项设为“Unequal”。然后手动检查自由度是否为总扫描数 - 条件数 - 组别数 1左右如果SPM报出的自由度明显高于实际就要怀疑是否有变量冗余需要调整模型。特别是组内变量的重复测量设计中别忽略“dependence”建模否则连正负对比都可能被方向搞反。6. 结果查看、汇报与再分析6.1 坐标体系不一致MNI还是TalairachSPM默认输出的是MNI坐标但很多老文献和Atlas用的是Talairach坐标。直接拿MNI坐标去查Talairach图集结果会偏移几个厘米。处理方法是使用tal2ichm转换例如GingerALE工具内置或者用icbm2tal进行坐标空间转换。此外在SPM结果界面点击“Volume”后如果显示的坐标是负值说明激活在左半球或后部不要以为是bug。结合NDAR和BrainMap数据库查找激活标签时一定要注明坐标空间否则审稿人一眼就看出问题。6.2 如何快速提取peak坐标和cluster大小SPM结果表上可以直接看到局部极大值坐标、cluster大小、z分数。但如果你想在论文中做成表格手动复制粘贴效率太低。我平时会写一段MATLAB脚本直接从SPM结果结构中提取xSPM struct; % 这是SPM结果界面的变量 [SPM, xSPM] spm_getSPM(xSPM); tab spm_list(Table, SPM, xSPM);也可以直接保存xSPM后用spm_clusters获取团块索引再导出到Excel。关键是每次点击结果界面时记得用“Save”保存xSPM到工作区否则脚本拿不到数据。6.3 结果图上激活区域的脑区归属激活峰值在哪个脑区不能只靠肉眼对坐标猜。推荐几种方案用SPM的spm_atlas工具挂载AAL/BA图集或者把峰值坐标导入xjView工具箱虽然老但非常好用一键查询脑区名称。对于报告的准确性建议把peak坐标、AAL标签、BA分区信息一起列在表格中。如果你的数据已经在标准空间里还可以用Freesurfer的Desikan-Killiany模板但必须先把MNI坐标重建到Freesurfer空间mri_b2a。注意匹配模板空间不然连Hippocampus这种位置都能对到脑干去。6.4 用SPM做ROI分析ROI分析在SPM里最直接的方式是先用MarsBaR工具箱提取ROI的时间序列再做相关分析或GLM分析。MarsBaR的坑在于ROI定义时如果使用的是MNI坐标要确保ROI模板也在MNI空间。做完ROI分析后很多人忽略了一个问题ROI时间序列是简单平均还是第一特征向量加权平均。简单平均对被试间配准误差敏感我通常用first eigenvariate因为它在几何上更能代表该ROI内部的一致性激活。最后再分享一个小技巧在SPM的Batch Editor里几乎所有的操作都能写成spm_jobman和cfg_util脚本。我用了几次后彻底放弃在图形界面上手动点击每一个被试改成写一个批处理脚本循环处理所有被试的预处理和一阶模型。这不仅省时间还能减少手动操作中的不同步骤参数不统一的潜在风险。如果你的分析经常需要重复强烈建议在摸索清楚步骤之后把所有参数固化到批处理脚本里。7. 高频报错速查表和排查思路我把平时被问得最多的几个报错和解决办法整理成了表方便直接对照检查报错信息或现象常见原因快速解决方案“No valid files found”文件路径含特殊字符或NIfTI扩展名不对统一改成.nii并检查路径是否纯英文“Index exceeds matrix dimensions” 出现于Realign功能像序列存在异常帧、或者文件列表里有结构像混入检查待Realign的文件列表逐项确认每个文件都是4D func数据Coregister结果明显偏移Reference图像选择错误或Source和Reference分辨率差太大用mean functional作为Reference并且在Estimation Options里勾选“Quality”为0.9“Failed to determine slice order”Slice Timing的切片数或TR参数设置错误或图像头文件meta丢失回查dcm2niix生成的JSON确认SliceTiming字段如果缺失向扫描员索取切片采集顺序Design矩阵里出现了一整列全0某个条件的Onset没有落在扫描时间窗内检查Onset是否超出总扫描时长或条件编码名称写错成空格组分析显示“Insufficient contrast”对比权重向量长度与beta数量不符在Contrast Manager里查看当前设计的参数数量逐个配平结果图一片红找不到激活阈值设置过高或contrast正负方向搞反检查对比方向例如“任务 休息”和“休息 任务”只差一个负号排查思路上我建议每一步都保留中间产物。SPM本身会记录很多*.mat文件这些mat文件里其实存了完整的处理参数和依赖关系。卡住时用load或spm_select查看某个Mat文件的spm结构通常能发现是哪个输入的路径出了问题。以前我常为了省事把一个被试的处理结果复制给另一个被试结果路径变量没有更新后面步骤全部基于错误输入这种低级错误最浪费时间。每次换被试或换数据务必重建任务数据对象不要偷懒复制。在SPM里做fMRI分析每一步看似都有默认值但默认值只适合“标准”数据。真实实验每个被试的扫描质量、结构像有没有缺失、任务时序有没有跑偏这些异常都会反映到某个具体报错或结果异常里。把这套排查方法跑熟了你对数据的理解会比只会点按钮的人深一大截。我最大的体会是SPM的问题其实没有一个是孤立的报错只是你在某个处理环节和数据特性发生冲突的外在表现。通过这条“出错—查看SPM结构—检查输入—调整参数—重新运行”的链路几乎能解决九成以上的“小问题”。后面如果再遇到数据处理上的新坑欢迎一起讨论毕竟这些坑换着花样来但基本逻辑是相通的。
返回列表