← スイートトップへ | MediSeg-AI 概要 | 深層学習・ANN解説

MediSeg-AIの技術Model architecture and training methods used in MediSeg-AI

MediSeg-AIは、脳MRI画像から病変領域を画素単位で抽出するセグメンテーションシステムです。 本ページでは、採用している畳み込みネットワーク(U-Net)、学習時の最適化、評価指標、 および自社で進めている3D学習の内容を、実装に即して整理しています。 ニューラルネットワークの基礎数理や他モジュール共通の原理は 機械学習解説(MobileNetV2 / EfficientNet等のバックボーン解説を含む)、 モデル構造のインタラクティブ表示は Architecture Visualizer、 学習済み重みの比較は Weight Visualizer、 3D学習の詳細は 3D学習の節をご覧ください。 他モジュールの技術解説は Detect / Search を参照してください。

本表示は研究・技術可視化用であり、診断・治療の判断には使用できません。 出典・ライセンスは データ・モデルについて を参照してください。

まず動画で見る — MediSeg-AI 技術解説(日本語ナレーション付き・約92秒・再生すると全画面表示)。 自社学習 2D U-Net の内部構造、実推論の特徴マップ、確率マップ、重み統計、学習設定と評価分布をコンパクトに解説しています。 検出デモ中心の プロモーション動画(約76秒) もあわせてご覧ください。

1畳み込みニューラルネットワーク(CNN)とは

CNNは、小さな「フィルタ(カーネル)」を画像の上でスライドさせながら、各位置で 要素ごとの掛け算 → 総和を計算していくネットワークです。1枚の画像全体に対して同じフィルタを 使い回す(重み共有)ため、全結合層に比べてパラメータ数が大幅に削減され、かつ「エッジ」「質感」のような 局所的なパターンを画像内のどこにあっても検出できるようになります。

入力の3×3の窓(青枠)とカーネルの重みを要素ごとに掛けて総和を取ると、 出力特徴マップの1ピクセルが決まる。この窓を1マスずつスライドさせて出力全体を埋めていく。

MediSeg-AIでは、1回の畳み込みブロック(DoubleConv)の中で Conv2d(3×3, padding=1) → BatchNorm → ReLU を2回連続で適用しています。 padding=1 なので出力の画像サイズは入力と変わりません(“same” 畳み込み)。 BatchNormは各バッチ内でチャンネルごとに正規化して学習を安定させ、ReLU (max(0, x))は負の値を0にすることでネットワークに非線形性を持たせます — もし全部が 線形変換(掛け算・足し算)だけなら、何層重ねても1層の線形変換と等価になってしまい、複雑なパターンを 学習できません。

実装backend/mediseg_ai/models/unet.py:11-28(DoubleConvクラス)
カーネルサイズ3×3、padding=1(出力サイズ = 入力サイズ)
正規化 / 活性化BatchNorm2d → ReLU(各畳み込みの直後)

2バッチ正規化(Batch Normalization)

前の節で触れたBatchNormを補足します。 学習が進むにつれて各層への入力の分布(平均・分散)がバッチごとに揺れ動くと、後ろの層は 毎回変化する入力分布に合わせて学習をやり直すことになり、学習が不安定になったり遅くなったりします (internal covariate shift と呼ばれる現象)。BatchNormは、その揺れをバッチ単位で打ち消す 仕組みです。

BN(x) = γ x − μB√(σB2 + ε) + β
①バッチ内の生の活性化値は、チャンネルごとに平均・広がりがバラバラ。②そのチャンネルの バッチ内平均 μB と分散 σB2 で正規化すると、 平均0・分散1に揃う。③学習可能なパラメータ γ(スケール)・β(シフト)で、 ネットワークが最適だと判断した分布に調整し直す。
  • μB, σB2: そのバッチ・そのチャンネルにおける平均と分散(学習時に動的に計算。推論時は学習中に蓄積した移動平均を使う)。
  • γ, β: 学習可能なパラメータ。「平均0・分散1」に固定してしまうとネットワークの表現力が落ちる場合があるため、 必要なら元の分布に近い形へ戻せるようにしている。
  • 効果: 各層への入力分布が安定するため、より大きな学習率を使っても発散しにくくなり、 重みの初期値への依存も減る。副次的に、バッチごとの統計量に含まれるノイズが軽い正則化効果も生む。
MediSeg-AIでの実際の使用箇所: DoubleConv (backend/mediseg_ai/models/unet.py:18-25)の中で、2回の畳み込みそれぞれの直後に nn.BatchNorm2dを適用しています。U-Net全体では DoubleConv が INC・DOWN1–4・UP1–4 の 9ブロックあり、1ブロックにつき2箇所なので、 合計 18箇所のBatchNorm2dがネットワーク全体に存在します。畳み込み層(Conv2d) 自体は bias=False で作られていますが、これはBatchNormの β がバイアス項の役割を 兼ねるため、素の畳み込みバイアスは不要だからです。

3活性化関数(シグモイド・ソフトマックス)

活性化関数(Activation Function)は、線形変換(入力×重み+バイアス)の出力を 非線形な値に変換して次の層へ渡す関数です。前のセクションで見たReLU (max(0, x))もその1つで、MediSeg-AIでは9つのDoubleConvブロックすべての中間層で使われています。 線形変換だけをいくら重ねても、結局は1つの大きな線形変換に集約されてしまい複雑な境界線を学習できないため、 この非線形性がネットワークの表現力の源になっています。ここでは中間層のReLUとは役割の異なる、 出力層で用いる2つの活性化関数 — シグモイド関数とソフトマックス関数 — を整理します。

シグモイド関数MediSeg-AIで使用

シグモイド関数(Sigmoid)は、任意の実数を 0〜1の範囲に滑らかに圧縮する関数です。

f(x) = 11 + e−x
左: ReLU — 0未満は0を出力し、0以上はそのまま通す。右: シグモイド — 出力が(0, 1)に収まり、 ±6を超えるとほぼ傾きゼロ(勾配消失)になる。
  • 出力を確率として解釈できる: 出力が(0, 1)に収まるため、「病変である確率」のような 二値の見込み値として扱いやすい。
  • 勾配消失問題: 入力の絶対値が大きい領域では傾き(微分値)がほぼ0になり、逆伝播で伝わる勾配も ほぼ0になってしまう。これが、深い中間層でシグモイドではなくReLUが使われる理由です (セクション1参照)。
MediSeg-AIでの実際の使用箇所: 出力層(OutC)自体には活性化関数を付けず、 生のロジット(実数)をそのまま出力しています。その代わり、ロジットを「病変らしさ0〜1」の 確率ヒートマップに変換する箇所すべてでシグモイドが使われています — 推論結果を表示する backend/mediseg_ai/inference/pipeline.py:48 (torch.sigmoid(output)、これが可視化ページの「確率ヒートマップ」の正体です)、 評価時の backend/mediseg_ai/evaluation/evaluate.py:124、 Diceスコア計算の backend/mediseg_ai/metrics/torch_adapter.py:12、 そして学習時の DiceLoss(backend/mediseg_ai/training/losses.py:16)。 なお CombinedLoss のもう半分である BCEWithLogitsLoss は、 数値的に安定な実装のためシグモイドと二値交差エントロピーを1つの関数の中で内部的に計算しており、 外からは明示的な sigmoid() 呼び出しは見えません。

ソフトマックス関数MediSeg-AIでは未使用

ソフトマックス関数(Softmax)は、複数の出力(ベクトル)を、合計が1(100%)になる確率分布に変換する関数です。 K個のクラスがある場合、i番目の出力 yi は次のようになります。

yi = exi KΣk=1exk
生のスコア(ロジット、負の値もあり得る)を、合計がちょうど100%になる確率分布に変換する。 一番大きいロジットが一番高い確率になるが、他のクラスにもわずかに確率が残る(ソフトな最大値選択)。
  • 多クラス分類の出力層: 「犬・猫・鳥」のように3つ以上の選択肢から1つを選ぶ分類問題の出力層で使われる。
  • シグモイド関数との関係: クラス数が2つの場合、ソフトマックスは数学的にシグモイド関数と等価になります — つまりシグモイドの多クラス拡張版がソフトマックスです。
なぜMediSeg-AIでは使われていないか: ソフトマックスは「複数クラスのうちどれか1つ」を選ぶ 多クラス分類のための関数です。MediSeg-AIの UNet は n_classes=1(backend/mediseg_ai/models/unet.py:93)で構築されており、 各ピクセルについて「病変か背景か」の二値だけを判定するタスクのため、多クラスに正規化する ソフトマックスではなく、単一の値を0〜1に収めるシグモイドで十分だからです。もしMediSeg-AIを 拡張して「腫瘍・浮腫・正常組織」のように3種類以上をピクセル単位で塗り分けるモデルにする場合は、 出力層をn_classes個に増やし、損失関数もソフトマックス+多クラス交差エントロピー (CrossEntropyLoss)に置き換える必要があります。

3つの関係性のまとめ

活性化関数ネットワークに非線形性を与える関数の総称。モデル全体の表現力を上げるため用いる。
ReLU負を0にする活性化関数。MediSeg-AIの全9ブロック(DoubleConv)の中間層で使用(出力層OUTCには活性化関数なし)。
シグモイド関数入力を0〜1に圧縮する活性化関数。二値分類の出力層向け。MediSeg-AIの推論・評価・損失計算で使用。
ソフトマックス関数複数の出力を合計1の確率分布に変換する関数。多クラス分類の出力層向け。MediSeg-AIでは未使用(二値タスクのため)。

4MediSeg-AIのU-Netアーキテクチャ全体像

U-Netは、画像をだんだん縮小させながら特徴を抽出するエンコーダ(収縮路)と、 その特徴を元の解像度まで復元しながら領域を塗り分けるデコーダ(拡張路)を、 “U” の字型に組み合わせたアーキテクチャです。名前の由来にもなっている スキップ接続(エンコーダの特徴を対応する解像度のデコーダへ直接コピーする経路)によって、 ダウンサンプリングで失われがちな輪郭などの位置情報を、デコーダ側で復元しやすくしています。

箱の幅 ≈ 画像の空間解像度、箱の高さ ≈ チャンネル数(実際の値)。エンコーダは MaxPool2dで解像度を半分にしながらチャンネル数を倍にし、デコーダは ConvTranspose2dで解像度を倍に戻しながらチャンネル数を半分にする。破線の弧がスキップ接続。

各ステージの役割

INC256×256×64ch — 最初のエッジ・色の変化などシンプルなパターンを検出
DOWN1–DOWN3128²→64²→32²、128→256→512ch — 解像度を落としながら段々複雑な構造を認識
BOTTLENECK16×16×1024ch — 画像を最も圧縮した中心部。「全体として何が写っているか」の要約
UP1–UP432²→64²→128²→256²、512→256→128→64ch — スキップ接続と合成しながら解像度を復元
OUTC256×256×1ch — Conv2d(1×1)で各ピクセルの「病変らしさ」を1つの値に集約
実装は backend/mediseg_ai/models/unet.py:93-122(UNetクラス)。 エンコーダ最深部からの復元には ConvTranspose2d(学習可能な逆畳み込み)を使う設定と、 単純な bilinear 補間を使う設定の2通りをサポートしていますが、公開されている3チェックポイント (LGG-only / MSD / Combined)はいずれも ConvTranspose2d 版で学習されています。 合計パラメータ数は約3,100万。入力256×256のRGB画像1枚につき、全10ブロックの畳み込みが この“U”の形で一度だけ実行されます。動かして回転できる3D版は Architecture Visualizerを、 実際に学習された重みの強弱はステージごとの色分けで Weight Visualizerで確認できます。

使用しているMRIモダリティ(FLAIR・T1gd・T2w)

MSDデータセット(公開事例「小さな病変を検出できた例」の元データ)は、1つの断面につき 4種類のMRI撮像法(モダリティ)を持っています。ところが UNet(n_channels=3) は 通常のRGB画像と同じ3チャンネル入力を前提にしたアーキテクチャなので、4つのうち3つだけを選んで R・G・Bの3チャンネルに割り当てています。

MSD/BraTS形式の4モダリティのうち、FLAIR・T1gd・T2wの3つがR・G・Bチャンネルに割り当てられ、 非造影T1w(index 1)は使われていない。
  • FLAIR: 脳脊髄液の信号を抑制した撮像法。浮腫や病変全体の広がりを最も見やすく捉える。
  • T1gd(造影T1強調): 造影剤(ガドリニウム)を使うことで、活発に増殖している腫瘍コアが 明るく強調される。
  • T2w: FLAIRを補完する形で浮腫や非造影腫瘍の広がりを捉える。
  • 非造影T1w(使われていない): 主に解剖学的な構造(脳の形やコントラスト)を映すための 撮像法で、腫瘍と正常組織のコントラストが他の3つほど際立ちません。4つのうち1つを削るなら 最も腫瘍の情報量が少ないT1wを外す、という判断は臨床的に見て妥当な選択と言えます。
コードでの実際の箇所: 学習データ生成 (notebooks/07_msd_finetune_from_lgg_checkpoint.ipynb) と showcase 生成 (notebooks/08_msd_showcase_export.ipynb) の両方に同一の slice_to_rgb() 関数があり、 chans = [volume[:, :, z, 0], volume[:, :, z, 2], volume[:, :, z, 3]] (MSDのモダリティ順序 0=FLAIR, 1=T1w, 2=T1gd, 3=T2w のうち index 1 を除く3つ)としてRGB化しています。 ただし、この3つを選んだ理由を明示するコメントはコード中には残っていません — 上記の臨床的な妥当性は 後付けの説明であり、実際に意図された設計判断だったのか、単に3チャンネルの枠に収める必要があった 結果なのかは、コードからは判別できない点に注意してください。

コンペティションレベルとの比較

脳腫瘍セグメンテーションの国際コンペ BraTS(MSD Task01 BrainTumourの元データ)は、 症例ごとに3D立体を復元して Dice を計算し、症例間で平均する公式プロトコルで採点します。 評価領域は入れ子の3つ — Whole Tumor(WT)・Tumor Core(TC)・ Enhancing Tumor(ET) — で、競技の本採点はこれらの平均です。 MediSeg-AIの3D学習(Phase 5)は、このプロトコルに揃えた 症例単位3D Diceで測定しています(学習・評価の症例分離を確保・全73症例)。

BraTS公式に近い症例単位3D Dice(MediSegは学習未使用の評価症例・全73件。nnU-NetはBraTS 2020公開値)。
  • MediSeg-AI 3Dモデル(UNet3D・領域出力): WT 0.8756 / TC 0.7870 / ET 0.7163 / 3領域平均 0.7930(2026-07時点。単一モデルへの単純な反転平均では改善せず)。
  • BraTS 2020優勝(nnU-Net): WT 0.8895 / TC 0.8506 / ET 0.8203 / 3領域平均約 0.853(テストセット。検証WTは0.9124)。
  • 差: WTは約 −0.014。競技採点に相当する3領域平均では約 −0.06 (主にTC/ET、とくにET)。
公開中の可視化・推論画面は、運用実績のある2D U-Net(3モダリティをRGB化)を用いています (左のモダリティ図)。並行して、3D U-Netによる領域分割(WT/TC/ET)の自社学習を進めており、 評価も症例単位の3D Diceに揃えています。4モダリティ化(T1w追加)は同一条件の比較でWT差分がほぼゼロ (−0.0067)だったため、現時点では追加していません。残差は主にTC/ETで、 補助損失の強化・学習収束の改善・複数モデルの統合などが次の施策です。 詳細は docs/COMPETITION_ROADMAP.md を参照してください。
開発状況
2Dモデルの提供に加え、3D U-Netの自社学習を実施中です。WTは国際コンペ上位手法と近い水準にあり、 残課題はTC/ETを含む3領域平均の約0.06差です。 同様の手法を医用画像に限らず、製造業の外観検査など、対象が小さく背景に対してまばらな領域へ 展開する検討も進めています。

4b3D学習 — UNet3D・パッチ学習・領域出力

現行の2D U-Netは、MRIボリュームを断面(スライス)単位に分解して学習します。 この方式では、前後のスライスにわたる病変のつながり(奥行き方向の文脈)を直接扱えません。 そこで自社では UNet3D (backend/mediseg_ai/models/unet3d.py)を別実装し、 ボリュームを3次元畳み込みで処理する学習を進めています。2Dモデル・可視化・既存の学習済み重みは そのまま併用できます。

左: 2D学習 — 各スライスを独立に処理(奥行き方向のつながりは見えない)。 右: 3D学習 — 3x3x3カーネルが深さ方向にも広がり、前後スライスの情報を同時に利用する。

3D化で変わる点

  • 入力形式: 2Dは (B, C, H, W)。3Dは (B, C, D, H, W) (Dはスライス方向)。演算は Conv3d / MaxPool3d / ConvTranspose3d。
  • ネットワーク構成: 2D U-Netと同様に DoubleConv / Down×4 / Up×4 / OutConv。 メモリ制約から base_channels=32(2Dの64ではない)。4チャンネル入力で約2,258万パラメータ。
  • 正規化: スライスごとではなく、ボリューム単位・チャンネルごとに強度を揃える (奥行き方向の一貫性を保つため)。
モデルUNet3D — backend/mediseg_ai/models/unet3d.py
データMSDVolumeDataset — backend/mediseg_ai/datasets/msd_volume.py
学習手順notebooks/17_msd_3d_training.ipynb(WT二値) / 18_msd_3d_region_training.ipynb(WT/TC/ET)
評価brats_eval_3d.py — スライディングウィンドウ + 症例単位3D Dice
計算資源128³パッチ / base=32 でGPUメモリ約16GBが目安

パッチ学習(128³)と前景サンプリング

1症例は数百スライスに及ぶため、学習時はボリューム全体ではなく sample_patch() で固定サイズ(例: 128×128×128)の領域を切り出します。 腫瘍は体積のごく一部であるため、一定確率で腫瘍ボクセルを中心にしたパッチを抽出し (foreground_prob)、小病変が学習から欠落しないようにしています。

全ボリュームから128³パッチを切り出す。オレンジは腫瘍領域。前景サンプリングは腫瘍近傍を優先する。

推論: スライディングウィンドウ

学習はパッチ単位でも、評価は症例全体でDiceを算出します。 predict_volume_with_3d_model は重なりを持たせた窓をずらしながら推論し、 重なり部分を加重平均して1つの3Dマスクに復元します。これにより、断面単位の評価とは異なる 「症例単位3D Dice」で性能を確認できます。

重なる窓を走査し、重み付き平均で全ボクセルを埋める。

領域出力: WT / TC / ET

BraTSの評価は入れ子の3領域です。n_classes=3 と RegionLoss (チャンネル毎のsigmoid BCE+Dice)により、相互排他ではなく 重なりを許す3つの二値出力を学習します。現時点の測定値は WT 0.8756 / TC 0.7870 / ET 0.7163 / 平均 0.7930(評価症例73件)。

ET ⊂ TC ⊂ WT。外側ほど検出しやすく、ETが最も難しい。
実装上の要点: 既存の SegmentationTrainer は次元に依存しないため、 追加の学習器なしで5次元テンソルのまま学習できます(test_train3d_smoke.py)。 Deep supervision(UNet3D(deep_supervision=True))は学習時のみ低解像度の補助出力を追加し、 推論時(eval())は従来どおり単一テンソルを返します。 単一モデルへの単純な反転平均は平均 −0.0034で採用していません。 次の施策は補助損失の強化、学習の収束改善、複数モデルの統合です。
2Dと3Dの位置づけ
可視化・現行推論で用いるのは主に2D U-Netです。 本節の3D学習は、国際的な評価指標に合わせた性能向上のための自社開発です。 掲載する評価値は セクション4のコンペ比較および docs/COMPETITION_ROADMAP.md にあります。

5誤差逆伝播法(バックプロパゲーション)

ネットワークは大きく2方向の計算からできています。順伝播(forward)は画像を入力してから予測を 出すまでの通常の計算方向。逆伝播(backward)は、その予測がどれだけ間違っていたか(損失)から 出発して、「この重みをほんの少し変えたら、損失はどれくらい変化するか」という勾配を、 出力側から入力側へ向かって連鎖律(chain rule)で層ごとに計算し直していく方向です。

上段(青)が順伝播、下段(赤)が逆伝播。 逆伝播は連鎖律 ∂L/∂x = ∂L/∂y · ∂y/∂x を使い、 出力側で計算した勾配を再利用しながら入力側へ伝えていく — だから全パラメータの勾配を 1回のバックワードパスだけで計算できる。

この連鎖律の計算は、PyTorchでは自動微分(autograd)が全て肩代わりします。 MediSeg-AIの学習コードでは、実際には次の1行を呼ぶだけで、約3,100万パラメータすべての勾配が 計算されます:

# backend/mediseg_ai/training/trainer.py:185
self.scaler.scale(loss).backward()  # ← ここで連鎖律が10ブロック分すべてに適用される
なぜ効率的か: 逆伝播は順伝播で計算した中間値(各層の出力)を再利用しながら勾配を計算するため、 パラメータ1個ずつ数値的に(少しだけ動かして損失の変化を測る、という総当たり方式で)勾配を求めるのに比べて パラメータ1個ずつ数値微分(微小変化を与えて損失の差分を計測する方式)で求める計算量に比べて計算効率が極めて高く、GPU向け数値計算カーネルと組み合わせることで約3,100万パラメータの大規模ネットワークでも高速なバッチ学習が可能となります。

6バッチ処理とミニバッチ勾配降下法

逆伝播で勾配を計算する対象は「1枚の画像」ではなく「バッチ(複数枚まとめた集合)」です。 この単位の選び方には3つの方式があります。

同じデータセット(24枚と仮定)を1エポックで学習する場合の重み更新回数の違い。 バッチが小さいほど更新回数(=学習の反応速度)は増えるが、1回ごとの勾配はノイズが大きくなる。
  • フルバッチ(Batch GD): 全データの平均勾配で1回だけ更新。勾配は正確だが、 データセット全体をメモリに載せる必要があり、1エポックに1回しか重みが動かないため学習が遅い。
  • 確率的勾配降下法(SGD, バッチサイズ1): 1枚ごとに更新。反応は速いが、1枚だけから 推定した勾配はノイズが大きく、損失が谷底付近でも大きくジグザグに振れ続けやすい。
  • ミニバッチ勾配降下法: 数枚〜数十枚をまとめて平均勾配を取る、両者の折衷案。 GPUは行列演算をバッチ方向にまとめて並列処理できるため、単純に「サンプル数分ループを回す」 SGDよりも同じ時間で多くのデータを処理でき、勾配のノイズも平均化である程度抑えられる。 現代のディープラーニングではほぼ標準の方式です。
MediSeg-AIでの実際の設定: DataConfig.batch_size (backend/mediseg_ai/config_schemas.py:13)の既定値は8ですが、 実際に使われるベース設定 default_base_config() (backend/mediseg_ai/config_schemas.py:96-102)では16に上書きされています。 つまり256×256のRGB画像を16枚まとめて1回の順伝播・逆伝播・重み更新を行っており、 データセット全体(例: LGGの陽性スライス約1,100枚)に対しては1エポックあたり約69回の重み更新が 発生する計算です。バッチの読み込み自体は torch.utils.data.DataLoader が num_workers=4(既定)で並列に行い、GPU側の計算を待たせないようにしています。

7勾配降下法とAdamオプティマイザ

逆伝播で「各重みをどちらに動かせば損失が減るか」(勾配)が分かったら、実際に重みを更新するのが 勾配降下法です。最もシンプルな形は次の更新式です:

w ← w − lr × ∂L/∂w

lr(学習率)が大きすぎると損失の谷を飛び越えて発散し、小さすぎると学習が遅すぎて 実用的な時間で収束しません。

横軸=ある重みの値、縦軸=損失。各点での接線の傾き(勾配)と逆方向に一歩進むことを繰り返し、 谷底(損失が最小になる重み)に向かって降りていく。谷に近づくほど傾きが緩やかになり、歩幅も自然に小さくなる。

MediSeg-AIが実際に使っている設定

MediSeg-AIのデフォルトオプティマイザはAdamです。Adamは全パラメータに同じ学習率を使う代わりに、 各パラメータごとに「勾配の移動平均(momentumに相当)」と「勾配の2乗の移動平均(更新の大きさを 自動調整する分母)」を保持し、パラメータごとに実効的な学習率を自動調整します。これにより、 素の勾配降下法より学習率のチューニングに敏感になりにくく、収束が速くなります。

オプティマイザAdam(既定)。SGD + momentum=0.9も選択可
学習率(lr)0.001(既定)
weight_decay1e-4(既定) — 重みの大きさ自体にもペナルティをかけ、過学習を抑制
学習率スケジューラCosineAnnealingLR(既定)/ StepLR / ReduceLROnPlateau から選択
実装backend/mediseg_ai/training/trainer.py:85-123(_setup_optimizer / _setup_scheduler)

8混合精度学習(Mixed Precision / AMP)

GPUの数値計算は通常32ビット浮動小数点(FP32)で行われますが、近年のGPU(NVIDIA Tensor Core搭載)は 16ビット(FP16)の行列演算を専用回路でさらに高速に処理できます。混合精度学習は、 畳み込みなど計算負荷の重い演算をFP16で行いつつ、勾配の累積など数値的に不安定になりやすい箇所だけ FP32を維持することで、精度をほぼ落とさずに学習を高速化・省メモリ化する手法です。

上: FP32のみで計算する通常の経路。下: AMPは大部分をFP16(半分のメモリ・より高速)で計算しつつ、 損失計算とその周辺だけFP32に保つ。FP16では小さすぎる勾配が0に潰れてしまう(アンダーフロー)ため、 GradScalerが損失を一時的に拡大してから逆伝播し、オプティマイザに渡す前に元の大きさへ戻す。
MediSeg-AIでの実際の使用箇所: backend/mediseg_ai/training/trainer.py:54 で self.use_amp = device.type == "cuda" — CUDA GPUがある場合のみ有効になり、 CPU学習時は自動的にFP32のみにフォールバックします。順伝播と損失計算を autocast(...)(trainer.py:180)で囲み、 逆伝播は self.scaler.scale(loss).backward()(trainer.py:185)、 重み更新は self.scaler.step(self.optimizer) と self.scaler.update() (trainer.py:186-187)という3段階で行っています。CPU実行時は GradScaler(..., enabled=False) が素通しになるため、学習コード自体は GPU/CPUで同じまま動作します。

9Dice係数とIoU(評価指標)

脳MRIの病変領域は、画像全体からするとごく一部の面積しかありません。そのため 「全ピクセルのうち何%正解したか」というピクセル精度(accuracy)は評価指標として不適切です — 極端な話、モデルが「どこにも病変はない」と常に予測しても、背景ピクセルが大半を占めるため accuracyは高く出てしまいます。そこで医用画像セグメンテーションでは、 予測領域と正解領域がどれだけ重なっているかを測る指標を使います。

Dice = 2|A ∩ B||A| + |B|     IoU = |A ∩ B||A ∪ B|
A=予測領域、B=正解領域(正解マスク)としたときの重なり具合を面積比で表す。 両方とも1.0で完全一致、0で重なりなし。同じ2つの領域でも、Dice(2倍した重なり÷合計)は IoU(重なり÷和集合)より常に高めの値になる。
  • Diceが高評価に慣れている理由: 「重なった部分を2倍数える」ことで、小さな病変領域でも 指標が極端に低く出にくいよう設計されている。医用画像分野で最もよく使われる評価指標。
  • IoU(Jaccard係数): 物体検出でもおなじみの指標で、Diceより厳しめの値になる (Dice = 2×IoU / (1+IoU) という数学的な関係がある)。
  • accuracyとの違い: 例えば正解領域が全体の2%しかない画像で「病変なし」と予測すると、 accuracyは98%になるがDice・IoUは0になる — こちらの方が実態を正しく反映している。
MediSeg-AIでの実際の実装: 評価指標アダプタ(Torch Metrics 連携) の dice_coefficient()、:24-29 の intersection_over_union() が上の式をそのまま実装しています(ゼロ除算を避けるための微小値 smooth=1e-6 付き)。 可視化ページや各データセット解説ページに出てくる「Dice: 0.6961」のような数値は、 すべてこの実装で計算された学習中の検証(validation)スコアです。 セクション10で説明する DiceLoss は、この関数を直接呼ぶのではなく 同じ 2|A∩B| / (|A|+|B|) の式を別実装しています — 違いは二値化の有無です。 評価指標である dice_coefficient() は 0.5 で閾値化した二値マスクどうしの重なりを計算する (最終的な出力の良し悪しをそのまま反映)のに対し、DiceLoss は逆伝播できるよう シグモイド後の連続値のまま(閾値化せず)計算します。

10学習ループ全体のまとめ

ここまでの3つの技術(CNNの順伝播、逆伝播、勾配降下法)は、実際の学習コードの中では 次のようにひとつのループとして組み合わさっています:

# backend/mediseg_ai/training/trainer.py:179-186 (要約)
self.optimizer.zero_grad(set_to_none=True)              # 前のバッチの勾配をクリア
outputs = self.model(images)                       # ① 順伝播: 画像 → U-Net → 予測ロジット
loss = self.criterion(outputs, masks.float())       # ② 損失計算: 予測 vs 正解マスク
self.scaler.scale(loss).backward()                    # ③ 逆伝播: 全パラメータの勾配を計算
self.scaler.step(self.optimizer)                   # ④ 勾配降下法: Adamが全重みを更新

②の損失関数(criterion)には CombinedLoss を使っています — BCE(二値交差エントロピー)0.5 と Dice損失 0.5 を足し合わせたものです。

  • BCE: 各ピクセルを独立に「病変か背景か」の2クラス分類として評価する、最も基本的な損失。
  • Dice損失: セクション9で説明したDice係数を 1 − Dice として損失にしたもの。脳MRIの病変は画像全体に対してごく一部の 面積(極端なクラス不均衡)しかないため、BCE単体ではモデルが「すべて背景と予測する極小解(自明な解)」に収束しやすくなります。 Dice損失を併用することで、前景領域の重なり度合いを直接評価し、クラス不均衡による学習の偏りを防止しています。
実装は backend/mediseg_ai/training/losses.py:39-52(CombinedLoss)。 この1バッチぶんのループ(① → ④)を、データセット全体・複数エポック分繰り返すことで、 ランダムに初期化されたU-Netの重みが少しずつ、脳MRIの病変を検出できる値へと調整されていきます。