2024年8月31日土曜日

人工知能(強化学習、推論)の仕組み、画像推論技術

(背景、目的)
MRI、CT、PETなどの画像診断において
人工知能を使った時間、空間に対する画像推論技術は
患者さんの安全性を確保しながら、
患者さん、測定装置両方の負担を低減しながら、
連続的なデータを得る上で必須です。
CT、PETなどで高エネルギー電磁場の被ばく量を減らせるため、
より高い解像度、大きな、長い画像データを取得できることにつながります。
MRIでも高い磁場で高解像度で広範囲、かつ長時間のデータ取得が可能になります。
それぞれの測定系のハードウェアの性能の向上と同時に
データ解析のためのソフトウェアの性能向上にもつながります。
この記事では時間的に細切れになった画像データを
どのように人工知能によって強化学習し、推論(推定、インファレンス)するか
その具体的プロセスについて確認します。
人工知能による強化学習は明確なゴールがあります。
例えば、画像推論技術を強化学習させるためには
初めにコストのかかる正確な計算モデルや実際の詳細なデータなどを
学習するための目標として、
それよりも正確性の低い、あるいは時間、空間的に足りないデータから
適切なニューラルネットワークモデルにあてはめ学習させていきます。
その目標が定まれば、その目標に対して各層の出力データが近いか?
それを数字化できるようになるため、
一定のアルゴリズムを元に強化学習ができるようになります。
それが多くの学習データによって一義的に決定できれば、
その汎用性を元に同じ条件での推論が可能になります。
これが最も基本的な骨子です。

(概要)
ここで考える機械学習は「教師データあり」のものですから
任意の時間解像度を持つ連続的な画像があり、
そこからDuty 1/2 。すなわち1つの置きの画像から
その間に存在する画像を推定する事を考えます。
その時にここで畳み込みニューラルネットワークを設定し、
教師データに対してどれだけの誤差があるかの損失関数を定義します。
この時に報酬が最大(損失が最小)、
すなわち完全な正解に限りなく近づけるように
具体的には特徴量の最適化と各特徴量の重みづけ、数字としての割り当てを行います。


(手順概要)
(1)データ準備
まず、MRIの動画データを用意し、Duty1/2の条件で「オン時間」のフレーム(画像)と
「オフ時間」のギャップを持つデータセットを作成します。
通常のフレームレートで撮影されたフルシーケンスのMRI動画も必要です。
これは、AIが推論を行う際のトレーニングデータとして使用します。

(2)フレーム補間モデルの設計
欠落したフレームを補完するために、
フレーム補間(Frame Interpolation)技術を用いる必要があります。
これには以下のような技術が使われます:
畳み込みニューラルネットワーク(CNN): 
 フレーム間の連続的な動きを学習し、欠落したフレームを生成します。
時系列モデル(LSTM, GRU):
 フレームの順序や時間的な変化を学習して、より自然な動画を生成するのに役立ちます。
光フロー(Optical Flow): 
 動きの方向や速度を考慮し、連続するフレーム間のピクセルの変化を予測します。

(3)強化学習の適用
動き補完の最適化には強化学習が有効です。エージェントがフレーム補完を行い、結果の品質を評価しながら逐次的に改善します。
環境設計: 
 状態は現在のフレームと前後のフレーム、アクションは次のフレームの生成、報酬は生成された動画の品質(例えば、滑らかさや自然さなど)によって定義されます。
エージェント学習: 
 エージェントがフレーム補完を行い、生成結果に基づいて報酬を受け取り、ポリシーを更新します。深層Q学習(Deep Q-Learning)やポリシー勾配法を使うことが一般的です。

(4)AI推論によるフレーム補完
 トレーニングが終了したモデルは、実際のDuty1/2条件で取得した画像に対して推論を行い、欠落したフレームを補完します。

(5)評価と改善
 推論によって生成された動画を評価し、時間的連続性、滑らかさ、MRI画像としての妥当性を確認します。必要に応じて、モデルの調整を行い、より高品質な結果を得るよう改善します。

Pythonプログラムの例
以下は、簡単なフレーム補間モデルを構築するためのPythonコードの例です。これを強化学習で改善する手順と組み合わせることを前提とします。

python
コードをコピーする
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, Dataset
from torchvision import transforms
from PIL import Image
import numpy as np

torchについて
オープンソースのディープラーニングライブラリであるPyTorchを使用する際のPythonモジュールです。
PyTorchは、機械学習やディープラーニングモデルの構築、
トレーニング、推論をサポートするためのツールを提供しています。
torchを使うことで、効率的な数値計算、GPUアクセラレーション、
動的計算グラフ(Dynamic Computational Graphs)を用いた
ニューラルネットワークの実装が可能になります。

import torch.nn as nn は、PyTorchのtorch.nnモジュールをインポートして、
nnというエイリアスを付けることを意味します
エイリアス(alias)とは別名と言いう意味で
プログラム上の名前の定義です。
torch.nnモジュールは、ニューラルネットワークの構築や
トレーニングに必要な多くの便利な機能やクラスを提供します。
このモジュールには、ネットワークの層(レイヤー)、
損失関数、最適化手法、活性化関数などが含まれています。

1. ニューラルネットワークの層(レイヤー)
(10)人工知能における層、レイヤーとは?
このレイヤー層とは、それぞれ別々の演算様式があり、
別の層として定義されます。
例えば、全結合層、畳み込み層、プーリング層、リカレント層。
これらは別々の特徴的な演算をしますが、
ネットワークとは点(ノード)と線(エッジ)のことですが、
そのデータが線形代数、すなわち行列として並列して扱われることで
これらのネットワークが精緻性を持ちます。
点と点をつなぐ線の部分がいわば、「演算」で
点、すなわち数字の集合は「層、レイヤー」として扱われます。
入力から出力まで演算ごと、層が変わり、多層が形成されます。
上述した具体的な演算を持って強化学習や推論が行われます。

torch.nnモジュールには、様々な種類の層が定義されています。これらの層を使って、ニューラルネットワークを構築します。

(11)全結合層 (Fully Connected Layer): nn.Linear
入力と出力の全てのノードが接続された層です。特に多層パーセプトロン(MLP)で使われます。
例: nn.Linear(in_features, out_features)

全結合層の基本的な概念

構造:
ノード(ユニット): 全結合層の各ノードは、前の層の全てのノードからの入力を受け取り、重み付きの合計を計算します。
重み(Weights): 各接続には重みが設定されており、これらは学習過程で調整されます。重みは入力データの重要性を示すもので、ネットワークのトレーニングによって最適な値が決まります。
バイアス(Bias): 各ノードにはバイアス項が加えられ、これも学習によって調整されます。バイアスは、出力に対してシフトを加える役割を持ちます。

計算:
入力ベクトル x が全結合層に渡されると、層内の各ノードは次のような計算を行います:

     z=W⋅x+b

ここで、W は重み行列、b はバイアスベクトル、x は入力ベクトルです。
計算結果 z は次に活性化関数に渡されます。

活性化関数 f(z) を適用して、最終的な出力 y を得ます:

      y=f(z)

活性化関数には、ReLU(Rectified Linear Unit)、Sigmoid、Tanhなどが使われることが多いです。

活性化関数:

活性化関数は、ネットワークに非線形性を導入し、複雑なパターンや関係性を学習できるようにします。例えば、ReLUは以下のように計算されます:

        f(z)=max(0,z)

        z が0以上のとき、f(z) は z と等しくなります。
        z が0未満のとき、f(z) は0になります。

重み行列について
強化学習(Reinforcement Learning, RL)における重み行列は
環境からの状態(state)を入力として受け取り、アクション(action)の
価値、累積報酬値(Qネットワーク)
確率分布(ポリシーネットワーク)
これらの予測を行います。
強化学習ではゴール、目標を明らかにし、報酬を設計し、具体的な実装を行います。

1. 目標設定
高品質な動画の生成: 
 目標は、細切れの画像からスムーズで自然な連続動画を生成することです。この目標は、生成される動画の画質や連続性に基づいて評価されます。
動画の連続性: 
 生成された動画が自然でスムーズに連続しているかどうかを評価することも重要です。これには、視覚的な一貫性や流暢さが含まれます。

2. 報酬の設計
報酬設計では、生成された動画の質を高めるために具体的な評価基準を設定します。以下のような評価基準が考えられます:
画質の評価: 
 生成された動画の画質を評価するために、既知の動画と比較して画質の違いを測定します。例えば、構造的類似性(SSIM)やピーク信号対雑音比(PSNR)などの指標を用いて、画質が高いほど報酬が高くなるように設定します。
動きの一貫性: 
 連続する画像間での動きの一貫性を評価します。これには、フレーム間の動きの滑らかさや、動きの予測が正確であるかどうかを測定します。例えば、光フロー(Optical Flow)を用いて動きのスムーズさを評価し、スムーズな動きに対して高い報酬を設定します。
生成物と目標の一致: 
 生成された動画と実際の連続動画(または教師データ)との一致度を評価します。例えば、生成された動画が実際の連続動画と視覚的に似ているかどうかを測定し、一致度が高いほど報酬が高くなるようにします。
エラーの最小化: 
 生成過程でのエラーを最小化するための報酬設計も考えられます。例えば、生成された画像と前後の画像との違いを最小化するように、エラーが少ないほど報酬が高くなるように設定します。

3. 具体的な実装
以下に、強化学習の実装での目標設定と報酬設計の具体例を示します。
例: 動画生成モデルの強化学習
環境の設定: 
 環境は細切れの画像を受け取り、連続したフレームを生成するモデルです。エージェントは生成された動画のフレームを出力します。
報酬関数の設計:
 画質の報酬: 生成されたフレームとターゲットフレーム(または教師データ)との画質評価指標(SSIMやPSNRなど)を計算し、そのスコアを報酬として使用します。
 動きのスムーズさの報酬: フレーム間の動きのスムーズさを評価するために、光フローを計算し、動きが滑らかであるほど高い報酬を設定します。
 生成物の一致度の報酬: 生成された動画と既知の連続動画との類似度を計算し、一致度が高いほど報酬が高くなるように設定します。

(12)畳み込み層 (Convolutional Layer): nn.Conv2d
p
主に画像データに対して使用される層で、畳み込み演算を行います。
例: nn.Conv2d(in_channels, out_channels, kernel_size)

(13)プーリング層 (Pooling Layer): nn.MaxPool2d, nn.AvgPool2d

データのダウンサンプリングを行う層で、畳み込み層の後に使用されることが多いです。
例: nn.MaxPool2d(kernel_size)

データのダウンサンプリングとは、データのサイズを縮小するプロセスで、特にニューラルネットワークや画像処理において重要な手法です。ダウンサンプリングを行うことで、計算負荷を減らし、特徴抽出やデータの要約を効果的に行います。以下に詳しく説明します。

ダウンサンプリングとは
ダウンサンプリングは、データのサイズや解像度を減少させるプロセスです。例えば、画像のダウンサンプリングは、元の画像よりも小さいサイズの画像を生成することを意味します。これにより、データの処理が効率的になり、特徴を強調しながら不要な詳細を除去します。

なぜダウンサンプリングが必要か
計算負荷の削減:
 大きなデータや高解像度の画像を処理するには、計算リソースやメモリが大量に必要です。ダウンサンプリングを行うことで、データのサイズを縮小し、処理速度を向上させることができます。
特徴抽出の効率化:
 高解像度のデータには多くの詳細が含まれていますが、これらの詳細が必ずしも有用であるとは限りません。ダウンサンプリングにより、重要な特徴を強調しながら不要な詳細を除去することができます。
過学習の防止:
 複雑すぎるモデルは、訓練データに過剰に適合する可能性があります。ダウンサンプリングを行うことで、モデルのパラメータ数を減らし、過学習のリスクを低減します。

プーリング層 (Pooling Layer)
プーリング層は、データのダウンサンプリングを行うための層で、通常は畳み込み層の後に使用されます。主なプーリング手法には、以下の2つがあります:

最大プーリング (Max Pooling): nn.MaxPool2d
説明: 特定のサイズの領域(フィルタ)内で最大の値を選択します。例えば、2x2の領域に対して最大値を取ると、その領域内の最も大きな値が選ばれます。
効果: 重要な特徴を強調し、データのサイズを縮小します。局所的な特徴を保持しつつ、計算量を減らします。
平均プーリング (Average Pooling): nn.AvgPool2d
説明: 特定のサイズの領域内の平均値を計算します。例えば、2x2の領域に対して平均値を取ると、その領域内の全ての値の平均が計算されます。
効果: 平均値を取ることでデータの平滑化を行い、局所的な特徴を保持しつつ、データのサイズを縮小します。

(14)リカレント層 (Recurrent Layer): nn.LSTM, nn.GRU

時系列データに適用される層で、長期依存性を学習するために使われます。
例: nn.LSTM(input_size, hidden_size)

リカレント層:
時系列データやシーケンスデータに特化し、時間的な依存関係を学習して未来を予測します。内部状態を保持することで、過去の情報を考慮に入れた予測を行います。

リカレント層(Recurrent Layer)は、時系列データやシーケンスデータに特化して設計された層で、データの時間的な依存関係を学習するために使用されます。特に、長期的な依存性や過去の情報が重要なタスクにおいて効果的です。リカレント層にはいくつかのバリエーションがありますが、ここでは代表的なものとして**LSTM(Long Short-Term Memory)とGRU(Gated Recurrent Unit)**について説明します。

(リカレント層の基本概念)
リカレント層は、データのシーケンスを処理するために「リカレント(再帰的)」な構造を持っています。これは、ネットワークが過去の情報を保持し、現在の入力とともにこの情報を使用して次の状態を生成することを意味します。リカレント層は、以下の基本的な要素を持っています:
内部状態: ネットワークが過去の情報を保持するための内部メモリです。この内部状態が時間的な依存関係を学習します。
隠れ状態: 各タイムステップでの出力で、次のタイムステップへの入力として使用されます。
再帰的接続: 隠れ状態が次のタイムステップの計算に使用され、これにより時系列データが扱われます。

(LSTM(Long Short-Term Memory))
LSTMは、リカレントニューラルネットワーク(RNN)の問題を解決するために設計された特別なリカレント層です。特に、長期的な依存関係を学習する能力が向上しています。LSTMは以下の3つのゲートを使用して、情報の流れを制御します:
入力ゲート (Input Gate): 
 目的: 現在の入力が内部状態にどの程度加わるべきかを決定します。
 動作: シグモイド関数とtanh関数を使用して、どの情報を追加するかを決定します。
忘却ゲート (Forget Gate):
 目的: 以前の内部状態のどの部分を忘れるべきかを決定します。
 動作: シグモイド関数を使用して、過去の情報をどの程度保持するかを決定します。
出力ゲート (Output Gate):
 目的: 現在の内部状態から最終的な出力を生成します。
 動作: シグモイド関数とtanh関数を使用して、内部状態から出力を生成します。
LSTMの内部構造
内部状態Ct : 長期記憶を保持します。
隠れ状態ht : 現在の出力を生成するために使用されます。

※:シグモイド関数、tanh関数の定義

シグモイド関数は lim-∞ σ(x) = 0 / σ(0) = 0.5 / lim∞ σ(x) = 1
このような点対称の漸近曲線を描く関数で

  σ(x) = 1 / (1 + exp(-x)) = (tanh(x/2) + 1) / 2

このように定義されます。

tanh関数は lim-∞ σ(x) = -1 / σ(0) = 0 / lim∞ σ(x) = 1
このような点対称の漸近曲線を描く関数です。

上述した、入力ゲート、忘却ゲート、出力ゲートは
           (過去の情報(忘却)▷) ※1に近いほど引き継がれる
        ↓
  (入力▷) ⇒ (内部状態) ⇒ (出力▷)

従って、内部状態が入力データ、過去のデータにどれくらい影響を受け、
そのデータが実際に出力するときにどれくらい影響を与えるかを定義します。

シグモイド関数や
tanh(ハイパボリックタンジェント;hyperbolic 双曲線の)関数とすることで
重みづけに対して線形性ではなく、曲線とすることで
より柔軟な重みづけの定義ができるようになります。

(GRU(Gated Recurrent Unit))
GRUは、LSTMの簡略化バージョンで、よりシンプルな構造を持ちながらも同様に長期的な依存関係を学習できます。GRUは以下の2つのゲートを使用します:
リセットゲート (Reset Gate):
 目的: 過去の隠れ状態をどの程度リセットするかを決定します。
 動作: 現在の入力に基づいて過去の情報をどの程度無視するかを決定します。
更新ゲート (Update Gate):
 目的: 新しい情報と過去の情報をどの程度組み合わせるかを決定します。
 動作: 新しい隠れ状態の更新に関する割合を決定します。
GRUの内部構造
リセットゲートrt : 過去の隠れ状態をリセットする割合を決定します。
更新ゲートzt : 新しい隠れ状態を生成するための情報の割合を決定します。
候補隠れ状態ht′ : 新しい情報を基に生成された隠れ状態です。

(LSTMとGRUの違い)
LSTM: より複雑で、入力ゲート、忘却ゲート、出力ゲートの3つのゲートがあります。これにより、長期的な依存関係をより細かく調整できます。
GRU: よりシンプルで、リセットゲートと更新ゲートの2つのゲートしかありません。計算が効率的で、LSTMと同等の性能を発揮することがあります。

実際に脳の血管の4D flow MRIの機械学習モデルは考えられています(1)。
参考の為、その内容について確認し、評価します。
脳腫瘍を呈する子供の脳の血流データが得られることは
血流が薬物送達に密接に関わる事と、
脳腫瘍が子どもの脳の血流をどのように乱すか(擾乱するか)?
これを評価できるため重要です。
MRIから動画を得る一つの出口戦略、医療的価値は
脳血管の血流を定量化する事です。
これを4DフローMRIといいます。
4DフローMRIは、血流速度や流れの方向を可視化できますが、
物理的な制約やノイズにより、正確な数値が得られない場合があります。
特に、近血管壁の速度(near-vessel-wall velocity)など、
細かな部分での誤差が発生しやすいです。
なぜなら、境界層付近では非線形に流れが小さくなり変化率が大きいため
画像診断による定量化は難しいからです。
David R. Rutkowski(敬称略)らは、この問題を解決するために
計算流体力学(CFD, Computational Fluid Dynamics)の手法を利用しました。
CFDは、物理学に基づいて流体(この場合は血液)の動きをシミュレーションする技術であり、
精密で低誤差な速度場を提供できます。
しかし、CFD自体は計算コストが高く、臨床的には適用が難しい場合もあります。
従って、MRI装置のコンピュータにこうした計算モデルを入れて
その都度、計算させる事は現実的ではありません。
従って、
①20人の実際の4DフローMRIのノイズや誤差を含んだ血流データ
②それぞれの人における計算流体力学による低誤差の血流データ計算結果
これらを用意します。
②がいわゆる「ゴール、目標、レファレンス、最高参照点」です。
ここに計算流体力学のような複雑な計算をしなくても
人工知能の強化学習、推論によってたどりつくようにすることが研究の目的です(1)。
そのために使われた強化学習の手法は
畳み込みニューラルネットワーク(CNN)です。
すなわち、信号強度をカーネル毎、任意関数を積算する事で強化しながら、
目標、ゴールに近づけていく事をします。
この時に具体的に何を学習するか?
それは実際に患者さんの血流データを
どういった関数を乗算すれば、目標となるシミュレーションデータに近づくか?
その関数の組み合わせを最適化する事にあります。
すでに20人の患者さんに対しては「実際のデータ」と「計算結果」がありますから
ゴールである計算結果に対する誤差の大きさを
クロスエントロピー損失関数に割り当てて、
その損失関数を最小化するようなニューラルネットワークルートを探索します。
その際のフィルタ(畳み込みの単位、カーネル)の重みづけは
誤差逆伝播法(Backpropagation)を使って更新されます。
この手法では、損失関数の勾配(導関数)を計算し、
その勾配に従ってフィルタの値が調整されます。
すなわち損失関数によって実際のデータと予測のデータを照合して
その誤差を定量化し、最小化する条件を探します。
これにより、実際のデータと最適な畳み込み演算の組み合わせ、ルートが明らかになり、
こうした畳み込み演算の組み合わせは
20人以外の一般的な患者さんの脳の血流データ(フロー4DMRIデータ)に対して
適用する事で、計算コストのかかる計算流体力学を使わなくても
高い精度で、その計算結果に一致するようになり、
生データに含まれるノイズや誤差を低減する事ができます。

実際にこうした出口があるわけですが、
私はさらに測定時間を減らすために血流データの時間拡張、時間発展を
人工知能のリカレントCNNを持ちいて試みるための基礎を提供します。
測定中、動作が多い子どもにおいて、時間効率が上がるような
システムをリカレントニューラルネットワークで考えます。
実際には脳腫瘍の診断の時に擁する時間は30-60分かかるので
年少のお子さんの場合は鎮静剤や全身麻酔が必要になる場合があります(2)。
全身麻酔をかけるとなると少なくとも12時間以上は絶食となるはずなので
様々な意味で、子どもへの負担は大きくなります。
本日は、fMRIの血流の動画撮影の時間発展を例に挙げて考えますが、
測定時間を人工知能を使って減らすということは
特に子どもの患者さんへに対してメリットをもたらすを想定されます。
より具体的には10秒間の脳血管の血流データを5秒で撮る事を考えます。
その場合、トレーニングの為の基準となる目標が必要です
例えば、
◎Navier-Stokes方程式(CFDモデル)
◎Windkesselモデル
◎Kalmanフィルタ
等を使って、計算コストがかかる方法で、
より正確なゴールとなる血流データを構築します。
このモデルに基づき、20人くらいの子どもの5秒のデータから10秒の血流データを構築します。
この計算結果を強化学習のゴールとして
5秒のデータからリカレントニューラルネットワークの
シグモイド関数を用いたLSTM(Long Short-Term Memory)で強化学習させます。
実際にMRIの数字データは5秒から10秒間で空間分解能、時間分解能に従って
多数数字データがあります。時間毎にレイヤー(層)をわりあて、
入力ゲート、忘却ゲート、出力ゲートにおいて
もっとも計算結果の軌跡を正確に捉えるシグモイド関数の組み合わせを
強化学習によって定義します。
ただし、領域毎分けて、こうした
シグモイド関数の適用を変えていく必要があるかもしれません。
それを20人の子どもに対して共通的に定義します。
そうするとこのシグモイド関数の組み合わせは
このMRI装置の測定条件においては汎用性が生まれる為、
同じ条件で測定する場合において、5秒のデータを10秒のデータに
◎Navier-Stokes方程式(CFDモデル)
◎Windkesselモデル
◎Kalmanフィルタ
これらの計算モデルに近づくように拡張できます。
この10秒のデータに対して重層的に上述した
計算流体力学(CFD, Computational Fluid Dynamics)の手法と
畳み込み演算ニューラルネットワークの畳み込み演算組み合わせを用いて
誤差の少ない血流データに変換します(1)。


(具体的な使用例)
自然言語処理: テキスト生成や機械翻訳など、文脈の理解が重要なタスクで使用されます。
音声認識: 音声データの時間的な依存関係を学習し、音声をテキストに変換します。
時系列予測: 株価予測やセンサーデータの解析など、時間的な変化を予測するタスクで使用されます。


2. 活性化関数
ニューラルネットワークのノードに適用される非線形関数です。
活性化関数を使う主な理由は、ネットワークに非線形性を導入することです。
もし活性化関数が線形であれば、ネットワーク全体も線形となり、
いくら多層であっても1層の線形モデルと同じ能力しか持たなくなります。
非線形な活性化関数を使うことで、
ニューラルネットワークは複雑なパターンや非線形な関係を学習することができます。

活性化関数の種類には上述したシグモイド関数や双曲線のタンジェント(tanh)があります。
その他に、

ReLU(x)=max(0,x)これがあります

z が0以上のとき、f(z) は z と等しくなります。
z が0未満のとき、f(z) は0になります。


ReLU (Rectified Linear Unit): nn.ReLU

非線形性を導入し、負の値をゼロに置き換えます。
例: nn.ReLU()
Sigmoid: nn.Sigmoid

出力を0から1の範囲に制限する関数です。
例: nn.Sigmoid()
Tanh: nn.Tanh

出力を-1から1の範囲に制限する関数です。
例: nn.Tanh()


3. 損失関数
モデルの予測と実際の値との誤差を計算します。

損失関数(Loss Function)は、機械学習モデルの予測と実際の正解データ
(ターゲット値)との間の誤差を計算するために使用される関数です。

損失関数は、機械学習モデルの学習プロセスにおいて非常に重要な役割を果たします。
モデルのパラメータ(重みやバイアス)は、損失関数を最小化する方向に調整されます。
この最適化プロセスにより、モデルは
トレーニングデータに対してより正確な予測ができるようになります。

(LF1)平均二乗誤差(Mean Squared Error, MSE)
回帰問題でよく使用されます。
予測値と実際の値の差の二乗の平均を取ります。
数式:
     MSE = (1/n)∑[n;i=1](yi - y'i)^2

yi:実際の値, y'i:モデルの予測値 n:データの数

(LF2)クロスエントロピー損失(Cross-Entropy Loss)
分類問題でよく使用されます。
モデルが予測したクラスの確率と、実際のクラスの確率(通常は0または1)の間の差異を計算します。
特に、バイナリ分類(2クラス分類)や多クラス分類で使用されます。
数式(バイナリクロスエントロピーの場合)(3)

  Loss = (-1/n)∑[n;i=1](yi*log(yi') + (1-yi)*log(1-yi'))

(LF3)ヒンジ損失(Hinge Loss)
サポートベクターマシン(SVM)などで使用される損失関数です。
数式: 

  Loss = max(0,1−yi*yi')


# 畳み込みネットワークモデル(シンプルなフレーム補間)
class FrameInterpolationCNN(nn.Module):
    def __init__(self):
        super(FrameInterpolationCNN, self).__init__()
        self.conv1 = nn.Conv2d(2, 64, kernel_size=3, padding=1)
        self.conv2 = nn.Conv2d(64, 128, kernel_size=3, padding=1)
        self.conv3 = nn.Conv2d(128, 64, kernel_size=3, padding=1)
        self.conv4 = nn.Conv2d(64, 2, kernel_size=3, padding=1)

ーー
(コードの説明)
 def __init__(self):
__init__は初期値の設定でselfはデータや処理様式(数式など)のアクセスを示しています。

 super(FrameInterpolationCNN, self).__init__()
FrameInterpolationCNN, selfすなわち
畳み込みニューラルネットワークの共通のアルゴリズムと
データ、処理様式親クラスを呼びだし、それを初期化するというものです。
親クラスは、複数のサブクラスで共通して使用される機能やデータを提供します。
親クラスは、モデルの一般的な構造やアルゴリズムの枠組みを提供し、サブクラスは具体的なモデルの詳細を定義します。

この親クラス、子クラスはオブジェクト指向プログラミングと呼ばれ、
クラスごとに抽象性と具体性をわけます。
例えば、生物(親クラス)、牛(子クラス)といったように
親クラスでは子クラスに対してより抽象的な概念が定義されます。
牛の名前や年齢は属性、
牛が牛乳を出す、肉を提供してくれる、メタンを出す、フンをする
そういった機能を一つ一つ共通な形で定義します。
そうするとプログラムとして非常に「3整然とした」形でコードを定義することができます。
--
例えば、親クラスが抽象的なので子クラスの細かな変更の際
プログラムを全部書き換えなくても、子クラスの一部の属性や機能を変更し
親クラスは「1そのまま利用すること」ができます。
従って、「2変更するコードも最小限」にできます。
親クラスではプログラムをどのように動かすかのより
抽象的な概念が定義されますから、
それに基づいて「4具象クラスの実装のテスト」を容易に行えます。
また、レイヤーに分けて子クラスを並列的に設定して
それらを束ねる親クラスがある事で
これらの子クラスを親クラスの定義に基づいた
同じインターフェースで動作させることができ統一的に操作できます。
例えば、人工知能の強化学習で
異なる計算アルゴリズムのどれが優れているかの比較をするときには
それぞれの計算アルゴリズムの共通概念を抽出し、
それを親クラスとして定義する事で
コンピュータ上でそれらの性能の比較がより整然として、容易になります。

super(FrameInterpolationCNN, self).__init__()
        self.conv1 = nn.Conv2d(2, 64, kernel_size=3, padding=1)
        self.conv2 = nn.Conv2d(64, 128, kernel_size=3, padding=1)
        self.conv3 = nn.Conv2d(128, 64, kernel_size=3, padding=1)
        self.conv4 = nn.Conv2d(64, 2, kernel_size=3, padding=1)

これはsuperとあるので親クラスの定義となります。
従って、このMRIの画像推定において
オン時間の画像全体から
どういう畳み込み処理によって特徴を掴み(特徴量)、
オフ時間の画像を生み出し、最終的に連続な画像、
すなわち動画を形成するかのより抽象的な定義となります。
従って、どういったカーネルで特徴量を段階的に最大何個まで抽出するかの定義です。
ここでは最大で128の特徴量としました
特徴量というのは
それぞれのピクセルが持つ数字の種類で、
例えば、赤、青、色、エッジ(水平、垂直、斜め)、コーナー、角
テクスチャ、より複雑な様々な形状です。
MRIのケースでいえば、グレースケールなので各ピクセルの特徴量として
基本的なデータである
電子信号の数(コントラスト)、散乱角(検出器が検出した場所)
これらがベースとしてあり、
人工知能が抽出する特徴量として
エッジ、コーナー、角、テクスチャーなどの抽象的な特徴量から
細胞膜、血管、小胞など、
あるいは物質の複雑な模様(タンパク質、脂質、糖など)など
より具体的な特徴量などがあります。
こうした特徴量は全て各ピクセルに数字として割り当てられます。
また、目的は時系列のデータの間の空白のオフ時間の画像を推定することですから
それを推定する重要な特徴量として
こうしたそれぞれの特徴量の時間的な変化量が含まれます。

こうした特徴量を反映した形で
最後に元の画像と整合させるため
基本的なデータであるコントラストと散乱角のデータを各ピクセルに割り当てて
オフ画像を生成します。

ーー

    self.relu = nn.ReLU()

    def forward(self, x):
        x = self.relu(self.conv1(x))
        x = self.relu(self.conv2(x))
        x = self.relu(self.conv3(x))
        x = self.conv4(x)
        return x

(コードの説明)
これは畳み込み処理に対して行う損失関数による評価を
どういった順序で行うかを概念のコードになります。
実際に処理するものではありません。ルールを示すものです。

# データセットクラスの定義
class MRIFrameDataset(Dataset):
    def __init__(self, image_pairs, transform=None):
        self.image_pairs = image_pairs
        self.transform = transform

これはデータセット(数字データ、関数設定など)の初期化するメソッドです。
self、データなど、image_pairsは参照画像と推論画像のペア
transform=noneは前処理しないということ
初期化の意味はその処理領域において前のデータがあるかないかはわかりませんが、
ある場合に備えて、それが干渉しないようにデータを消去する事と
その処理領域においてMRI画像データをどのように扱うか?
その環境を整えるものです。
すなわちデータ、解析関数、参照画像、推論画像などが
素因として存在するということです。

    def __len__(self):
        return len(self.image_pairs)

    def __getitem__(self, idx):
        img1, img2, target = self.image_pairs[idx]
        if self.transform:
            img1 = self.transform(img1)
            img2 = self.transform(img2)
            target = self.transform(target)
        return torch.cat([img1, img2], dim=0), target

# データセットの準備
transform = transforms.Compose([
    transforms.Resize((256, 256)),
    transforms.ToTensor(),
])

# 例として2枚の画像間に補完フレームを作る
image_pairs = [(Image.open('frame1.png'), Image.open('frame3.png'), Image.open('frame2.png'))]
dataset = MRIFrameDataset(image_pairs, transform=transform)
dataloader = DataLoader(dataset, batch_size=1, shuffle=True)

# モデル、損失関数、最適化手法の定義
model = FrameInterpolationCNN()
criterion = nn.MSELoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

# トレーニングループ
for epoch in range(10):  # 10エポック実行
    for inputs, target in dataloader:
        optimizer.zero_grad()
        outputs = model(inputs)
        loss = criterion(outputs, target)
        loss.backward()
        optimizer.step()
    print(f'Epoch {epoch+1}, Loss: {loss.item()}')

# 推論の実行
model.eval()
with torch.no_grad():
    for inputs, _ in dataloader:
        outputs = model(inputs)
        # 生成されたフレームを保存するなどの処理
強化学習を組み込むための考慮点
環境設計: 環境は、フレームの状態と推論結果を報酬として返す機構を作成します。
報酬関数: 生成されたフレームと実際のフレームの相似性や、動きの自然さを測定するための報酬関数を設計します。
ポリシー更新: 強化学習エージェントの更新アルゴリズム(例:Deep Q-NetworkやActor-Critic法)を選定し、報酬に基づいてフレーム補完のポリシーを改善します。


(参考文献)
(1)
David R. Rutkowski, Alejandro Roldán-Alzate & Kevin M. Johnson 
Enhancement of cerebrovascular 4D flow MRI velocity fields using machine learning and computational fluid dynamics simulation data
Scientific Reports volume 11, Article number: 10240 (2021) 
(2)
Alok Jaju, Kristen W. Yeom and Maura E. Ryan
MR Imaging of Pediatric Brain Tumors
Diagnostics 2022, 12, 961.
(3)
Shruti Jadon
A survey of loss functions for semantic segmentation
978-1-7281-9468-4/20/$31.00 ©2020 IEEE

0 コメント:

コメントを投稿

 
;