【MRIcron/MRIcroGL】MRIcron/MRIcroGLを用いたノイズ除去とマスク画像の作成


1. 目的
2. MRIcronを用いる場合
3. MRIcroGLを用いる場合


1. 目的

  • MRIcron/MRIcroGLを用いたバイナリーマスク画像の作成

ここでは、拡散MRI(b=0, SE-EPI)の脳周囲にあるノイズ除去するため、マスク画像を生成しノイズを除去する方法を解説する。

まず、画像を見てみる。拡散MRI(b=0, SE-EPI)を信号値0-10のスケールで表示すると、以下のような画像が表示される。この内、脳周囲の白い点々(ごま塩ノイズ)は本来観測すべきではない信号、つまりノイズである。これから、このノイズを除去していく。

2. MRIcronを用いる場合

MRIcronの基本操作は、以下の記事を参考にするとよい。

脳のマスク画像を作るには、ツールタブの「Draw/Intensity filter」を選択。あるいは、「Ctrl + I」を押す。

脳実質が欠けないようにThresholdを設定する。設定ができたら「Save highlighted as NIfTI or VOI」を選択。

保存先を指定して保存する。この時NIfTI形式として保存しておくと、他の脳画像解析ソフトで扱いやすい。

保存した画像を開くと、Intensity filterでしきい値処理された画像が表示される。この時、画像はまだ二値化されていない状態である。

ここで、スケールを最初と同じ0-10に設定して、脳周囲のノイズを確認してみる。脳周囲のノイズを除去されていることが分かる。

この画像を二値化してバイナリーマスク画像を作成したい場合、ツールタブの「Draw/Advanced/Brain mask」を選択し、ファイル名を指定して保存する。

二値化されたバイナリーマスク画像が生成される。

3. MRIcroGLを用いる場合

MRIcroGLの基本操作は、以下の記事を参考にするとよい。

脳のマスク画像を作るには、ツールタブの「Draw/Advanced/Intensity Filter」を選択。

「Action: Add to Drawing」となっている状態で、脳実質が欠けないようにしきい値を設定し「Apply」をクリック。

Intensity Filterのしきい値処理が適用されて残った領域が、関心領域として設定される。

関心領域が設定されている状態で、上タブの「Draw/Advanced/Mask Image」から、「Delete/Preserve regions with VOI」を選択。

ここで、スケールを最初と同じ0-10に設定して、脳周囲のノイズを確認してみる。脳周囲のノイズを除去されていることが分かる。

この画像を二値化してバイナリーマスク画像を作成したい場合、ツールタブの「Draw/Save VOI」を選択し、ファイル名を指定して保存する(ショートカットキー:CTRL+S)。

二値化されたバイナリーマスク画像が生成される。

【FSL】 FSLを用いた定量値の計測 ~Sampling~


1. 目的
2. コマンド
3. 使用例
3.1. 最小値と最大値
3.2. ボクセル数および容積
3.3. 平均値と標準偏差
3.4. マスク画像を用いた計測


1. 目的

  • 定量値(容積や拡散定量値など)の算出

2. コマンド

FSLfslstatsコマンドを用いて、定量値を算出することが可能。

fslstatsのヘルプは次の通り。

Usage: fslstats [preoptions] <input> [options]

preoption -t will give a separate output line for each 3D volume of a 4D timeseries
preoption -K < indexMask > will generate seperate n submasks from indexMask, for indexvalues 1..n where n is the maximum index value in indexMask, and generate statistics for each submask
Note - options are applied in order, e.g. -M -l 10 -M will report the non-zero mean, apply a threshold and then report the new nonzero mean

-l <lthresh> : set lower threshold
-u <uthresh> : set upper threshold
-r           : output <robust min intensity> <robust max intensity>
-R           : output <min intensity> <max intensity>
-e           : output mean entropy ; mean(-i*ln(i))
-E           : output mean entropy (of nonzero voxels)
-v           : output <voxels> <volume>
-V           : output <voxels> <volume> (for nonzero voxels)
-m           : output mean
-M           : output mean (for nonzero voxels)
-s           : output standard deviation
-S           : output standard deviation (for nonzero voxels)
-w           : output smallest ROI <xmin> <xsize> <ymin> <ysize> <zmin> <zsize> <tmin> <tsize> containing nonzero voxels
-x           : output co-ordinates of maximum voxel
-X           : output co-ordinates of minimum voxel
-c           : output centre-of-gravity (cog) in mm coordinates
-C           : output centre-of-gravity (cog) in voxel coordinates
-p <n>       : output nth percentile (n between 0 and 100)
-P <n>       : output nth percentile (for nonzero voxels)
-a           : use absolute values of all image intensities
-n           : treat NaN or Inf as zero for subsequent stats
-k <mask>    : use the specified image (filename) for masking - overrides lower and upper thresholds
-d <image>   : take the difference between the base image and the image specified here
-h <nbins>   : output a histogram (for the thresholded/masked voxels only) with nbins
-H <nbins> <min> <max>   : output a histogram (for the thresholded/masked voxels only) with nbins and histogram limits of min and max

Note - thresholds are not inclusive ie lthresh<allowed<uthresh

基本的な使い方は、次の通り。

# 基本
fslstats  <入力画像> [オプション]

# マスクを適用する場合
fslstats  <入力画像> -mas <マスク画像> [オプション]

3. 使用例

fslstatsコマンドで、よくある使用例を紹介する。

3.1. 最小値と最大値

最小値と最大値は、オプション-Rを用いて計測する。

以下では、頭蓋除去済みの脳画像(T1_skull_stripped.nii.gz)における最小値と最大値を計測している。

fslstats T1_skull_stripped.nii.gz -R
0.000000 1307.000000 

3.2. ボクセル数および容積

ボクセル数および容積の計測は、オプション-Vを用いる。似ているオプションで-vがあるが、オプション-Vでは、信号値が0あるいはnanでない領域を対象に、計測する。他にも小文字・大文字で区別しているオプションがあるが、信号値がある領域のみを対象にしたい場合は、基本的に大文字オプション(例:-V, -M, -S)で計測するとよい。

以下では、灰白質(GM_seg.nii.gz)の容積を計測している。

fslstats GM_seg.nii.gz -V
882175 697814.250000  # 左からボクセル数、容積(mm^3)

3.3. 平均値と標準偏差

平均値を計測するにはオプション-Mを、標準偏差を計測するにはオプション-Sを用いる。

以下では、FA(FA.nii.gz)の平均値を算出している。

fslstats FA.nii.gz -M
0.276669

以下では、FA(FA.nii.gz)の標準偏差を算出している。

fslstats FA.nii.gz -S
0.186857

3.4. マスク画像を用いた計測

計測したい領域がある場合、オプション-kで領域(マスク画像)を指定して計測するとよい。

例えば、白質(WM_seg.nii.gz)領域における、FAの平均値を計測したい場合、次のようになる。

fslstats FA.nii.gz -k WM_seg.nii.gz -M
0.507313

計測したい領域が複数あり、それらの領域にインデックス(値)が割り振られている画像(例:CSF=1, GM=2, WM=3の画像)があるとき、オプション-Kを用いると便利である。

例えば、白質を48領域分割したアトラス(JHU-ICBM-labels-1mm_indiv.nii.gz)を用いて平均値を計測する場合、次のようになる。

fslstats -K JHU-ICBM-labels-1mm_indiv.nii.gz FA.nii.gz  -M
0.491841 
0.512530 
0.591286 
0.667444 
... [省略]
0.294148

【FSL】FDT pipelineを用いた標準空間(MNI空間)への位置合わせ


1. 目的
2. 必要なファイル
3. 実行
4. 実際に実行されているコマンド
5. 出力画像


1. 目的

  • FDT pipelineを用いて、個人のDiffusion画像と標準空間(MNI空間)の構造画像の位置合わせ

2. 必要なファイル

必要なファイルは次の通り。

BEDPOSTXの使い方はこちら

.
├── DTI.bedpostX  # BEDPOSTXの出力フォルダ
├── T1.nii.gz  # BET前のBrain 3D-T1WI (FNIRT用)
└── T1_brain.nii.gz  # BET後のBrain 3D-T1WI (FLIRT用)

これに加えて、b0画像から脳を抽出した画像、nodif_brain.nii.gz を DTI.bedpostX にコピーしておく必要がある。

3. 実行

ターミナル(端末)でfslと入力。

fsl

Windowが立ち上がったら、「FDT diffusion」を選択。

各項目ごとにファイルを選択。注意すべきことは次の通り。

  • 「Main structural image」に指定する画像はBET後のBrain 3D-T1WI
  • 「Non-betted structural」に指定する画像は、BET前のBrain 3D-T1WI
  • Normal searchを「Full search」に変更

以上の設定ができたら、「Go」を選択して位置合わせを実行する。

4. 実際に実行されているコマンド

上記の操作を実行すると、ターミナル上にFSLのコマンドが生成され位置合わせが実行される。その時のコマンドは次の通り。

flirt -in DTI.bedpostX/nodif_brain \
    -ref T1_brain.nii.gz \
    -omat DTI.bedpostX/xfms/diff2str.mat \
    -searchrx -180 180 -searchry -180 180 -searchrz -180 180 \
    -dof 6 -cost corratio

convert_xfm -omat DTI.bedpostX/xfms/str2diff.mat \
    -inverse DTI.bedpostX/xfms/diff2str.mat

flirt -in T1_brain.nii.gz \
    -ref /opt/fsl/data/standard/MNI152_T1_2mm_brain \
    -omat DTI.bedpostX/xfms/str2standard.mat \
    -searchrx -180 180 -searchry -180 180 -searchrz -180 180 \
    -dof 12 -cost corratio

convert_xfm -omat DTI.bedpostX/xfms/standard2str.mat \
    -inverse DTI.bedpostX/xfms/str2standard.mat

convert_xfm -omat DTI.bedpostX/xfms/diff2standard.mat \
    -concat DTI.bedpostX/xfms/str2standard.mat DTI.bedpostX/xfms/diff2str.mat

convert_xfm -omat DTI.bedpostX/xfms/standard2diff.mat \
    -inverse DTI.bedpostX/xfms/diff2standard.mat

fnirt --in=T1.nii.gz \
    --aff=DTI.bedpostX/xfms/str2standard.mat \
    --cout=DTI.bedpostX/xfms/str2standard_warp \
    --config=T1_2_MNI152_2mm

invwarp -w DTI.bedpostX/xfms/str2standard_warp \
    -o DTI.bedpostX/xfms/standard2str_warp \
    -r T1_brain.nii.gz

convertwarp -o DTI.bedpostX/xfms/diff2standard_warp \
    -r /opt/fsl/data/standard/MNI152_T1_2mm \
    -m DTI.bedpostX/xfms/diff2str.mat \
    -w DTI.bedpostX/xfms/str2standard_warp

convertwarp -o DTI.bedpostX/xfms/standard2diff_warp \
    -r DTI.bedpostX/nodif_brain_mask \
    -w DTI.bedpostX/xfms/standard2str_warp \
    --postmat=DTI.bedpostX/xfms/str2diff.mat

5. 出力画像

処理が終わると、BEDPOSTXの出力フォルダ(DTI.bedpostX)に位置合わせの出力ファイルが保存される。

DTI.bedpostX/xfms/
├── diff2standard.mat
├── diff2standard_warp.nii.gz
├── diff2str.mat
├── eye.mat
├── standard2diff.mat
├── standard2diff_warp.nii.gz
├── standard2str.mat
├── standard2str_warp.nii.gz
├── str2diff.mat
├── str2standard.mat
└── str2standard_warp.nii.gz

【FSL】BEDPOSTXの使い方


* 1. 目的
* 2. BEDPOSTX
* 3.


1. 目的

  • BEDPOSTXの利用方法の取得

2. BEDPOSTX

BEDPOSTXの実行には、次のようなファイルが必要。

さらに、ファイル名は次のようにしておく必要がある。

Sub001/
├── bvals  # DWIのGradient Table
├── bvecs  # DWIのGradient Table
├── data.nii.gz  # DWI
└── nodif_brain_mask.nii.gz  # b=0のマスク

BEDPOSTXは、次のコマンドで実行できる。

bedpostx Sub001
# Usage: bedpostx <subject directory> [options]

3.

次のように、データを用意する。

$ tree HC003/
HC003/
├── HC003.bval   # DWIのGradient Table
├── HC003.bvec  # DWIのGradient Table
├── drHC003.nii.gz  # DWI
└── maskdrHC003.nii.gz  # b=0のマスク

この時、BEDPOSTXを実行するために必要なファイルが揃っているかをbedpostx_datacheckで確認することができる。

$ bedpostx_datacheck HC003/
HC003//data does not exist
HC003//nodif_brain_mask does not exist
 num lines in HC003//bvals 
cat: HC003//bvals: No such file or directory
0
 num words in HC003//bvals 
cat: HC003//bvals: No such file or directory
0
 num lines in HC003//bvecs 
cat: HC003//bvecs: No such file or directory
0
 num words in HC003//bvecs 
cat: HC003//bvecs: No such file or directory
0

ファイル名を修正。

tree HC003
HC003/
├── bvals
├── bvecs
├── data.nii.gz
└── nodif_brain_mask.nii.gz

再度、bedpostx_datacheckを実行する。

$ bedpostx_datacheck HC003/
HC003//data
data_type	INT16
dim1		120
dim2		120
dim3		84
dim4		137
datatype	4
pixdim1		1.700000
pixdim2		1.700000
pixdim3		1.700000
pixdim4		0.000000
cal_max		0.000000
cal_min		0.000000
file_type	NIFTI-1+

HC003//nodif_brain_mask
data_type	INT16
dim1		120
dim2		120
dim3		84
dim4		1
datatype	4
pixdim1		1.700000
pixdim2		1.700000
pixdim3		1.700000
pixdim4		0.000000
cal_max		0.000000
cal_min		0.000000
file_type	NIFTI-1+

 num lines in HC003//bvals 
1
 num words in HC003//bvals 
137
 num lines in HC003//bvecs 
3
 num words in HC003//bvecs 
411

BEDPOSTXデータのチェックができたら、BEDPOSTXを実行する。

$ bedpostx HC003/
subjectdir is /home/neuro/Documents/Yuya_S/temp/bedpostx/bedpostx_dir/HC003
Making bedpostx directory structure
Queuing preprocessing stages
...

BEDPOSTXを実行すると、「.bedpostX」フォルダが生成されこれがBEDPOSTXの生成ファイルである。

$ ls
HC003  HC003.bedpostX

BEDPOSTXで出力されるファイルは次の通り。

  • merged_th<i>samples – 4D volume – Samples from the distribution on theta
  • merged_ph<i>samples – 4D volume – Samples from the distribution on phi
  • theta and phi together represent the principal diffusion direction in spherical polar co-ordinates
  • merged_f<i>samples – 4D volume – Samples from the distribution on anisotropic volume fraction (see technical report).
  • mean_th<i>samples – 3D Volume – Mean of distribution on theta
  • mean_ph<i>samples – 3D Volume – Mean of distribution on phi
  • mean_f<i>samples – 3D Volume – Mean of distribution on f anisotropy. Note that in each voxel, fibres are ordered according to a decreasing mean f-value
  • an_dsamples – 3D Volume – Mean of distribution on diffusivity d
  • mean_d_stdsamples – 3D Volume – Mean of distribution on diffusivity variance parameter d_std (not produced if –model=1)
  • mean_S0samples – 3D Volume – Mean of distribution on T2w baseline signal intensity S0
  • dyads<i> – Mean of PDD distribution in vector form. Note that this file can be loaded into FSLeyes for easy viewing of diffusion directions
  • dyads<i>_dispersion – 3D Volume – Uncertainty on the estimated fibre orientation. Characterizes how wide the orientation distribution is around the respective PDD.(how is this calculated?)
  • nodif_brain_mask – binary mask created from nodif_brain – copied from input directory

結果を確認するには、以下のコマンドを実行。

cd HC003.bedpostX
fsleyes mean_fsumsamples.nii.gz \
  dyads1.nii.gz         -ot linevector -xc 1 0 0 -yc 1 0 0 -zc 1 0 0 -lw 2 \
  dyads2_thr0.05.nii.gz -ot linevector -xc 0 1 0 -yc 0 1 0 -zc 0 1 0 -lw 2 \
  dyads3_thr0.05.nii.gz -ot linevector -xc 0 0 1 -yc 0 0 1 -zc 0 0 1 -lw 2

【PyTorch】サンプル⑨ 〜 動的グラフ 〜


1. 目的
2. 前準備
3. 計算グラフ
4. パッケージのインポート
5. モデルの定義
6. 使用するデータ
7. ニューラルネットワークのモデルの構築
8. 損失関数の定義
9. 最適化関数の定義
10. 学習回数
11. モデルへのデータ入出力 (Forward pass)
12. 損失(loss)の計算
13. 勾配の初期化
14. 勾配の計算
15. パラメータ(Weight)の更新
16. 実行
16.1. 9_dynamic_graph.py


1. 目的

  • PyTorchの特徴の一つである動的グラフに挑戦する。

2. 前準備

PyTorchのインストールはこちらから。

初めて、Google Colaboratoryを使いたい方は、こちらをご覧ください。

3. 計算グラフ

計算グラフには、静的グラフ動的グラフがあります。

TensorFlowやTheanoといったフレームワークは静的グラフを用いていますが、それに対してPyTorchは動的グラフを採用しています。

静的グラフと動的グラフの説明についてはこちらをご覧ください。

簡単に言うと、静的グラフは、事前にニューラルネットワーク構造を決めてから学習・推定を実行します。この時、一度決めたネットワーク構造は学習や推定時に変更することができません。

これに対し、動的グラフは、ネットワーク構造を定義する時点でネットワーク構造を固定する必要はなく、ネットワークに入力されるデータの様子をみて、ネットワークを構造を適宜変えることができます。故に、動的な計算グラフになります。

この動的グラフにより、柔軟な学習や推測が可能となります。

4. パッケージのインポート

import random
import torch

5. モデルの定義

【PyTorch】サンプル⑧ 〜 複雑なモデルの構築方法 〜で紹介した方法でニューラルネットワークモデルを構築していきます。

これまでは、レイヤ(層)を積み重ねるようにして、ニューラルネットワークモデルを定義していましたが、今回はネットワークモデルをクラスを使って定義します。クラスについては、こちらが参考になりました。

簡単のために、線形結合のみのネットワークを構築していきます。

クラスの名前をDynamicNetとします。引数torch.nn.Moduleはお決まりです。

class DynamicNet(torch.nn.Module):

コンストラクタで2層の線形結合層をインスタンス化します。

コンストラクタとインスタンスについては、こちらが参考になりました。

    def __init__(self, D_in, H, D_out):
        super(DynamicNet, self).__init__()
        self.input_linear = torch.nn.Linear(D_in, H)
        self.middle_linear = torch.nn.Linear(H, H)
        self.output_linear = torch.nn.Linear(H, D_out)

コンストラクタで定義された線形結合層をつかってモデルの予測値y_predを計算します。

テンソルの演算式は、forwardオブジェクトで自由に定義できます。

このチュートリアルでは、動的グラフの特徴を感じてもらうために、不思議なモデルを作っていきます。

    def forward(self, x):
        h_relu = self.input_linear(x).clamp(min=0)
        for _ in range(random.randint(0, 3)):
            h_relu = self.middle_linear(h_relu).clamp(min=0)
        y_pred = self.output_linear(h_relu)
        return y_pred

ポイントは、この部分です。

        for _ in range(random.randint(0, 3)):
            h_relu = self.middle_linear(h_relu).clamp(min=0)

この部分でやっていることは、forループの回数を0〜3回をランダムに決めて、中間層の線形結合層の数を随時変更しています。

6. 使用するデータ

バッチサイズNを64、入力の次元D_inを1000、隠れ層の次元Hを100、出力の次元D_outを10とします。

# N is batch size; D_in is input dimension;
# H is hidden dimension; D_out is output dimension.
N, D_in, H, D_out = 64, 1000, 100, 10

入力xと予測したいyを乱数で定義します。

# Create random input and output data
x = torch.randn(N, D_in)
y = torch.randn(N, D_out)

7. ニューラルネットワークのモデルの構築

先程作成したニューラルネットワークモデルのクラスDynamicNetをつかってモデルmodelを定義します。

model = DynamicNet(D_in, H, D_out)

8. 損失関数の定義

二乗誤差をこのモデルの損失関数とします。

reductionのデフォルトはmeanですので、何も指定しなければtorch.nn.MSELossは平均二乗誤差を返します。

reduction=sumとした場合は、累積二乗誤差を算出します。

criterion = torch.nn.MSELoss(reduction='sum')

9. 最適化関数の定義

optimパッケージを使って最適化関数としてMomentum SGDを定義します。

lrは学習率です。

optimizer = torch.optim.SGD(model.parameters(), lr=1e-4, momentum=0.9)

10. 学習回数

学習回数は500回にします。

for t in range(500):

11. モデルへのデータ入出力 (Forward pass)

定義したニューラルネットワークモデルmodelへデータxを入力し、予測値y_predを取得します。

    y_pred = model(x)

12. 損失(loss)の計算

定義した損失関数Momentum SGDで予測値y_predと真値yとの間の損失を計算します。

    loss = criterion(y_pred, y)
    if t % 100 == 99:
        print(t, loss.item())

13. 勾配の初期化

逆伝播(backward)させる前に、モデルのパラメータが持つ勾配を0(ゼロ)で初期化します。

    optimizer.zero_grad()

14. 勾配の計算

backwardメソッドでモデルパラメータ(Weight)の勾配を算出します。

    loss.backward()

15. パラメータ(Weight)の更新

stepメソッドでモデルのパラメータを更新します。

    optimizer.step()

16. 実行

以下のコードを9_dynamic_graph.pyとして保存します。

16.1. 9_dynamic_graph.py

import random
import torch


class DynamicNet(torch.nn.Module):
    def __init__(self, D_in, H, D_out):
        super(DynamicNet, self).__init__()
        self.input_linear = torch.nn.Linear(D_in, H)
        self.middle_linear = torch.nn.Linear(H, H)
        self.output_linear = torch.nn.Linear(H, D_out)

    def forward(self, x):
        h_relu = self.input_linear(x).clamp(min=0)
        for _ in range(random.randint(0, 3)):
            h_relu = self.middle_linear(h_relu).clamp(min=0)
        y_pred = self.output_linear(h_relu)
        return y_pred


# N is batch size; D_in is input dimension;
# H is hidden dimension; D_out is output dimension.
N, D_in, H, D_out = 64, 1000, 100, 10

# Create random Tensors to hold inputs and outputs
x = torch.randn(N, D_in)
y = torch.randn(N, D_out)

# Construct our model by instantiating the class defined above
model = DynamicNet(D_in, H, D_out)

# Construct our loss function and an Optimizer.
criterion = torch.nn.MSELoss(reduction='sum')
optimizer = torch.optim.SGD(model.parameters(), lr=1e-4, momentum=0.9)
for t in range(500):
    # Forward pass: Compute predicted y by passing x to the model
    y_pred = model(x)

    # Compute and print loss
    loss = criterion(y_pred, y)
    if t % 100 == 99:
        print(t, loss.item())

    # Zero gradients, perform a backward pass, and update the weights.
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

コードを保存したら、実行してみましょう。

左の数字が学習回数、右の数値がモデルの推定値と実際の答えと二乗誤差です。

python3 9_dynamic_graph.py
99 3.707427978515625
199 1.2958085536956787
299 1.837145209312439
399 0.9760080575942993
499 0.43565574288368225

参考のために、静的グラフの場合の結果を載せておきます。

動的グラフの方が損失の収束が早いことがわかります。

In:

python3 9_static_graph.py

Out:

99 2.5070412158966064
199 0.04455046355724335
299 0.0011508199386298656
399 3.597284376155585e-05
499 1.361027443635976e-06

【PyTorch】サンプル⑧ 〜 複雑なモデルの構築方法 〜


1. 目的
2. 前準備
3. PyTorchのインポート
4. モデルの定義
5. 使用するデータ
6. ニューラルネットワークのモデルを構築
7. 損失関数の定義
8. 最適化関数の定義
9. 学習回数
10. モデルへのデータ入出力 (Forward pass)
11. 損失(loss)の計算
12. 勾配の初期化
13. 勾配の計算
14. 実行
14.1. 8_class_model.py
15. 終わりに


1. 目的

このチュートリアルに至るまでは、ニューラルネットワークモデルの定義を積み木を積み重ねるように単純なシーケンスtorch.nn.Sequentialで構築していました。

このtorch.nn.Sequentialを用いた方法は、モデルの定義が簡単である反面、ネットワーク構造も簡素なものしか作ることができません。

例えば、torch.nn.Sequentialでは、ResNetネットワーク構造を構築することがきません。

(Credit:Deep Residual Learning for Image Recognition. Kaiming He, Xiangyu Zhang, Shaoqing Ren, Jian Sun. arXiv:1512.03385 [cs.CV]
(or arXiv:1512.03385v1 [cs.CV] for this version) )

このチュートリアルでは、PyTorch: Custom nn Modulesを参考に、より複雑なニューラルネットワークのモデルが構築できる方法を紹介します。

2. 前準備

PyTorchのインストールはこちらから。

初めて、Google Colaboratoryを使いたい方は、こちらをご覧ください。

3. PyTorchのインポート

import torch

4. モデルの定義

これまでは、レイヤ(層)を積み重ねるようにして、ニューラルネットワークモデルを定義していましたが、今回はネットワークモデルをクラスを使って定義します。クラスについては、こちらが参考になりました。

簡単のために、線形結合が2層のネットワークを構築していきます。

クラスの名前をTwoLayerNetとします。引数torch.nn.Moduleはお決まりです。

class TwoLayerNet(torch.nn.Module):

コンストラクタで2層の線形結合層をインスタンス化します。

コンストラクタとインスタンスについては、こちらが参考になりました。

    def __init__(self, D_in, H, D_out):
        super(TwoLayerNet, self).__init__()
        self.linear1 = torch.nn.Linear(D_in, H)
        self.linear2 = torch.nn.Linear(H, D_out)

コンストラクタで定義された線形結合層をつかってモデルの予測値y_predを計算します。

テンソルの演算式は、forwardオブジェクトで自由に定義できます。

    def forward(self, x):
        h_relu = self.linear1(x).clamp(min=0)
        y_pred = self.linear2(h_relu)
        return y_pred

5. 使用するデータ

バッチサイズNを64、入力の次元D_inを1000、隠れ層の次元Hを100、出力の次元D_outを10とします。

# N is batch size; D_in is input dimension;
# H is hidden dimension; D_out is output dimension.
N, D_in, H, D_out = 64, 1000, 100, 10

入力xと予測したいyを乱数で定義します。

# Create random input and output data
x = torch.randn(N, D_in)
y = torch.randn(N, D_out)

6. ニューラルネットワークのモデルを構築

先程作成したニューラルネットワークモデルのクラスTwoLayerNetをつかってモデルを定義します。

model = TwoLayerNet(D_in, H, D_out)

7. 損失関数の定義

二乗誤差をこのモデルの損失関数とします。

reductionのデフォルトはmeanですので、何も指定しなければtorch.nn.MSELossは平均二乗誤差を返します。

reduction=sumとした場合は、累積二乗誤差を算出します。

criterion = torch.nn.MSELoss(reduction='sum')

8. 最適化関数の定義

optimパッケージを使って最適化関数として確率勾配降下法(SGD: stochastic gradient descent)を定義します。

lrは学習率です。

optimizer = torch.optim.SGD(model.parameters(), lr=1e-4)

9. 学習回数

学習回数は500回にします。

for t in range(500):

10. モデルへのデータ入出力 (Forward pass)

定義したニューラルネットワークモデルmodelへデータxを入力し、予測値y_predを取得します。

    y_pred = model(x)

11. 損失(loss)の計算

定義した損失関数SGDで予測値y_predと真値yとの間の損失を計算します。

    loss = criterion(y_pred, y)
    if t % 100 == 99:
        print(t, loss.item())

12. 勾配の初期化

逆伝播(backward)させる前に、モデルのパラメータが持つ勾配を0(ゼロ)で初期化します。

    optimizer.zero_grad()

13. 勾配の計算

backwardメソッドでモデルパラメータ(Weight)の勾配を算出します。

    loss.backward()

パラメータ(Weight)の更新

stepメソッドでモデルのパラメータを更新します。

    optimizer.step()

14. 実行

以下のコードを8_class_model.pyとして保存します。

14.1. 8_class_model.py

import torch


class TwoLayerNet(torch.nn.Module):
    def __init__(self, D_in, H, D_out):
        super(TwoLayerNet, self).__init__()
        self.linear1 = torch.nn.Linear(D_in, H)
        self.linear2 = torch.nn.Linear(H, D_out)

    def forward(self, x):
        h_relu = self.linear1(x).clamp(min=0)
        y_pred = self.linear2(h_relu)
        return y_pred


# N is batch size; D_in is input dimension;
# H is hidden dimension; D_out is output dimension.
N, D_in, H, D_out = 64, 1000, 100, 10

# Create random Tensors to hold inputs and outputs
x = torch.randn(N, D_in)
y = torch.randn(N, D_out)

# Construct our model by instantiating the class defined above
model = TwoLayerNet(D_in, H, D_out)

# Construct our loss function and an Optimizer.
criterion = torch.nn.MSELoss(reduction='sum')
optimizer = torch.optim.SGD(model.parameters(), lr=1e-4)
for t in range(500):
    # Forward pass: Compute predicted y by passing x to the model
    y_pred = model(x)

    # Compute and print loss
    loss = criterion(y_pred, y)
    if t % 100 == 99:
        print(t, loss.item())

    # Zero gradients, perform a backward pass, and update the weights.
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

コードを保存したら、実行してみましょう。
In:

python3 8_class_model.py 

Out:

99 2.5070412158966064
199 0.04455046355724335
299 0.0011508199386298656
399 3.597284376155585e-05
499 1.361027443635976e-06

左の数字が学習回数、右の数値がパーセプトロンの推定値と実際の答えと二乗誤差です。

学習を重ねるごとに、二乗誤差が小さくなることがわかります。

15. 終わりに

今回は、クラスを使ってニューラルネットワークモデルを定義しました。

より複雑なモデルを構築する場合には、コンストラクタに線形結合層、畳み込み層、プーリング層などのコンポーネントを増やし、それらをforwardオブジェクトで、自由に組み替えてみてください。

【PyTorch】サンプル⑦ 〜 optim パッケージ 〜


1. 目的
2. 前準備
3. optim パッケージ
4. PyTorchのインポート
5. 使用するデータ
6. ニューラルネットワークのモデルを定義
7. 損失(loss)の定義
8. 学習パラメータ
9. 最適化関数(optimizer)の定義
10. 学習回数
11. モデルへのデータ入出力 (Forward pass)
12. 損失(loss)の計算
13. 勾配の初期化
14. 勾配の計算
15. パラメータ(Weight)の更新
16. 実行
16.1. 7_optim_package.py


1. 目的

  • PyTorch: optimを参考にPyTorchのoptimパッケージを使って最適化関数(optimizer)を定義する。

2. 前準備

PyTorchのインストールはこちらから。

初めて、Google Colaboratoryを使いたい方は、こちらをご覧ください。

3. optim パッケージ

optimは、最適化アルゴリズムの定義に用います。

これまでは、モデルのパラメータ(weight)を更新するために、自分の手で確率勾配降下法(SGD: stochastic gradient descent)のコードを作っていました。

今回のチュートリアルでは、optimパッケージを使ってパラメータを更新する最適化関数(optimizer)を定義していきます。

optimパッケージには、SGD+momentum、RMSProp, Adamなどディープラーニング界でよく使われる最適化アルゴリズムが複数あります。

4. PyTorchのインポート

import torch

5. 使用するデータ

バッチサイズNを64、入力の次元D_inを1000、隠れ層の次元Hを100、出力の次元D_outを10とします。

# N is batch size; D_in is input dimension;
# H is hidden dimension; D_out is output dimension.
N, D_in, H, D_out = 64, 1000, 100, 10

入力(x)と予測したい(y)を乱数で定義します。

# Create random input and output data
x = torch.randn(N, D_in)
y = torch.randn(N, D_out)

6. ニューラルネットワークのモデルを定義

前回のnnパッケージのチュートリアルと同じように、ニューラルネットワークのモデルをnnパッケージを用いて定義します。

input > Linear(線型結合) > ReLU(活性化関数) > Linear(線型結合) > outputの順に層を積み重ねます。

model = torch.nn.Sequential(
    torch.nn.Linear(D_in, H),
    torch.nn.ReLU(),
    torch.nn.Linear(H, D_out),
)

7. 損失(loss)の定義

二乗誤差をnnパッケージを用いて計算します。

reductionのデフォルトはmeanですので、何も指定しなければtorch.nn.MSELossは平均二乗誤差を返します。

reduction=sumとした場合は、累積二乗誤差を算出します。

loss_fn = torch.nn.MSELoss(reduction='sum')

8. 学習パラメータ

学習率learning_rate1e-4とします。

learning_rate = 1e-4

9. 最適化関数(optimizer)の定義

最適化関数の定義は、torch.optimを使って簡単にできます。

このチュートリアルでは、最適化関数としてAdamを選択しています。

torch.optim.Adamの引数は、モデルのパラメータmodel.parameters()と学習率lr=learning_rateです。

optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)

10. 学習回数

学習回数を500回とします。

for t in range(500):

11. モデルへのデータ入出力 (Forward pass)

定義したニューラルネットワークモデルmodelへデータxを入力し、予測値y_predを取得します。

    y_pred = model(x)

12. 損失(loss)の計算

定義した損失関数で予測値y_predと真値yとの間の損失を計算します。

    loss = loss_fn(y_pred, y)
    if t % 100 == 99:
        print(t, loss.item())

13. 勾配の初期化

逆伝播(backward)させる前に、モデルのパラメータが持つ勾配を0(ゼロ)で初期化します。

    optimizer.zero_grad()

(参考) optimパッケージを使わない場合、以下のように記述していました。

    model.zero_grad()

14. 勾配の計算

backwardメソッドでモデルパラメータ(Weight)の勾配を算出します。

    loss.backward()

15. パラメータ(Weight)の更新

stepメソッドでモデルのパラメータを更新します。

    optimizer.step()

(参考) optimパッケージを使わない場合、以下のように記述していました。

    with torch.no_grad():
        for param in model.parameters():
            param -= learning_rate param.grad

16. 実行

以下のコードを7_optim_package.pyとして保存します。

16.1. 7_optim_package.py

import torch

# N is batch size; D_in is input dimension;
# H is hidden dimension; D_out is output dimension.
N, D_in, H, D_out = 64, 1000, 100, 10

# Create random Tensors to hold inputs and outputs
x = torch.randn(N, D_in)
y = torch.randn(N, D_out)

# Use the nn package to define our model and loss function.
model = torch.nn.Sequential(
    torch.nn.Linear(D_in, H),
    torch.nn.ReLU(),
    torch.nn.Linear(H, D_out),
)
loss_fn = torch.nn.MSELoss(reduction='sum')

# Use the optim package to define an Optimizer that will update the weights of
# the model for us.
learning_rate = 1e-4
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)
for t in range(500):
    # Forward pass: compute predicted y by passing x to the model.
    y_pred = model(x)

    # Compute and print loss.
    loss = loss_fn(y_pred, y)
    if t % 100 == 99:
        print(t, loss.item())

    # Before the backward pass, use the optimizer object to zero all of the
    # gradients for the variables it will update (which are the learnable
    # weights of the model).
    optimizer.zero_grad()

    # Backward pass: compute gradient of the loss with respect to model
    # parameters
    loss.backward()

    # Calling the step function on an Optimizer makes an update to its
    # parameters
    optimizer.step()

保存ができたら実行しましょう。

左の数字が学習回数、右の数値がパーセプトロンの推定値と実際の答えと二乗誤差です。

学習を重ねるごとに、二乗誤差が小さくなることがわかります。

In:

python3 7_optim_package.py 

Out:

99 54.53140640258789
199 0.6615686416625977
299 0.004912459757179022
399 3.133853169856593e-05
499 1.0647939063801459e-07

【PyTorch】サンプル⑥ 〜 nn パッケージ 〜


1. 目的
2. 前準備
3. nn パッケージ
4. PyTorchのインポート
5. 使用するデータ
6. ニューラルネットワークのモデルを定義
7. 損失(loss)の定義
8. 学習パラメータ
9. モデルへのデータ入出力 (Forward pass)
10. 損失(loss)の計算
11. 勾配の初期化
12. 勾配の計算
13. パラメータ(Weight)の更新
14. 実行
14.1. 6_nn_package.py
15. 終わりに


1. 目的

  • PyTorch: nnを参考にPyTorchのnnパッケージを扱う。
  • nnパッケージの便利さを感じる。

2. 前準備

PyTorchのインストールはこちらから。

初めて、Google Colaboratoryを使いたい方は、こちらをご覧ください。

3. nn パッケージ

nnは、ニューラルネットワークの構築に用いる。

PyTorchの自動微分autogradによって、計算グラフやパラメータの勾配を簡単に計算することができます。ですが、自動微分だけで複雑なニューラルネットワークを定義するのは困難です。そこで活躍するのがnnパッケージです。

nnパッケージを用いて、ニューラルネットワークを一つのモジュールとして定義することができます。

4. PyTorchのインポート

import torch

5. 使用するデータ

バッチサイズNを64、入力の次元D_inを1000、隠れ層の次元Hを100、出力の次元D_outを10とします。

# N is batch size; D_in is input dimension;
# H is hidden dimension; D_out is output dimension.
N, D_in, H, D_out = 64, 1000, 100, 10

入力(x)と予測したい(y)を乱数で定義します。

# Create random input and output data
x = torch.randn(N, D_in)
y = torch.randn(N, D_out)

6. ニューラルネットワークのモデルを定義

ニューラルネットワークのモデルをnnパッケージを用いて定義します。

定義の仕方は、大きく2つありますがここでは一番簡単なtorch.nn.Sequentialを使います。

作り方は簡単で、任意の層を積み重ねていくだけです。この例では、input > Linear(線型結合) > ReLU(活性化関数) > Linear(線型結合) > outputの順に層が積み重なっています。

model = torch.nn.Sequential(
    torch.nn.Linear(D_in, H),
    torch.nn.ReLU(),
    torch.nn.Linear(H, D_out),
)

7. 損失(loss)の定義

二乗誤差もnnパッケージを用いて計算することができます。

reductionのデフォルトはmeanですので、何も指定しなければtorch.nn.MSELossは平均二乗誤差を返します。

reduction=sumとした場合は、累積二乗誤差を算出します。

loss_fn = torch.nn.MSELoss(reduction='sum')

(参考) nnパッケージを使う前は以下のように記述していた。

    loss = (y_pred - y).pow(2).sum()

8. 学習パラメータ

学習率を1e-4として、学習回数を500回とします。

learning_rate = 1e-4
for t in range(500):

9. モデルへのデータ入出力 (Forward pass)

定義したニューラルネットワークモデルへデータxを入力し、予測値y_predを取得します。

y_pred = model(x)

(参考) nnパッケージを使う前は以下のように記述していた。

    y_pred = x.mm(w1).clamp(min=0).mm(w2)

10. 損失(loss)の計算

定義した損失関数で予測値y_predと真値yとの間の損失を計算します。

    loss = loss_fn(y_pred, y)
    if t % 100 == 99:
        print(t, loss.item())

11. 勾配の初期化

逆伝播(backward)させる前に、モデルのパラメータが持つ勾配を0(ゼロ)で初期化します。

    model.zero_grad()

12. 勾配の計算

backwardメソッドでモデルパラメータ(Weight)の勾配を算出します。

    loss.backward()

13. パラメータ(Weight)の更新

確率勾配降下法(SGD: stochastic gradient descent)で、Weightを更新する。

    with torch.no_grad():
        for param in model.parameters():
            param -= learning_rate param.grad

(参考) nnパッケージを使う前は以下のように記述していた。

    with torch.no_grad():
        w1 -= learning_rate w1.grad
        w2 -= learning_rate w2.grad

14. 実行

以下のコードを6_nn_package.pyとして保存します。

14.1. 6_nn_package.py

import torch

# N is batch size; D_in is input dimension;
# H is hidden dimension; D_out is output dimension.
N, D_in, H, D_out = 64, 1000, 100, 10

# Create random Tensors to hold inputs and outputs
x = torch.randn(N, D_in)
y = torch.randn(N, D_out)

# Use the nn package to define our model as a sequence of layers. 
model = torch.nn.Sequential(
    torch.nn.Linear(D_in, H),
    torch.nn.ReLU(),
    torch.nn.Linear(H, D_out),
)

# The nn package also contains definitions of popular loss functions; in this
# case we will use Mean Squared Error (MSE) as our loss function.
loss_fn = torch.nn.MSELoss(reduction='sum')

learning_rate = 1e-4
for t in range(500):
    # Forward pass: compute predicted y by passing x to the model. 
    y_pred = model(x)

    # Compute and print loss.
    loss = loss_fn(y_pred, y)
    if t % 100 == 99:
        print(t, loss.item())

    # Zero the gradients before running the backward pass.
    model.zero_grad()

    # Backward pass: compute gradient of the loss with respect to all the learnable
    loss.backward()

    # Update the weights using gradient descent.
    with torch.no_grad():
        for param in model.parameters():
            param -= learning_rate param.grad

保存ができたら実行しましょう。

左の数字が学習回数、右の数値がパーセプトロンの推定値と実際の答えと二乗誤差です。

学習を重ねるごとに、二乗誤差が小さくなることがわかります。

$ python3 6_nn_package.py 
99 2.5003600120544434
199 0.06977272033691406
299 0.003307548351585865
399 0.00018405442824587226
499 1.1299152902211063e-05

15. 終わりに

多層のニューラルネットワークには、膨大な量のパラメータが存在しています。

nnパッケージを用いる前は、各パラメータごとに勾配計算やパラメータの更新などをしていましたが、それでは記述が困難です。

nnパッケージを用いることで、楽に勾配計算やパラメータの更新等が実行できることが感じてもらえたら嬉しいです(^^)!。

【PyTorch】サンプル⑤ 〜Static Graphs(静的グラフ)〜


1. 目的
2. 前準備
3. 静的グラフ動的グラフ
4. パッケージのインポート
5. 使用するデータとプレースフォルダplaceholdersの用意
6. 重み付けの初期化
7. データ入力 (Forward pass)
8. 損失の計算
9. 重み(Weight)の勾配計算
10. 重み(w)の更新
11. 計算グラフの実行
12. 実行
12.1. 5_static_graphs.py


1. 目的

Deep Learning(ディープラーニング)で、よく使われるTensorFlowを使って計算グラフの設計からニューラルネットワークの学習をする。

PyTorchが採用している動的グラフを理解するために、静的グラフの代表であるTensorFlowに触れる。

2. 前準備

PyTorchのインストールはこちらから。

初めて、Google Colaboratoryを使いたい方は、こちらをご覧ください。

3. 静的グラフ動的グラフ

TensorFlowとPyTorchの大きな違いは、TensorFlowが静的計算グラフ(define-by-run)を採用しているのに対し、PyTorchは動的計算グラフ(define-and-run)を使用している点です。

静的グラフと動的グラフの説明についてはこちらをご覧ください。

簡潔に言うと、静的グラフは、事前にネットワーク構造を決めてから学習・推定を実行します。

これに対し、動的グラフは、ネットワーク構造を定義する時点でネットワーク構造を固定する必要はなく、ネットワークに入力されるデータの様子をみて、ネットワークを構造を適宜変えることができます。故に、動的な計算グラフになります。

この動的グラフにより、柔軟な学習や推測が可能となります。

4. パッケージのインポート

TensorFlowとNumPyをimportする。

import tensorflow as tf
import numpy as np

5. 使用するデータとプレースフォルダplaceholdersの用意

バッチサイズNを64、入力の次元D_inを1000、隠れ層の次元Hを100、出力の次元D_outを10とします。

# N is batch size; D_in is input dimension;
# H is hidden dimension; D_out is output dimension.
N, D_in, H, D_out = 64, 1000, 100, 10

入力値xと予測したい値yの入れ物tf.placeholdersを用意する。

x = tf.placeholder(tf.float32, shape=(None, D_in))
y = tf.placeholder(tf.float32, shape=(None, D_out))

6. 重み付けの初期化

乱数random_normalメソッドを使って重みwを初期化します。

w1 = tf.Variable(tf.random_normal((D_in, H)))
w2 = tf.Variable(tf.random_normal((H, D_out)))

7. データ入力 (Forward pass)

入力xと重みw1を掛け算matmulすることで重み付けをしますh

重み付けした値(h)の要素から、maximum(h,tf.zeros(1))で、0以上のものは残し、0以下のものは0とします。

最後に、重みw2を掛け合わせてmatmul重み付けします。この値がモデルの予測値y_predとなります。

h = tf.matmul(x, w1)
h_relu = tf.maximum(h, tf.zeros(1))
y_pred = tf.matmul(h_relu, w2)

8. 損失の計算

モデルが予測した値y_predと答えyとの間の二乗誤差を計算しこれを損失lossとします。

tf.reduce_sumは、テンソルの足し算総和の計算をします。

loss = tf.reduce_sum((y - y_pred) *2.0)

9. 重み(Weight)の勾配計算

これより先は、モデルが予測した値y_predと答えyを見比べて、正しく答えyを予測できるようにモデルのパラメータを更新していきます。

具体的には、重みw1, w2の勾配(grad_w1, grad_w2)を計算します。

TensorFlowでは、gradientsメソッドで勾配が計算できます。

grad_w1, grad_w2 = tf.gradients(loss, [w1, w2])

10. 重み(w)の更新

計算した勾配(grad_w1, grad_w2)をもとに、重み(w1, w2)を更新します。自動微分を用いた場合のパラメータの更新は、torch.no_grad()で対象のパラメータをくくることで計算できます。

確率勾配降下法(SGD: stochastic gradient descent)は、重みを更新する上でよく使われる最適化アルゴリズムで、以下の式で表されます。

weight = weight - learning_rate gradient

今回は、学習率を1e-6、新しい重みw1, w2new_w1new_w2にして、SGDを用いて重みを更新します。

learning_rate = 1e-6
new_w1 = w1.assign(w1 - learning_rate grad_w1)
new_w2 = w2.assign(w2 - learning_rate grad_w2)

11. 計算グラフの実行

設計した計算グラフをこのコード部分で実行する。

with tf.Session() as sess:
    # Run the graph once to initialize the Variables w1 and w2.
    sess.run(tf.global_variables_initializer())

    # Create numpy arrays holding the actual data for the inputs x and targets
    # y
    x_value = np.random.randn(N, D_in)
    y_value = np.random.randn(N, D_out)
    for t in range(500):
        # Execute the graph many times.
        loss_value, _, _ = sess.run([loss, new_w1, new_w2],
                                    feed_dict={x: x_value, y: y_value})
        if t % 100 == 99:
            print(t, loss_value)

TensorFlowで、計算グラフを実行するには、with tf.Session() as sess:でSessionオブジェクトを作成する。

with tf.Session() as sess:

重みw1w2を初期化するため、Sessionをrunする。

    # Run the graph once to initialize the Variables w1 and w2.
    sess.run(tf.global_variables_initializer())

ニューラルネットワークに入力するデータx_valueとニューラルネットワークが予測すべき値y_valueを乱数np.random.randn`で決める。

    # Create numpy arrays holding the actual data for the inputs x and targets
    # y
    x_value = np.random.randn(N, D_in)
    y_value = np.random.randn(N, D_out)

今回は、学習回数を500回とする。

各学習ごとに、損失関数loss、更新された重みw1w2から損失値(二乗誤差)を計算する。

100回学習するごとに、学習回数tと二乗誤差loss_valueを出力する。

    for t in range(500):
        loss_value, _, _ = sess.run([loss, new_w1, new_w2],
                                    feed_dict={x: x_value, y: y_value})
        if t % 100 == 99:
            print(t, loss_value)

12. 実行

以下のコードを5_static_graphs.pyとして保存します。

12.1. 5_static_graphs.py

import tensorflow as tf
import numpy as np

# First we set up the computational graph:

# N is batch size; D_in is input dimension;
# H is hidden dimension; D_out is output dimension.
N, D_in, H, D_out = 64, 1000, 100, 10

# Create placeholders for the input and target data; these will be filled
# with real data when we execute the graph.
x = tf.placeholder(tf.float32, shape=(None, D_in))
y = tf.placeholder(tf.float32, shape=(None, D_out))

# Create Variables for the weights and initialize them with random data.
# A TensorFlow Variable persists its value across executions of the graph.
w1 = tf.Variable(tf.random_normal((D_in, H)))
w2 = tf.Variable(tf.random_normal((H, D_out)))

# Forward pass
h = tf.matmul(x, w1)
h_relu = tf.maximum(h, tf.zeros(1))
y_pred = tf.matmul(h_relu, w2)

# Compute loss using operations on TensorFlow Tensors
loss = tf.reduce_sum((y - y_pred) *2.0)

# Compute gradient of the loss with respect to w1 and w2.
grad_w1, grad_w2 = tf.gradients(loss, [w1, w2])

# Update the weights using gradient descent.
learning_rate = 1e-6
new_w1 = w1.assign(w1 - learning_rate grad_w1)
new_w2 = w2.assign(w2 - learning_rate grad_w2)

# actually execute the graph.
with tf.Session() as sess:
    # Run the graph once to initialize the Variables w1 and w2.
    sess.run(tf.global_variables_initializer())

    # Create numpy arrays holding the actual data for the inputs x and targets
    # y
    x_value = np.random.randn(N, D_in)
    y_value = np.random.randn(N, D_out)
    for t in range(500):
        loss_value, _, _ = sess.run([loss, new_w1, new_w2],
                                    feed_dict={x: x_value, y: y_value})
        if t % 100 == 99:
            print(t, loss_value)

保存ができたら実行しましょう。

左の数字が学習回数、右の数値がパーモデルの推定値と実際の答えと二乗誤差です。

学習を重ねるごとに、二乗誤差が小さくなることがわかります。

$ python3 5_static_graphs.py
99 1597.0376
199 16.325699
299 0.24026018
399 0.0046504317
499 0.00029210464

【PyTorch】サンプル④ 〜Defining New autograd Functions(自動微分関数の定義)〜


1. 目的
2. 前準備
3. PyTorchのインポート
4. クラスの定義
5. データタイプとデバイスの選択
6. 使用するデータ
7. 重み付けの初期化
8. 学習
9. データの入力 (forward)
10. 損失の計算
11. 重み(Weight)の勾配計算
12. 重みの更新
13. 実行
13.1. 4_autograd_func.py


1. 目的

PyTorchのチュートリアルPyTorch: Defining New autograd Functionsを参考にPyTorchテンソル(tensor)と自動微分(autograd)を使って、損失(loss)や重み(weight)の計算をする。

前回の、【PyTorch】サンプル③ 〜TENSORS AND AUTOGRAD(テンソルと自動微分)〜では、自動微分の基本的な扱いをご紹介しました。

今回は、前回使用した自動微分のコードを関数化してみましょう。

2. 前準備

PyTorchのインストールはこちらから。

初めて、Google Colaboratoryを使いたい方は、こちらをご覧ください。

3. PyTorchのインポート

import torch

4. クラスの定義

自動微分のコード部分を以下のようなMyReLUというクラスで定義します。

class MyReLU(torch.autograd.Function):

    @staticmethod
    def forward(ctx, input):
        ctx.save_for_backward(input)
        return input.clamp(min=0)

    @staticmethod
    def backward(ctx, grad_output):
        input, = ctx.saved_tensors
        grad_input = grad_output.clone()
        grad_input[input < 0] = 0
        return grad_input

ここでは、torch.autograd.Functionを継承したサブクラスでMyReLUを定義します。

class MyReLU(torch.autograd.Function):

こちらが、パーセプトロンにデータをインプット(forward)する部分です。

@staticmethodはスタティックメソッドといって、そのクラスでしか使用しない関数を定義する時に使います。

また、スタティックメソッドの特徴として、クラスでよくあるselfを第一引数として取りません。

ctxは、逆伝播(backward)計算の際に用いる隠しオブジェクトです。

ctx.save_for_backwardメソッドを使うことで逆伝播計算に用いるオブジェクトのキャッシュを取って置くことができる。

input.clamp(min=0)で、入力されたinputの要素の中で0以下は0、0以上はそのままの要素の値として出力を返す。

    @staticmethod
    def forward(ctx, input):
        ctx.save_for_backward(input)
        return input.clamp(min=0)

この部分が、逆伝播計算の部分。

ctx.saved_tensorsで、パーセプトロンに入力された値inputを取り出します。

パーセプトロンからの出力勾配をgrad_output.clone()でコピーする。

    @staticmethod
    def backward(ctx, grad_output):
        input, = ctx.saved_tensors
        grad_input = grad_output.clone()
        grad_input[input < 0] = 0
        return grad_input
`

このように、PyTorchにおける自動微分関数の定義では、`forward`と`backward`をクラスのメソッドとして定義します。

###  5. <a name='-3'></a>データタイプとデバイスの選択
これから作成するテンソルのデータ型`dtype`を`float`にします。

テンソル計算をするデバイスは、`torch.device`で指定することができます。
今回は、CPUを使用することにします。
[python]
dtype = torch.float
device = torch.device("cpu")

GPUを使う方は、deviceの部分を以下のコードに変えて下さい。

device = torch.device("cuda:0") # Uncomment this to run on GPU

6. 使用するデータ

バッチサイズNを64、入力の次元D_inを1000、隠れ層の次元Hを100、出力の次元D_outを10とします。

# N is batch size; D_in is input dimension;
# H is hidden dimension; D_out is output dimension.
N, D_in, H, D_out = 64, 1000, 100, 10

入力(x)と予測したい(y)を乱数で定義します。

PyTorchテンソルを定義する場合には、どのデバイスでdevice、どのデータ型dtypeかを指定することができます。

# Create random input and output data
x = torch.randn(N, D_in, device=device, dtype=dtype)
y = torch.randn(N, D_out, device=device, dtype=dtype)

7. 重み付けの初期化

乱数を使って重みを初期化します。

自動微分をしたい場合には、初期化する時点でrequires_gradのフラグをTrueにしておきます。デフォルトはrequires_grad=Falseです。

# Randomly initialize weights
w1 = torch.randn(D_in, H, device=device, dtype=dtype, requires_grad=True)
w2 = torch.randn(H, D_out, device=device, dtype=dtype, requires_grad=True)

8. 学習

学習率を1e-6として、学習回数を500回とします。

learning_rate = 1e-6
for t in range(500):

9. データの入力 (forward)

データをパーセプトロンに入力して、パーセプトロンの予測値y_predを計算します。

以前の自動微分のサンプルでは、

    y_pred = x.mm(w1).clamp(min=0).mm(w2)

このように記述しましたが、今回は自動微分の関数をMyReLUとしてに定義してあるので、上のコードをMyReLUを用いて以下のように書き換えます。

    # Forward pass: compute predicted y
    relu = MyReLU.apply
    y_pred = relu(x.mm(w1)).mm(w2)

10. 損失の計算

パーセプトロンが予測した値(y_pred)と答え(y)との間の二乗誤差を計算しこれを損失(loss)とします。

損失の値を、tensor配列からスカラー値で取得したい場合には、item()メソッドを用います。

各学習回数ごとに、学習回数(t)と二乗誤差(loss)を表示します。

今回は、結果を見やすくするためにif t % 100 == 99:の部分で、学習回数100回ごとに結果を出力します。
やっていることは、学習回数(t)を100で割ったときのあまりが99であるときにprint(t, loss)を実行です。

    # Compute and print loss
    loss = (y_pred - y).pow(2).sum()
    if t % 100 == 99:
        print(t, loss.item())

11. 重み(Weight)の勾配計算

これより先は、パーセプトロンが予測した値(y_pred)と答え(y)を見比べて、正しく答え(y)を予測できるようにパーセプトロンのパラメータを更新していきます。

    loss.backward()

12. 重みの更新

計算した勾配(grad_w1, grad_w2)をもとに、重み(w1, w2)を更新します。自動微分を用いた場合のパラメータの更新は、torch.no_grad()で対象のパラメータをくくることで計算できます。

確率勾配降下法(SGD: stochastic gradient descent)は、重みを更新する上でよく使われる最適化アルゴリズムで、以下の式で表されます。

weight = weight - learning_rate gradient

SGDを用いてパラメータを更新するには、このように記述します。

一度更新した、パラメータはgrad.zero()でゼロに初期化します。

    with torch.no_grad():
        w1 -= learning_rate w1.grad
        w2 -= learning_rate w2.grad

        # Manually zero the gradients after updating weights
        w1.grad.zero_()
        w2.grad.zero_()

13. 実行

以下のコードを4_autograd_func.pyとして保存します。

13.1. 4_autograd_func.py

import torch

dtype = torch.float
device = torch.device("cpu")
# device = torch.device("cuda:0") # Uncomment this to run on GPU

# N is batch size; D_in is input dimension;
# H is hidden dimension; D_out is output dimension.
N, D_in, H, D_out = 64, 1000, 100, 10

# Create random Tensors to hold input and outputs.
x = torch.randn(N, D_in, device=device, dtype=dtype)
y = torch.randn(N, D_out, device=device, dtype=dtype)

# Create random Tensors for weights.
w1 = torch.randn(D_in, H, device=device, dtype=dtype, requires_grad=True)
w2 = torch.randn(H, D_out, device=device, dtype=dtype, requires_grad=True)

learning_rate = 1e-6
for t in range(500):
    # Forward pass: compute predicted y using operations on Tensors
    y_pred = x.mm(w1).clamp(min=0).mm(w2)

    # Compute and print loss using operations on Tensors.
    loss = (y_pred - y).pow(2).sum()
    if t % 100 == 99:
        print(t, loss.item())

    # Use autograd to compute the backward pass.
    loss.backward()

    # Manually update weights using gradient descent. Wrap in torch.no_grad()
    with torch.no_grad():
        w1 -= learning_rate w1.grad
        w2 -= learning_rate w2.grad

        # Manually zero the gradients after updating weights
        w1.grad.zero_()
        w2.grad.zero_()

保存ができたら実行しましょう。

左の数字が学習回数、右の数値がパーセプトロンの推定値と実際の答えと二乗誤差です。

学習を重ねるごとに、二乗誤差が小さくなることがわかります。

$ python3 4_autograd_func.py 
99 763.6893310546875
199 9.174347877502441
299 0.22351056337356567
399 0.007267479319125414
499 0.0004901751526631415

Ubuntu 20.04 の mini.iso

Ubuntu 20.04 の mini.iso はかなり隠れたところにあります。
こちらから入手できます。

http://archive.ubuntu.com/ubuntu/dists/focal/main/installer-amd64/current/legacy-images/netboot/

日本のミラーサイトはこちら。
http://jp.archive.ubuntu.com/ubuntu/dists/focal/main/installer-amd64/current/legacy-images/netboot/

いつも探すので備忘録として書いておきます。

【PyTorch】サンプル③ 〜TENSORS AND AUTOGRAD(テンソルと自動微分)〜


1. 目的
2. 前準備
3. 自動微分(AUTOGRAD)
4. PyTorchのインポート
5. データタイプとデバイスの選択
6. 使用するデータ
7. 重み付けの初期化
8. 学習
9. データの入力
10. 損失の計算
11. 重み(Weight)の勾配計算
12. 重みの更新
13. 実行
13.1. 3_autograd.py


1. 目的

PyTorchのチュートリアルPyTorch: Tensors and autogradを参考にPyTorchテンソル(tensor)と自動微分(autograd)を使って、損失(loss)や重み(weight)の計算をする。

これまでは、PyTorchに実装されている自動微分機能を使わずにニューラルネットワークのパラメータの勾配を計算していましたが、PyTorchの自動微分(autograd)機能を使うとパラメータの勾配計算簡単にすることができます。

2. 前準備

PyTorchのインストールはこちらから。

初めて、Google Colaboratoryを使いたい方は、こちらをご覧ください。

3. 自動微分(AUTOGRAD)

PyTorchにおけるテンソルは計算グラフでは、ノード(node)と表現されます。

例えば、テンソルxx.requires_grad=Trueとフラグを設定すると、x.gradでxの勾配を計算することができます。

4. PyTorchのインポート

import torch

5. データタイプとデバイスの選択

これから作成するテンソルのデータ型dtypefloatにします。

テンソル計算をするデバイスは、torch.deviceで指定することができます。
今回は、CPUを使用することにします。

dtype = torch.float
device = torch.device("cpu")

GPUを使う方は、deviceの部分を以下のコードに変えて下さい。

device = torch.device("cuda:0") # Uncomment this to run on GPU

6. 使用するデータ

バッチサイズNを64、入力の次元D_inを1000、隠れ層の次元Hを100、出力の次元D_outを10とします。

# N is batch size; D_in is input dimension;
# H is hidden dimension; D_out is output dimension.
N, D_in, H, D_out = 64, 1000, 100, 10

入力(x)と予測したい(y)を乱数で定義します。

PyTorchテンソルを定義する場合には、どのデバイスでdevice、どのデータ型dtypeかを指定することができます。

# Create random input and output data
x = torch.randn(N, D_in, device=device, dtype=dtype)
y = torch.randn(N, D_out, device=device, dtype=dtype)

7. 重み付けの初期化

乱数を使って重みを初期化します。

注目してほしい点は、requires_grad=Trueです。
自動微分をしたい場合には、初期化する時点でrequires_gradのフラグをTrueにしておきます。デフォルトはrequires_grad=Falseです。

# Randomly initialize weights
w1 = torch.randn(D_in, H, device=device, dtype=dtype, requires_grad=True)
w2 = torch.randn(H, D_out, device=device, dtype=dtype, requires_grad=True)

8. 学習

学習率を1e-6として、学習回数を500回とします。

learning_rate = 1e-6
for t in range(500):

9. データの入力

二次元テンソルの入力(x)と重み(w1)をmmで掛け算することで重み付けをします(h)。(一次元テンソルの積は、dotで計算できます。)

重み付けした値の要素から、clamp(min=~0)で、0以上の要素は残し、0以下のものは0とします。

最後に、重み(w2)を掛け合わせて重み付けしますmm(w2)
この値がパーセプトロンの予測値(y_pred)となります。

    # Forward pass: compute predicted y
    y_pred = x.mm(w1).clamp(min=0).mm(w2)

10. 損失の計算

パーセプトロンが予測した値(y_pred)と答え(y)との間の二乗誤差を計算しこれを損失(loss)とします。

損失の値を、tensor配列からスカラー値で取得したい場合には、item()メソッドを用います。

各学習回数ごとに、学習回数(t)と二乗誤差(loss)を表示します。

今回は、結果を見やすくするためにif t % 100 == 99:の部分で、学習回数100回ごとに結果を出力します。
やっていることは、学習回数(t)を100で割ったときのあまりが99であるときにprint(t, loss)を実行です。

    # Compute and print loss
    loss = (y_pred - y).pow(2).sum()
    if t % 100 == 99:
        print(t, loss.item())

11. 重み(Weight)の勾配計算

これより先は、パーセプトロンが予測した値(y_pred)と答え(y)を見比べて、正しく答え(y)を予測できるようにパーセプトロンのパラメータを更新していきます。

NumPyを用いたサンプルPyTorchを用いたサンプルでは、ガリガリ勾配の計算式を書いていましたが、自動微分を使えば、一行で勾配を計算できます

    loss.backward()

12. 重みの更新

計算した勾配(grad_w1, grad_w2)をもとに、重み(w1, w2)を更新します。自動微分を用いた場合のパラメータの更新は、torch.no_grad()で対象のパラメータをくくることで計算できます。

確率勾配降下法(SGD: stochastic gradient descent)は、重みを更新する上でよく使われる最適化アルゴリズムで、以下の式で表されます。

weight = weight - learning_rate gradient

SGDを用いてパラメータを更新するには、このように記述します。

一度更新した、パラメータはgrad.zero()でゼロに初期化します。

    with torch.no_grad():
        w1 -= learning_rate w1.grad
        w2 -= learning_rate w2.grad

        # Manually zero the gradients after updating weights
        w1.grad.zero_()
        w2.grad.zero_()

13. 実行

以下のコードを3_autograd.pyとして保存します。

13.1. 3_autograd.py

import torch

dtype = torch.float
device = torch.device("cpu")
# device = torch.device("cuda:0") # Uncomment this to run on GPU

# N is batch size; D_in is input dimension;
# H is hidden dimension; D_out is output dimension.
N, D_in, H, D_out = 64, 1000, 100, 10

# Create random Tensors to hold input and outputs.
x = torch.randn(N, D_in, device=device, dtype=dtype)
y = torch.randn(N, D_out, device=device, dtype=dtype)

# Create random Tensors for weights.
w1 = torch.randn(D_in, H, device=device, dtype=dtype, requires_grad=True)
w2 = torch.randn(H, D_out, device=device, dtype=dtype, requires_grad=True)

learning_rate = 1e-6
for t in range(500):
    # Forward pass: compute predicted y using operations on Tensors
    y_pred = x.mm(w1).clamp(min=0).mm(w2)

    # Compute and print loss using operations on Tensors.
    loss = (y_pred - y).pow(2).sum()
    if t % 100 == 99:
        print(t, loss.item())

    # Use autograd to compute the backward pass.
    loss.backward()

    # Manually update weights using gradient descent. Wrap in torch.no_grad()
    with torch.no_grad():
        w1 -= learning_rate w1.grad
        w2 -= learning_rate w2.grad

        # Manually zero the gradients after updating weights
        w1.grad.zero_()
        w2.grad.zero_()

保存ができたら実行しましょう。

左の数字が学習回数、右の数値がパーセプトロンの推定値と実際の答えと二乗誤差です。

学習を重ねるごとに、二乗誤差が小さくなることがわかります。

$ python3 3_autograd.py 
99 743.60400390625
199 7.154838562011719
299 0.13969427347183228
399 0.0036201069597154856
499 0.000234781633480452

【PyTorch】サンプル② 〜TENSOR (テンソル) 〜


1. 目的
2. 前準備
3. TENSOR(テンソル)
4. PyTorchのインポート
5. データタイプとデバイスの選択
6. 使用するデータ
7. 重み付けの初期化
8. 学習
9. データの入力
10. 損失の計算
11. 重み(Weight)の勾配計算
12. 重みの更新
13. 実行
13.1. 2_tensor.py


### 1. 目的
PyTorchのチュートリアルPyTorch: Tensorsを参考にPyTorch Tensor(テンソル)を使って、損失(loss)や重み(weight)の計算をする。

PyTorchの特徴の一つである、テンソルとNumPyの違いを手を動かしながら感じる。

NumPyを用いた例は、こちらから。

2. 前準備

PyTorchのインストールはこちらから。

初めて、Google Colaboratoryを使いたい方は、こちらをご覧ください。

3. TENSOR(テンソル)

PyTorchのtensor(テンソル)は、基本的にはnumpy配列と同じです。

しかし、PyTorchのテンソルは、deep learning(深層学習)や計算グラフ、勾配といった概念に合わせた配列です。

PyTorchテンソルだけがCPUとGPUのどちらでも計算が実行でき、これが、numpy配列とPyTorchテンソルの最も大きな違いです。

4. PyTorchのインポート

import torch

5. データタイプとデバイスの選択

これから作成するテンソルのデータ型dtypefloatにします。

テンソル計算をするデバイスは、torch.deviceで指定することができます。
今回は、CPUを使用することにします。

dtype = torch.float
device = torch.device("cpu")

GPUを使う方は、deviceの部分を以下のコードに変えて下さい。

device = torch.device("cuda:0") # Uncomment this to run on GPU

6. 使用するデータ

バッチサイズNを64、入力の次元D_inを1000、隠れ層の次元Hを100、出力の次元D_outを10とします。

# N is batch size; D_in is input dimension;
# H is hidden dimension; D_out is output dimension.
N, D_in, H, D_out = 64, 1000, 100, 10

入力(x)と予測したい(y)を乱数で定義します。

PyTorchテンソルを定義する場合には、どのデバイスでdevice、どのデータ型dtypeかを指定することができます。

# Create random input and output data
x = torch.randn(N, D_in, device=device, dtype=dtype)
y = torch.randn(N, D_out, device=device, dtype=dtype)

7. 重み付けの初期化

乱数を使って重みを初期化します。

# Randomly initialize weights
w1 = torch.randn(D_in, H, device=device, dtype=dtype)
w2 = torch.randn(H, D_out, device=device, dtype=dtype)

8. 学習

学習率を1e-6として、学習回数を500回とします。

learning_rate = 1e-6
for t in range(500):

9. データの入力

二次元テンソルの入力(x)と重み(w1)をmmで掛け算することで重み付けをします(h)。(一次元テンソルの積は、dotで計算できます。)

重み付けした値(h)の要素から、clamp(min=~0)で、0以上のものは残し、0以下のものは0とします。numpyでは、numpy.maximum(h,0)と書きます。

最後に、重み(w2)を掛け合わせて重み付けしますmm(w2)
この値がパーセプトロンの予測値(y_pred)となります。

    # Forward pass: compute predicted y
    h = x.mm(w1)
    h_relu = h.clamp(min=0)
    y_pred = h_relu.mm(w2)

10. 損失の計算

パーセプトロンが予測した値(y_pred)と答え(y)との間の二乗誤差を計算しこれを損失(loss)とします。
numpyでは、二乗計算をnumpy.squareで実行していましたが、PyTorchテンソルでの二乗計算はpow(2)と記述します。

損失の値を、tensor配列からスカラー値で取得したい場合には、item()メソッドを用います。

各学習回数ごとに、学習回数(t)と二乗誤差(loss)を表示します。

今回は、結果を見やすくするためにif t % 100 == 99:の部分で、学習回数100回ごとに結果を出力します。
やっていることは、学習回数(t)を100で割ったときのあまりが99であるときにprint(t, loss)を実行です。

    # Compute and print loss
    loss = (y_pred - y).pow(2).sum().item()
    if t % 100 == 99:
        print(t, loss)

11. 重み(Weight)の勾配計算

これより先は、パーセプトロンが予測した値(y_pred)と答え(y)を見比べて、正しく答え(y)を予測できるようにパーセプトロンのパラメータを更新していきます。

まず、重み(w1, w2)の勾配(grad_w1, grad_w2)を計算します。

numpyと違う点は、表のとおりです。

PyTorch NumPy
配列の転置 .t() .T
二次元配列の掛け算 .mm() .dot()
配列のコピー .clone() .copy()
    grad_y_pred = 2.0 (y_pred - y)
    grad_w2 = h_relu.t().mm(grad_y_pred)
    grad_h_relu = grad_y_pred.mm(w2.t())
    grad_h = grad_h_relu.clone()
    grad_h[h < 0] = 0
    grad_w1 = x.t().mm(grad_h)

12. 重みの更新

計算した勾配(grad_w1, grad_w2)をもとに、重み(w1, w2)を更新します。

確率勾配降下法(SGD: stochastic gradient descent)は、重みを更新する上でよく使われる最適化アルゴリズムで、以下の式で表されます。
weight = weight - learning_rate gradient

SGDは、以下のコードで実行できます。
ここの部分は、numpyもPyTorchテンソルも同じコードです。

    # Update weights using gradient descent
    w1 -= learning_rate grad_w1
    w2 -= learning_rate grad_w2

13. 実行

以下のコードを2_tensor.pyとして保存します。

13.1. 2_tensor.py

import torch


dtype = torch.float
device = torch.device("cpu")
# device = torch.device("cuda:0") # Uncomment this to run on GPU

# N is batch size; D_in is input dimension;
# H is hidden dimension; D_out is output dimension.
N, D_in, H, D_out = 64, 1000, 100, 10

# Create random input and output data
x = torch.randn(N, D_in, device=device, dtype=dtype)
y = torch.randn(N, D_out, device=device, dtype=dtype)

# Randomly initialize weights
w1 = torch.randn(D_in, H, device=device, dtype=dtype)
w2 = torch.randn(H, D_out, device=device, dtype=dtype)

learning_rate = 1e-6
for t in range(500):
    # Forward pass: compute predicted y
    h = x.mm(w1)
    h_relu = h.clamp(min=0)
    y_pred = h_relu.mm(w2)

    # Compute and print loss
    loss = (y_pred - y).pow(2).sum().item()
    if t % 100 == 99:
        print(t, loss)

    # Backprop to compute gradients of w1 and w2 with respect to loss
    grad_y_pred = 2.0 (y_pred - y)
    grad_w2 = h_relu.t().mm(grad_y_pred)
    grad_h_relu = grad_y_pred.mm(w2.t())
    grad_h = grad_h_relu.clone()
    grad_h[h < 0] = 0
    grad_w1 = x.t().mm(grad_h)

    # Update weights using gradient descent
    w1 -= learning_rate grad_w1
    w2 -= learning_rate grad_w2

保存ができたら実行しましょう。

左の数字が学習回数、右の数値がパーセプトロンの推定値と実際の答えと二乗誤差です。

学習を重ねるごとに、二乗誤差が小さくなることがわかります。

$ python3 2_tensor.py 
99 306.79400634765625
199 0.4559670686721802
299 0.0014809096464887261
399 6.634114834014326e-05
499 1.7021899111568928e-05

【PyTorch】サンプル① 〜NUMPY〜


1. 目的
2. 前準備
3. 予備知識
4. NumPyのインポート
5. データ
6. 重み付けの初期化
7. 学習
8. データの入力
9. 重み(Weight)の勾配計算
10. 損失の計算
11. 重みの更新
12. 実行
12.1. 1_numpy.py


1. 目的

PyTorchのチュートリアルWarm-up: numpyを参考にNumpyを使って、損失(loss)や重み(weight)の計算をする。

PyTorchの特徴の一つである、テンソルとNumpyの違いを理解するための前準備。

2. 前準備

PyTorchのインストールはこちらから。

初めて、Google Colaboratoryを使いたい方は、こちらをご覧ください。

3. 予備知識

脳神経細胞は、樹上突起(Dendrites)、細胞体(Soma)、核(Nucleus)、軸索(Axon)、軸索終末(Axon Terminals)で構成されます。

(Credit: https://commons.wikimedia.org/wiki/File:Neuron_-_annotated.svg)

この脳神経細胞を数学的にモデルしたのが、パーセプトロンです。
神経樹上から細胞体に向けてやってくる信号(Xn)は、すべてが重要であるわけではありません。
入力される信号の中には、必要なものとそうでないものが混じっていると考えて、各信号に対して重み付け(Wn)をします。
神経樹上からの信号(Xn)は重みづけ(Wn)され、合算(Sum Σ)されます。
その合算した信号(z)が、その神経細胞を活性化させるかどうかを活性化関数(Activation function σ)でモデルします。
最後に、活性化関数からの出力に対して重み付けをして次のパーセプトロンに信号(a)を渡します。

(Credit: https://pythonmachinelearning.pro/perceptrons-the-first-neural-networks/)

今回は、パーセプトロンを用いて、入力される信号(x)からyを予測するケースを考えます。

4. NumPyのインポート

import numpy as np

5. データ

バッチサイズNを64、入力の次元D_inを1000、隠れ層の次元Hを100、出力の次元D_outを10とします。

# N is batch size; D_in is input dimension;
# H is hidden dimension; D_out is output dimension.
N, D_in, H, D_out = 64, 1000, 100, 10

入力(x)と予測したい(y)を乱数で定義します。

# Create random input and output data
x = np.random.randn(N, D_in)
y = np.random.randn(N, D_out)

6. 重み付けの初期化

乱数を使って重みを初期化します。

# Randomly initialize weights
w1 = np.random.randn(D_in, H)
w2 = np.random.randn(H, D_out)

7. 学習

学習率を1e-6として、学習回数を500回とします。

learning_rate = 1e-6
for t in range(500):

8. データの入力

入力(x)と重み(w1)を掛け算.dotすることで重み付けをします(h)。
重み付けした値(h)の要素から、np.maximum(h,0)で、0以上のものは残し、0以下のものは0とします。
最後に、重み(w2)を掛け合わせて重み付けします。この値がパーセプトロンの予測値(y_pred)となります。

    # Forward pass: compute predicted y
    h = x.dot(w1)
    h_relu = np.maximum(h, 0)
    y_pred = h_relu.dot(w2)

9. 重み(Weight)の勾配計算

これより先は、パーセプトロンが予測した値(y_pred)と答え(y)を見比べて、正しく答え(y)を予測できるようにパーセプトロンのパラメータを更新していきます。

まず、重み(w1, w2)の勾配(grad_w1, grad_w2)を計算します。

    # Backprop to compute gradients of w1 and w2 with respect to loss
    grad_y_pred = 2.0 * (y_pred - y)
    grad_w2 = h_relu.T.dot(grad_y_pred)
    grad_h_relu = grad_y_pred.dot(w2.T)
    grad_h = grad_h_relu.copy()
    grad_h[h < 0] = 0
    grad_w1 = x.T.dot(grad_h)

10. 損失の計算

パーセプトロンが予測した値(y_pred)と答え(y)との間の二乗誤差を計算しこれを損失(loss)とします。
np.squreareでy_predとyの差を二乗して、sum()で平均しています。
各学習回数ごとに、学習回数(t)と二乗誤差(loss)を表示します。

    # Compute and print loss
    loss = np.square(y_pred - y).sum()
    print(t, loss)

11. 重みの更新

計算した勾配(grad_w1, grad_w2)をもとに、重み(w1, w2)を更新します。

確率勾配降下法(SGD: stochastic gradient descent)は、重みを更新する上でよく使われる最適化アルゴリズムで、以下の式で表されます。

weight = weight - learning_rate * gradient

SGDは、以下のコードで実行できます。

    # Update weights
    w1 -= learning_rate * grad_w1
    w2 -= learning_rate * grad_w2

12. 実行

以下のコードを1_numpy.pyとして保存します。

12.1. 1_numpy.py

import numpy as np

# N is batch size; D_in is input dimension;
# H is hidden dimension; D_out is output dimension.
N, D_in, H, D_out = 64, 1000, 100, 10

# Create random input and output data
x = np.random.randn(N, D_in)
y = np.random.randn(N, D_out)

# Randomly initialize weights
w1 = np.random.randn(D_in, H)
w2 = np.random.randn(H, D_out)

learning_rate = 1e-6
for t in range(500):
    # Forward pass: compute predicted y
    h = x.dot(w1)
    h_relu = np.maximum(h, 0)
    y_pred = h_relu.dot(w2)

    # Compute and print loss
    loss = np.square(y_pred - y).sum()
    print(t, loss)

    # Backprop to compute gradients of w1 and w2 with respect to loss
    grad_y_pred = 2.0 * (y_pred - y)
    grad_w2 = h_relu.T.dot(grad_y_pred)
    grad_h_relu = grad_y_pred.dot(w2.T)
    grad_h = grad_h_relu.copy()
    grad_h[h < 0] = 0
    grad_w1 = x.T.dot(grad_h)

    # Update weights
    w1 -= learning_rate * grad_w1
    w2 -= learning_rate * grad_w2

保存ができたら実行しましょう。
左の数字が学習回数、右の数値がパーセプトロンの推定値と実際の答えと二乗誤差です。
学習を重ねるごとに、二乗誤差が小さくなることがわかります。

$ python3 1_numpy.py 
0 33318410.89325847
1 33449484.266180404
2 42189212.89431849
3 51379306.420906566
4 48992878.8013583

...

499 1.529654790074364e-05

【FreeSurfer】FreeSurferを用いた脳構造解析


1. 目的
2. FreeSurferの概要
3. 準備するデータ
4. 実行
5. 結果
5.1. aparc.stats
5.2. wmparc.stats


1. 目的

  • 構造MRI (3D-T1WI)から、の脳構造を解析

2. FreeSurferの概要

準備中。。。

3. 準備するデータ

準備するデータは、3D-T1WIのみである。

.
└── Subj001.nii.gz

4. 実行

FreeSurferのrecon-allの基本的な使い方は、次の通り。Subjects DIR-sdは、被験者データが集められているフォルダを指定する。

recon-all -i <Input 3D-T1WI> -subjid <Subject ID> -all -sd .<Subject DIR>

例えば、次のようにコマンドを打ち込むことで、FreeSurferを実行できる。

recon-all -i Subj001.nii.gz -subjid Subj001 -all -sd .

5. 結果

FreeSurferの処理が完了すると、Subj001/mriフォルダに灰白質(aparc+aseg.mgz)と白質wmparc.mgzが各脳領域ごとに分割された画像が生成される。これを脳画像に重ねて表示するには、次のコマンドを実行する。

freeview -v Subj001/mri/brain.mgz \
	Subj001/mri/aparc+aseg.mgz:colormap:lut:opacity=0.2 \
	Subj001/mri/wmparc.mgz:colormap:lut:opacity=0.2

以下のような画像が表示される。

また、各脳領域の厚さ・面積・体積・脳回の曲率等の情報がSubj001/statsフォルダに保存される。

5.1. aparc.stats

aparc.statsには、皮質および深部灰白質の構造情報が記載されている。また、aparc.statsは、左半球 (lh) と右半球 (rh) ごとに保存される(例: lh.aparc.stats)。

lh.aparc.statsの中身は、次の通り。

# Table of FreeSurfer cortical parcellation anatomical statistics 
# 
# CreationTime 2020/12/09-20:01:37-GMT
# generating_program mris_anatomical_stats
# cvs_version $Id: mris_anatomical_stats.c,v 1.79 2016/03/14 15:15:34 greve Exp $
# mrisurf.c-cvs_version $Id: mrisurf.c,v 1.781.2.6 2016/12/27 16:47:14 zkaufman Exp $
# cmdline mris_anatomical_stats -th3 -mgz -cortex ../label/lh.cortex.label -f ../stats/lh.aparc.stats -b -a ../label/lh.aparc.annot -c ../label/aparc.annot.ctab Subj001 lh white 
# sysname  Linux
# hostname neuro
# machine  x86_64
# user     neuro
# 
# SUBJECTS_DIR /home/neuro/Documents/Yuya_S/FreeSurfer/2_ANALYZE
# anatomy_type surface
# subjectname Subj001
# hemi lh
# AnnotationFile ../label/lh.aparc.annot
# AnnotationFileTimeStamp 2020/12/10 04:34:33
# Measure Cortex, NumVert, Number of Vertices, 134464, unitless
# Measure Cortex, WhiteSurfArea, White Surface Total Area, 91536.6, mm^2
# Measure Cortex, MeanThickness, Mean Thickness, 2.5011, mm
# Measure BrainSeg, BrainSegVol, Brain Segmentation Volume, 1249289.000000, mm^3
# Measure BrainSegNotVent, BrainSegVolNotVent, Brain Segmentation Volume Without Ventricles, 1227719.000000, mm^3
# Measure BrainSegNotVentSurf, BrainSegVolNotVentSurf, Brain Segmentation Volume Without Ventricles from Surf, 1227368.805429, mm^3
# Measure Cortex, CortexVol Total cortical gray matter volume, 509668.845191, mm^3
# Measure SupraTentorial, SupraTentorialVol, Supratentorial volume, 1091814.805429, mm^3
# Measure SupraTentorialNotVent, SupraTentorialVolNotVent, Supratentorial volume, 1073466.805429, mm^3
# Measure EstimatedTotalIntraCranialVol, eTIV, Estimated Total Intracranial Volume, 1529098.631452, mm^3
# NTableCols 10
# TableCol  1 ColHeader StructName
# TableCol  1 FieldName Structure Name
# TableCol  1 Units     NA
# TableCol  2 ColHeader NumVert
# TableCol  2 FieldName Number of Vertices
# TableCol  2 Units     unitless
# TableCol  3 ColHeader SurfArea
# TableCol  3 FieldName Surface Area
# TableCol  3 Units     mm^2
# TableCol  4 ColHeader GrayVol
# TableCol  4 FieldName Gray Matter Volume
# TableCol  4 Units     mm^3
# TableCol  5 ColHeader ThickAvg 
# TableCol  5 FieldName Average Thickness
# TableCol  5 Units     mm
# TableCol  6 ColHeader ThickStd
# TableCol  6 FieldName Thickness StdDev
# TableCol  6 Units     mm 
# TableCol  7 ColHeader MeanCurv
# TableCol  7 FieldName Integrated Rectified Mean Curvature
# TableCol  7 Units     mm^-1
# TableCol  8 ColHeader GausCurv 
# TableCol  8 FieldName Integrated Rectified Gaussian Curvature
# TableCol  8 Units     mm^-2
# TableCol  9 ColHeader  FoldInd
# TableCol  9 FieldName  Folding Index 
# TableCol  9 Units      unitless 
# TableCol 10 ColHeader CurvInd
# TableCol 10 FieldName Intrinsic Curvature Index
# TableCol 10 Units     unitless
# ColHeaders StructName NumVert SurfArea GrayVol ThickAvg ThickStd MeanCurv GausCurv FoldInd CurvInd
bankssts                                 1706   1193   3135  2.764 0.427     0.112     0.019       15     1.3
caudalanteriorcingulate                  1136    745   1942  2.368 0.668     0.158     0.026       24     1.1
caudalmiddlefrontal                      3765   2557   6665  2.419 0.421     0.116     0.020       35     3.2
cuneus                                   2066   1395   2739  1.905 0.546     0.151     0.034       29     3.0
entorhinal                                637    497   2185  3.351 0.914     0.119     0.024        5     0.7
fusiform                                 4365   3016   9776  2.901 0.595     0.135     0.029       65     5.2
inferiorparietal                         6556   4462  12218  2.510 0.460     0.122     0.024       81     6.2
inferiortemporal                         5440   3693  12466  2.853 0.620     0.126     0.027       75     6.2
isthmuscingulate                         1738   1138   2947  2.372 0.814     0.127     0.030       24     2.0
lateraloccipital                         7720   5101  12698  2.301 0.458     0.137     0.028      103     8.8
lateralorbitofrontal                     3955   2697   7867  2.600 0.577     0.134     0.031       55     5.0
lingual                                  5496   3835   8069  2.020 0.578     0.144     0.035       77     7.4
medialorbitofrontal                      3388   2238   6023  2.430 0.704     0.120     0.032       47     4.0
middletemporal                           5073   3489  12496  2.881 0.655     0.130     0.026       75     5.4
parahippocampal                          1129    718   2446  2.897 0.776     0.090     0.021        8     0.8
paracentral                              2097   1424   3854  2.502 0.497     0.119     0.026       20     2.0
parsopercularis                          2432   1679   4984  2.566 0.543     0.115     0.021       28     2.1
parsorbitalis                             977    670   2470  2.621 0.598     0.152     0.037       19     1.7
parstriangularis                         1970   1406   4121  2.453 0.467     0.134     0.033       30     2.6
pericalcarine                            2208   1531   1980  1.593 0.432     0.154     0.037       31     3.3
postcentral                              7825   5228  12158  2.086 0.553     0.120     0.022       89     7.0
posteriorcingulate                       1949   1342   3783  2.631 0.793     0.152     0.037       35     2.5
precentral                               8007   5252  14452  2.557 0.498     0.109     0.021       69     6.6
precuneus                                6465   4325  11805  2.509 0.500     0.122     0.026       72     6.6
rostralanteriorcingulate                 1421    964   3191  2.943 0.740     0.130     0.032       27     2.0
rostralmiddlefrontal                     8606   6003  15441  2.212 0.553     0.140     0.034      140    12.7
superiorfrontal                          9981   7035  21086  2.588 0.559     0.133     0.029      118    11.8
superiorparietal                         8285   5587  13849  2.271 0.415     0.123     0.023       96     7.4
superiortemporal                         6222   4206  13988  2.956 0.616     0.115     0.024       78     6.3
supramarginal                            6623   4574  13160  2.579 0.520     0.130     0.028       92     7.8
frontalpole                               347    235   1010  2.809 0.788     0.178     0.056       11     0.8
temporalpole                              673    484   1882  3.001 0.960     0.165     0.071       18     1.8
transversetemporal                        692    435   1114  2.318 0.447     0.096     0.018        5     0.4
insula                                   3655   2496   7826  3.170 0.729     0.121     0.033       37     4.8

5.2. wmparc.stats

wmparc.statsには、白質の構造情報が記載されている。

# Title Segmentation Statistics 
# 
# generating_program mri_segstats
# cvs_version $Id: mri_segstats.c,v 1.121 2016/05/31 17:27:11 greve Exp $
# cmdline mri_segstats --seg mri/wmparc.mgz --sum stats/wmparc.stats --pv mri/norm.mgz --excludeid 0 --brainmask mri/brainmask.mgz --in mri/norm.mgz --in-intensity-name norm --in-intensity-units MR --subject Subj001 --surf-wm-vol --ctab /opt/freesurfer/WMParcStatsLUT.txt --etiv 
# sysname  Linux
# hostname neuro
# machine  x86_64
# user     neuro
# anatomy_type volume
# 
# SUBJECTS_DIR /home/neuro/Documents/Yuya_S/FreeSurfer/2_ANALYZE
# subjectname Subj001
# Measure VentricleChoroidVol, VentricleChoroidVol, Volume of ventricles and choroid plexus, 18348.000000, mm^3
# Measure lhCerebralWhiteMatter, lhCerebralWhiteMatterVol, Left hemisphere cerebral white matter volume, 247050.231251, mm^3
# Measure rhCerebralWhiteMatter, rhCerebralWhiteMatterVol, Right hemisphere cerebral white matter volume, 245715.728986, mm^3
# Measure CerebralWhiteMatter, CerebralWhiteMatterVol, Total cerebral white matter volume, 492765.960237, mm^3
# Measure Mask, MaskVol, Mask Volume, 1604897.000000, mm^3
# Measure EstimatedTotalIntraCranialVol, eTIV, Estimated Total Intracranial Volume, 1529098.631452, mm^3
# SegVolFile mri/wmparc.mgz 
# SegVolFileTimeStamp  2020/12/10 05:19:19 
# ColorTable /opt/freesurfer/WMParcStatsLUT.txt 
# ColorTableTimeStamp 2017/01/19 07:00:02 
# InVolFile  mri/norm.mgz 
# InVolFileTimeStamp  2020/12/09 23:13:15 
# InVolFrame 0 
# PVVolFile  mri/norm.mgz 
# PVVolFileTimeStamp  2020/12/09 23:13:15 
# ExcludeSegId 0 
# Only reporting non-empty segmentations
# VoxelVolume_mm3 1 
# TableCol  1 ColHeader Index 
# TableCol  1 FieldName Index 
# TableCol  1 Units     NA 
# TableCol  2 ColHeader SegId 
# TableCol  2 FieldName Segmentation Id
# TableCol  2 Units     NA
# TableCol  3 ColHeader NVoxels 
# TableCol  3 FieldName Number of Voxels
# TableCol  3 Units     unitless
# TableCol  4 ColHeader Volume_mm3
# TableCol  4 FieldName Volume
# TableCol  4 Units     mm^3
# TableCol  5 ColHeader StructName
# TableCol  5 FieldName Structure Name
# TableCol  5 Units     NA
# TableCol  6 ColHeader normMean 
# TableCol  6 FieldName Intensity normMean
# TableCol  6 Units     MR
# TableCol  7 ColHeader normStdDev
# TableCol  7 FieldName Itensity normStdDev
# TableCol  7 Units     MR
# TableCol  8 ColHeader normMin
# TableCol  8 FieldName Intensity normMin
# TableCol  8 Units     MR
# TableCol  9 ColHeader normMax
# TableCol  9 FieldName Intensity normMax
# TableCol  9 Units     MR
# TableCol 10 ColHeader normRange
# TableCol 10 FieldName Intensity normRange
# TableCol 10 Units     MR
# NRows 70 
# NTableCols 10 
# ColHeaders  Index SegId NVoxels Volume_mm3 StructName normMean normStdDev normMin normMax normRange  
  1 3001      3524     3508.1  wm-lh-bankssts                    98.6405     7.9080    71.0000   118.0000    47.0000 
  2 3002      3041     3012.8  wm-lh-caudalanteriorcingulate    106.1743     9.7316    71.0000   126.0000    55.0000 
  3 3003      6943     6929.7  wm-lh-caudalmiddlefrontal         95.6736     9.8735    63.0000   117.0000    54.0000 
  4 3005      2121     2119.2  wm-lh-cuneus                      91.1391    10.4037    66.0000   114.0000    48.0000 
  5 3006      1049     1088.4  wm-lh-entorhinal                  80.1049     8.8624    56.0000   112.0000    56.0000 
  6 3007      6820     6751.2  wm-lh-fusiform                    90.8783    10.4333    56.0000   114.0000    58.0000 
  7 3008      9829     9877.3  wm-lh-inferiorparietal            96.3825    10.2446    63.0000   120.0000    57.0000 
  8 3009      7034     7002.1  wm-lh-inferiortemporal            86.2688    12.7662    51.0000   113.0000    62.0000 
  9 3010      4036     4077.5  wm-lh-isthmuscingulate           105.1016     9.5987    37.0000   124.0000    87.0000 
 10 3011      9108     9298.5  wm-lh-lateraloccipital            90.5220     9.1699    63.0000   114.0000    51.0000 
 11 3012      6634     6595.1  wm-lh-lateralorbitofrontal        99.7825    11.2891    66.0000   126.0000    60.0000 
 12 3013      6526     6406.8  wm-lh-lingual                     89.2861    10.1055    51.0000   117.0000    66.0000 
 13 3014      4776     4756.7  wm-lh-medialorbitofrontal        100.5992    12.2001    24.0000   127.0000   103.0000 
 14 3015      5454     5569.1  wm-lh-middletemporal              85.9773     9.9489    57.0000   112.0000    55.0000 
 15 3016      1589     1661.2  wm-lh-parahippocampal             89.3222     8.3148    65.0000   112.0000    47.0000 
 16 3017      4042     4074.3  wm-lh-paracentral                 92.4000     8.4372    66.0000   115.0000    49.0000 
 17 3018      3752     3724.1  wm-lh-parsopercularis             95.7439    10.4939    68.0000   119.0000    51.0000 
 18 3019       934      925.5  wm-lh-parsorbitalis               84.3351    10.0562    61.0000   106.0000    45.0000 
 19 3020      2889     2864.7  wm-lh-parstriangularis            91.3375    11.1110    62.0000   117.0000    55.0000 
 20 3021      3880     3534.2  wm-lh-pericalcarine               90.3379    10.1190    62.0000   113.0000    51.0000 
 21 3022      9048     9133.1  wm-lh-postcentral                 90.5553    10.1126    63.0000   117.0000    54.0000 
 22 3023      4979     4930.6  wm-lh-posteriorcingulate         102.9735     9.9418    48.0000   122.0000    74.0000 
 23 3024     14565    14538.0  wm-lh-precentral                  93.4461     9.1777    63.0000   119.0000    56.0000 
 24 3025     10517    10495.6  wm-lh-precuneus                   99.6304     9.6149    36.0000   154.0000   118.0000 
 25 3026      2602     2514.4  wm-lh-rostralanteriorcingulate   101.8966    14.8008    35.0000   134.0000    99.0000 
 26 3027     12713    12821.3  wm-lh-rostralmiddlefrontal        98.1206    10.5230    65.0000   120.0000    55.0000 
 27 3028     17112    17150.4  wm-lh-superiorfrontal             94.3707    10.5009    61.0000   123.0000    62.0000 
 28 3029     12917    13041.8  wm-lh-superiorparietal            96.7138    10.0283    62.0000   117.0000    55.0000 
 29 3030      8258     8541.9  wm-lh-superiortemporal            93.0829    10.6918    63.0000   118.0000    55.0000 
 30 3031      9396     9420.8  wm-lh-supramarginal               96.7631    10.6806    63.0000   121.0000    58.0000 
 31 3032       214      221.1  wm-lh-frontalpole                 92.0561     9.4181    74.0000   116.0000    42.0000 
 32 3033       624      687.3  wm-lh-temporalpole                79.2388     7.5813    57.0000   110.0000    53.0000 
 33 3034       642      622.6  wm-lh-transversetemporal          94.6511     8.5727    75.0000   115.0000    40.0000 
 34 3035     10580    10410.0  wm-lh-insula                      96.7245    10.8609    58.0000   124.0000    66.0000 
 35 4001      2904     2910.9  wm-rh-bankssts                    95.7104     7.8693    67.0000   110.0000    43.0000 
 36 4002      2504     2512.0  wm-rh-caudalanteriorcingulate    105.3910     9.3044    69.0000   123.0000    54.0000 
 37 4003      7042     6997.0  wm-rh-caudalmiddlefrontal         96.0454     9.8148    65.0000   116.0000    51.0000 
 38 4005      2412     2543.3  wm-rh-cuneus                      88.8429     8.8231    63.0000   113.0000    50.0000 
 39 4006       692      766.1  wm-rh-entorhinal                  78.1604     8.9138    59.0000   113.0000    54.0000 
 40 4007      7157     7093.4  wm-rh-fusiform                    89.5639     9.5493    59.0000   111.0000    52.0000 
 41 4008     12012    12138.6  wm-rh-inferiorparietal            94.7903     9.8456    65.0000   115.0000    50.0000 
 42 4009      6736     6716.1  wm-rh-inferiortemporal            88.8744    10.5262    59.0000   110.0000    51.0000 
 43 4010      3673     3687.3  wm-rh-isthmuscingulate           103.4236    10.2888    26.0000   124.0000    98.0000 
 44 4011      9750     9938.5  wm-rh-lateraloccipital            89.0627     8.8456    53.0000   109.0000    56.0000 
 45 4012      6075     6100.6  wm-rh-lateralorbitofrontal        94.8914    10.2953    62.0000   118.0000    56.0000 
 46 4013      6756     6633.9  wm-rh-lingual                     86.4899    10.0740    11.0000   111.0000   100.0000 
 47 4014      3719     3732.0  wm-rh-medialorbitofrontal         94.9559    11.6434    30.0000   119.0000    89.0000 
 48 4015      5794     5999.5  wm-rh-middletemporal              88.0036     9.7096    59.0000   110.0000    51.0000 
 49 4016      1574     1625.5  wm-rh-parahippocampal             88.7154     8.8436    44.0000   112.0000    68.0000 
 50 4017      4166     4199.9  wm-rh-paracentral                 93.8337     8.2716    69.0000   116.0000    47.0000 
 51 4018      3510     3504.4  wm-rh-parsopercularis             96.5960    10.2199    66.0000   117.0000    51.0000 
 52 4019      1053     1065.9  wm-rh-parsorbitalis               86.1054     9.5682    62.0000   109.0000    47.0000 
 53 4020      3448     3427.2  wm-rh-parstriangularis            91.6995    10.0940    62.0000   114.0000    52.0000 
 54 4021      2888     2670.2  wm-rh-pericalcarine               86.4228     8.9538    26.0000   108.0000    82.0000 
 55 4022      7978     7981.5  wm-rh-postcentral                 90.6651    10.5013    63.0000   116.0000    53.0000 
 56 4023      4712     4661.9  wm-rh-posteriorcingulate         103.0671     8.9448    49.0000   124.0000    75.0000 
 57 4024     14926    14925.4  wm-rh-precentral                  94.0721     8.5665    67.0000   117.0000    50.0000 
 58 4025     10712    10698.6  wm-rh-precuneus                   99.2039     9.2602    54.0000   121.0000    67.0000 
 59 4026      1870     1846.9  wm-rh-rostralanteriorcingulate   103.8059    10.5876    70.0000   131.0000    61.0000 
 60 4027     11835    12171.9  wm-rh-rostralmiddlefrontal        96.1585     9.2493    66.0000   115.0000    49.0000 
 61 4028     17809    18126.3  wm-rh-superiorfrontal             94.0811     9.3702    63.0000   119.0000    56.0000 
 62 4029     13213    13320.4  wm-rh-superiorparietal            95.6500     9.6784    63.0000   116.0000    53.0000 
 63 4030      7743     7980.9  wm-rh-superiortemporal            93.0976     9.4358    65.0000   113.0000    48.0000 
 64 4031      9885     9909.1  wm-rh-supramarginal               96.7241    10.3698    63.0000   118.0000    55.0000 
 65 4032       240      265.8  wm-rh-frontalpole                 91.2417     7.4440    75.0000   107.0000    32.0000 
 66 4033       723      784.5  wm-rh-temporalpole                78.9391     8.1127    57.0000    98.0000    41.0000 
 67 4034       585      577.1  wm-rh-transversetemporal          96.0513     7.4146    73.0000   112.0000    39.0000 
 68 4035     11695    11518.1  wm-rh-insula                      94.4427    11.1743    41.0000   120.0000    79.0000 
 69 5001     37165    37072.8  Left-UnsegmentedWhiteMatter      103.1510     9.9691    27.0000   127.0000   100.0000 
 70 5002     35548    35459.7  Right-UnsegmentedWhiteMatter     102.0210     9.6094    23.0000   126.0000   103.0000 

【DIPY】DIPYを用いたギブズのリンギングアーチファクト(Gibbs ringing)の除去


1. 目的
2. 準備
2.1. DIPYのインストール
2.2. 使用データ
3. 拡散MRIのノイズ除去
3.1. 必要なパッケージをインポート
3.2. 画像およびMPG軸情報の読み込み
3.3. マスク画像の作成
3.4. ギブズのリンギングアーチファクト除去
3.5. NIfTI形式で保存
3.6. 結果


### 1. 目的

  • DIPYを用いたギブズのリンギングアーチファクト(Gibbs ringing)の除去

2. 準備

2.1. DIPYのインストール

pip3 install dipy

2.2. 使用データ

データを次のフォルダ構造で用意する。

Study/
└── Subject
    ├── DWI.nii.gz  # 拡散MRI
    ├── DWI_mask.nii.gz  # 拡散MRIマスク画像
    ├── bvals  # b-values
    └── bvecs  # b-vectors

3. 拡散MRIのノイズ除去

Pythonで以下のコマンドを実行。

3.1. 必要なパッケージをインポート

from dipy.denoise.gibbs import gibbs_removal
import matplotlib.pyplot as plt
import numpy as np
from dipy.segment.mask import median_otsu
from dipy.io.image import load_nifti, save_nifti
from dipy.io.gradients import read_bvals_bvecs
from dipy.core.gradients import gradient_table

3.2. 画像およびMPG軸情報の読み込み

DWI_FILE = 'DWI.nii.gz'
BVALS_FILE = 'bvals'
BVECS_FILE = 'bvecs'

# Import data
data, affine = load_nifti(DWI_FILE)
bvals, bvecs = read_bvals_bvecs(BVALS_FILE, BVECS_FILE)
gtab = gradient_table(bvals, bvecs)

3.3. マスク画像の作成

median_otsu関数を用いて、b=0画像からマスク画像を生成する。vol_idxには、b0 volumeのvolume indexを渡す。

maskdata, mask = median_otsu(
    data, vol_idx=np.where(bvals == 0)[0])

3.4. ギブズのリンギングアーチファクト除去

gibbs_removal関数を用いて、リンギングアーチファクトを除去する。

data_corrected = gibbs_removal(maskdata)

3.5. NIfTI形式で保存

save_nifti関数で、画像をNIfTI形式で保存する。

save_nifti('DWI_degibbs.nii.gz', data_corrected.astype(np.float32), affine)

3.6. 結果

補正前後の画像は、以下の通り。

【MRtrix】MRtrixを用いたギブズのリンギングアーチファクト(Gibbs ringing)の除去


1. 目的
2. コマンド
3. 使用例
3.1. 構造MRI(3D-T1WI)への適用
3.2. 拡散MRIへの適用


### 1. 目的

  • MRtrixを用いたギブズのリンギングアーチファクト(Gibbs ringing)の除去

2. コマンド

ギブズのリンギングアーチファクト(Gibbs ringing)を除去するには、MRtrixmrdegibbsを用いる。

mrdegibbsのヘルプは、以下の通り。

クリックして展開
SYNOPSIS

     Remove Gibbs Ringing Artifacts

USAGE

     mrdegibbs [ options ] in out

        in           the input image.

        out          the output image.


DESCRIPTION

     This application attempts to remove Gibbs ringing artefacts from MRI
     images using the method of local subvoxel-shifts proposed by Kellner et
     al. (see reference below for details).

     This command is designed to run on data directly after it has been
     reconstructed by the scanner, before any interpolation of any kind has
     taken place. You should not run this command after any form of motion
     correction (e.g. not after dwifslpreproc). Similarly, if you intend
     running dwidenoise, you should run denoising before this command to not
     alter the noise structure, which would impact on dwidenoise's performance.

     Note that this method is designed to work on images acquired with full
     k-space coverage. Running this method on partial Fourier ('half-scan')
     data may lead to suboptimal and/or biased results, as noted in the
     original reference below. There is currently no means of dealing with
     this; users should exercise caution when using this method on partial
     Fourier data, and inspect its output for any obvious artefacts. 

OPTIONS

  -axes list
     select the slice axes (default: 0,1 - i.e. x-y).

  -nshifts value
     discretization of subpixel spacing (default: 20).

  -minW value
     left border of window used for TV computation (default: 1).

  -maxW value
     right border of window used for TV computation (default: 3).

Data type options

  -datatype spec
     specify output image data type. Valid choices are: float32, float32le,
     float32be, float64, float64le, float64be, int64, uint64, int64le,
     uint64le, int64be, uint64be, int32, uint32, int32le, uint32le, int32be,
     uint32be, int16, uint16, int16le, uint16le, int16be, uint16be, cfloat32,
     cfloat32le, cfloat32be, cfloat64, cfloat64le, cfloat64be, int8, uint8,
     bit.

Standard options

  -info
     display information messages.

  -quiet
     do not display information messages or progress status; alternatively,
     this can be achieved by setting the MRTRIX_QUIET environment variable to a
     non-empty string.

  -debug
     display debugging messages.

  -force
     force overwrite of output files (caution: using the same file as input and
     output might cause unexpected behaviour).

  -nthreads number
     use this number of threads in multi-threaded applications (set to 0 to
     disable multi-threading).

  -config key value  (multiple uses permitted)
     temporarily set the value of an MRtrix config file entry.

  -help
     display this information page and exit.

  -version
     display version information and exit.

基本的な使い方は、以下の通り。

mrdegibbs <入力画像> <出力画像> -axes 0,1  # Axial収集
mrdegibbs <入力画像> <出力画像> -axes 0,2  # Coronal収集
mrdegibbs <入力画像> <出力画像> -axes 1,2  # Sagittal収集

3. 使用例

3.1. 構造MRI(3D-T1WI)への適用

3D-T1WI(T1w.nii.gz)を入力としてmrdegibbsを実行する。

mrdegibbs T1w.nii.gz T1w_unringed.nii.gz -axes 0,1

実行した結果は、以下の通り。

3.2. 拡散MRIへの適用

mrdegibbsは、拡散MRIにも適用できる。ただし以下の条件がある。

  • dwidenoiseでノイズを除去した後に実行
  • dwifslpreprocのような歪み・頭の動き補正をする前に実行

dwidenoiseでノイズ除去された拡散強調像(DWI_denoised.nii.gz)を入力としてmrdegibbsを実行するには、以下のコマンドを実行する。

mrdegibbs DWI_denoised.nii.gz DWI_denoised_unringed.nii.gz -axes 0,1

実行した結果は、以下の通り。

【FSL】大脳基底核のセグメンテーション


1. 目的
2. コマンド
3. 使用例
3.1. 頭蓋除去をしていない場合
3.2. 頭蓋除去を既にしている場合
4. 結果
5. おまけ


1. 目的

  • 大脳基底核のセグメンテーション

2. コマンド

FSLrun_first_allを用いる。

run_first_allのヘルプは、以下。

Usage: run_first_all [options] -i <input_image> -o <output_image>

Optional arguments:
  -m <method>      : method must be one of auto, fast, none or a (numerical) threshold value
  -b               : input is already brain extracted
  -s <name>        : run only on one specified structure (e.g. L_Hipp) or a comma separated list (no spaces)
  -a <img2std.mat> : use affine matrix (do not re-run registration)
  -3               : use 3-stage affine registration (only currently for hippocampus)
  -d               : do not cleanup image output files (useful for debugging)
  -v               : verbose output
  -h               : display this help message

e.g.:  run_first_all -i im1 -o output_name 

基本的な使い方は、次の通り。

頭蓋除去をしていない3D-T1WIを入力する場合。

run_first_all -i <入力画像(3D-T1WI)> -o <出力画像(の接頭辞)>

頭蓋除去済みの3D-T1WIを入力する場合。

run_first_all -i <入力画像(3D-T1WI)> -o <出力画像(の接頭辞)> -b

3. 使用例

3.1. 頭蓋除去をしていない場合

頭蓋除去をしていない3D-T1WI(T1w.nii.gz)に対して、run_first_allをかけるには、次のようにコマンドを実行する。

ここでは、出力画像の接頭辞(オプション:-o)を「output」とした。

run_first_all -i T1w.nii.gz -o output

3.2. 頭蓋除去を既にしている場合

まず、頭蓋除去済みの3D-T1WI(T1_skull_stripped.nii.gz)を用意する。

頭蓋除去のやり方は、以下の記事を参考にするとよい。

頭蓋除去した画像(T1_skull_stripped.nii.gz)に対して、run_first_allコマンドを実行する。

ここでは、出力画像の接頭辞(オプション:-o)を「output」とし、頭蓋除去済みの脳を入力していることを示すオプション-bを付けている。

run_first_all -i T1_skull_stripped.nii.gz -o output -b

4. 結果

処理が完了すると、次のファイルが出力される。

  • output_name_all_fast_firstseg.nii.gz:大脳基底核がセグメンテーションされた画像(3D画像)
  • output_name_all_fast_origsegs.nii.gz:大脳基底核の各セグメンテーション画像(4D画像)
  • output_name_first.vtk:セグメンテーションをする際のメッシュ。FSLViewの3Dモードで見ることができる。
  • output_name_first.bvars:パラメータファイル。

大脳基底核のセグメント(output_all_fast_firstseg.nii.gz)と頭蓋除去した画像(T1_skull_stripped.nii.gz)を、重ね合わせてみる。

fsleyes T1_skull_stripped.nii.gz output_all_fast_firstseg.nii.gz -cm random

run_first_allでは、大脳基底核を次の領域にセグメント(区域分け)する。

Label Index 領域
10 Left-Thalamus-Proper
11 Left-Caudate
12 Left-Putamen
13 Left-Pallidum
16 Brain-Stem/ 4th Ventricle
17 Left-Hippocampus
18 Left-Amygdala
26 Left-Accumbens-area
49 Right-Thalamus-Proper
50 Right-Caudate
51 Right-Putamen
52 Right-Pallidum
53 Right-Hippocampus
54 Right-Amygdala
58 Right-Accumbens-area

5. おまけ

複数の被験者のセグメンテーション結果をQCしたい場合、first_roi_slicesdirコマンドを用いると便利である。

基本な使い方は、以下。

first_roi_slicesdir <入力画像(3D-T1WI)のリスト> <セグメンテーション画像のリスト>

例えば、次のような被験者Subj001, Subj002, Subj003がいた場合。

.
├── Subj001_T1_skull_stripped.nii.gz
├── Subj001_output_all_fast_firstseg.nii.gz
├── Subj002_T1_skull_stripped.nii.gz
├── Subj002_output_all_fast_firstseg.nii.gz
├── Subj003_T1_skull_stripped.nii.gz
└── Subj003_output_all_fast_firstseg.nii.gz

first_roi_slicesdirを、次のように実行する。

first_roi_slicesdir *_t1.nii.gz *_all_fast_firstseg.nii.gz

処理が完了すると、「slicesdir」フォルダが生成される。

slicesdir/
├── Subj001_T1_skull_stripped_t1grot1_to_Subj001_output_all_fast_firstseglbgrot1.png
├── Subj002_T1_skull_stripped_t1grot2_to_Subj002_output_all_fast_firstseglbgrot2.png
├── Subj003_T1_skull_stripped_t1grot3_to_Subj003_output_all_fast_firstseglbgrot3.png
├── grota.png
├── grotb.png
├── grotc.png
├── grotd.png
├── grote.png
├── grotf.png
├── grotg.png
├── groth.png
├── groti.png
└── index.html

slicesdirフォルダの「index.html」を開くと、結果が見れる。

【FreeSurfer】FreeSurferを用いた頭蓋除去 ~Skull-stripping~


1. 目的
2. コマンド
3. 使用例
4. 結果


1. 目的

脳画像解析で、対象となる領域は脳実質でありその他の骨・筋・脂肪・眼球等の組織は、解析する上でノイズとなる。そのため、解析精度を高めるには頭蓋を除去することが重要である。

この記事では、FreeSurferを用いて頭蓋を除去する方法を解説する。

  • FreeSurferを用いた頭蓋除去

2. コマンド

ここでは、FreeSurferの関数であるrecon-allコマンドを用いて、頭蓋を除去する。

基本的な使い方は、次の通り。

# FreeSurfer作業ディレクトリを指定
export SUBJECTS_DIR=<PATH>

# Autorecon1を実行
recon-all -subjid <被験者ID (任意)> -i <入力画像 (3D-T1WI)> -autorecon1

Autorecon1では、以下の処理を実行している。

  1. 動き補正(同一被験者の3D-T1WIが二つある場合には、平均画像を生成)
  2. Talairach変換
  3. 信号ムラ(バイアス)の補正
  4. 信号値の正規化
  5. 頭蓋除去

3. 使用例

頭蓋除去前の3D-T1WI(T1w.nii.gz)に対して、recon-all -autorecon1コマンドを実行する。ここでは、被験者ID(-subjid)を「Subj001」にした。これは、自由に変えてもよい。

# FreeSurfer作業ディレクトリを指定
export SUBJECTS_DIR=$PWD

# Autorecon1を実行
recon-all -subjid Subj001 -i T1w.nii.gz -autorecon1

# Autorecon1で作成した頭蓋除去済みの脳マスク画像(brainmask)をNIfTIに変換
# ただし、元画像(T1w.nii.gz)と同じヘッダー情報を参考に(--likeオプション)
mri_convert Subj001/mri/brainmask.mgz brainmask.nii.gz --like T1w.nii.gz

# 元のT1WIにbrainmaskを適用
fslmaths T1w.nii.gz -mas brainmask.nii.gz T1_skull_stripped.nii.gz

recon-allでの処理が完了すると、被験者IDディレクトリ(Subj001)が生成され、このディレクトリ内に処理された結果が保存される。

Subj001/
├── label
├── mri
│   ├── T1.mgz
│   ├── brainmask.auto.mgz
│   ├── brainmask.mgz
│   ├── mri_nu_correct.mni.log
│   ├── mri_nu_correct.mni.log.bak
│   ├── nu.mgz
│   ├── orig
│   │   └── 001.mgz
│   ├── orig.mgz
│   ├── orig_nu.mgz
│   ├── rawavg.mgz
│   ├── talairach_with_skull.log
│   └── transforms
│       ├── bak
│       ├── talairach.auto.xfm
│       ├── talairach.auto.xfm.lta
│       ├── talairach.xfm
│       ├── talairach.xfm.lta
│       ├── talairach_avi.log
│       ├── talairach_avi_QA.log
│       ├── talairach_with_skull.lta
│       └── talsrcimg_to_711-2C_as_mni_average_305_t4_vox2vox.txt
├── scripts
│   ├── build-stamp.txt
│   ├── lastcall.build-stamp.txt
│   ├── patchdir.txt
│   ├── recon-all-status.log
│   ├── recon-all.cmd
│   ├── recon-all.done
│   ├── recon-all.env
│   ├── recon-all.local-copy
│   ├── recon-all.log
│   ├── recon-config.yaml
│   └── unknown-args.txt
├── stats
├── surf
├── tmp
├── touch
│   ├── conform.touch
│   ├── inorm1.touch
│   ├── nu.touch
│   ├── skull.lta.touch
│   ├── skull_strip.touch
│   └── talairach.touch
└── trash

頭蓋除去された画像は、「Subj001/mri/brainmask.mgz」にある。brainmask.mgz画像は、頭蓋除去済みの脳画像であるが、信号値の階調が0~225に圧縮(正規化)されてしまう。そこで、このbrainmask.mgzをマスク画像として扱い、元の3D-T1WIのマスク処理(マスキング)として適用する。

結果

処理前後の画像は次の通り。

【FSL】FSLを用いた頭蓋除去 ~Skull-stripping~


1. 目的
2. コマンド
3. 使用例
4. 結果


1. 目的

脳画像解析で、対象となる領域は脳実質でありその他の骨・筋・脂肪・眼球等の組織は、解析する上でノイズとなる。そのため、解析精度を高めるには頭蓋を除去することが重要である。

この記事では、FSLを用いて頭蓋を除去する方法を解説する。

  • FSLを用いた頭蓋除去

2. コマンド

ここでは、FSLの関数であるBETコマンドを用いて、頭蓋を除去する。

betコマンドのヘルプは次の通り。


Usage:    bet <input> <output> [options]

Main bet2 options:
  -o          generate brain surface outline overlaid onto original image
  -m          generate binary brain mask
  -s          generate approximate skull image
  -n          don't generate segmented brain image output
  -f <f>      fractional intensity threshold (0->1); default=0.5; smaller values give larger brain outline estimates
  -g <g>      vertical gradient in fractional intensity threshold (-1->1); default=0; positive values give larger brain outline at bottom, smaller at top
  -r <r>      head radius (mm not voxels); initial surface sphere is set to half of this
  -c <x y z>  centre-of-gravity (voxels not mm) of initial mesh surface.
  -t          apply thresholding to segmented brain image and mask
  -e          generates brain surface as mesh in .vtk format

Variations on default bet2 functionality (mutually exclusive options):
  (default)   just run bet2
  -R          robust brain centre estimation (iterates BET several times)
  -S          eye & optic nerve cleanup (can be useful in SIENA - disables -o option)
  -B          bias field & neck cleanup (can be useful in SIENA)
  -Z          improve BET if FOV is very small in Z (by temporarily padding end slices)
  -F          apply to 4D FMRI data (uses -f 0.3 and dilates brain mask slightly)
  -A          run bet2 and then betsurf to get additional skull and scalp surfaces (includes registrations)
  -A2 <T2>    as with -A, when also feeding in non-brain-extracted T2 (includes registrations)

Miscellaneous options:
  -v          verbose (switch on diagnostic messages)
  -h          display this help, then exits
  -d          debug (don't delete temporary intermediate images)

主要なオプションの役割は、次の通り。

  • -o:脳表の輪郭画像を生成
  • -m:バイナリ―のマスク画像を生成
  • -s:骨画像を生成
  • -n:セグメントした画像を生成しない
  • -f :信号値に対するしきい値(0-1, 初期値:0.5). 小さいほど脳が大きく出力される.
  • -g :信号値しきい値における垂直方向の勾配. 大きくなるほど脳幹付近の脳が大きくなり、頭頂部の脳が小さくなる.
  • -t:セグメントされた脳とマスク画像にしきい値処理を適用
  • -R:堅牢性の高い脳の中心推定(BETを何回か繰り返す)
  • -S :眼球と視神経を除去
  • -B :バイアスフィールドの補正と首の除去
  • -Z:z方向(スライス方向)のFOVがかなり小さい場合にBETの精度を上げる(一時的にスライスをパディング)
    • -F:4DのfMRIデータに適用(”-f 0.3″として少し脳マスクを大きくする)

基本的な使い方は、次の通り。

bet <入力画像> <出力画像> [オプション]

オプションについては、出力画像の結果を見ながら調節するとよい。

3. 使用例

頭蓋除去前の3D-T1WI(T1w.nii.gz)に対して、betコマンドを実行する。

bet T1w.nii.gz T1_skull_stripped.nii.gz -f 0.3 -R -S -B

処理が完了すると、頭蓋除去された画像(T1_skull_stripped.nii.gz)とそのマスク画像(T1_skull_stripped_mask.nii.gz)が生成される。

ls  # カレントディレクトリのファイルを確認

Out:

    T1_skull_stripped.nii.gz  T1_skull_stripped_mask.nii.gz  T1w.nii.gz

4. 結果

処理前後の画像は次の通り。