[Clinical AI Coding 100 : C32] AIの心臓部を覗く:PyTorchでディープラーニングを動かそう

医療AI開発の要点まとめ

複雑な数式計算を自動化し、臨床データを「計算可能な形」に変換する3つの核心技術。PyTorchを使えば、医学的知見を直感的にAIモデルへ実装できます。

🧊 Tensor
データの「器」

CT画像や検査値を、AIが計算可能な「多次元の数値」に変換して格納します。GPUを使うことで、膨大な医療データを高速に処理できます。

⚙️ Autograd
学習の「エンジン」

「どこをどう直せば正解に近づくか(勾配)」を全自動で計算します。複雑な微積分を手計算する必要がなくなり、課題解決に集中できます。

🏗️ nn.Module
構築の「設計図」

ニューラルネットワークをレゴブロックのように組み立てるための枠組みです。__init__で部品を揃え、forwardで処理の流れを定義します。

Data 🩻 画像・カルテ Tensor 🧊 数値化 Model (nn.Module) 🧠 Forward (順伝播) Loss 📉 誤差の計算 Autograd (自動修正) 医療AI学習の全体像 データ変換 → 順伝播(推論) → 誤差計算 → 逆伝播(学習) のサイクル

医療AIの研究開発の最前線において、現在、事実上の標準(デファクトスタンダード)として多くの研究者に選ばれているフレームワーク、それが PyTorch です。

もちろん、Googleが開発したTensorFlowも非常に強力なツールであり、産業界での実装では広く使われています。しかし、私たち医療従事者や研究者が、臨床現場の課題解決に向けて新しいアイデアを形にしようとしたとき、PyTorchが持つ「直感的な記述法」と「柔軟性」は大きな武器になります。

実際、主要なAI国際会議で発表される最新論文の多くが、PyTorchを用いて実装されているというデータもあります (Paszke et al. 2019)。これは、PyTorchが単なる計算ツールにとどまらず、研究者の思考プロセスをそのままコードに落とし込める、親和性の高い「言語」として機能しているからだと言えるでしょう。

本稿では、このPyTorchを使いこなすために避けては通れない、以下の3つの「心臓部」となる概念について、その数理的背景と実装方法を徹底的に解説します。

  • Tensor(テンソル)
    CTやMRI、検査値といった医学データを、AIが計算可能な形(多次元の数値の塊)に変換するための「器」です。
  • 自動微分(Autograd)
    AIが賢くなる(学習する)ためには、間違い(誤差)の原因を突き止める複雑な微積分計算が必要です。これを全自動で肩代わりしてくれる「エンジン」です。
  • nn.Module
    人間の脳を模したニューラルネットワークを、まるでレゴブロックのように部品を組み合わせて構築するための「設計図」です。

これらは、単なるプログラムの呪文やコードの羅列ではありません。これらを理解することは、医学的知見や臨床データを、計算可能な数理モデルとして表現するための「新しい言葉」を習得することに他なりません。初めて触れる方は少し抽象的に感じるかもしれませんが、コードを動かしながら一つひとつ丁寧に紐解いていきましょう。

Medical AI & PyTorch 研究開発のデファクトスタンダード Google発のTensorFlowも強力ですが、医療現場の課題解決には PyTorchの「直感的な記述」と「柔軟性」が大きな武器となります。 🧊 Tensor (テンソル) 医学データを AIが計算可能な 「多次元の数値の塊」 にするための器。 CT/MRI → 🔢 📉 Autograd (自動微分) 学習に必要な 複雑な微積分計算を 全自動で肩代わりする 「エンジン」。 誤差逆伝播 🔄 🧩 nn.Module (構築の設計図) ニューラルネットを レゴブロックのように 組み立てて構築する ための「部品」。 脳の模倣 🧠 数理モデルとして表現するための「新しい言葉」 コードを動かしながら、一つひとつ丁寧に紐解いていきましょう。

目次

0. Deep Learningが「学習」する仕組み:魔法ではなく、泥臭い最適化

PyTorchのコードを書き始める前に、そもそも「AIが学習する」とは、一体何を行っているのかを、数式ではなく直感的なイメージで理解しておきましょう。

多くの人が「AIは大量のデータを読んで勝手に賢くなる」という魔法のようなイメージを持っています。しかし、その実体は「数億個のつまみ(パラメータ)を、ただひたすら微調整し続ける泥臭い作業」に他なりません。

0.1. そもそも「学習」とは?:巨大なブラックボックスの調整

ニューラルネットワークの仕組みを数式で理解しようとすると、どうしても難しく感じてしまうかもしれません。そこで、まずは数学的な定義を一旦脇に置いて、この技術を「無数の調整つまみ(ノブ)がついた、巨大なブラックボックス」として想像してみてください。

ニューラルネットワークの学習:ブラックボックスの調整 「学習」とは?:巨大なブラックボックスの調整 入力データ 胸部X線画像 ニューラルネットワーク 数億個の「つまみ」を調整 モデルの出力 疾患確率 肺炎: 80% 正解(医師の診断) 👨‍⚕️ 「肺炎」 誤差 Check! 誤差を減らすように「つまみ」を微調整 💡 学習のポイント 初期状態は「デタラメ」ですが、この 比較 → 誤差計算 → 微調整 サイクルを数万回繰り返すことで、 精度高いモデルへと進化します。

入力と出力の関係

例えば、日常の診療で目にする「胸部X線画像」をこの箱に入力するとしましょう。

  • 入力(Input):患者さんの胸部X線画像データ
  • 箱の中身(Model):画像の色や形、濃淡に反応する数億個の計算式(ここにつまみがついています)
  • 出力(Output):その画像が示す疾患の確率(例:「肺炎である確率:80%」)

初期状態は「デタラメ」

工場から出荷されたばかりの(まだ何も学習していない)AIは、この数億個ある「つまみ」の位置がすべてデタラメに設定されています。そのため、医師が見れば一目瞭然の肺炎画像を入力しても、「異常なし(確率 5%)」や「気胸(確率 90%)」といった、全く見当違いな答えを返してきます。

「学習」=「つまみ合わせ」の職人芸

ここで言う「学習(Training)」とは、AIが出したデタラメな答えと、正解(医師の確定診断)を見比べながら、誤差を埋める作業のことです。

「もっと正解(肺炎)に近づけるためには、1番目のつまみを少し右に回して、2番目は左に回して……」といった具合に、予測が正解と一致するように、数億個あるつまみの一つひとつを最適な位置にカチカチと回して微調整し続けること。この泥臭いチューニング作業こそが、ディープラーニングにおける「学習」の正体なのです。

0.2. 「診断のズレ」を数値化する:損失関数 (Loss Function)

数億個の「つまみ」を調整し始める前に、現在のAIが「どれくらい間違っているか」を正確に採点する必要があります。「なんとなく間違っている」という感覚的なフィードバックでは、機械は計算ができないからです。

この間違いの大きさを表すスコアを、専門用語で「損失(Loss)」と呼びます。ゴルフのスコアと同じで、この数値は小さければ小さいほど優秀であることを意味します。

「診断のズレ」を数値化する 損失関数 (Loss Function) の仕組み 損失 (Loss) = 「間違いの大きさ」のスコア ゴルフのスコアと同じで、0に近いほど優秀です。 🤖 学習前のAI 自信がない状態 正解 (Truth) 予測 (Pred) t = 1.0 悪性腫瘍 y = 0.2 たぶん違う… Loss = (0.2 – 1.0)2 = 0.64 学習 🎓 学習後のAI 賢くなった状態 正解 (Truth) 予測 (Pred) t = 1.0 悪性腫瘍 y = 0.9 高確率で悪性 Loss = (0.9 – 1.0)2 = 0.01 ゴール:Loss(損失)を限りなく 0 に近づけること

具体例:悪性腫瘍の予測

例えば、ある検査画像が「悪性腫瘍(正解ラベル \(t = 1.0\))」であるケースを考えてみましょう。

  • 正解 (\(t\)):1.0(確実に悪性)
  • 学習前のAIの予測 (\(y\)):0.2(「20%くらいの確率で悪性かも…?」という自信のない状態)

このとき、AIの診断ミス(損失)は、例えば「予測と正解の引き算(差)の二乗」で計算されます。

\[ \text{Loss} = (\text{予測} – \text{正解})^2 = (0.2 – 1.0)^2 = (-0.8)^2 = 0.64 \]

この 0.64 という数字が、現在のAIの「未熟さ」を表すスコアです。

学習が進むと…

学習が進み、AIが賢くなって予測値が 0.9(「90%の確率で悪性!」)まで改善したとします。すると、損失は劇的に小さくなります。

\[ \text{Loss} = (0.9 – 1.0)^2 = (-0.1)^2 = 0.01 \]

つまり、ディープラーニングにおける「学習のゴール」とは、「この損失(Loss)を限りなく \(0\) に近づけること」と言い換えることができます。

Easy Mode
Loss Function Simulation: “Target Practice”
正解 (Truth) AI
ズレの大きさ(Loss)
0に近いほど優秀
Step 1: Start ここに解説が表示されます。

0.3. 霧の山を下る:勾配降下法 (Gradient Descent)

損失(Loss)を計算できるようになったら、次は「どうすればこの損失を最小(ゼロ)にできるか?」を考えます。ここで登場するのが、ディープラーニングの学習を支える最も重要なアルゴリズム、「勾配降下法(こうばいこうかほう)」です。

この仕組みを直感的に理解するために、少し想像力を働かせてみましょう。

勾配降下法:霧の山を下るアルゴリズム 勾配降下法 (Gradient Descent):霧の山を下る パラメータ w (位置) 損失 L (標高) 🌫️ FOGGY ZONE (視界不良) 🌫️ 現在地 傾き(勾配) 一歩進む 次の地点 ゴール(谷底) 📐 勾配 (Gradient) 足元の傾斜。「こっちが登り坂」 という情報を教えてくれる。 👟 学習率 (Learning Rate) 一歩の大きさ(歩幅)。 大きすぎると飛び越えてしまう。 🏔️ 遭難時の脱出戦略 1. 足元の傾きを確認する 2. 傾きと逆方向(下り) 3. 一歩(η)だけ進む wnew 新しい位置 = wold 現在地 η 学習率 (歩幅) × L / ∂w 勾配 (傾き) 👉 マイナス方向へ進むことで、 山を下り(損失を減らし)ます。

アナロジー:濃霧の山岳遭難と脱出

あなたは今、「損失山(Loss Mountain)」という名の、険しい山のどこかに立っています。あなたの目的は、この山の最も標高が低い場所、すなわち「谷底(損失ゼロの地点)」まで下山することです。

しかし、あいにく周りは深い霧に包まれていて、一寸先も見えません。谷底がどの方角にあるのか、地図もコンパスもありません。この絶望的な状況で、確実に標高を下げるための唯一の戦略は何でしょうか?

それは、「足元の地面の傾斜(勾配)を確かめ、傾斜が最も急に下っている方向へ、慎重に一歩踏み出すこと」です。

これを何千歩も繰り返せば、いつかは必ず一番低い場所(あるいは局所的な窪み)にたどり着けるはずです。これが勾配降下法の基本的な考え方です。

学習を左右する2つの要素

この山下りを成功させるためには、2つの重要な要素があります。

  • 勾配(Gradient)
    足元の傾斜のことです。「右に行けば登り坂、左に行けば下り坂」という情報を教えてくれます。数式では微分(\(\frac{\partial L}{\partial w}\))で求めます。
  • 学習率(Learning Rate)
    一歩の歩幅の大きさです。これを \(\eta\)(イータ)という記号で表します。
    歩幅が大きすぎると、勢いあまって谷底を飛び越えてしまい、いつまで経っても着地できません(発散)。逆に小さすぎると、谷底に着くまでに日が暮れてしまいます(収束が遅い)。適切な歩幅を設定することが、AIエンジニアの腕の見せ所です。

数式で見る「一歩」の更新

このプロセスを数式で表すと、以下のようになります。「現在の位置(パラメータ \(w_{\text{old}}\))」から、「傾きの逆方向(下り方向)」へ、「歩幅(\(\eta\))」の分だけ移動して、「新しい位置(\(w_{\text{new}}\))」を決めます。

\[ w_{\text{new}} = w_{\text{old}} – \eta \cdot \dfrac{\partial L}{\partial w} \]

ここでマイナス(\(-\))がついているのは、「傾きがプラス(登り坂)なら、マイナス方向(左)へ戻る」ためです。常に傾きと逆方向に進むことで、確実に山を下ることができます。

この「傾きを計算して、下る方向へ修正する」という地道な作業を、コンピュータの計算力で何万回も高速に繰り返すこと。これこそが、AIが少しずつ賢くなっていくプロセスの正体なのです。

読み込み中…
📍 損失山 (Loss Mountain) 🏁 谷底 (Loss=0) Wait! 霧の中の探索 視界不良… パラメータ更新式 wnew = wold η ∇L

0.4. 責任のなすりつけ:誤差逆伝播法 (Backpropagation)

勾配降下法で「傾き(勾配)に沿ってパラメータを修正すればよい」ということは分かりました。しかし、ここでディープラーニング特有の巨大な壁が立ちはだかります。

現代のAIモデルは、単純な一つの数式ではありません。何十、何百もの層(Layer)が重なった、巨大で複雑な連鎖構造になっています。

順伝播:情報のバトンリレー

入力された画像データは、第1層、第2層、第3層……と、まるでバトンリレーのように次々と計算・変換され、最後に「予測」が出力されます。これを「順伝播 (Forward Propagation)」と呼びます。

もし、最後のゴールで「損失が大きい(大失敗)」と判明したとしましょう。このとき、チーム監督(最適化アルゴリズム)は困ってしまいます。「一体、どの層の、どのパラメータが足を引っ張ったせいで失敗したのか?」を特定しなければ、修正のしようがないからです。

逆伝播:責任(誤差)の逆流

そこで行われるのが、ゴールからスタートに向かって責任を及ぼしていく「責任のなすりつけ合い」です。

イメージとしては、以下のような「伝言ゲーム」が猛スピードで行われます。

  1. 出力層:「予測が外れた! これは私の計算ミスのせいじゃない。直前の中間層Bから変なデータが送られてきたせいだ! 修正しろ!」と、誤差の責任(勾配)を後ろへ投げます。
  2. 中間層B:「いや、それはその前の中間層Aから受け取ったデータがおかしかったせいだ!」と、さらに責任を後ろへ投げます。
  3. 中間層A:「それは入力層の重みの設定がおかしいせいだ!」

このように、出口(Loss)から入口(Input)に向かって、「お前のせいでこれだけ誤差が出た(だからこれだけ値を修正しろ)」という情報(勾配)を、微分の「連鎖律(Chain Rule)」というルールを使って伝えていく手法。これを「誤差逆伝播法(バックプロパゲーション)」と呼びます (Rumelhart, Hinton and Williams 1986)。

データの流れの全体像

この「順方向の予測」と「逆方向の学習」の流れを整理すると、以下のようになります。

0.4. 責任のなすりつけ:誤差逆伝播法 (Backpropagation) 順伝播で「予測」し、逆伝播で「責任(誤差)」を追及して修正する仕組み データの流れと計算プロセス 入力層 📥 x 中間層 A 🏃 h1 中間層 B 🏃‍♂️ h2 出力層 🏁 y 誤差 L 💥 重み W1 重み W2 予測 修正量 責任追及 微分の連鎖 🔵 順伝播 (Forward) 入力から出力へデータを変換していくプロセス。 入力 x ➔ 中間層 h ➔ 出力 y ➔ 誤差 L 🟠 逆伝播 (Backward) 誤差の原因(責任)を逆順に特定して修正する。 誤差 L ➔ 連鎖律で各層の重みWを更新

順伝播で「予測」し、逆伝播で「修正量」を計算するイメージ

この仕組みにより、出口での「診断ミス」という一つの結果から、数億個あるパラメータ一つひとつに対して、「君はこれくらい修正が必要」という個別の処方箋(勾配)を配ることができるのです。

誤差逆伝播法 (Backpropagation) 順伝播で「予測」し、逆伝播で「責任」をなすりつけて修正する 入力層 Input 📥 中間層A Wait… 🏃 入力のせい! 中間層B Wait… 🏃‍♂️ Aのせい! 出力層 Pred 🏁 Bのせい! 誤差(Loss)発生 💥 状態: 待機中…

0.5. この仕組みを実現するための「PyTorch」

ここまで、AIが学習する理論として「勾配降下法」と「誤差逆伝播法」のお話をしました。理屈としては、「誤差を逆流させて、責任の分だけ修正すればいい」という非常にシンプルなものです。

しかし、これを実際にゼロからプログラミングしようとすると、話は別です。最新の画像診断AIには、パラメータが数千万〜数億個も存在します。「数億個の変数を持つ連立方程式の偏微分」を、人間が手計算で行うことは不可能ですし、それをプログラムで記述しようとしても、あまりに複雑すぎてバグの温床になってしまいます。

そこで、私たちの強力な味方となるのが PyTorch です。

PyTorchは、この複雑怪奇な「学習」のプロセスを、裏側で驚くほど効率的に処理してくれる、医療AI開発のための専用ツールボックスです。私たちが学ぶべきは、複雑な微積分の解き方ではなく、以下の3つの機能の使い方だけです。

  • Tensor(テンソル)
    CT画像や検査値など、あらゆるデータを計算可能な形に統一して扱う「器」です。
  • Autograd(自動微分)
    面倒な微分の計算(誤差逆伝播)を、コマンド一つで全自動で行ってくれる「エンジン」です。
  • nn.Module
    脳の神経回路網(ニューラルネットワーク)を、レゴブロックのように組み立てるための「設計図」です。

これらを使いこなせば、私たちは「数式の計算」という重労働から解放され、「どのようなモデルを作れば患者さんの役に立つか」という、本質的な課題解決に集中することができるようになります。

AI実装の強力な味方「PyTorch」 数億個のパラメータを持つ複雑な方程式を手計算する必要はありません。 PyTorchは、医療AI開発のための「全自動ツールボックス」です。 De facto Standard 📦 Tensor (テンソル) データの「器」 CT画像や検査値など、 あらゆる医学データを 計算可能な形に統一して 格納します。 ⚙️ Autograd (自動微分) 全自動「エンジン」 面倒な微分の計算 (誤差逆伝播)を、 コマンド一つで全自動で 行ってくれます。 🏗️ nn.Module (モジュール) 構築の「設計図」 ニューラルネットワークを レゴブロックのように 自由に組み立てるための 基本部品です。 💡 数式の重労働からの解放 「どのようなモデルを作れば患者さんの役に立つか」という、本質的な課題解決に集中できます。

それでは次の章から、まずこのすべての基礎となるデータの器、「Tensor」について、実際にコードを動かしながら学んでいきましょう。


1. Tensor(テンソル):多次元データを統一的に扱う「器」

ディープラーニングの世界に足を踏み入れると、必ず耳にする言葉が「Tensor(テンソル)」です。数学的な定義は少し難解ですが、私たち医療者にとっては、「あらゆる医療データを、AIが計算しやすい形に整頓した『多次元の数値の箱』」だとイメージすると分かりやすいでしょう。

例えば、電子カルテの数値データ、心電図の波形、レントゲン写真、そしてMRIの立体画像。これらは一見まったく異なるデータ形式に見えますが、AI(特にPyTorch)の視点から見れば、すべて「Tensor」という共通のフォーマットで記述できます。

この「統一された器」があるおかげで、私たちはデータの種類に関わらず、同じプログラムコードを使って解析を進めることができるのです。

Tensorの「階数 (Rank)」と医療データの対応

Tensorには「階数(Rank)」という概念があります。これは「データの次元数」や「箱の深さ」のようなものです。医療現場で扱うデータが、それぞれどの階数のTensorに対応するのか、以下の表で整理してみましょう。

Tensorの「階数 (Rank)」と医療データの対応 Tensorの「階数」はデータの次元数や深さを表します。 医療現場のデータがどの階数に対応するかを視覚的に整理しました。 点 (0-D) 0階 (Rank 0): スカラー ただ1つの数値。 🌡️ 体温 37.5℃ / CRP値 0.05 mg/dL 線 (1-D) 1階 (Rank 1): ベクトル 数値の列・配列。 📋 患者検査リスト [ 年齢, 身長, 体重, HbA1c ] 面 (2-D) 2階 (Rank 2): 行列 表形式や画像(縦H × 横W)。 🖼️ グレースケール画像(レントゲンなど) ピクセルごとの輝度値の並び 立体 (3-D) 3階 (Rank 3): 立体・スタック 行列を重ねたもの(縦 × 横 × 深さ)。 🧠 CT・MRI(ボリュームデータ)、カラー画像 H × W × D(スライス数 or RGB) 束 (Batch) 4階以上: バッチ(データの束) 複数のデータをひとまとめにして処理する単位。 ( N 人数 × C 深さ × H 縦 × W 横 ) 📦 例: MRI画像を32人分まとめて計算 💡 複数の患者データを並列処理することで、AIの学習効率が劇的に向上します
階数 (Rank)数学的イメージ医療データの具体例
0階 (0-D)スカラー
(ただ1つの数値)
体温(例:37.5℃) CRP値(例:0.05 mg/dL) 収縮期血圧(1回測定の値)
1階 (1-D)ベクトル
(数値の列・配列)
1人の患者の検査値リスト
例:[年齢, 身長, 体重, HbA1c, eGFR] 短時間の心電図波形データ
2階 (2-D)行列
(表形式・グリッド)
1枚のグレースケール画像(レントゲンなど)
縦 \(H\) ピクセル × 横 \(W\) ピクセルの輝度値の並び 患者データセットの表(行:患者、列:検査項目)
3階 (3-D)立体・スタック
(行列を重ねたもの)
ボリュームデータ(CT・MRI)
縦 \(H\) × 横 \(W\) × 深さ(スライス数) \(D\) カラー画像(皮膚科画像・眼底写真)
縦 \(H\) × 横 \(W\) × 色チャンネル(RGBの3色)
4階以上バッチ
(データを束ねたもの)
ミニバッチ学習用データ
例:MRI画像を32人分まとめて処理する場合
\(N(\text{人数}) \times C(\text{深さ}) \times H(\text{縦}) \times W(\text{横})\)

特に「4階以上」のTensorは、AIの学習効率を高めるために非常に重要です。患者さん一人ひとりデータを処理するのではなく、数人〜数十人分を「ひとまとめ(バッチ)」にして計算することで、処理時間を劇的に短縮できるからです。

なぜ NumPy ではなく PyTorch の Tensor なのか?

Pythonで数値計算を行う場合、通常は「NumPy(ナムパイ)」というライブラリの ndarray が使われます。PyTorchの torch.Tensor は、このNumPyと使い勝手が非常によく似ており、相互に変換も可能です。

では、なぜAI開発ではわざわざ PyTorch の Tensor を使うのでしょうか? 決定的な理由は以下の2点に集約されます。

  1. GPU(Graphics Processing Unit)による高速演算
    NumPyは基本的にCPU(コンピュータの頭脳)で計算を行いますが、PyTorchのTensorはGPU(画像処理用チップ)上で動作します。
    医療画像解析では、数百万画素のデータを数千枚単位で、何万回も繰り返し計算します。これをCPUで行うと数週間かかる処理が、GPUを使うことで数時間で終わることも珍しくありません。GPUへのデータ転送は、現代の医療AI開発において「必須」に近い手順です (Litjens et al. 2017)。
  2. 勾配(Gradient)を自動で保持できる
    これがAI学習の「魔法」のタネです。PyTorchのTensorは、自分がどのような計算に使われたかという「履歴」を記憶し、後から「自動微分」によって勾配(学習に必要な修正量)を計算する機能を備えています (LeCun, Bengio and Hinton 2015)。NumPyにはこの機能がありません。
NumPy vs PyTorch Tensor なぜAI開発では PyTorch が選ばれるのか? 決定的な2つの理由 NumPy (ndarray) 🔢 🔹 CPUで計算 (汎用数値計算) 🔹 自動微分機能なし 🔹 科学技術計算のスタンダード ※ PyTorchと相互変換が可能 似ているが… PyTorch (Tensor) 🔥 ✨ GPUで超高速計算 ✨ 自動微分 (Autograd) 🔹 AI・深層学習に特化 ※ 決定的な違いはこの2点! 理由 1 GPUによる圧倒的な高速演算 医療画像解析など、膨大なデータを単純計算する処理において必須の能力です。 NumPy (CPU) 🐢 数週間かかることも… PyTorch (GPU) 🚀 数時間で完了! 例:数百万画素 × 数千枚の医療画像を、何万回も計算する場合 (Litjens et al. 2017) 理由 2 勾配を自動で保持 (Autograd) AI学習の「魔法」のタネ。計算の履歴を記憶し、修正量(勾配)を逆算します。 Input 演算履歴 📝 Loss 自動微分 (Backprop) NumPyにはない機能 PyTorchは「どの計算を経て結果が出たか」を 覚えているため、後から学習(修正)が可能。

【実装】Tensorの操作とGPU転送

それでは、実際にコードを動かして Tensor の感覚を掴んでみましょう。
以下のコードは、Python環境でPyTorchがインストールされていることを前提としています。実際に手を動かして、コピペして実行してみてください。

【実行前の準備】
以下のコードには日本語が含まれています。グラフ描画などで日本語を用いる場合に備えて、あらかじめターミナルやコマンドプロンプトで以下のコマンドを実行し、ライブラリをインストールしておくことを強く推奨します。

pip install japanize-matplotlib
🧬 PyTorch Tensor Operations データの生成からGPU転送、ブロードキャスティング演算まで Step 1: 生成 Python List (臨床データ) clinical_data_list = [ [65, 28.5, 140], [54, 24.0, 128], ... ] torch.tensor() PyTorch Tensor (Float32) 65.0 28.5 140.0 54.0 24.0 128.0 72.0 31.2 155.0 Shape: (3, 3) Dtype: float32 Device: 'cpu' Step 2: 転送 💻 CPU Memory x_tensor .to(device) cuda.is_available() 🚀 GPU Memory (CUDA) x_gpu (高速計算可能) Step 3: ブロードキャスティング x_tensor (3, 3) 65 28.5 140 54 24 128 72 31.2 155 * factor (3,) 1.0 1.1 1.0 1.0 1.1 1.0 1.0 1.1 1.0 自動拡張 (Broadcasting) = x_updated (3, 3) 65.0 31.35 140.0 54.0 26.40 128.0 72.0 34.32 155.0 💡 真ん中の列(BMI)だけが1.1倍に補正されました
import torch
import numpy as np

# 【重要】再現性のためのシード固定
# AIの計算には「乱数(ランダムな数)」が多用されますが、
# 実験のたびに結果が変わると困るため、乱数の種(シード)を固定して
# いつ誰がやっても同じ結果が出るようにします。
torch.manual_seed(42)

# ==========================================
# 1. Tensorの生成:臨床データをAIの言葉に翻訳する
# ==========================================

# 架空の臨床データ(リスト形式)
# 例:3人の患者さんの [年齢, BMI, 収縮期血圧]
clinical_data_list = [
    [65, 28.5, 140], # 患者Aさん
    [54, 24.0, 128], # 患者Bさん
    [72, 31.2, 155]  # 患者Cさん
]

# Pythonのリストから、PyTorchのTensor(32bit浮動小数点数)を作成します
# 医療データ解析では、精度とメモリのバランスが良い 'float32' が一般的です
x_tensor = torch.tensor(clinical_data_list, dtype=torch.float32)

print("--- Tensorの基本情報 ---")
print(f"データの中身:\n{x_tensor}")
# 形状の確認: (3, 3) は「3行3列」を意味します(3人の患者 × 3つの検査項目)
print(f"データの形状 (Shape): {x_tensor.shape}")  
print(f"データの型 (Dtype): {x_tensor.dtype}")


# ==========================================
# 2. GPUへの転送:計算を加速させる
# ==========================================

# お使いのPCでNVIDIA製のGPU(CUDA)が使えるか確認します。
# 医療画像などの重い計算では、このステップが非常に重要です。
if torch.cuda.is_available():
    device = torch.device("cuda") # GPUデバイスを指定
    x_gpu = x_tensor.to(device)   # データをCPUのメモリからGPUのメモリへ転送
    print(f"\n成功: データをデバイス {x_gpu.device} に転送しました。")
    # これ以降、x_gpu を使った計算は爆速になります
else:
    print("\n注意: GPUは利用できません。CPUで実行します。")


# ==========================================
# 3. 基本的な演算:ブロードキャスティング
# ==========================================

# 全員のデータに対して、特定の項目だけ補正を行いたい場面を想像してください。
# 例:「BMI(2列目)だけ、機器の誤差補正のために1.1倍したい」

# [年齢用, BMI用, 血圧用] の補正係数を作成
factor = torch.tensor([1.0, 1.1, 1.0]) 

# Tensor同士の掛け算
# x_tensorは (3, 3) の行列、factorは (3) のベクトルですが、
# PyTorchが気を利かせて、factorを自動的に3人分に「拡張」して計算してくれます。
# これを「ブロードキャスティング (Broadcasting)」と呼びます。便利です。
x_updated = x_tensor * factor

print(f"\n演算後のデータ:\n{x_updated}")
# BMI(真ん中の列)だけが 1.1倍 されていることを確認してください。
--- Tensorの基本情報 ---
データの中身:
tensor([[ 65.0000,  28.5000, 140.0000],
        [ 54.0000,  24.0000, 128.0000],
        [ 72.0000,  31.2000, 155.0000]])
データの形状 (Shape): torch.Size([3, 3])
データの型 (Dtype): torch.float32

注意: GPUは利用できません。CPUで実行します。

演算後のデータ:
tensor([[ 65.0000,  31.3500, 140.0000],
        [ 54.0000,  26.4000, 128.0000],
        [ 72.0000,  34.3200, 155.0000]])

このように、Tensorを使うことで、複数の患者さんの複数の検査データを一括で、しかも直感的な記述で処理することができます。この「データの入れ物」の扱いに慣れることが、医療AIプログラミングの第一歩です。


2. 自動微分(Autograd):学習のエンジン

前章までで、医療データを扱うための器である「Tensor」について学びました。ここからは、その器に入ったデータを使って、AIがどのように「学習」を進めていくのか、そのエンジンの部分に触れていきます。

ディープラーニングにおける「学習」とは、人間が教科書を読んで知識を蓄えるような受動的なプロセスとは少し異なります。AIにとっての学習とは、実のところ、「予測結果と正解データ(医師の診断など)との誤差を最小にするように、モデル内部にある数億個のパラメータ(重み)をひたすら微調整し続ける、能動的な最適化プロセス」に他なりません (Goodfellow, Bengio and Courville 2016)。

例えば、あるAIが胸部X線画像から「肺炎」を見落としてしまったとします。このとき、AI内部の数あるニューロンのうち、どの接続(重み)がその「判断ミス」の主犯だったのかを突き止め、次からは間違えないようにその接続の強さを少しだけ修正する必要があります。

自動微分 (Autograd) のメカニズム 学習とは「予測と正解の誤差を最小化」するプロセス。そのための羅針盤が「勾配」です。 🧭 計算グラフの流れ 順伝播 (Forward): 予測データの作成 🩻 入力データ (X線画像など) 🧠 モデル (パラメータ) 数億個の重み w 📊 予測結果 y 「異常なし」 ⚠️ 損失 (Loss) 正解とのズレ 逆伝播 (Backward): 責任追及(勾配計算) 各パラメータの「判断ミスへの寄与度」を計算 💡 根本原因分析 (RCA) Autogradは「インシデント分析」のような ものです。最終的な「誤差(事故)」から プロセスを遡り、どのパラメータ(工程) が原因だったかを自動特定します。 📐 パラメータ更新則 wnew = wold - η · L η (イータ): 学習率(一歩の大きさ) ∇L: 勾配(エラーの傾き)

2.1. 羅針盤としての「勾配(Gradient)」

この修正作業において、私たちが頼りにする唯一の羅針盤が「勾配(Gradient)」です。

第0章でイメージした「濃霧の山下り」を思い出してください。私たちは今、「損失(Loss)」という険しい山の中腹にいて、目指すのは標高がゼロになる「谷底(正解)」です。周りは霧で見えませんが、足元の地面の傾きだけは感じ取ることができます。

勾配とは、数学的には偏微分のことですが、直感的には「あるパラメータ(足元の位置)を少し動かしたとき、エラー(標高)がどれくらい増えるか、あるいは減るか」を示す変化率のことです。

もし、あるパラメータの勾配が「正(プラス)」であれば、それは登り坂を意味します。エラーを減らす(山を下る)ためには、その逆方向である「負(マイナス)」の方向へパラメータを動かせばよいことになります。これを数式で表現すると、以下の「パラメータ更新則」になります (Rumelhart, Hinton and Williams 1986)。

\[ w_{\text{new}} = w_{\text{old}} - \eta \cdot \dfrac{\partial L}{\partial w} \]

  • \( w_{\text{new}}, w_{\text{old}} \):パラメータ(重み)の新しい値と古い値
  • \( \eta \)(イータ):学習率(Learning Rate)。一歩の歩幅の大きさ。
  • \( \dfrac{\partial L}{\partial w} \):勾配。損失 \(L\) をパラメータ \(w\) で微分したもの。

この式にある「マイナス」は、「傾きと逆方向(下り坂)へ進む」という意思表示です。この単純なルールを数億回繰り返すことで、AIは徐々に正解へと近づいていきます。

2.2. なぜ「自動」微分が必要なのか?

理屈はシンプルですが、実務上ではここで巨大な壁に直面します。現代の医療AIモデルは、パラメータを数個ではなく、数千万から数億個も持っています。これら一つひとつについて、人間が手計算で偏微分を行い、勾配を求めることは物理的に不可能です。

そこで登場するのが、PyTorchの心臓部とも言える機能、Autograd(自動微分) です。

Autogradは、順方向(入力→出力)の計算を行う際に、その「計算の履歴」を全て裏側で記録(録画)しています。そして、いざ誤差が判明すると、今度はそのテープを逆再生するように、出口から入り口に向かって「連鎖律(Chain Rule)」という数学のルールを適用し、全てのパラメータの勾配を一瞬で自動計算してくれるのです。

これは、病院での医療安全管理における「根本原因分析(RCA)」に似ています。あるインシデント(誤差)が発生したとき、「最終的な結果」から「プロセス」を遡り、「どの段階のどの処置が原因だったのか」を特定していく作業を、AIが全自動で行ってくれるイメージです。

2.3. 計算グラフとデータの流れ:Define-by-Run

PyTorchが多くの研究者に支持される大きな理由の一つに、「Define-by-Run(動的計算グラフ)」という仕組みがあります (Paszke et al. 2019)。これは、プログラムを実行するたびに、その場で柔軟に計算の設計図(グラフ)を作り直す方式です。

これがなぜ医療AI開発で有利なのでしょうか? それは、医療データが常に定型とは限らないからです。

  • 可変長データ:患者AさんのCT画像は30スライスだが、患者Bさんは50スライスある。
  • 条件分岐:「検査値Xが異常値の場合のみ、追加で詳細な分析回路を通す」といったトリアージ的な処理。

Define-by-Runであれば、Pythonの標準的な if 文や for 文を使って、こうした複雑な処理を直感的に記述しても、Autogradが自動的に追従して正しい勾配を計算してくれます。

以下は、この「データの流れ(順伝播)」と「責任の追及(逆伝播)」の関係を示した概念図です。

計算グラフ:責任の所在を突き止める ニューラルネットワークにおけるデータの流れ(順伝播)と責任の追求(逆伝播) 順伝播 (Forward): 予測へ向かう 逆伝播 (Backward): 誤差情報の伝達 📊 入力 x ⚙️ 重み W1 中間 h1 ReLU 活性化 ⚙️ 重み W2 予測 yへ 📏 損失関数 正解と比較 🎯 誤差 L ∂L / ∂y 予測のズレ ∂L / ∂W2 W2の責任 ∂L / ∂ReLU 活性化の勾配 ∂L / ∂W1 W1の責任 ∂L / ∂x 入力への感度 📝 計算グラフのポイント 1. 右端の「誤差」が出発点となり、赤い点線のルートを逆走します。 2. 「連鎖律 (Chain Rule)」により、各パラメータがどれだけ誤差に寄与したか(責任)を計算します。
計算グラフの責任追及:順伝播と逆伝播 順伝播 (予測) 逆伝播 (勾配) 入力 x Data ∂L/∂x 変換 W1 Weights ∂L/∂W1 ReLU ∂L/∂h 変換 W2 Weights ∂L/∂W2 予測 y ∂L/∂y 誤差 L ERROR!

この「逆伝播(Backpropagation)」によって、出口でのたった一つの「診断ミス」という結果から、その原因となった数億個のパラメータそれぞれの「責任の重さ」を特定できるのです。

2.4. 【実装】コードで見る自動微分の仕組み

それでは、実際にコードを動かして、PyTorchがどのように勾配を計算しているのか体感してみましょう。ここでは最も単純な線形モデル \( y = w \cdot x + b \) を例にします。

PyTorch Autograd Lab
PyTorchの自動微分(Autograd)をシミュレートします。Startを押してください。
Copy Source Code
import torch w = torch.tensor([2.0], requires_grad=True) b = torch.tensor([1.0], requires_grad=True) x = torch.tensor([3.0]) y = w * x + b print(f"y: {y.item()}") y.backward() print(f"w.grad: {w.grad.item()}") print(f"b.grad: {b.grad.item()}")

import torch

# ==========================================
# 自動微分の実験:y = wx + b
# ==========================================

# 1. パラメータの定義(テンソルの作成)
# requires_grad=True は「この変数の計算履歴を追跡して!」というフラグです。
# AIモデルの「重み(w)」や「バイアス(b)」は、学習によって値を更新する必要があるため、
# 必ず True に設定します。
w = torch.tensor([2.0], requires_grad=True)
b = torch.tensor([1.0], requires_grad=True)

# 入力データ(x)は単なる観測データ(患者データなど)なので、
# 勾配計算(学習による更新)は不要です。デフォルト(False)のままでOKです。
x = torch.tensor([3.0])

# 2. 順伝播(Forward Pass)
# 通常の数式通りに計算します。
# この裏側で、PyTorchは動的に「計算グラフ」を構築し、
# 「wとxを掛けて、それにbを足した」という履歴をメモリ上に記録しています。
y = w * x + b

# 計算結果の確認: 2.0 * 3.0 + 1.0 = 7.0
print(f"計算結果 y: {y.item()}")

# 3. 逆伝播(Backward Pass)
# ここがハイライトです。
# .backward() を呼ぶだけで、y を起点として、計算に関わった全ての変数(w, b)
# に対する微分係数(勾配)が自動計算され、各変数の .grad 属性に格納されます。
y.backward()

print("\n--- 勾配(Gradient)の確認 ---")

# 数学的な解説:
# 数式 y = wx + b を w で偏微分すると、∂y/∂w = x (つまり 3.0)
# 数式 y = wx + b を b で偏微分すると、∂y/∂b = 1 (つまり 1.0)
# PyTorchがこの計算を正確に行えているか確認しましょう。

print(f"wの勾配 (w.grad): {w.grad.item()}")
print(f"bの勾配 (b.grad): {b.grad.item()}")

# --- 学習のステップ(参考イメージ) ---
# 実際の学習ループでは、この勾配情報を使って以下のようにパラメータを更新します。
# learning_rate = 0.01
# with torch.no_grad():
# w -= learning_rate * w.grad # 勾配の逆方向へ少し動かす
# b -= learning_rate * b.grad
計算結果 y: 7.0

--- 勾配(Gradient)の確認 ---
wの勾配 (w.grad): 3.0
bの勾配 (b.grad): 1.0

実行結果の解釈

上記のコードを実行すると、w.grad には 3.0 が、b.grad には 1.0 が格納されていることが確認できるはずです。

これはつまり、「現時点で、\(w\) を 1 増やすと、結果 \(y\) は 3 増える(だから \(y\) を減らしたければ \(w\) を大きく減らすべきだ)」 という関係性をPyTorchが正確に把握していることを意味します。この情報さえあれば、どの方向にどれだけパラメータを動かせばよいかという「次のアクション」が決まります。

このように、.backward() というたった一行のコマンドの裏側で、複雑な微積分計算がすべて処理されています。この強力なエンジンがあるおかげで、私たち医療者は数学的な計算の細部にとらわれることなく、「どのようなモデル構造にすれば病変の特徴を捉えやすいか」という、より本質的な課題解決に集中することができるのです。


3. nn.Moduleによるネットワーク構築:AIの「設計図」を描く

データの器である「Tensor」、学習のエンジンである「自動微分」について学びました。これらを使って、いよいよ独自のAIモデル(ニューラルネットワーク)を構築します。

PyTorchでモデルを作る際、必ず torch.nn.Module というクラスを継承します。これは、AIモデルを作るための「標準規格」のようなものです。この規格に従うことで、PyTorchはモデル内の数億個のパラメータを一括管理し、GPUへの転送や保存を簡単に行えるようになります (Paszke et al. 2019)。

初心者がまず理解すべきは、このクラスには「2つの必須記入欄」があるということです。それは、__init__(部品の調達)forward(データの流れ)です。

nn.Module によるAI構築:2つの役割 PyTorchのモデル作成は「病院の開設(準備)」と「診療フロー(実行)」の定義に分かれます。 🏥 1. __init__ (初期化) 📦 役割:部品の調達と配置 「診察室(レイヤー)」や「スタッフ」を 確保する段階。まだ患者はいない。 self.fc1 = nn.Linear(5, 10) self.fc2 = nn.Linear(10, 1) self.dropout = nn.Dropout(0.2) 2. forward (順伝播) 🏃 役割:診療プロセスの定義 データ(患者)が来た時の手順。 受付→検査→診断の順序を決める。 x = F.relu(self.fc1(x)) x = self.dropout(x) return torch.sigmoid(self.fc2(x)) MedicalDiagnosticNet のデータフロー 📄 入力(5) Linear 重みづけ (5 → 10) ReLU 活性化 Dropout 間引き (20% OFF) Linear 集約 (10 → 1) Sigmoid 1. nn.Linear (重み) 入力の重要度を計算。 年齢×係数A + BMI×係数B... y = xAT + b 2. ReLU (フィルタ) 微弱な信号(負の値)を ゼロにしてカットする。 3. Dropout (特訓) 一部のニューロンをランダムに 無効化し、過学習を防ぐ。 🚫 ⚠️ 呼び出しの作法: model.forward(x) ではなく model(x) と書くのがルールです!

3.1. 病院に例える「2つのメソッド」の役割

この仕組みを、新しい「診療科(AIモデル)」を開設するプロセスに例えてみましょう。

  • __init__(初期化):部品の調達と配置
    診療に必要な「設備」や「スタッフ」を確保する場所です。「入力層から信号を受け取るニューロンを5個用意する」「計算ユニットを1個用意する」といった具合に、使用するレイヤー(層)を定義します。
    イメージ:「まだ患者さんは来ていません。診察室に机やPC、聴診器を配置している状態(静的)」です。
  • forward(順伝播):診療プロセスの定義
    患者さん(データ)が来たときに、どの順番で診察を行うかという「手順(クリニカルパス)」を決める場所です。「データが入ってきたら、まず第1層に通し、次に活性化関数でフィルタリングし、最後に第2層で確率を出す」という処理の流れを記述します。
    イメージ:「実際に患者さんが来院しました。受付→問診→検査→診断というフローで案内します(動的)」という処理です。
病院で例える:AIモデルの「2つの役割」 新しい診療科(ニューラルネットワーク)を開設するプロセス __init__ (初期化) 診療室(まだ誰もいない) 🪑 💻 🩺 部品の調達・配置 役割:設備とスタッフの確保 ・入力層や計算ユニットを「定義」する場所 ・まだ患者(データ)は来ていない状態 「静的」な準備フェーズ def __init__(self): # 診察道具(レイヤー)を用意 self.fc1 = nn.Linear(10, 5) forward (順伝播) 🏃 患者(データ) 🩺 💊 診断(出力) 診療フローの実行 役割:診療手順(クリニカルパス) ・患者(データ)が来た時の順番を決める ・受付→問診→診断の流れを記述 「動的」な実行フェーズ def forward(self, x): # 患者xを診察室に通す x = self.fc1(x) return x まとめ:__init__ で「環境」を作り、forward で「処理の流れ」を作る

3.2. 【実装】医療データ分類モデルの構築

では、具体的なコードを見てみましょう。ここでは、5つの検査項目(年齢、BMI、血圧、HbA1c、CRP)から、特定の疾患リスク(あり/なし)を判定するAIを作成します。

Medical Diagnostic Net
PyTorchで医療診断AIモデルを構築します。Startを押してください。

import torch
import torch.nn as nn
import torch.nn.functional as F

# 1. モデルの設計図(クラス)を作成
class MedicalDiagnosticNet(nn.Module):
    def __init__(self, input_dim, hidden_dim):
        """
        【ステップ1:部品の調達】
        診察室に必要な「部品(レイヤー)」をあらかじめ定義します。
        input_dim : 入力特徴量数(例:年齢、血圧など5項目)
        hidden_dim: 中間層のニューロン数(思考の複雑さ)
        """
        # おまじない:親クラス(nn.Module)の初期化機能を必ず呼び出します
        super(MedicalDiagnosticNet, self).__init__()

        # --- 第1診察室(入力層 -> 中間層) ---
        # nn.Linear(線形変換): データの重要度(重み)を計算する基本部品
        # input_dim 個の検査値を受け取り、hidden_dim 個の特徴に変換します
        self.fc1 = nn.Linear(input_dim, hidden_dim)

        # --- 第2診察室(中間層 -> 出力層) ---
        # 最終的に「疾患あり」かどうかの確率を出したいので、出力は 1つ です
        self.fc2 = nn.Linear(hidden_dim, 1)

        # --- 安全装置(Dropout) ---
        # 特定のデータやニューロンに依存しすぎる「過学習(丸暗記)」を防ぐため、
        # ランダムに情報の20%を遮断して、応用力を鍛える部品です
        self.dropout = nn.Dropout(p=0.2)

    def forward(self, x):
        """
        【ステップ2:診療フローの定義】
        患者データ x が入ってきたら、どう処理して結果を返すか記述します。
        """
        # 1. 第1診察室に通す
        x = self.fc1(x)

        # 2. 活性化関数(ReLU)を通す
        # ReLU: 「負の値(意味のない信号)ならゼロにする」というフィルタ。
        # これにより、複雑な非線形な関係性を表現できます。
        x = F.relu(x)

        # 3. Dropoutを通す(訓練時のみランダムに一部を無効化)
        x = self.dropout(x)

        # 4. 第2診察室(出力層)に通す
        x = self.fc2(x)

        # 5. 確率に変換(Sigmoid関数)
        # 出てきた数値を 0.0 ~ 1.0 の範囲(確率)に押し込めます
        output = torch.sigmoid(x)

        return output

# --- 実際にモデルを動かしてみる ---

# 設定:入力は5項目、中間層のニューロンは10個
input_features = 5  
hidden_neurons = 10 

# 設計図から実体(インスタンス)を作成
model = MedicalDiagnosticNet(input_features, hidden_neurons)

print("--- モデルの構造 ---")
print(model)

# ダミーの患者データを作成(4人分 x 5項目)
# randnは正規分布に従う乱数を生成します
dummy_input = torch.randn(4, input_features)

# 診断を実行(forwardメソッドが自動的に呼ばれます)
# ※ model.forward(dummy_input) と直接書かないのが作法です
diagnosis_result = model(dummy_input)

print(f"\n入力形状: {dummy_input.shape}")
print(f"出力形状: {diagnosis_result.shape}")
print(f"診断結果(確率):\n{diagnosis_result.detach().numpy()}")
--- モデルの構造 ---
MedicalDiagnosticNet(
  (fc1): Linear(in_features=5, out_features=10, bias=True)
  (fc2): Linear(in_features=10, out_features=1, bias=True)
  (dropout): Dropout(p=0.2, inplace=False)
)

入力形状: torch.Size([4, 5])
出力形状: torch.Size([4, 1])
診断結果(確率):
[[0.46522382]
 [0.47800177]
 [0.37911454]
 [0.49364242]]

3.3. 初学者が知っておくべき3つの「重要部品」

上記のコードで登場した3つの重要な部品について、医学的なアナロジーで補足します。

初学者が知るべき3つの「重要部品」 AIモデル構築の基礎となるパーツと、その医学的な役割のイメージ 1. nn.Linear (全結合層) 重みづけ 年齢 BMI y y = xAT + b 🩺 医学的アナロジー:医師の判断 入力情報に対して「重要度」を調整する役割です。 「高齢だから年齢リスクを重視しよう(太い線)」 「今回はBMIの影響は小さい(細い線)」といった さじ加減を、行列演算によって行います。 2. F.relu (活性化関数) フィルタリング 発火! 医学的アナロジー:神経細胞の発火 「閾値(ゼロ)以下の微弱な刺激は無視し、 強い刺激だけを伝える」というフィルタです。 これがあることで、AIは単純な足し算だけでなく 複雑で非線形な判断ができるようになります。 3. nn.Dropout (ドロップアウト) 過学習防止 × × × 🛡️ 医学的アナロジー:指導医不在の特訓 「頼れる特定の指導医(ニューロン)をあえて 不在」にして、残されたチーム全員で協力して 診断する訓練です。特定の人への依存を防ぎ、 未知の症例にも対応できる「応用力」を育てます。

1. nn.Linear(全結合層)

入力データに対して「重みづけ」を行います。医師が診断する際、「年齢」と「BMI」を同じ重要度では見ないように、「今回は高齢だから年齢のリスク係数を高くしよう」といった加重計算を行う部分です。

数式では、入力ベクトル \(x\) に対して、重み行列 \(A\) とバイアス \(b\) を用いて以下の計算を行っています。

\[ y = xA^T + b \]

2. F.relu(活性化関数 ReLU)

神経細胞(ニューロン)の発火に近い挙動を模倣する関数です。「ある閾値以下の微弱な刺激には反応せず(0を出力)、強い刺激だけをそのまま伝える」というフィルタリングを行います。

もしこれがないと、どれだけ層を重ねても単純な足し算(線形変換)の繰り返しにしかなりません。このReLUを挟むことで、AIは初めて「複雑で非線形な境界線」を引くことができるようになります (Nair and Hinton 2010)。

3. nn.Dropout(ドロップアウト)

過学習(Overfitting)を防止するためのテクニックです。これは、研修医のチームトレーニングにおいて、あえて「頼りになる特定の指導医(特定のニューロン)」をランダムに不在にさせるようなものです。

すると、残されたチームは特定の人に頼らず、自分たち全体で協力して診断しようと努力します。結果として、未知の症例(テストデータ)にも強い「汎用的な診断能力」が身につくのです (Srivastava et al. 2014)。

3.4. なぜ model.forward(x) と呼ばないのか?

コードの実行部分で、model.forward(dummy_input) ではなく、model(dummy_input) と記述していることに注目してください。

これはPyTorchの非常に重要な作法です。model(x) と記述すると、内部で __call__ という特殊なメソッドが起動し、その中で様々な「準備運動(Hooks)」を行ってから、最後にユーザーが定義した forward(x) を呼び出してくれます。直接 forward を呼んでしまうと、これらの重要な準備処理がスキップされてしまい、正しく動作しない可能性があります。


4. 医療AI開発への示唆:コードと臨床の架け橋

今回解説した nn.ModuleAutograd の仕組みは、単なるプログラミングの文法ではありません。これらは、現在世界中で発表されている最先端の医療AI研究の「共通言語」であり、そのすべての基礎となる基盤技術です。

具体的に、歴史的なインパクトを与えた2つの研究事例を、PyTorchの視点(Tensorの形とモデルの構造)から解剖してみましょう。私たちが学んだ概念が、実際の最先端研究の中でどのように生きているかが見えてくるはずです。

4.1. ケーススタディ:論文の中のTensorとModule

事例1:皮膚がんの画像診断

スタンフォード大学の研究チームは、スマートフォン等で撮影された皮膚画像から、悪性黒色腫(メラノーマ)などの皮膚がんを皮膚科専門医と同等の精度で識別するAIを発表しました (Esteva et al. 2017)。

  • 入力データ(Tensor)
    皮膚のカラー画像です。これは今回学んだ「3階テンソル(高さ \(H\) × 幅 \(W\) × RGB色 \(C\))」として表現されます。
  • モデル構造(nn.Module)
    「畳み込みニューラルネットワーク(CNN)」と呼ばれる、視覚野の動きを模倣したモデル(Inception v3)が使われています。
  • 処理の流れ(forward)
    画像データが層を通るたびに、「エッジ(輪郭)」→「テクスチャ(模様)」→「病変の特徴」と、徐々に医学的に意味のある情報へと変換されていくプロセスが記述されています。
Esteva et al. 2017
事例1:皮膚がんの画像診断 AI スマートフォン画像から皮膚科専門医レベルの精度で識別 (Esteva et al. 2017) 入力データ (Tensor) 📱 RGB 皮膚のカラー画像 「3階テンソル」として表現。 縦(H) × 横(W) × 色(C) 膨大な画素数値の集合体として AIに入力されます。 モデル構造 (CNN) 🧠 🕸️ 畳み込みニューラルネット 生物の「視覚野」の働きを模倣。 局所的な特徴(線や点)から 全体像を認識する構造。 ※Inception v3モデルを採用 処理の流れ (Forward) 1 エッジ(輪郭) 2 テクスチャ(模様) 3 病変の特徴 🏥 診断結果 良性 or 悪性 層が深くなるにつれて、単純な「線」から医学的に意味のある「特徴」へと情報が変換される

事例2:電子カルテからの予後予測

Googleと複数の大学病院のチームは、電子カルテ(EHR)に含まれる非構造化データから、入院患者の死亡リスクや再入院確率を予測するモデルを開発しました (Rajkomar et al. 2018)。

  • 入力データ(Tensor)
    カルテの自由記述や検査オーダーの履歴です。これらは単語やコードを数値化した列(ベクトル)として扱われ、「2階テンソル(時間 \(T\) × 特徴量 \(D\))」の時系列データとなります。
  • モデル構造(nn.Module)
    文章の文脈や前後のつながりを読み取るための「リカレントニューラルネットワーク(RNN)」や、現在の主流である「Transformer」が用いられます。
  • 処理の流れ(forward)
    過去の受診歴から現在の症状まで、時間の流れに沿って情報を蓄積・統合し、将来のリスクを計算するフローが定義されています。
事例2:電子カルテからの予後予測 AI 入院患者の死亡リスクや再入院率を予測 (Google & 大学病院, 2018) 入力データ (Tensor) 📄 時間 T 特徴 D カルテ・検査履歴・メモ 「2階テンソル」の時系列データ。 時間(T) × 特徴量(D) 言葉やコードを数値ベクトル化し 時間の流れとして扱います。 モデル構造 (RNN) 🔗 文脈の記憶 系列データの読解モデル RNNやTransformerを採用。 「前回の入院で何があったか」 「症状がどう変化したか」など 文脈(Context) を理解します。 処理の流れ (Forward) 過去:入院歴あり 現在:検査異常値 再入院リスク 85% 情報の蓄積と未来予測 過去から現在への情報の流れを 順伝播(Forward)で統合。 全データを加味して確率を計算。 時間の流れに沿って情報を蓄積・統合し、将来のリスクを計算するプロセス

4.2. Computational Thinking(計算論的思考)の獲得

これらの研究は、扱う臓器もデータの種類も異なります。しかし、「臨床データをTensorという数値の箱に入れ、nn.Moduleという関数に通し、Autogradで誤差を修正する」という本質的な構造は、今回私たちが書いたコードと全く同じです。

米国の循環器内科医エリック・トポル博士が著書『Deep Medicine』で指摘するように、医療者がプログラミングを学ぶ最大の意義は、単にコードを書けるようになること自体ではありません (Topol 2019)。

それは、「目の前の臨床課題を、計算可能な数理モデルとして再定義する力(Computational Thinking)」を身につけることです。

「この診断プロセスは、どのような入力と出力の関数として表現できるか?」
「医師の経験則(暗黙知)は、どのようなパラメータとして重みづけできるか?」

この「翻訳」を行う思考法こそが、AI時代の医療者に求められる新しいリテラシーであり、医学と工学をつなぐ共通言語となるのです。

PyTorchを学ぶことは、この新しい思考法を体得するための、最も実践的で強力なトレーニングと言えるでしょう。


次回予告:モデルに命を吹き込む「学習」

設計図(モデル)は完成しました。しかし、今のままでは、まだ一度も患者さんを診たことのない「新人研修医」のような状態です。知識(パラメータ)が空っぽだからです。

次回は、このモデルに大量のデータを与え、正解との誤差を修正しながら賢くしていく「学習ループ(Training Loop)」の実装について詳説します。いよいよ、AIが学習を始める瞬間です。


参考文献

  • Esteva, A. et al. (2017) 'Dermatologist-level classification of skin cancer with deep neural networks', Nature, 542(7639), pp. 115–118.
  • Goodfellow, I., Bengio, Y. and Courville, A. (2016) Deep Learning. Cambridge, MA: MIT Press.
  • LeCun, Y., Bengio, Y. and Hinton, G. (2015) 'Deep learning', Nature, 521(7553), pp. 436–444.
  • Litjens, G. et al. (2017) 'A survey on deep learning in medical image analysis', Medical Image Analysis, 42, pp. 60–88.
  • Nair, V. and Hinton, G.E. (2010) 'Rectified linear units improve restricted Boltzmann machines', in Proceedings of the 27th International Conference on Machine Learning (ICML-10), pp. 807–814.
  • Paszke, A. et al. (2019) 'PyTorch: An imperative style, high-performance deep learning library', in Advances in Neural Information Processing Systems 32, pp. 8026–8037.
  • Rajkomar, A. et al. (2018) 'Scalable and accurate deep learning with electronic health records', NPJ Digital Medicine, 1(1), p. 18.
  • Rumelhart, D.E., Hinton, G.E. and Williams, R.J. (1986) 'Learning representations by back-propagating errors', Nature, 323(6088), pp. 533–536.
  • Srivastava, N. et al. (2014) 'Dropout: a simple way to prevent neural networks from overfitting', The Journal of Machine Learning Research, 15(1), pp. 1929–1958.
  • Topol, E.J. (2019a) Deep Medicine: How Artificial Intelligence Can Make Healthcare Human Again. New York: Basic Books.
  • Topol, E.J. (2019b) 'High-performance medicine: the convergence of human and artificial intelligence', Nature Medicine, 25(1), pp. 44–56.

※本記事は情報提供を目的としたものであり、特定の治療法を推奨するものではありません。健康に関するご懸念やご相談は、必ず専門の医療機関にご相談ください。


ご利用規約(免責事項)

当サイト(以下「本サイト」といいます)をご利用になる前に、本ご利用規約(以下「本規約」といいます)をよくお読みください。本サイトを利用された時点で、利用者は本規約の全ての条項に同意したものとみなします。

第1条(目的と情報の性質)

  1. 本サイトは、医療分野におけるAI技術に関する一般的な情報提供および技術的な学習機会の提供を唯一の目的とします。
  2. 本サイトで提供されるすべてのコンテンツ(文章、図表、コード、データセットの紹介等を含みますが、これらに限定されません)は、一般的な学習参考用であり、いかなる場合も医学的な助言、診断、治療、またはこれらに準ずる行為(以下「医行為等」といいます)を提供するものではありません。
  3. 本サイトのコンテンツは、特定の製品、技術、または治療法の有効性、安全性を保証、推奨、または広告・販売促進するものではありません。紹介する技術には研究開発段階のものが含まれており、その臨床応用には、さらなる研究と国内外の規制当局による正式な承認が別途必要です。
  4. 本サイトは、情報提供を目的としたものであり、特定の治療法を推奨するものではありません。健康に関するご懸念やご相談は、必ず専門の医療機関にご相談ください。

第2条(法令等の遵守)
利用者は、本サイトの利用にあたり、医師法、医薬品、医療機器等の品質、有効性及び安全性の確保等に関する法律(薬機法)、個人情報の保護に関する法律、医療法、医療広告ガイドライン、その他関連する国内外の全ての法令、条例、規則、および各省庁・学会等が定める最新のガイドライン等を、自らの責任において遵守するものとします。これらの適用判断についても、利用者が自ら関係各所に確認するものとし、本サイトは一切の責任を負いません。

第3条(医療行為における責任)

  1. 本サイトで紹介するAI技術・手法は、あくまで研究段階の技術的解説であり、実際の臨床現場での診断・治療を代替、補助、または推奨するものでは一切ありません。
  2. 医行為等に関する最終的な判断、決定、およびそれに伴う一切の責任は、必ず法律上その資格を認められた医療専門家(医師、歯科医師等)が負うものとします。AIによる出力を、資格を有する専門家による独立した検証および判断を経ずに利用することを固く禁じます。
  3. 本サイトの情報に基づくいかなる行為によって利用者または第三者に損害が生じた場合も、本サイト運営者は一切の責任を負いません。実際の臨床判断に際しては、必ず担当の医療専門家にご相談ください。本サイトの利用によって、利用者と本サイト運営者の間に、医師と患者の関係、またはその他いかなる専門的な関係も成立するものではありません。

第4条(情報の正確性・完全性・有用性)

  1. 本サイトは、掲載する情報(数値、事例、ソースコード、ライブラリのバージョン等)の正確性、完全性、網羅性、有用性、特定目的への適合性、その他一切の事項について、何ら保証するものではありません。
  2. 掲載情報は執筆時点のものであり、予告なく変更または削除されることがあります。また、技術の進展、ライブラリの更新等により、情報は古くなる可能性があります。利用者は、必ず自身で公式ドキュメント等の最新情報を確認し、自らの責任で情報を利用するものとします。

第5条(AI生成コンテンツに関する注意事項)
本サイトのコンテンツには、AIによる提案を基に作成された部分が含まれる場合がありますが、公開にあたっては人間による監修・編集を経ています。利用者が生成AI等を用いる際は、ハルシネーション(事実に基づかない情報の生成)やバイアスのリスクが内在することを十分に理解し、その出力を鵜呑みにすることなく、必ず専門家による検証を行うものとします。

第6条(知的財産権)

  1. 本サイトを構成するすべてのコンテンツに関する著作権、商標権、その他一切の知的財産権は、本サイト運営者または正当な権利を有する第三者に帰属します。
  2. 本サイトのコンテンツを引用、転載、複製、改変、その他の二次利用を行う場合は、著作権法その他関連法規を遵守し、必ず出典を明記するとともに、権利者の許諾を得るなど、適切な手続きを自らの責任で行うものとします。

第7条(プライバシー・倫理)
本サイトで紹介または言及されるデータセット等を利用する場合、利用者は当該データセットに付随するライセンス条件および研究倫理指針を厳格に遵守し、個人情報の匿名化や同意取得の確認など、適用される法規制に基づき必要とされるすべての措置を、自らの責任において講じるものとします。

第8条(利用環境)
本サイトで紹介するソースコードやライブラリは、執筆時点で特定のバージョンおよび実行環境(OS、ハードウェア、依存パッケージ等)を前提としています。利用者の環境における動作を保証するものではなく、互換性の問題等に起因するいかなる不利益・損害についても、本サイト運営者は責任を負いません。

第9条(免責事項)

  1. 本サイト運営者は、利用者が本サイトを利用したこと、または利用できなかったことによって生じる一切の損害(直接損害、間接損害、付随的損害、特別損害、懲罰的損害、逸失利益、データの消失、プログラムの毀損等を含みますが、これらに限定されません)について、その原因の如何を問わず、一切の法的責任を負わないものとします。
  2. 本サイトの利用は、学習および研究目的に限定されるものとし、それ以外の目的での利用はご遠慮ください。
  3. 本サイトの利用に関連して、利用者と第三者との間で紛争が生じた場合、利用者は自らの費用と責任においてこれを解決するものとし、本サイト運営者に一切の迷惑または損害を与えないものとします。
  4. 本サイト運営者は、いつでも予告なく本サイトの運営を中断、中止、または内容を変更できるものとし、これによって利用者に生じたいかなる損害についても責任を負いません。

第10条(規約の変更)
本サイト運営者は、必要と判断した場合、利用者の承諾を得ることなく、いつでも本規約を変更することができます。変更後の規約は、本サイト上に掲載された時点で効力を生じるものとし、利用者は変更後の規約に拘束されるものとします。

第11条(準拠法および合意管轄)
本規約の解釈にあたっては、日本法を準拠法とします。本サイトの利用および本規約に関連して生じる一切の紛争については、東京地方裁判所を第一審の専属的合意管轄裁判所とします。


For J³, may joy follow you.

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

医師・医学博士・AI研究者・連続起業家
元厚生労働省幹部・ハーバード大学理学修士・ケンブリッジ大学MBA・コロンビア大学行政修士(経済)
岡山大学医学部卒業後、内科・地域医療に従事。厚生労働省で複数室長(医療情報・救急災害・国際展開等)を歴任し、内閣官房・内閣府・文部科学省でも医療政策に携わる。
退官後は、日本大手IT企業や英国VCで新規事業開発・投資を担当し、複数の医療スタートアップを創業。現在は医療AI・デジタル医療機器の開発に取り組むとともに、東京都港区で内科クリニックを開業。
複数大学で教授として教育・研究活動に従事し、医療関係者向け医療AIラボ「Medical AI Nexus」、医療メディア「The Health Choice | 健康の選択」を主宰。
ケンブリッジ大学Associate・社会医学系指導医・専門医・The Royal Society of Medicine Fellow

目次