Qiita中没有fMRI数据的分析示例... !!
前几天,我想:"有很多关于qiita的文章……我认为没有很多使用python或机器学习来分析fMRI数据的例子。"因此,我在qiita中搜索了一个实际分析fMRI fMRI数据的示例。
那?并不是的 ...? !!
在工具和神经科学领域有评论文章,但我找不到使用实际fMRI数据进行的任何分析。
因此,这一次,我们将使用fMRI数据分析机器学习。即使是大脑图像,也毕竟是三维图像。通过将大脑切成两半而获得的二维图像的截面图是通过重叠形成的,每个像素仅具有称为"体素"的单位的信息。对于4D脑部图像,只是每个三维体素信息都包含时间序列信息(使用fMRI扫描时)。
脑部图像通常以称为NifTY格式的特殊格式发布,需要阅读。这次,我们将使用称为Nilearn的python库(并记住任何人都可以分析它。Matlab已退出),而不是据说是学生和研究人员经常使用的Matlab,而是读取和分析大脑图像。机器学习分析本身是由scikit-learn的SVM学习和预测的,任何进行过机器学习的人都将使用或听说过。
另外,尽管这是一种使用机器学习的方法,但是这次我们将使用掩盖特定大脑区域的fMRI数据进行分析,以预测"当时fMRI从大脑活动中看到了什么"。 。在大脑分析行业中,它似乎被称为大脑信息解码,但这只是机器学习(对行业内的人们感到抱歉)。过去,我们使用诸如编码之类的方法,其中将在某些实验任务中执行的行为用作解释变量,而将通过fMRI执行时在统计上有显着响应的大脑区域检测为自变量。它之所以被称为解码,是因为它采取了相反的方法(也就是说,与其查看统计上被任务显着激发的大脑区域,而是查看一个大脑区域的活动,然后从中预测任务)。
关于实际使用的数据
这次,我参考了Nilearn:Python中的神经影像的机器学习的解码分析教程来进行分析,该教程用于读取fMRI数据,但在其中使用的Haxby 2001实验中。 。
fMRI解码入门教程
如果您遵循上面的教程,它说您可以使用以下代码轻松地从The Haxby 2001实验中收集主题的数据集。
1 2 3 | from nilearn import datasets # デフォルトでは、二番目の被験者のデータが取得される haxby_dataset = datasets.fetch_haxby() |
但是,提供数据的NIRTC数据库似乎有问题,但是我无法下载它,因此我采取了直接下载并将其保存在本地的方法。您可以从此URL下载subj2-2010.01.14.tar.gz,其中包含第二个主题的数据,因此,如果要分析数据,请从此处下载(甚至是其他主题编号的数据)。没关系)。
http://data.pymvpa.org/datasets/haxby2001/
在此实验中,在fMRI扫描仪中为受试者呈现各种类别的视觉刺激(剪刀,人脸,猫等)。在本教程中,记录在腹侧皮层视神经区域(与颜色和形状的表示有关)内的fMRI脑活动可预测对象使用fMRI查看的类别。
分析程序
*在源代码中,已下载文件夹的名称已更改为subj2。
加载并可视化图像
首先,让我们阅读4D fMRI数据。如上所述,当在作为体素数据(三维数据)的大脑图像中执行实验任务时,该格式包含时间序列信息。
但是,由于它是4D fMRI数据,因此无法通过对应于3D fMRI数据(无时间序列信息)的处理(如nilearn.plotting.plot_epi)来可视化。因此,将可视化的4D fMRI数据平均并转换为一个3D fMRI数据。这可以通过导入
1 2 3 4 5 | fmri_filename = "~/Desktop/nilearn/subj2/bold.nii.gz" from nilearn import plotting from nilearn.image import mean_img plotting.view_img(mean_img(fmri_filename), threshold=None) |
结果
首先,我能够可视化大脑图像。能以几行显示就很棒。
提取fMRI数据的特征并将其转换为numpy格式的数据
也许有些人看到
numpy这个词感到宽慰。在这里,我们使用一个名为
首先,让我们仅可视化腹侧皮层视觉通道的fMRI数据。
1 2 3 4 5 6 | mask_filename = "~/Desktop/nilearn/subj2/mask4_vt.nii.gz" anat_filename = "~/Desktop/nilearn/subj2/anat.nii.gz" # 被験者の解剖画像を背景にしてマスクの範囲を表示させてみる plotting.plot_roi(mask_filename, bg_img=anat_filename, cmap='Paired') |
结果
接下来,我们创建一个遮罩。我想对数据进行标准化,以提高机器学习的准确性。准备好使用nilearn进行机器学习时,将以2D数组的形式提取遮罩。
1 2 3 4 | from nilearn.input_data import NiftiMasker masker = NiftiMasker(mask_img=mask_filename, standardize=True) # マスクを標準化する fmri_masked = masker.fit_transform(fmri_filename) # 標準化させたマスクに対してBOLD信号を当てはめる |
顺便说一句,通过执行
现在,您已提取fmri_masked为numpy格式的屏蔽数据矩阵。我会尝试的。
1 2 3 4 5 | # fmri_masked はnumpy配列の形式になっています。 print(fmri_masked) # このデータのサイズは、ボクセル数と時系列点(スキャン数)の総数で構成されています。 # shape[0]はスキャン数, shape[1]はボクセル数 print(fmri_masked.shape) |
输出结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | [[ 7.6757896e-01 2.3108697e+00 -2.0519443e-01 ... -1.0261143e+00 8.7993503e-02 2.0720518e+00] [ 5.5640817e-01 1.6833434e+00 -2.4644937e-01 ... -7.0238107e-01 -3.4570047e-01 2.0341001e+00] [ 7.6757896e-01 1.9186659e+00 1.0802225e-03 ... -9.9374104e-01 -2.7630943e-01 2.1479552e+00] ... [-4.2905563e-01 -1.6896105e+00 -7.4150854e-01 ... -1.5440876e+00 1.8054217e+00 -1.6709718e-01] [-1.4749455e-01 -1.8072717e+00 -2.4644937e-01 ... -1.7707009e+00 1.5452052e+00 7.8169477e-01] [-2.1788482e-01 -1.4542881e+00 1.0802225e-03 ... -1.6412076e+00 1.2676411e+00 8.9554977e-01]] (1452, 464) |
如在
作为试用,如果您取出前三个体素的时间序列信息,将如下所示。
1 2 3 4 5 6 7 8 | # 行列から選ばれた最初の三つのボクセルを表示させる。 import matplotlib.pyplot as plt plt.plot(fmri_masked[5:150, :3]) plt.title('Voxel Time Series') plt.xlabel('Scan number') plt.ylabel('Normalized signal') plt.tight_layout() |
顺便说一句,敏锐的人们可能会怀疑为什么
在实验任务期间捕获行为标签
由于我们能够以numpy格式提取腹侧皮层视觉通道的fMRI数据,因此我们将使用此数据进行机器学习。这次我们将在有监督的学习之下,因此除数据外,我们还需要标签。因此,我们将从下载的数据中以标签的形式取出实验时受试者正在查看的类别的信息。
1 2 3 | import pandas as pd # 行動結果の情報を読み込む。スキャン数だけラベルがある behavioral = pd.read_csv('~/Desktop/nilearn/subj2/labels.txt', delimiter=' ') |
输出结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | labels chunks 0 rest 0 1 rest 0 2 rest 0 3 rest 0 4 rest 0 5 rest 0 6 scissors 0 7 scissors 0 8 scissors 0 9 scissors 0 10 scissors 0 11 scissors 0 12 scissors 0 13 scissors 0 14 scissors 0 ... ... ... 1426 cat 11 1427 cat 11 1428 cat 11 1429 cat 11 1430 cat 11 1431 cat 11 1432 rest 11 1433 rest 11 1434 rest 11 1435 rest 11 1436 rest 11 1437 scissors 11 1438 scissors 11 1439 scissors 11 1440 scissors 11 1441 scissors 11 1442 scissors 11 1443 scissors 11 1444 scissors 11 1445 scissors 11 1446 rest 11 1447 rest 11 1448 rest 11 1449 rest 11 1450 rest 11 1451 rest 11 1452 rows × 2 columns |
由于该任务是如上所述的视觉识别任务,因此标签显示了实验条件(显示给对象的对象的类别)。块代表会话的数量(因为如果让受试者永久执行任务,则身体负担并不奇怪,通常您在会话休息时检查身体状况时会多次执行类似的任务。我会继续)。
1 | conditions = behavioral['labels'] |
输出结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | 0 rest 1 rest 2 rest 3 rest 4 rest 5 rest 6 scissors 7 scissors 8 scissors 9 scissors 10 scissors 11 scissors 12 scissors 13 scissors 14 scissors 15 rest 16 rest 17 rest 18 rest 19 rest 20 rest 21 face 22 face ... 1431 cat 1432 rest 1433 rest 1434 rest 1435 rest 1436 rest 1437 scissors 1438 scissors 1439 scissors 1440 scissors 1441 scissors 1442 scissors 1443 scissors 1444 scissors 1445 scissors 1446 rest 1447 rest 1448 rest 1449 rest 1450 rest 1451 rest Name: labels, Length: 1452, dtype: object |
这次,根据教程,我们将仅使用猫和脸的实验条件。还有许多其他标签,因此,如果您有兴趣,请尝试在其他类别中进行验证。
1 2 3 4 5 6 7 8 | # マスキングした脳画像に対して顔と猫を提示した実験条件時のスキャン数に対応するボクセルを取ってくる condition_mask = conditions.isin(['face','cat']) fmri_masked = fmri_masked[condition_mask] print(fmri_masked.shape) # ラベルに対しても同じ条件を適応させる conditions = conditions[condition_mask] print(conditions.shape) |
使用SVM
解码
这次,我们将使用scikit-learn的机器学习工具箱来获取所提取的fmri_masked数据。我正在使用SVM线性内核作为解码器。由于它是scikit-learn,因此您可以在几行之内完成从学习到预测的所有操作
1 2 3 4 5 6 | from sklearn.svm import SVC svc = SVC(kernel='linear') # 学習して予測を行う svc.fit(fmri_masked, conditions) prediction = svc.predict(fmri_masked) |
但是,这对于分析完全没有用,因此(显然)执行交叉验证。
执行交叉验证以计算预测分数
KFold交叉验证
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | from sklearn.model_selection import KFold import numpy as np cv = KFold(n_splits=5) scores = [] for train, test in cv.split(X=fmri_masked): svc.fit(fmri_masked[train], conditions.values[train]) prediction = svc.predict(fmri_masked[test]) scores.append((prediction == conditions.values[test]).sum() / float(len(conditions.values[test]))) # 予測性能の平均 print(np.mean(scores)) 出力結果: 0.7628964059196617 |
也可以使用cross_val_score函数使用机器上的多个CPU进行分布式处理,以计算每个子集的评估值。 (可以n_jobs = n的形式指定,如果设置为-1,则可以使用计算机上所有可用的CPU进行并行处理)。
1 2 3 4 5 6 | from sklearn.model_selection import cross_val_score cv_score = cross_val_score(svc, fmri_masked, conditions) print(cv_score) 出力結果: [0.86363636 0.76744186 0.74418605 0.69767442 0.81395349] |
但是,这仍然不足以进行验证。原因是,绩效评估未考虑每个会话的影响。
评估每个会话的预测准确性
fMRI数据是在逐个会话的基础上获取的,并且每个特定会话的噪声都是自相关的。因此,在考虑到这些影响进行交叉验证时,有必要跨会话进行预测。
1 2 3 4 5 6 7 8 9 10 | from sklearn.model_selection import LeaveOneGroupOut cv = LeaveOneGroupOut() cv_score = cross_val_score(svc, fmri_masked, conditions, cv=cv, groups=session_label, ) # セッションごとの検証データでの予測精度を取得する print(cv_score) |
1 2 3 | 出力結果: [0.55555556 1. 0.66666667 0.66666667 0.77777778 0.72222222 0.88888889 0.38888889 0.66666667 0.5 0.77777778 0.66666667] |
估计经过训练的模型的权重
最后,估计并显示模型的权重。为此,我们首先将权重转换为NIfTY图像。
1 2 3 4 | coef_ = svc.coef_ # numpy arrayの形式になっており、ボクセルごとに一つの係数(重み)を持っている print(coef_.shape) |
1 2 | 出力結果: (1, 464) |
1 2 | coef_img = masker.inverse_transform(coef_) print(coef_img) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | 出力結果: <class 'nibabel.nifti1.Nifti1Image'> data shape (40, 64, 64, 1) affine: [[ -3.5 0. 0. 68.25 ] [ 0. 3.75 0. -118.125] [ 0. 0. 3.75 -118.125] [ 0. 0. 0. 1. ]] metadata: <class 'nibabel.nifti1.Nifti1Header'> object, endian='<' sizeof_hdr : 348 data_type : b'' db_name : b'' extents : 0 session_error : 0 regular : b'' dim_info : 0 dim : [ 4 40 64 64 1 1 1 1] intent_p1 : 0.0 intent_p2 : 0.0 intent_p3 : 0.0 intent_code : none datatype : float64 bitpix : 64 slice_start : 0 pixdim : [-1. 3.5 3.75 3.75 1. 1. 1. 1. ] vox_offset : 0.0 scl_slope : nan scl_inter : nan slice_end : 0 slice_code : unknown xyzt_units : 0 cal_max : 0.0 cal_min : 0.0 slice_duration : 0.0 toffset : 0.0 glmax : 0 glmin : 0 descrip : b'' aux_file : b'' qform_code : unknown sform_code : aligned quatern_b : 0.0 quatern_c : 1.0 quatern_d : 0.0 qoffset_x : 68.25 qoffset_y : -118.125 qoffset_z : -118.125 srow_x : [-3.5 0. 0. 68.25] srow_y : [ 0. 3.75 0. -118.125] srow_z : [ 0. 0. 3.75 -118.125] intent_name : b'' magic : b'n+1' |
以nii.gz格式保存创建的NIfTY格式图像
1 2 | # nii.gzファイル形式で保存する coef_img.to_filename('haxby_svc_weights.nii.gz') |
最后,让我们将加权因子可视化为大脑图像。
1 2 3 4 5 | # 実際にSVMの重みをプロットしてみる from nilearn.plotting import plot_stat_map, show plot_stat_map(coef_img, bg_img=anat_filename, title="SVM weights", display_mode='yx') show() |
以上,我们使用机器学习进行了常规fMRI数据分析。
摘要
我打算在个人博客上发布此分析,但是找不到在Qiita上分析fMRI数据的人,因此我将其发布在这里(个人博客发布在我的个人资料上)。
fMRI数据图像...似乎很难听见,但最终,杂波比普通图像多,尺寸又大,可以删除处理特殊格式的部分,这只是图像分析。 。目标之一是能够分析任何人的疯狂数据,因此,我正在其个人博客和Qiita上发布该数据,因此希望您对分析在此活动中很少接触到的数据感兴趣。
如果您愿意,LG ...感谢您的合作!哈哈
名为PS
的预防性声明
这一次,我们将使用原始的fMRI时间序列数据进行分析,但是有必要对最初称为HRF函数进行卷积并将其从任务表现(神经活动)转换为血流动力学(BOLD反应)。 。此外,为了获得按试验的活动图(β图),必须使用通用线性模型进行第一级拟合。这使您可以在每个试验的相关条件之间进行预测。实际上,实际的fMRI数据分析需要各种处理过程,但这一次是针对将机器学习应用于fMRI数据的介绍,因此,我决定(实际上)执行上述处理。不会适合一篇文章(多汗)。
请注意(如果有机会,我想认真分析一些内容)