PyTorchを学び始めたとき、テンソルの形状変換やバックプロパゲーションの仕組みがどうにもイメージしにくかったんですよね。数式だけだとピンとこない。でも、テンソルを「箱」や「立体ブロック」として視覚的に捉えると、一気に理解が進みました。この記事では、PyTorchの基本をビジュアルに解説していきます。

テンソルとは「多次元の数値の箱」

PyTorchの中心にあるのがテンソル(Tensor)です。NumPyのndarrayに似ていますが、GPU上で計算できることと、自動微分の仕組みを持っている点が異なります。

  • スカラー: 0次元テンソル(ただの数値、例: 3.14)
  • ベクトル: 1次元テンソル(数値の列、例: [1, 2, 3])
  • 行列: 2次元テンソル(数値の表、例: 3×4の表)
  • 3次元以上: カラー画像(高さ×幅×RGB)やバッチ処理(バッチ数×高さ×幅×チャンネル)

視覚的に捉えると、1次元は線、2次元は面、3次元は立方体、4次元は立方体が並んだ棚、というイメージが分かりやすいかもしれません。

テンソル操作を図で理解する

PyTorchで頻繁に使うテンソル操作には、reshape、squeeze、unsqueeze、transposeなどがあります。これらは「箱の形を変える」操作として捉えると直感的です。

import torch

# 12個の要素を持つ1次元テンソル
x = torch.arange(12)          # [0,1,2,...,11]

# 3×4の行列に変形
y = x.reshape(3, 4)           # 3行4列

# 2×2×3の立方体に変形
z = x.reshape(2, 2, 3)        # 同じ12個の数値、形だけ変わる

# 次元を追加 (unsqueeze)
a = y.unsqueeze(0)             # shape: 1×3×4(バッチ次元を追加)

# 次元を削除 (squeeze)
b = a.squeeze(0)               # shape: 3×4に戻る

reshapeは「中身はそのまま、箱の形だけ変える」操作です。12個のブロックを1列に並べるか、3行4列に並べるか、2段×2列×3個に積むか。データは同じで、見方だけが変わるんですよね。

自動微分(Autograd)の仕組み

PyTorchの最大の特徴が自動微分です。requires_grad=Trueを指定したテンソルで計算を行うと、PyTorchが内部で計算グラフを構築します。これは「どの計算をどの順番で行ったか」の記録ですね。

x = torch.tensor([2.0, 3.0], requires_grad=True)
y = x * 3          # 各要素を3倍
z = y.sum()         # 合計
z.backward()        # 逆伝播
print(x.grad)       # tensor([3., 3.]) — zをxで微分した結果

ビジュアルに考えると、計算グラフは木構造のようなものです。根っこが最終的な損失値で、枝が各演算、葉がパラメータ。backward()を呼ぶと、根から葉に向かって勾配が流れていく。この仕組みのおかげで、複雑なニューラルネットワークでも効率的にパラメータを更新できるわけです。

MicroGPTの可視化ツールのように、内部動作を目で見て理解できると学習効率が格段に上がります。

ニューラルネットワークの構築

PyTorchでニューラルネットワークを作るには、torch.nn.Moduleを継承したクラスを定義します。各層(レイヤー)は「入力テンソルを受け取って、変換して、出力テンソルを返す箱」として考えると分かりやすいです。

import torch.nn as nn

class SimpleNet(nn.Module):
    def __init__(self):
        super().__init__()
        self.layer1 = nn.Linear(784, 128)   # 784入力 → 128出力
        self.relu = nn.ReLU()                # 活性化関数
        self.layer2 = nn.Linear(128, 10)     # 128入力 → 10出力

    def forward(self, x):
        x = self.layer1(x)    # 線形変換
        x = self.relu(x)      # 非線形変換(負の値を0に)
        x = self.layer2(x)    # 最終出力
        return x

このネットワークを図にすると、784個のノードが128個のノードに全結合し、さらに10個のノードに接続される3層構造になります。PyTorch公式のnnモジュールドキュメントには、もっと多くの層の種類が載っています。

学習ループを理解する

PyTorchの学習ループは、以下の4ステップの繰り返しです。

  1. 順伝播(Forward): 入力データをネットワークに通して予測を得る
  2. 損失計算(Loss): 予測と正解の差を数値化する
  3. 逆伝播(Backward): 損失に対する各パラメータの勾配を計算する
  4. 更新(Update): 勾配の方向にパラメータを少し動かす
model = SimpleNet()
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
criterion = nn.CrossEntropyLoss()

for epoch in range(10):
    for inputs, labels in dataloader:
        optimizer.zero_grad()          # 勾配リセット
        outputs = model(inputs)        # 1. 順伝播
        loss = criterion(outputs, labels)  # 2. 損失計算
        loss.backward()                # 3. 逆伝播
        optimizer.step()               # 4. パラメータ更新

この4ステップを何千回も繰り返すことで、モデルが徐々にデータのパターンを学んでいきます。Qwen3.5のようなマルチモーダルAIも、基本的にはこの学習ループの延長線上にあるわけです。

GPU活用のポイント

PyTorchでGPUを使うのは驚くほど簡単です。テンソルとモデルを.to('cuda')でGPUに送るだけで、計算が自動的にGPU上で実行されます。

device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = SimpleNet().to(device)
inputs = inputs.to(device)

注意点として、テンソルとモデルが同じデバイス上にないとエラーになります。「CPUのテンソルとGPUのモデルは混ぜられない」ということですね。LLMエージェントのコスト問題でも触れましたが、GPU利用は計算速度と引き換えにコストやリソース管理の課題が出てきます。

まとめ

PyTorchの学習は、テンソルを「形のある箱」として、計算グラフを「木構造」として、ニューラルネットワークを「変換の連鎖」として捉えると、ぐっと理解しやすくなります。数式が苦手でも、視覚的なイメージがあれば十分に入門できるはずです。

まずはPyTorch公式チュートリアルで手を動かしてみることをお勧めします。コードを書きながら、テンソルの形が変わっていく様子をprint(tensor.shape)で確認していくと、感覚が掴めてくると思います。