[Medical AI with Python:P21] Tensorと自動微分を体験しよう

PyTorch学習サイクルのポイント

PyTorchは、Tensorという多次元配列を用いて計算を行い、その過程を「計算グラフ」として自動記録します。このグラフを逆向きにたどる「自動微分」によって、モデルが賢くなるためのヒント(勾配)を算出し、パラメータを更新するのが学習の基本フローです。

直感的なフレームワーク
書けばすぐ動く「Define-by-Run」

Pythonコードを上から順に実行するだけで、裏側で計算の繋がり(計算グラフ)が自動的に構築されます。複雑な「お作法」を覚える必要がなく、直感的でデバッグしやすいのが最大の特徴です。

計算の基本単位
微分できる多次元配列:Tensor

数値を格納する多次元配列です。NumPyに似ていますが、requires_grad=Trueと設定することで、そのTensorが関わる全ての計算履歴を追跡し、後の「自動微分」の対象にすることができます。

学習の心臓部
自動微分(Autograd)

最終的な出力(例: 損失)から .backward() を呼び出すだけで、計算グラフを逆向きにたどり、各パラメータが結果に与えた影響度(勾配)を自動で計算します。計算結果は .grad に格納されます。

誤差逆伝播の全体像
4つのステップで理解する学習サイクル
この章で達成する学習目標
✔ PyTorchの基本思想と、Tensorおよび自動微分の仕組みをコードレベルで理解する。
計算グラフと誤差逆伝播法が、モデル学習の根幹をなすプロセスであることを直感的に把握する。
✔ Tensorと自動微分だけを使い、ゼロから簡単な線形回帰モデルを実装・学習させるスキルを習得する。
学習を始める前の前提知識
💡 Pythonの基本文法
変数、リスト、forループ、関数の定義など、基本的なプログラミングの知識。
💡 高校レベルの数学(特に微分)
勾配(=関数の傾き)の概念を理解するために、微分の基本的な意味を知っていると学習がスムーズに進みます。
💡 (推奨) NumPyの利用経験
PyTorchのTensor操作はNumPyに似ています。必須ではありませんが、経験があるとより早く概念を掴めます。
目次

7.1 PyTorchとは?

これまでに学んできたのは、深層学習の仕組みや数学的な直感でした。
ここからはいよいよ、本物のコードを書いて、自分の手で深層学習モデルを動かしてみるパートに入ります。

この実装編で使っていくのが、Meta(旧Facebook)が開発した深層学習ライブラリ「PyTorch(パイトーチ)」です。


🧠 PyTorchとは?

PyTorchは、数ある機械学習ライブラリの中でも特に人気の高いフレームワークです。

その理由はシンプル:

  • 書きやすい(Pythonライク)
  • 柔軟(ループや条件分岐も自然に使える)
  • 強力(GPU対応や自動微分が標準)

簡単なコードで、数学的に複雑な処理(勾配計算など)をサポートしてくれるため、研究から実務まで幅広く活用されています。


✅ PyTorchの主な特徴

特徴説明
Pythonライクな記述NumPyと似た書き方で直感的に使える
define-by-run(後述)実行しながら計算グラフを構築
自動微分(autograd).backward() だけで勾配計算ができる
GPUサポートが簡単.to("cuda") で高速処理
柔軟なモデル構築が可能再帰や条件分岐を含む複雑なモデルも書きやすい

💡 define-by-runとは?

PyTorchの革新性を象徴する考え方が、「define-by-run(実行しながら定義)」というスタイルです。

これは、「まずモデルを設計してから実行する」従来の方法とは違い、Pythonのコードを実行するその場で、計算の流れ(計算グラフ)を構築していくスタイルです。


▶ PyTorchの例:define-by-run

import torch
x = torch.tensor(2.0, requires_grad=True)
y = x**2 + 3*x + 1
y.backward()
print(x.grad)  # 出力: tensor(7.)

このように、変数xを定義 → 計算式を書く → 勾配を求める という一連の流れが、自然なPythonコードのまま書けるのが最大の魅力です。

PyTorchは、コードを実行する中で自動的に「どんな演算が行われたか」を記録してくれています。これをもとに .backward() で逆向きの微分(誤差逆伝播)を自動計算できるのです。


▶ 従来の方法:TensorFlow v1の define-then-run

参考までに、旧来のスタイルである TensorFlow v1 の例も見てみましょう:

import tensorflow as tf

# 1. 演算グラフを「定義」する(まだ実行されない)
x = tf.Variable(2.0)
y = x**2 + 3*x + 1
dy_dx = tf.gradients(y, x)

# 2. セッションを開いて「実行」する
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())  # 変数の初期化
    grad = sess.run(dy_dx)  # 微分(勾配)を計算
    print(grad)  # 出力: [7.0]

このコードでは、**まず計算の“設計図”だけを作成し(ステップ1)、その後に明示的に「この設計図を実行して」と指示する(ステップ2)**というスタイルをとっています。

具体的には:

  • x に値を代入しても、その時点では計算は行われていません
  • y = x**2 + 3*x + 1 も同様に、数式のような形で計算グラフ(処理の流れ)を構築しているだけです。
  • tf.gradients(y, x) は「yx で微分するノードを追加する」だけで、これもまだ実行されていません。
  • 実際の値の計算(勾配を求めるなど)は、sess.run() によって初めて行われます。

このように、計算グラフの「定義」と「実行」が分かれているため、柔軟ではあるものの、以下のような欠点もありました:

  • 毎回 sess.run() を書かないと値が見られず、直感的でない
  • デバッグ時に「何が今の値なのか」がわかりづらい
  • 状態管理(変数の初期化や再代入など)が煩雑
  • 初学者にとって学習コストが高い

この欠点を克服する形で登場したのが、PyTorch に代表される define-by-run(定義しながら即実行)スタイルです。


✅ define-by-run vs define-then-run

比較項目PyTorch(define-by-run)TensorFlow v1(define-then-run)
コードの自然さPythonらしい特殊なセッション管理が必要
柔軟性高い(if文、for文も使える)低い(動的構造に不向き)
学習コスト低い(すぐに実行・試行錯誤できる)高い(定義と実行の分離が複雑)
デバッグのしやすさ実行時に追いやすい抽象的なグラフ内でトラブルが起きやすい

PyTorchでは、「書けばすぐ動く」「思考の流れそのままにコードを書ける」という感覚があり、学習体験として非常にスムーズです。

【参考】TensorFlow v2 も define-by-run を採用

TensorFlow v2 も define-by-run を採用

実は、TensorFlow も v2 以降では、PyTorchと同じ define-by-run(実行しながら定義)スタイル を基本とするようになりました。つまり、「コードを実行するその場で、計算の流れ(計算グラフ)を自動的に作っていく」仕組みに進化したのです。

TensorFlow v2 の例:

import tensorflow as tf

x = tf.Variable(2.0)  # 微分したい変数を定義

# ここから「計算を記録しますよ」という範囲を明示的に指定
with tf.GradientTape() as tape:
    y = x**2 + 3*x + 1  # この計算が自動的に記録される

# y を x で微分(∂y/∂x)して勾配を取得
grad = tape.gradient(y, x)
print(grad.numpy())  # 出力: 7.0

🧠 このコードの意味をわかりやすく解説:

  • GradientTape() は、**「この中の計算はあとで微分に使うから覚えておいてね」**というメモのようなものです。
  • with ブロックの中で行った演算(ここでは y = x² + 3x + 1)が、自動的に記録されます。
  • そして、tape.gradient(y, x) を呼ぶと、「yをxでどれだけ変えたら、どれだけ結果が変わるか?」(=勾配)を計算してくれます。

これは、PyTorch の .backward() と同じような役割です。
ただし PyTorchでは .backward() を出力に対して直接呼ぶのに対して、TensorFlow v2 では 勾配を知りたい対象を引数として渡すのが特徴です。


PyTorchとの比較(まとめ)

項目PyTorchTensorFlow v2
自動微分の方法.backward()GradientTape().gradient()
勾配を得たい変数の指定requires_grad=Truetf.Variable()(暗黙に追跡される)
計算の記録方法自動で記録with tf.GradientTape(): で明示
実行スタイルdefine-by-rundefine-by-run

現在では、PyTorch も TensorFlow v2 もどちらも「書けばすぐ動く」define-by-runスタイルを採用しており、試行錯誤やデバッグがしやすいため、初心者にとっても扱いやすいフレームワークになっています。


🧪 もう一度:PyTorchでの自動微分

import torch
x = torch.tensor(2.0, requires_grad=True)
y = x**2 + 3*x + 1
y.backward()
print(x.grad)  # 出力: tensor(7.)

このコードでは、PyTorchが「xからyを計算するまでの演算」をすべて記録し、.backward() を呼ぶだけで微分結果(勾配)を x.grad に返してくれます。

まるで「自分が計算した式の意味」を、AIがちゃんと覚えていてくれる感覚です。


🔜 次は「Tensorとは何か?」へ

PyTorchの魅力は、「人間が自然に書いたコード」をそのままモデルとして動かせることにあります。

その土台となるのが ― Tensor(テンソル) です。

次の章では、PyTorchの中核をなす「Tensor」とは何か? NumPyとどう違うのか?
そして、実際にPyTorchでデータを扱うにはどんな流れになるのか? を、手を動かしながら理解していきましょう。


7.2 Tensorとは何か?

7.1 では、PyTorchの基本的な考え方や「define-by-run」の特徴、そして .backward() による自動微分のイメージを学びました。

ここからは、PyTorchであらゆるデータを扱う際の「基本単位」である Tensor(テンソル) について、Python初心者でも安心して読めるように、手を動かしながら一緒に学んでいきましょう。


🔍 Tensorとは?ざっくり言うと「数のかたまり」

Tensorとは、数値をまとめて扱うための入れ物です。
Pythonでの list や NumPyでの ndarray に似ていますが、PyTorchのTensorはさらに進化しており、GPU対応や自動微分機能まで備えています。

簡単に言えば、「スカラー(ただの数)」「ベクトル(1列の数)」「行列(2次元の表)」から、もっと高次元の構造までを1つの仕組みで統一的に扱えるのがTensorです。


▶ Tensorの次元と意味を確認しよう

次元呼び方説明
0次元スカラー3.0単なる1つの数値
1次元ベクトル[1.0, 2.0, 3.0]数の並び(配列)
2次元行列[[1.0, 2.0], [3.0, 4.0]]表のような形(行と列)
3次元以上テンソル画像データなど(例:チャンネル×高さ×幅)高次元の数値構造

✏️ PyTorchでTensorを作ってみよう

import torch  # PyTorchをインポートします

# 0次元(スカラー):1つの数だけ
x0 = torch.tensor(3.0)

# 1次元(ベクトル):複数の数を並べたもの
x1 = torch.tensor([1.0, 2.0, 3.0])

# 2次元(行列):行と列を持つデータ
x2 = torch.tensor([[1.0, 2.0], [3.0, 4.0]])

# 内容を表示
print("0次元(スカラー):", x0)
print("1次元(ベクトル):", x1)
print("2次元(行列):\n", x2)

📐 Tensorの「形(shape)」を調べてみよう

# 各Tensorの「形」を表示してみよう
print("x0の形:", x0.shape)  # → torch.Size([]) スカラー
print("x1の形:", x1.shape)  # → torch.Size([3]) 要素3つの1次元
print("x2の形:", x2.shape)  # → torch.Size([2, 2]) 2行2列の行列

.shape を使うと、「何次元か」「各次元にいくつ要素があるか」がわかります。


🔁 NumPyみたいに演算もできる!

# 2つの1次元Tensorを用意
a = torch.tensor([2.0, 3.0])
b = torch.tensor([1.0, 4.0])

# 要素ごとの加算(2+1, 3+4)
print("a + b =", a + b)

# 要素ごとの掛け算(2*1, 3*4)
print("a * b =", a * b)

# ベクトルの内積(dot product):2*1 + 3*4 = 14
print("内積 =", torch.dot(a, b))

PyTorchのTensorは、NumPyのような計算がそのままできます。


🚀 TensorをGPUに移す(あれば)

# CPU上にあるTensor
x_cpu = torch.tensor([1.0, 2.0])

# GPUが使える場合、GPUに移動して計算可能(なければエラー)
x_gpu = x_cpu.to("cuda")  # "cuda" = GPUのこと
print("CPU上のTensor:", x_cpu.device)
print("GPU上のTensor:", x_gpu.device)

.to("cuda") を使うと、計算が高速なGPU上にTensorを転送できます(※GPUが搭載されていない場合は使えません)。


🧠 requires_grad=True とは? ― 微分の準備をするスイッチ

ここで、PyTorchのもう一つの大事な仕組みを紹介しましょう。

なぜ「勾配(微分)」が必要?

深層学習では、モデルの出力と正解のズレ(損失)を小さくするために、「どの方向に変数を変えればよいか」を知る必要があります。
その方向を教えてくれるのが、勾配(=関数の傾き、微分)です。

そこで登場するのが…

# requires_grad=True を指定することで「この変数に注目して!」と伝える
x = torch.tensor(2.0, requires_grad=True)

# y = x^2 + 3x + 1 を定義(xを使って計算)
y = x**2 + 3*x + 1

# y を x で微分(=勾配計算)する
y.backward()

# 結果を確認:dy/dx = 2x + 3 → 7.0
print("dy/dx =", x.grad)

解説:何が起きている?

  1. requires_grad=True をつけたことで、PyTorchは「このxを使った計算は全部覚えておこう」と構えてくれます。
  2. y = x**2 + 3*x + 1 の演算が行われると、その計算の流れが記録されます(これが「計算グラフ」)。
  3. .backward() を呼ぶと、PyTorchがそのグラフを逆方向にたどり、xに対する微分(勾配)を自動で計算してくれます。
  4. 結果が x.grad に格納されます。

🧴 たとえるなら:

  • requires_grad=True は、「この変数に色をつけて追跡してね」という印
  • .backward() は、「今から結果を調べるよ」という号令
  • .grad は、「どれくらい結果に影響していたか」を示す数値(=傾き)

✅ よくあるエラー:つけ忘れると…

x = torch.tensor(2.0)  # ← ここで requires_grad=True がない!
y = x**2 + 3*x + 1
y.backward()  # ← これをするとエラーになります

requires_grad=True をつけないと、PyTorchは「この変数に関して微分をとる必要はない」と判断してしまい、後で .backward() を呼んでも勾配を計算してくれません


📌 今回のまとめ

学んだこと内容
Tensorとは?多次元の数値構造(スカラー〜高次元まで統一的に扱える)
Tensorの作り方torch.tensor() で簡単に定義できる
Tensorの形(shape).shape で確認
演算NumPyのような計算が可能(+、×、内積など)
デバイス.to("cuda") でGPUにも対応(ある場合)
requires_grad=True微分したい変数に指定。自動微分の準備になる
.backward() / .grad勾配の計算と、結果の取り出し

🔜 次回:いよいよ「自動微分」を深掘り!

ここまでで、Tensorが「ただの数値の入れ物」ではなく、深層学習における“学習そのもの”を支える重要な存在であることが見えてきました。

特に、requires_grad=True を指定することで、Tensorは微分(=勾配)を計算するための特別な変数へと変わります。これは、モデルが「どこをどれだけ修正すればよいか?」を知るための重要な仕組みです。

次回は、PyTorchがどのように演算の流れを記録し、自動で勾配を計算してくれるのかを、シンプルな例から少しずつ体験しながら学んでいきましょう。


7.3「自動微分を使ってみよう」

前回は、PyTorchの「Tensor(テンソル)」がただの数値の入れ物ではなく、
requires_grad=True をつけることで「勾配計算に対応できる特別な変数」になることを学びました。

今回は、いろいろなTensorを使って演算し、その中で自動微分がどう働くかを体験してみましょう。


💡 そもそも「自動微分」ってなに?

数学でいう「微分」とは、「ある変数を少し動かしたときに、結果がどれくらい変わるか?」を調べる操作です。
たとえば、損失関数を最小化したいときには、「どの方向に変数を動かせば損失が減るか?」を知る必要があります。

これを毎回手計算するのは大変ですが、PyTorchでは 自動で微分を計算してくれる機能 があります。
それが 自動微分(automatic differentiation) です。

PyTorchは、計算の過程(演算の履歴)を裏で記録しておき、.backward() を使うと、出力から逆向きにたどって、必要な変数に対する勾配を自動で計算してくれます。


🔧 .backward() の使い方をわかりやすく!

.backward() は、最終的に出てきた「1つの値(スカラー)」に対して呼び出します。
この1つの値とは、たとえば損失関数の出力など「何を最適化したいか」のゴールとなる値です。

この .backward() を呼ぶことで、その値に影響を与えた変数のうち、requires_grad=True が設定されているもの に対して、勾配(どの方向に動かせばゴールが改善されるか)を計算します。


📌 具体例で見てみよう:

import torch

# 微分したい変数(勾配を知りたい対象)
# torch.tensor(2.0) は、値が 2.0 のテンソル(PyTorchの数値オブジェクト)を作ります。
# requires_grad=True を指定することで、
# → 「この変数は、あとで勾配を計算したい対象なので、演算の履歴を記録して追跡しておいてね」という指示になります。
x = torch.tensor(2.0, requires_grad=True)

# x を使って関数を定義(この演算の流れが記録される)
y = x**2 + 3*x + 1  # y = x² + 3x + 1

# 出力(y)は1つの値なので、ここに .backward() を呼ぶ
y.backward()

# x に対する勾配(∂y/∂x)は x.grad に格納されている
print(x.grad)  # 出力: tensor(7.)

🧠 補足ポイント(重要)— .backward() って結局なにをしているの?

.backward() は、次のような命令です:

「この出力(たとえば損失関数の結果)をもとに、計算に関わった変数がどれくらい影響を与えていたか(= 勾配)を全部自動で計算して!」

この命令によって、PyTorchは計算グラフを出力側からさかのぼって、勾配(=偏微分値)を求めていきます。
この仕組みは「誤差逆伝播(backpropagation)」と呼ばれ、深層学習の学習アルゴリズムの核心部分です。


🔍 でも、すべての変数に対して計算されるわけではありません。

勾配を計算したい変数には、あらかじめ次のようにしておく必要があります:

x = torch.tensor(2.0, requires_grad=True)

この requires_grad=True を設定することで、

  • 「この変数には勾配を記録してほしい」
  • 「あとで学習に使うから追跡してね」

という指示をPyTorchに出していることになります。

.backward() を実行したときに勾配が計算されるのは、この requires_grad=True が設定された変数だけです。
他の変数には、勾配は計算も保存もされません。


🔢 勾配はどこに保存されるの? → .grad に入ります

勾配が計算されると、それは各変数の .grad 属性に自動で保存されます:

print(x.grad)  # ∂y/∂x の値が入っている

この .grad に入った値は、たとえば 確率的勾配降下法(SGD) などの最適化アルゴリズムで、パラメータを更新するのに使われます。


💡 さらに補足:出力がスカラーでないときは?

通常、.backward()スカラー(1つの値) に対して使います。
たとえば、損失関数の出力などがそうです。

y = loss(x)  # y がスカラーならそのまま y.backward() できる

もし y がスカラーでない場合(例:ベクトルや行列)には、.backward()勾配の初期値(外から与える勾配)gradient= 引数として渡す必要があります:

y.backward(gradient=torch.tensor([1.0, 0.0, 0.0]))  # ベクトル出力の場合など

これは、「どの方向からの微分を計算したいのか」を明示的に指定しているイメージです。


📌 まとめ:

  • .backward() は「出力から入力へ逆向きにたどって、勾配を自動計算して!」という命令。
  • 勾配が計算されるのは requires_grad=True の変数だけ。
  • 計算された勾配は .grad に格納され、学習アルゴリズムで使われる。
  • 出力がスカラーでない場合は gradient= を指定する。

⚠️ 注意点

  • 出力がスカラー(1つの数値)でない場合(例:ベクトルなど)には、.backward()gradient= 引数を指定する必要があります。
  • 同じ計算グラフで .backward() を2回以上使いたいときは、retain_graph=True を指定します。

🎯 まとめ

  • .backward() は「この出力から逆向きにたどって、必要な変数に対する勾配を計算して!」という命令。
  • 勾配を計算したい変数には requires_grad=True を設定する必要がある。
  • 計算された勾配は .grad に格納され、最適化や重みの更新に使われる。

📚 複数の変数がある場合

深層学習のモデルでは、**複数の変数(重みやバイアスなど)**を使って計算するのが普通です。
ここでは、シンプルな線形モデル y = wx + b を例にして、それぞれの変数に対して自動微分がどう働くかを見てみましょう。


🧪 例:y = wx + b に対してそれぞれ微分してみよう

# 変数 x, w, b を定義(すべて微分を追跡するように指定)
x = torch.tensor(1.0, requires_grad=True)  # 入力値
w = torch.tensor(2.0, requires_grad=True)  # 重み
b = torch.tensor(1.0, requires_grad=True)  # バイアス

# 線形モデル:y = wx + b
y = w * x + b

# yを3つの変数で微分(逆伝播)
y.backward()

# 各変数に対する勾配(偏微分)を表示
print("dy/dx =", x.grad)  # ∂y/∂x = w → 2.0
print("dy/dw =", w.grad)  # ∂y/∂w = x → 1.0
print("dy/db =", b.grad)  # ∂y/∂b = 1.0

🧠 この例で起きていること

  • y = w * x + b という関数は、3つの変数 x, w, b によって構成されています。
  • .backward() を呼ぶと、PyTorchはそれぞれの変数に対して以下の微分を自動で行います:
項目数式微分結果
∂y/∂xw2.0
∂y/∂wx1.0
∂y/∂b11.0

すべて正しく grad プロパティに格納されています。便利すぎる!


🧼 注意:gradは累積される!

# 同じ変数を使ってもう一度 .backward() を呼ぶと、勾配が「加算」されてしまう!
y = w * x + b
y.backward()
print(w.grad)  # 前の値にさらに加算される → 1.0 + 1.0 = 2.0 になる

これを防ぐには、都度 .grad.zero_() でリセットしましょう:

# 勾配をクリアしてから再計算
w.grad.zero_()
x.grad.zero_()
b.grad.zero_()

📌 今回のまとめ

学んだこと内容
自動微分とは?計算の履歴から、変数ごとの勾配を自動で計算するしくみ
.backward() の役割演算を逆向きにたどり、勾配を計算する
.grad に入るのは?変数ごとの「変化のしやすさ(傾き)」
複数の変数があるときの挙動すべての変数に対して同時に微分してくれる
勾配は累積する.backward() を複数回使うときは .grad.zero_() を忘れずに

🔜 次回:「計算グラフと連鎖律の直感的な理解」へ

ここまでで、PyTorchが自動的に「どこをどれだけ変えればいいか(勾配)」を計算してくれることがわかりました。

でも、なぜ .backward() でそれができるのか?
それを理解するカギが「計算グラフ」と「連鎖律(Chain Rule)」です。


7.4 計算グラフで誤差逆伝播を直感的に理解しよう

「.backward() を呼ぶだけで、どうして“勾配”がわかるの?」

そんな疑問がわいてきませんか?

これを理解するには、「計算グラフ」と「誤差逆伝播(backpropagation)」、そして「連鎖律(Chain Rule)」という考え方がカギになります。

難しそうに聞こえるかもしれませんが、大丈夫です。
ここでは中学生でもわかるレベルから、じっくり順番に解説していきます。

計算グラフで誤差逆伝播を直感的に理解
計算グラフで誤差逆伝播を直感的に理解

ニューラルネットワークの学習における誤差逆伝播(Backpropagation)を視覚的に体験できるインタラクティブシミュレーターです。
順伝播で計算された結果から、逆伝播で各パラメータの勾配がどのように伝わっていくかを直感的に理解できます。


🧱 そもそも「計算グラフ」ってなに?

✅ 例:y = (x + 2) × (x + 3)

この式を見てください:

y = (x + 2) * (x + 3)

この式の中には、いくつかの「小さな計算」が組み合わさっていますよね?

  • x + 2
  • x + 3
  • その2つを掛ける

このような「計算のつながり」を図にしてみると、こうなります:

      x
     / \
  +2    +3
   |     |
   a     b
     \  /
      ×
      |
      y

これが「計算グラフ(Computational Graph)」です。

計算をブロックに分けて、「何をどう計算してできたか」を記録した図です。


🔍 どうしてグラフを記録するの?

この「計算の流れ」がわかっていると、あとで

「出力 y に対して、x はどれくらい影響を与えているのか?」

という 「傾き(=微分・勾配)」 を調べることができるからです。

つまり:

  • 順方向に:値を計算する
  • 逆方向に:影響(勾配)をたどる

という2つの流れがあるんです。


🧠 PyTorchのすごいところ

PyTorchでは、式を書いて計算するだけで、その計算グラフを裏で自動的に作ってくれます!

しかも .backward() を呼ぶと、そのグラフをもとに「勾配」を計算してくれるのです。


🧪 実験してみよう(超シンプルな例)

import torch

# xを定義(勾配を計算したいので requires_grad=True をつける)
x = torch.tensor(1.0, requires_grad=True)

# 式を定義: y = (x + 2) * (x + 3)
a = x + 2
b = x + 3
y = a * b

# y を x で微分(自動で計算!)
y.backward()

# 結果を表示
print("dy/dx =", x.grad)  # 出力: tensor(7.)

📖 実際に紙に書くとこういう流れになります

  1. a = x + 2 → a = 3.0
  2. b = x + 3 → b = 4.0
  3. y = a * b → y = 12.0

そして .backward() を呼ぶと…

PyTorchは、このような逆向きの流れを使って、以下を計算してくれます:

dy/dx = da/dx × dy/da + db/dx × dy/db
      = 1 × b + 1 × a
      = 4 + 3 = 7

こうして dy/dx = 7 という結果が x.grad に入るのです!


💡 「連鎖律」ってなに?

難しそうに聞こえますが、連鎖律はこういうルールです:

「ある関数がいくつもの段階でできているときは、それぞれの段階の“傾き”を掛け合わせれば、全体の“傾き”になる」

つまり:

もし、 y = f(g(x)) ならば、dy/dx = dy/dg × dg/dx

🎨 たとえるなら…

あなたが高い塔を登るとき、階段が5段あったとします。

  • 1段ごとの高さ(=小さな変化)を足し合わせていくと、**全体の高さ(=全体の変化)**がわかりますよね?

逆に言えば、変化の連なりを「積み重ねる」ことで、全体の影響が見える
それが「連鎖律」の本質です。


✅ PyTorchがやっていること

PyTorchは以下のことを完全に自動でやってくれます

  1. 計算の履歴をグラフとして記録
  2. それぞれの演算に必要な“傾きの計算ルール”も保存
  3. .backward() を呼ばれたら、
  4. 連鎖律を使って、逆方向にすべての勾配を計算していく

🔁 簡単な関数でもう一度確認: y = x³

x = torch.tensor(2.0, requires_grad=True)
y = x ** 3
y.backward()
print("dy/dx =", x.grad)  # 出力: 12.0(3x²)
  • この式のグラフはとても単純ですが…
  • PyTorchは x**3 という演算の微分ルール(3x²)をちゃんと持っていて、
  • .backward() を呼ぶと、自動で dy/dx = 12.0 を出してくれます。

📌 まとめ:「どうして backward() で勾配が出せるのか?」

概念説明
計算グラフ式の中で行われた演算のつながりを図で表したもの
順伝播入力 → 出力へ向かう通常の計算の流れ
逆伝播出力 → 入力へ向かって影響をたどる流れ(微分)
連鎖律(Chain Rule)複数の変化を掛け合わせて、全体の変化を求めるルール
.backward()計算グラフを逆にたどって、勾配を自動で求める関数

🔜 次回:「誤差逆伝播」を視覚的に体験しよう

ここまでで、「計算のつながり(計算グラフ)」をたどることで、
**どの変数がどれだけ出力に影響を与えているか(=勾配)**がわかることが見えてきました。

次の章では、実際のニューラルネットの小さな例を使って、
手で「誤差逆伝播」のステップを追ってみる体験をしていきましょう!


7.5 誤差逆伝播を視覚的に体験しよう

前回の 7.4 では、「計算グラフ」と「連鎖律(Chain Rule)」を通して、.backward() が勾配(=変化のしやすさ)をどうやって求めているかを学びました。

今回は、実際のコードと図解を使って、“誤差逆伝播”がどう流れているのかを視覚的に体験してみましょう。


🏗️ まずは簡単な「小さなネットワーク」を作ってみよう

次のような式を考えてみましょう:

x → a = x × 2  
     ↓  
     b = a + 3  
     ↓  
     y = b²

つまり:
y = ((x × 2) + 3)²

この数式を PyTorch で実装し、順伝播(forward pass)と逆伝播(backward pass) の両方を体験してみます。


✏️ PyTorchで実装してみよう(詳細コメント付き)

import torch

# 入力 x を定義(微分の追跡をONにする)
x = torch.tensor(1.0, requires_grad=True)

# 順伝播(Forward pass):値を計算する
a = x * 2           # a = x × 2
b = a + 3           # b = a + 3
y = b ** 2          # y = b²

# 出力を確認
print("出力 y =", y.item())  # item() でスカラー値を取り出す

👁️‍🗨️ 計算グラフ(視覚的イメージ)

PyTorchは、このような「計算のつながり」を自動的に記録しています。

🔷 順方向の流れ(forward pass)

        x = 1.0      ← 入力(スタート)
          │
          ▼
     × 2(掛け算)  ← x を2倍
          │
        a = 2.0
          │
          ▼
     + 3(足し算) ← a に3を足す
          │
        b = 5.0
          │
          ▼
     **2(二乗)   ← b を2乗する
          │
        y = 25.0     ← 出力(ゴール)

🔁 逆方向の流れ(backward pass)

.backward() を呼ぶと、PyTorchは上記の逆順で勾配を計算してくれます。

# 逆伝播:y を x で微分
y.backward()

# 勾配を表示
print("dy/dx =", x.grad)

🔍 どうやって dy/dx を求めたの?

手でたどるとこうなります:

  1. y = b²
     → dy/db = 2b = 2 × 5 = 10
     → bが1増えたら、yは10増える
  2. b = a + 3
     → db/da = 1
     → aが1増えたら、bも1増える
  3. a = x × 2
     → da/dx = 2
     → xが1増えたら、aは2増える

🔗 これを連鎖律でつなげて…

dy/dx = dy/db × db/da × da/dx
       = 10 × 1 × 2
       = 20

つまり、「xが1だけ増えると、yは20増える」
これが x.grad に自動で入っているのです!


🧠 計算グラフとその流れを整理すると:

ステップ計算内容微分(勾配)
x入力1.0
ax × 22.0da/dx = 2
ba + 35.0db/da = 1
y25.0dy/db = 2b = 10
graddy/dx = 10 × 1 × 2 = 20

🎨 たとえるなら…

「坂を転がるボール」のようなイメージです。

  • 最後の結果(y)が坂の下にあるとすると、
  • 坂の形(b²)や高さ(+3)や角度(×2)を逆向きにたどっていくと、
  • 一番上にいたxが、**結果にどれだけ影響したか(傾き=勾配)**がわかります。

📌 誤差逆伝播とは?

誤差逆伝播(Backpropagation) とは、ニューラルネットワークの学習の中核を担う仕組みであり、

出力で生じた「誤差(ズレ)」が、
ネットワーク内部のどの変数や重みによって引き起こされたのかを
逆向きにたどって計算していく方法です。

これはまさに、前のセクションで見た .backward() の動作そのものです。


🔁 なぜ「逆」からたどるのか?

ニューラルネットワークは、入力から出力へとデータを流して予測を出します(これが「順伝播」)。


しかし学習を行うには、「出力の誤差」がどこから生じたのかを探る必要があるため、逆方向にたどる必要があります。

たとえば今回の例では、
出力 y = ((x×2)+3)² にズレ(誤差)があったとしても、
その原因は x にも ×2 にも +3 にも関係しているかもしれません。

誤差逆伝播では、この因果関係を数式で正確にたどり直し
それぞれの変数(や重み)が「誤差にどれだけ貢献していたか(=勾配)」を計算します。


🔗 どうやって勾配を伝えるの? → 連鎖律(Chain Rule)

これは、連鎖律という微分のルールを使って行われます。

出力 y に対する変数 x の勾配を求めるとき、
中間変数を通じて:

dy/dx = dy/db × db/da × da/dx

のように、1つ1つの「変化のしやすさ」を掛け合わせて伝えていくのが誤差逆伝播の基本です。


🎯 何のためにやるの?

こうして得られた「各パラメータの勾配」は、学習の次のステップである パラメータ更新 に使われます。

たとえば:

  • 勾配が正なら、値を少し減らす
  • 勾配が負なら、値を少し増やす

というように、勾配の向きに従って重みを調整していくことで、予測誤差が小さくなるようにネットワークが学習していくのです。


🧠 つまり、誤差逆伝播とは:

「出力の誤差が、ネットワークのどの部分にどれだけ由来しているのか」を、
数学的に丁寧に逆追跡しながら勾配を求め、
その情報を使ってネットワークを賢くしていく方法

これが、深層学習の学習プロセスの土台となっています。


✅ PyTorchがやっていること(まとめ)

処理内容
計算グラフの記録すべての演算を「ノード」として記録する
微分のルール管理各ノードが自分の微分方法を持っている
.backward()出力から入力へ、連鎖律を使って自動で微分
.gradそれぞれの変数に対する「勾配」が格納される

🔜 次回はいよいよ「本物のニューラルネット」へ!

ここまでは、PyTorchの「内部のしくみ」をじっくりと学んできました。
次回はいよいよ、torch.nn を使って、ニューラルネットワークを自分で作るステップに進みます!

  • nn.Module って何?
  • forward() メソッドってどう使うの?
  • 線形モデルはどう定義するの?

7.6 Tensorだけで線形モデルを作って学習してみよう

これまでの章で、PyTorchには「Tensor」という“数のかたまり”があり、それを使って計算したり、自動で勾配(傾き)を計算できる仕組みがあることを学びました。

今回はそれらを使って、**「自分でシンプルな線形モデルを作り、それを学習させる」**ことを体験してみましょう。


🎯 この章で目指すこと

  • PyTorchの基本機能(Tensorと自動微分)だけで、学習を体験する
  • ニューラルネットを使わず、自分で「重みwとバイアスb」を定義する
  • .backward() と勾配を使って、パラメータを少しずつ更新する流れを理解する

🧠 今回つくるモデル: y = wx + b

とてもシンプルな「一次関数の式」です。

  • x:入力データ(たとえば身長など)
  • w:重み(どれだけ影響するか)
  • b:バイアス(切片、つまり調整値)
  • y:予測される出力(たとえば体重など)

この wb を、自動微分と勾配降下法を使って学習で少しずつ調整していくのが目標です。


🧪 実験:データを自分で用意してみよう

import torch

# 入力データ(x)と、正解データ(t)を準備

# 今回は、「y = 2x + 1」という法則に従ったデータを用意します
x = torch.tensor([[1.0], [2.0], [3.0]])   # 入力:3つの値
t = torch.tensor([[3.0], [5.0], [7.0]])   # 正解:それぞれ 2x + 1 の値

ここでは x = 1 のとき y = 3x = 2 のとき y = 5、…という対応関係になっています。


⚙️ 自分で学習するパラメータ(wとb)を定義しよう

# w と b は「学習する変数」です
# requires_grad=True をつけると、後で自動で勾配が計算されるようになります
w = torch.tensor(0.0, requires_grad=True)  # 最初は 0 からスタート
b = torch.tensor(0.0, requires_grad=True)  # バイアスも 0 からスタート

最初は適当な値(ここでは0.0)でよいですが、学習を通じて良い値に近づいていきます。


🔁 学習のしくみ:「勾配降下法」とは?

「学習」とは、予測のズレ(=誤差)を見ながら、wとbをちょっとずつ修正していくことです。

その修正方法として「勾配降下法(gradient descent)」という方法を使います。

新しい w = 今の w - 学習率 × w の勾配

勾配は「この方向に変えると、損失が減りそうですよ〜」という“ヒント”です。
それを使って、wやbを少しずつよくしていくのです。


🏃 学習を実際にやってみよう!

# 学習率(どれくらいの速さで更新するか)
learning_rate = 0.1

# 学習ループ(何度も繰り返して学ぶ)
for epoch in range(100):  # 100回繰り返す

    # 【順伝播】今の w, b で予測を出す
    y = x * w + b

    # 【損失計算】予測と正解のズレ(二乗誤差)を出す
    loss = torch.mean((y - t)**2)  # MSE(平均二乗誤差)

    # 【勾配を初期化】
    w.grad = None  # 前回の勾配を消してから
    b.grad = None

    # 【逆伝播】勾配(∂loss/∂w, ∂loss/∂b)を自動で計算
    loss.backward()

    # 【パラメータの更新】
    with torch.no_grad():  # 勾配追跡をオフにして、安全に更新
        w -= learning_rate * w.grad
        b -= learning_rate * b.grad

    # 【進捗表示】
    if epoch % 10 == 0:
        print(f"Epoch {epoch}: Loss = {loss.item():.4f}, w = {w.item():.2f}, b = {b.item():.2f}")

📊 実行結果の一例

Epoch 0: Loss = 22.6667, w = 1.87, b = 0.70
Epoch 10: Loss = 0.0570, w = 2.01, b = 0.95
Epoch 20: Loss = 0.0023, w = 2.00, b = 0.99
...
Epoch 90: Loss = 0.0000, w = 2.00, b = 1.00

見ての通り、最初は w = 0b = 0 でしたが、学習が進むと w ≈ 2b ≈ 1 に近づいていきます!


✅ 今回の学びのまとめ

ステップやったこと
Tensorの定義wbrequires_grad=True で定義
順伝播y = x*w + b で予測
損失計算(y - t)**2 の平均で誤差を測る
勾配計算loss.backward() で自動微分
パラメータ更新勾配をもとに w, b を更新

🧠 なぜこれが大事なのか?

このようにして、**PyTorchの最小限の機能だけで「AIが学ぶってこういうことか!」**という体験ができます。

この章で学んだことは、これから nn.Module を使ったモデル構築や optimizer.step() を使った高度な手法につながる基本中の基本です。


🔜 次回:nn.Module を使ってもっとスマートにモデルを作ろう!

Tensorだけでモデルを定義すると、柔軟な反面、やや手間がかかります。
次回からは PyTorch の**本格的な「モデル構築の方法」**である nn.Module を使って、より複雑なネットワークを組んでいきます。

👉 続きはこちら → 第8章:ニューラルネットワークをPyTorchで定義してみよう



ご利用規約(免責事項)

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

第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

目次