PyTorch の基本 — 概念をコードで実装するための第一歩
このトピックを終えると
ニューラルネットワークの原理(線形変換、勾配降下法、逆伝播)を、第 2 章から第 4 章で学んだ内容に基づいて、実際の PyTorch コードに実装できるようになります。これは、第 3 段階のツールセクションの始まりです。このエピソードは、第 13 章(Hugging Face / API)および第 14 章(Claude Code / Cursor)の実践演習のための基礎となります。
短いコードスニペットを順に見ていくのではなく、最初から最後まで完全な学習ループを構築することを目指します。第 3 章のシナリオで使用した、細胞タイプ分類器を例として使用します。
PyTorch とは何か?
PyTorch は、Meta AI が開発した深層学習フレームワークです。ニューラルネットワークのトレーニングと推論に必要なコンポーネントのための Python API を提供します。
主なコンポーネントは次の 4 つです。
- Tensor: 多次元配列。NumPy に似ていますが、GPU アクセラレーションと自動微分をサポートします。
- Autograd: 第 4 章の逆伝播を自動化します。バックグラウンドで計算グラフを作成し、
backward()という 1 行のコードで、すべての偏微分を計算します。 - nn.Module: ニューラルネットワークのレイヤーとモデルを定義するためのクラスインターフェイス。
- Optimizer: 第 3 章の Adam や AdamW などの最適化アルゴリズムを実装します。
これらのコンポーネントを組み合わせることで、トレーニングループを作成できます。
インストール: Colab にはすでにインストールされています。ローカルにインストールする場合は、pip install torch を使用します。GPU を使用する場合は、CUDA バージョンに一致するホイールを指定します。
代替手段: TensorFlow、JAX、MLX (Apple)。PyTorch は、現在の研究および実用的なアプリケーションにおける標準です。ほとんどのオープンソースモデル(LLaMA、Mistral、Qwen など)は、PyTorch で配布されています。
Tensor — 第 2 章の線形変換とピクセルのためのコンテナ
Tensor は、実数の多次元配列です。第 2 章で説明したレイヤーの活性化、重み、バイアスはすべてテンソルです。
import torch
# スカラー (0 次元)x = torch.tensor(3.14)
# ベクトル (1 次元)v = torch.tensor([1.0, 2.0, 3.0])
# 行列 (2 次元)m = torch.tensor([[1.0, 2.0], [3.0, 4.0]])
# 3 次元 (例: バッチサイズ 32、シーケンス長 100、埋め込みサイズ 512)t = torch.randn(32, 100, 512)print(t.shape) # torch.Size([32, 100, 512])dtype と device: テンソルには、dtype(データ型)と device(CPU / GPU)があります。
# fp32 CPU (デフォルト)x = torch.randn(1000, 1000)
# fp16 GPUy = torch.randn(1000, 1000, dtype=torch.float16, device="cuda")
# デバイスの移動x_gpu = x.to("cuda")x_cpu = y.to("cpu")GPU 計算ははるかに高速です。ほとんどの実用的なトレーニングは GPU で行われます。
テンソルの演算: NumPy とほぼ同じ API です。
a = torch.randn(3, 4)b = torch.randn(4, 5)
c = a @ b # 行列乗算、形状 (3, 5)d = a + 1 # ブロードキャストe = a.sum(dim=1) # 行に沿った合計、形状 (3,)f = a.mean() # スカラーg = a.relu() # 要素ごとの ReLU第 2 章と第 5 章で説明した活性化関数(ReLU、GELU、Sigmoid など)はすべて、テンソルのメソッドとして提供されます。
Autograd — 第 4 章の逆伝播を 1 行で
テンソルに requires_grad=True をアタッチすると、PyTorch はそのテンソルを偏微分計算のために追跡します。第 4 章の計算グラフが自動的に作成されます。
# パラメータ (偏微分の対象)w = torch.randn(3, requires_grad=True)b = torch.randn(1, requires_grad=True)
# データx = torch.tensor([1.0, 2.0, 3.0])y = torch.tensor(10.0)
# 順伝播y_pred = w @ x + bloss = (y_pred - y) ** 2
# 逆伝播 — すべての偏微分がこの 1 行のコードで計算されますloss.backward()
# 計算された勾配print(w.grad) # ∂loss/∂wprint(b.grad) # ∂loss/∂b第 4 章のすべてのステップがここに含まれています。
- 順伝播 (
y_pred = w @ x + b,loss = ...): 計算グラフが自動的に構築されます。 - 損失の計算 (
(y_pred - y) ** 2)。 - 逆伝播 (
loss.backward()): グラフを逆方向にたどり、すべての偏微分を計算します。 - パラメータの更新: 次に説明します。
重要な規則: .backward() を呼び出した後、パラメータの勾配は累積されます。次のステップの前に、勾配を 0 に初期化する必要があります(後で optimizer.zero_grad() を使用します)。
nn.Module — 第 5 章のモデルの定義
大規模なモデルは、nn.Module クラスを継承して定義されます。第 3 章で使用した細胞タイプ分類器を実際に実装してみましょう。
import torch.nn as nn
class CellTypeClassifier(nn.Module): def __init__(self, n_genes, n_types, hidden=512): super().__init__() self.encoder = nn.Sequential( nn.Linear(n_genes, hidden), nn.ReLU(), nn.Dropout(0.3), nn.Linear(hidden, hidden // 2), nn.ReLU(), nn.Linear(hidden // 2, n_types) )
def forward(self, x): return self.encoder(x)
model = CellTypeClassifier(n_genes=20000, n_types=25)print(sum(p.numel() for p in model.parameters()))# パラメータの数を表示します説明:
__init__: レイヤーを定義します。nn.Linear(in, out)は、y = W·x + bです。第 2 章の全結合層です。nn.Sequential: レイヤーを順番に接続します。nn.ReLU(),nn.Dropout(0.3): 活性化関数と正則化。forward: 順伝播のロジック。PyTorch はこの関数を自動的に呼び出します。
自動パラメータ追跡: nn.Linear 内の重みとバイアスは、自動的に requires_grad=True に設定され、model.parameters() に含まれます。torch.tensor(..., requires_grad=True) を使用して、手動で管理する必要はありません。
呼び出し: モデルは関数のように呼び出されます。
x = torch.randn(32, 20000) # バッチサイズ 32、20000 個の遺伝子logits = model(x) # 自動的に forward が呼び出されますprint(logits.shape) # torch.Size([32, 25])
## 損失関数とオプティマイザ — エピソード3の勾配降下法
エピソード3で学習した損失関数とオプティマイザについてです。
**損失関数:**
```python# 分類問題 - エピソード3 A.1のクロスエントロピーloss_fn = nn.CrossEntropyLoss()
# 回帰問題 - エピソード3 A.1の平均二乗誤差loss_fn = nn.MSELoss()オプティマイザ:
# SGD - エピソード3 A.3optimizer = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.9)
# Adam - エピソード3 A.7optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)
# AdamW - エピソード3 A.8 (LLMの学習で標準)optimizer = torch.optim.AdamW(model.parameters(), lr=1e-3, weight_decay=0.01)学習率スケジューラ:
scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=100)各エポックの最後にscheduler.step()を呼び出して、学習率を自動的に減少させます。
学習ループ — すべてを組み合わせる
次に、すべての要素を組み合わせて、完全な学習ループを作成します。
import torchimport torch.nn as nnfrom torch.utils.data import DataLoader, TensorDataset
# データを準備(ダミー例)n_samples = 10000n_genes = 20000n_types = 25
X = torch.randn(n_samples, n_genes)y = torch.randint(0, n_types, (n_samples,))
dataset = TensorDataset(X, y)train_loader = DataLoader(dataset, batch_size=128, shuffle=True)
# モデルmodel = CellTypeClassifier(n_genes, n_types)model = model.to("cuda")
# 損失関数とオプティマイザloss_fn = nn.CrossEntropyLoss()optimizer = torch.optim.AdamW(model.parameters(), lr=1e-3, weight_decay=0.01)
# 学習ループn_epochs = 10for epoch in range(n_epochs): model.train() total_loss = 0.0 for batch_x, batch_y in train_loader: batch_x = batch_x.to("cuda") batch_y = batch_y.to("cuda") # 順伝播 (パート#2) logits = model(batch_x) loss = loss_fn(logits, batch_y) # 逆伝播 (パート#4) optimizer.zero_grad() # 前のステップからの勾配を初期化 loss.backward() # Autograd # パラメータの更新 (パート#3) optimizer.step() total_loss += loss.item() print(f"エポック {epoch}: 損失 = {total_loss / len(train_loader):.4f}")このループは、パート#3の図と完全に対応しています。
パラメータをランダムに初期化 ← モデルの作成時に自動的に行われます
繰り返します(数千回から数百万回):
ミニバッチをランダムに選択 ← DataLoader
ミニバッチに対して順伝播を実行 ← logits = model(batch_x)
予測と正解に基づいてコストを計算 ← loss = loss_fn(logits, batch_y)
逆伝播を実行 → 勾配を計算 ← loss.backward()
オプティマイザを使用してパラメータを更新 ← optimizer.step()PyTorchは、パート#2から#4の原則をAPIとして公開しています。
データセットとDataLoader — パート#3のミニバッチ
パート#3では、ミニバッチSGDが必要な理由について説明しました。PyTorchは、Dataset + DataLoaderを使用してこれをサポートします。
データセット: __len__と__getitem__を定義するクラスです。
from torch.utils.data import Dataset
class ScRNADataset(Dataset): def __init__(self, expression_matrix, labels): self.expr = expression_matrix self.labels = labels def __len__(self): return len(self.labels) def __getitem__(self, idx): return self.expr[idx], self.labels[idx]DataLoader: バッチ処理、シャッフル、並列読み込みを行います。
train_loader = DataLoader( dataset, batch_size=128, shuffle=True, # 各エポックでデータの順序をシャッフル num_workers=4, # 並列読み込み(CPUコアを使用) pin_memory=True, # GPUへの転送を高速化 drop_last=True # 最後の不完全なバッチを破棄)生物学的実践: scRNA-seq、画像、シーケンスデータなど、各ドメインに固有のカスタムデータセットを実装します。Scanpy、AnnData、PyTorch Geometricは、ドメイン固有のラッパーを提供します。
GPUの利用 — 高速な学習
GPUは、CPUよりも数十倍から数百倍高速です。ほとんどの実用的な学習は、GPUで行われます。
# 利用可能なGPUを確認print(torch.cuda.is_available())print(torch.cuda.device_count())
# テンソルとモデルを移動device = "cuda" if torch.cuda.is_available() else "cpu"model = model.to(device)batch_x = batch_x.to(device)マルチGPU: 大規模なモデルとデータセットには、複数のGPUを使用します。
- DataParallel (DP): 単一ノード上の複数のGPU。シンプルですが、ボトルネックになる可能性があります。
- DistributedDataParallel (DDP): 標準。各GPUが個別のプロセスであり、勾配のオールリデュースを行います。
- Fully Sharded Data Parallel (FSDP): パラメータとオプティマイザの状態も分散されます。70B+モデルの学習に不可欠です。
Apple Silicon: mpsデバイスをサポートします。device = "mps"。個人用ラップトップでの小規模な実験を可能にします。
混合精度 — 学習時間とメモリ使用量の削減
FP32の代わりにFP16またはBF16を使用して計算することで、学習時間とメモリ使用量を削減します。
from torch.cuda.amp import autocast, GradScaler
scaler = GradScaler()
for batch_x, batch_y in train_loader: optimizer.zero_grad() with autocast(dtype=torch.bfloat16): logits = model(batch_x) loss = loss_fn(logits, batch_y) scaler.scale(loss).backward() scaler.step(optimizer) scaler.update()BF16とFP16:
- FP16: 半精度。オーバーフローを避けるためには、GradScalerが必要です。
- BF16 (bfloat16): FP32と同じ指数範囲を持ちます。オーバーフローのリスクが低くなります。A100およびH100シリーズのGPUでサポートされています。実際に標準になりつつあります。
効果: 学習時間が2〜4倍高速になり、メモリ使用量が半分になります。精度の低下は最小限です。
保存と読み込み — チェックポイント
学習の中間状態と最終状態を保存します。
# 保存torch.save({ "epoch": epoch, "model_state_dict": model.state_dict(), "optimizer_state_dict": optimizer.state_dict(), "loss": total_loss,}, "checkpoint.pt")
# 読み込みcheckpoint = torch.load("checkpoint.pt")model.load_state_dict(checkpoint["model_state_dict"])optimizer.load_state_dict(checkpoint["optimizer_state_dict"])start_epoch = checkpoint["epoch"] + 1学習に数日かかる場合は、クラッシュや停電に備えて、定期的にチェックポイントを保存するようにしてください。
推論 — 学習済みモデルの使用
学習済みモデルを使って予測を行います。
model.eval() # ドロップアウトとバッチ正規化を推論モードに設定with torch.no_grad(): # 勾配計算を無効にする(メモリを節約し、計算を高速化) for batch_x, _ in test_loader: batch_x = batch_x.to(device) logits = model(batch_x) preds = logits.argmax(dim=-1) # ... preds を使用no_grad は、Autograd が計算グラフを作成するのを防ぎ、メモリを節約し、速度を向上させます。
デプロイ: 学習済みモデルを本番環境にデプロイするには、いくつかの方法があります。
- TorchScript: PyTorch モデルを JIT コンパイルして、C++ やモバイルにデプロイします。
- ONNX: フレームワークに依存しない形式。TensorRT や OpenVINO で最適化します。
- HuggingFace Hub: モデルを共有および提供します。
- vLLM、TGI、SGLang: LLM の提供に特化したフレームワークです。
生物学的実践のヒント
データの読み込み
scRNA-seq: scanpy + AnnData → PyTorch データセットラッパーを使用します。
タンパク質配列: biopython を使って解析し、データセットを作成します。
画像 (病理/顕微鏡): torchvision.datasets.ImageFolder を利用し、拡張を適用します。
学習の最適化
- 勾配累積: GPU メモリが不足している場合に、大きなバッチをシミュレートします。
- 勾配チェックポイント: 活性化を再計算することで、メモリを節約します (パート #4 A.7)。
- DeepSpeed/FSDP: 大規模なモデルを学習します。
実験管理
- Weights & Biases (wandb): 実験のロギングと可視化。
- MLflow: オープンソースの実験追跡。
- Lightning: 学習ループの冗長なコードを削減します。
PyTorch Lightning: 学習ループの反復的なコードを隠蔽するラッパーです。
import pytorch_lightning as pl
class CellClassifierLightning(pl.LightningModule): def __init__(self, model, lr=1e-3): super().__init__() self.model = model self.lr = lr self.loss_fn = nn.CrossEntropyLoss() def training_step(self, batch, batch_idx): x, y = batch logits = self.model(x) loss = self.loss_fn(logits, y) self.log("train_loss", loss) return loss def configure_optimizers(self): return torch.optim.AdamW(self.parameters(), lr=self.lr)
trainer = pl.Trainer(max_epochs=10, accelerator="gpu", devices=1)trainer.fit(model_lightning, train_loader)自動的にマルチ GPU、混合精度、チェックポイントを処理します。本番環境での学習に便利です。
生物学的応用シナリオ
シナリオ 1 — 実践的な細胞タイプ分類器
パート #3 のシナリオ 1 を完全に実装します。AnnData → PyTorch データセット → 学習。Scanpy チュートリアルを参照してください。
シナリオ 2 — タンパク質接触予測(ミニ AlphaFold)
タンパク質配列 → アテンションベースのニューラルネットワーク → アミノ酸ペアの接触確率。パート #5 および #6 の概念を実践します。小さなデータセット (例: CATH) で学習できます。
シナリオ 3 — 組織病理画像分類
パート #2 および #6 の病理スライドのシナリオを実装します。ResNet-50 のファインチューニング。Torchvision.datasets を使用します。
主要なまとめ
- PyTorch の 4 つのコンポーネント: Tensor、Autograd、nn.Module、Optimizer。パート #2 から #4 までの原則は、API で直接適用されます。
- 学習ループの 5 つのステップ: forward、loss、backward、zero_grad、step。パート #3 の図に示すとおりです。
- バッチ処理には DataLoader を使用し、GPU を利用するには
to(device)を使用します。 - 混合精度 (BF16) を使用して、学習を 2 ~ 4 倍に高速化します。
- チェックポイント を定期的に保存します。
- マルチ GPU (DDP/FSDP) を使用して、大規模なモデルを学習します。
- Lightning や wandb などのツールを使用して、実用的な利便性を高めます。
📐 付録 — 専門家向けの実践的なヒントと公式
難易度: 非常に難しい 対象読者: PyTorch のトレーニングに関する高度な最適化とシステムレベルの理解を求める読者。
A.1 Autograd の内部メカニズム
requires_grad=True を持つテンソルが演算で使用されると、各演算はグラフノードとして記録されます。
関数クラス: 各演算(加算、乗算、行列乗算など)は、torch.autograd.Function のサブクラスであり、forward と backward という静的メソッドを定義します。
カスタム Autograd:
class MyReLU(torch.autograd.Function): @staticmethod def forward(ctx, x): ctx.save_for_backward(x) return x.clamp(min=0)
@staticmethod def backward(ctx, grad_output): x, = ctx.saved_tensors grad_input = grad_output.clone() grad_input[x < 0] = 0 return grad_inputctx.save_for_backward は、逆伝播に必要なテンソルを保存します。セクション 4 で説明されている活性化値の保存は、このメカニズムを利用しています。
A.2 勾配チェックポイント
セクション 4、A.7 を参照してください。 torch.utils.checkpoint。
from torch.utils.checkpoint import checkpoint
class MyBlock(nn.Module): def forward(self, x): # この関数の活性化は保存されず、逆伝播中に再計算されます。 return checkpoint(self._forward, x, use_reentrant=False)
def _forward(self, x): # 実際の計算 return self.layers(x)メモリ使用量は O(√L) です。実行時間は 1.5 倍に増加します。大規模モデルに不可欠です。
A.3 混合精度演算の数値的な側面
FP32: 指数部 8、仮数部 23。範囲: 約 1e-38 から 1e+38、精度: 約 7 桁。
FP16: 指数部 5、仮数部 10。範囲: 約 6e-5 から 6.5e+4、精度: 約 3 桁。 オーバーフローのリスクがあります。
BF16: 指数部 8、仮数部 7。範囲: FP32 と同じ、精度: 約 2 桁。 オーバーフローに対して安全です。
損失スケーリング (FP16 に必要):
scaled_loss = loss * scale_factor
scaled_loss.backward() # 勾配は scale_factor で乗算されます。
# 最適化ステップの前に勾配をスケール解除します。損失スケーリングは BF16 には必要ありません。
A.4 DDP 同期
全減少勾配同期: 各 GPU は、ミニバッチの勾配を計算し、次にすべての GPU が勾配の平均を計算します。
時間計算量: O(P / bandwidth)。ここで、P はパラメータの数です。
NCCL バックエンド: NVIDIA の最適化されたライブラリで、InfiniBand と NVLink を使用して GPU 間の通信を行います。
バケット化: 勾配をチャンクにグループ化して、通信と計算をオーバーラップさせます。 torch.nn.parallel.DistributedDataParallel はこれを自動的に行います。
A.5 FSDP (完全シャーディング データ並列)
パラメータと最適化器の状態は、GPU にシャーディングされます。
from torch.distributed.fsdp import FullyShardedDataParallel as FSDP
model = FSDP(model, sharding_strategy=ShardingStrategy.FULL_SHARD)メモリ削減: 各 GPU はパラメータの 1/N のみを保存します。700 億パラメータのモデルは、8 つの 40GB GPU に収まります。
通信オーバーヘッド: パラメータは、順伝播と逆伝播中に収集および解放されます。NCCL の最適化が不可欠です。
A.6 カスタム CUDA カーネル
一部の演算は、PyTorch で最適に実装されていません。Triton または CUDA C を使用して、カスタムカーネルを記述できます。
Flash Attention 2: セクション 6、A.8 のオンラインソフトマックスは、Triton と CUDA で実装されています。
from flash_attn import flash_attn_func
output = flash_attn_func(q, k, v, causal=True)nn.functional.scaled_dot_product_attention よりも 2 ~ 4 倍高速です。
最近のトレンド: torch.compile (PyTorch 2.0) は、JIT コンパイルによる自動最適化を行います。 model = torch.compile(model)。ほとんどの場合、パフォーマンスを 2 倍以上に向上させます。
A.7 プロファイリング
PyTorch プロファイラー:
with torch.profiler.profile( activities=[torch.profiler.ProfilerActivity.CPU, torch.profiler.ProfilerActivity.CUDA], schedule=torch.profiler.schedule(wait=1, warmup=1, active=3), on_trace_ready=torch.profiler.tensorboard_trace_handler("./log")) as prof: for step, batch in enumerate(train_loader): # ... トレーニングステップ prof.step()TensorBoard で可視化します。ボトルネック (遅いカーネル、CPU-GPU 転送、データロード) を特定します。
A.8 再現可能性
同じ実験を繰り返した場合でも、常に同じ結果が得られるようにします。
import torchimport randomimport numpy as np
def set_seed(seed): torch.manual_seed(seed) torch.cuda.manual_seed_all(seed) random.seed(seed) np.random.seed(seed) torch.backends.cudnn.deterministic = True torch.backends.cudnn.benchmark = False完全な再現性は困難です (一部の CUDA 演算は非決定論的です)。実際には、シードを固定し、ハードウェアと PyTorch のバージョンを記録します。
A.9 デバッグのヒント
NaN 検出:
torch.autograd.set_detect_anomaly(True)逆伝播中に NaN が発生した場合、これは NaN が発生した演算にトレースバックします。
勾配クリッピング (セクション 4、A.8):
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)loss.backward() の後、optimizer.step() の前に適用します。
バッチサイズファインダー (Lightning): 自動的に可能な最大バッチサイズを検索します。
A.10 最新のトレンド
torch.compile (PyTorch 2.0 以降): JIT コンパイルによる自動最適化。
torch.export: モデルをグラフに抽出します (ONNX、TensorRT、モバイルへのデプロイ)。
AOT autograd: コンパイル時に逆伝播グラフを生成します。
ネストされたテンソル: 可変長のシーケンスをパディングなしで処理します。
FunctorchModule → torch.func: JAX スタイルの関数型 API。
これらのツールは、最新の大規模モデルのトレーニングと提供の標準になりつつあります。
参考文献
このセクションで紹介したすべてのコンテンツ、シナリオ、アナロジー、および数式は、BioPlaygroundによって社内で開発されました。以下は、概念学習に役立つ外部リソースです。
- PyTorch 公式ドキュメント: pytorch.org/docs
- PyTorch 60分チュートリアル: pytorch.org/tutorials/beginner/deep_learning_60min_blitz.html
- PyTorch Lightning: lightning.ai
- HuggingFace Transformers: huggingface.co/docs/transformers
- Flash Attention: github.com/Dao-AILab/flash-attention
- DeepSpeed: deepspeed.ai
- PyTorch Profiler: pytorch.org/tutorials/recipes/recipes/profiler_recipe.html
- Scanpy: scanpy.readthedocs.io — scRNA-seqの標準ライブラリ
- PyTorch Geometric: pyg.org — グラフニューラルネットワーク(分子、タンパク質)
- TorchVision: pytorch.org/vision — 画像ドメイン
セクション12では、PyTorchの基本事項のレビューが完了します。セクション13では、HuggingFaceモデルハブとOpenAI/Anthropic APIの使用方法について説明します。
次のテーマ
- 編 #13
huggingface-and-openai— Hugging Face Model Hubと商用APIの活用。 - 編 #14
claude-code-and-cursor— AIコーディングエージェントの実践。 - 編 #15
bio-ai-integration— すべてをバイオパイプラインに統合。