Skip to content
Published on

AI のための数学完全ガイド — 線形代数から情報理論まで

Authors
  • Name
    Twitter
Math for AI

はじめに

「AIを勉強するには数学をどこまでやればいいの?」

答え:線形代数 + 微積分 + 確率/統計 + 最適化。この4つがあれば論文の90%を読むことができます。

この記事は数学の教科書ではありません。AIでなぜこの数学が使われるのかをコードと直感で説明します。nanoGPTをゼロから作るとき、画像生成モデル(DDPM)を学習するとき — この数学がどこで登場するかを紐づけます。

Part 1:線形代数(Linear Algebra) — AIの骨格

なぜ必要か?

ニューラルネットワークのすべての演算は行列の掛け算です。

import numpy as np

# ニューロン1つ = ベクトルの内積
weights = np.array([0.5, -0.3, 0.8])  # 重み
inputs = np.array([1.0, 2.0, 3.0])     # 入力
bias = 0.1

output = np.dot(weights, inputs) + bias
# 0.5*1.0 + (-0.3)*2.0 + 0.8*3.0 + 0.1 = 2.5

# レイヤー全体 = 行列の掛け算
W = np.random.randn(4, 3)  # 3 → 4 ニューロン
x = np.random.randn(3)      # 入力ベクトル
h = W @ x                    # 行列-ベクトル積 = レイヤー出力

ベクトル(Vector) — データの表現

# 単語をベクトルで表現(Word Embedding)
king = np.array([0.8, 0.2, 0.9, -0.5])
queen = np.array([0.7, 0.8, 0.85, -0.4])
man = np.array([0.9, 0.1, 0.5, -0.6])
woman = np.array([0.8, 0.7, 0.45, -0.5])

# king - man + woman ≈ queen(有名な関係!)
result = king - man + woman
print(f"king - man + woman = {result}")
print(f"queen              = {queen}")
# ほぼ同じ!

# コサイン類似度 — 2つのベクトルがどれくらい似ているか
def cosine_similarity(a, b):
    return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))

print(cosine_similarity(result, queen))  # ~0.95(非常に類似!)

行列の掛け算(Matrix Multiplication) — ニューラルネットワークの核心

# TransformerのSelf-Attentionも行列の掛け算!
# Q, K, V = 入力に重み行列を掛けたもの
batch_size, seq_len, d_model = 2, 10, 64

X = np.random.randn(batch_size, seq_len, d_model)
W_Q = np.random.randn(d_model, d_model)
W_K = np.random.randn(d_model, d_model)
W_V = np.random.randn(d_model, d_model)

Q = X @ W_Q  # Query: (2, 10, 64)
K = X @ W_K  # Key:   (2, 10, 64)
V = X @ W_V  # Value: (2, 10, 64)

# Attention Score = Q × K^T / √d
scores = Q @ K.transpose(0, 2, 1) / np.sqrt(d_model)
# scores shape: (2, 10, 10) — 各トークンが他のトークンに与えるattention

固有値分解(Eigenvalue Decomposition) — PCA、SVD

# PCA:データの主要方向を見つける
from sklearn.decomposition import PCA

# 100次元データ → 2次元に圧縮
data = np.random.randn(1000, 100)
pca = PCA(n_components=2)
reduced = pca.fit_transform(data)

# 内部で起きていること:
# 1. 共分散行列の計算:C = X^T X / n
# 2. 固有値分解:C = V Λ V^T
# 3. 最大の固有値に対応する固有ベクトルを選択
# → データの分散が最も大きい方向!
# SVD(特異値分解) — LLMの圧縮に使用!
# LoRAがまさにこれ:大きな行列を小さな行列2つに分解

W = np.random.randn(768, 768)  # GPT-2のattention weight

# SVD: W = U × Σ × V^T
U, S, Vt = np.linalg.svd(W)

# 上位r個だけ残して近似(LoRAの原理!)
r = 16  # rank
W_approx = U[:, :r] @ np.diag(S[:r]) @ Vt[:r, :]

# 元:768×768 = 589,824 パラメータ
# LoRA:768×16 + 16×768 = 24,576 パラメータ(たった4%!)
error = np.linalg.norm(W - W_approx) / np.linalg.norm(W)
print(f"Rank-{r} 近似誤差: {error:.4f}")

Part 2:微積分(Calculus) — 学習のエンジン

なぜ必要か?

ニューラルネットワークの学習 = 損失関数の最小化 = 微分で勾配を求めてパラメータを更新

偏微分(Partial Derivative)

# f(x, y) = x² + 2xy + y²
# ∂f/∂x = 2x + 2y(yを定数として扱う)
# ∂f/∂y = 2x + 2y(xを定数として扱う)

def f(x, y):
    return x**2 + 2*x*y + y**2

def df_dx(x, y):
    return 2*x + 2*y  # x方向の勾配

def df_dy(x, y):
    return 2*x + 2*y  # y方向の勾配

# 勾配ベクトル(Gradient)
gradient = np.array([df_dx(3, 2), df_dy(3, 2)])
print(f"∇f(3,2) = {gradient}")  # [10, 10]
# → この方向の反対に進めば関数値が減少する!

連鎖律(Chain Rule) — 逆伝播の数学的基礎!

# y = f(g(x)) → dy/dx = df/dg × dg/dx

# ニューラルネットワークでは:
# Loss = CrossEntropy(softmax(Wx + b), target)
# dLoss/dW = dLoss/dsoftmax × dsoftmax/d(Wx+b) × d(Wx+b)/dW

# PyTorchがこれを自動で行う!
import torch

x = torch.tensor([1.0, 2.0, 3.0], requires_grad=True)
W = torch.randn(2, 3, requires_grad=True)
b = torch.randn(2, requires_grad=True)

# Forward
h = W @ x + b
loss = h.sum()

# Backward(連鎖律が自動適用!)
loss.backward()

print(f"dLoss/dW = {W.grad}")  # 自動微分!
print(f"dLoss/db = {b.grad}")
print(f"dLoss/dx = {x.grad}")

勾配降下法(Gradient Descent) — 山を下りる目隠し登山家

# 損失関数:L(w) = (w - 3)²
# 最小値:w = 3

def loss(w):
    return (w - 3) ** 2

def grad(w):
    return 2 * (w - 3)

# 勾配降下法
w = 10.0  # 開始点(見当違いの場所)
lr = 0.1  # 学習率

for step in range(20):
    g = grad(w)
    w = w - lr * g  # 勾配の反対方向に移動!
    if step % 5 == 0:
        print(f"Step {step}: w={w:.4f}, loss={loss(w):.4f}")

# Step 0:  w=8.6000, loss=31.3600
# Step 5:  w=3.2150, loss=0.0462
# Step 10: w=3.0070, loss=0.0000
# Step 15: w=3.0002, loss=0.0000
# → wが3に収束!

学習率の重要性

lrが大きすぎると:
  w: 10-418-22 → 発散!

lrが小さすぎると:
  w: 109.869.72...100万ステップ後 → 3.001

適切なlr:
  w: 108.67.48...20ステップ → 3.0002

実践オプティマイザ:Adam

# Adam = Momentum + RMSprop(現代のディープラーニング標準)
class Adam:
    def __init__(self, params, lr=0.001, beta1=0.9, beta2=0.999, eps=1e-8):
        self.lr = lr
        self.beta1 = beta1  # モメンタム(慣性)
        self.beta2 = beta2  # 勾配の二乗の移動平均
        self.eps = eps
        self.m = {id(p): 0 for p in params}  # 1次モーメント
        self.v = {id(p): 0 for p in params}  # 2次モーメント
        self.t = 0

    def step(self, params, grads):
        self.t += 1
        for p, g in zip(params, grads):
            pid = id(p)
            # モメンタム:以前の勾配方向を記憶
            self.m[pid] = self.beta1 * self.m[pid] + (1 - self.beta1) * g
            # 適応的学習率:勾配の大きさに応じて調整
            self.v[pid] = self.beta2 * self.v[pid] + (1 - self.beta2) * g**2
            # バイアス補正
            m_hat = self.m[pid] / (1 - self.beta1**self.t)
            v_hat = self.v[pid] / (1 - self.beta2**self.t)
            # 更新
            p -= self.lr * m_hat / (np.sqrt(v_hat) + self.eps)

Part 3:確率と統計 — AIの言語

なぜ必要か?

AIモデルの出力はほぼ常に確率分布です。

# GPTの出力 = 次のトークンの確率分布
logits = np.array([2.0, 1.0, 0.1, -1.0, 3.0])  # モデル出力(生の値)
vocab = ["the", "cat", "sat", "on", "mat"]

# Softmax:logits → 確率
def softmax(x):
    exp_x = np.exp(x - np.max(x))  # 数値安定性
    return exp_x / exp_x.sum()

probs = softmax(logits)
for word, p in zip(vocab, probs):
    print(f"  {word}: {p:.4f}")
# the: 0.2312, cat: 0.0851, sat: 0.0346, on: 0.0115, mat: 0.6276
# → "mat"が最も高い確率!

ベイズの定理(Bayes' Theorem)

# P(A|B) = P(B|A) × P(A) / P(B)
# 「データを見たときにモデルが正しい確率」

# 例:スパムフィルター
# P(スパム|"無料") = P("無料"|スパム) × P(スパム) / P("無料")
p_free_given_spam = 0.8    # スパムで「無料」が出現する確率
p_spam = 0.3               # 全メール中のスパム割合
p_free = 0.35              # 全メールで「無料」が出現する確率

p_spam_given_free = (p_free_given_spam * p_spam) / p_free
print(f"P(スパム|'無料') = {p_spam_given_free:.2f}")  # 0.69 (69%!)

確率分布

# ガウス(正規)分布 — Diffusion Modelの核心!
def gaussian(x, mu=0, sigma=1):
    return (1 / (sigma * np.sqrt(2 * np.pi))) * np.exp(-0.5 * ((x - mu) / sigma) ** 2)

# DDPM(画像生成):
# Forward:きれいな画像 → ガウスノイズ追加(徐々に破壊)
# Reverse:ノイズ → ノイズ除去(ニューラルネットが学習) → きれいな画像!

# ノイズ追加プロセス
def add_noise(image, t, noise_schedule):
    """x_t = √(α_bar_t) × x_0 + √(1 - α_bar_t) × ε"""
    alpha_bar = noise_schedule[t]
    noise = np.random.randn(*image.shape)  # ガウスノイズ
    noisy = np.sqrt(alpha_bar) * image + np.sqrt(1 - alpha_bar) * noise
    return noisy, noise

クロスエントロピー(Cross-Entropy) — 損失関数の王様

# モデルの予測が正解とどれだけ違うかを測定
def cross_entropy(predictions, targets):
    """H(p, q) = -Σ p(x) log q(x)"""
    return -np.sum(targets * np.log(predictions + 1e-9))

# 正解:"cat"(ワンホットエンコーディング)
target = np.array([0, 1, 0, 0, 0])  # [the, cat, sat, on, mat]

# 良い予測
good_pred = np.array([0.05, 0.85, 0.03, 0.02, 0.05])
print(f"Good: {cross_entropy(good_pred, target):.4f}")  # 0.1625(低い)

# 悪い予測
bad_pred = np.array([0.3, 0.1, 0.2, 0.2, 0.2])
print(f"Bad:  {cross_entropy(bad_pred, target):.4f}")  # 2.3026(高い)

Part 4:情報理論 — LLMの数学的基礎

エントロピー(Entropy) — 不確実性の尺度

def entropy(probs):
    """H(X) = -Σ p(x) log₂ p(x)"""
    return -np.sum(probs * np.log2(probs + 1e-9))

# 公正なコイン:H = 1 bit(最大の不確実性)
fair_coin = np.array([0.5, 0.5])
print(f"公正なコイン: {entropy(fair_coin):.2f} bits")  # 1.00

# 偏ったコイン:H は 1 bit 未満(予測可能)
biased_coin = np.array([0.9, 0.1])
print(f"偏ったコイン: {entropy(biased_coin):.2f} bits")  # 0.47

# GPTの出力エントロピーが低ければ → モデルが確信している
# Temperatureを上げれば → エントロピー増加 → より多様な出力

KL Divergence — 2つの分布の差

def kl_divergence(p, q):
    """D_KL(P || Q) = Σ p(x) log(p(x) / q(x))"""
    return np.sum(p * np.log(p / (q + 1e-9) + 1e-9))

# VAE(Variational Autoencoder)では:
# KL(q(z|x) || p(z))を最小化
# = エンコーダの出力分布を標準正規分布に近づける!

# RLHFでは:
# KL(π_new || π_ref)をペナルティとして追加
# = 新しいモデルが元のモデルから離れすぎないように!

数学 → AI マッピング要約

数学概念AIでの役割登場する場所
行列の掛け算レイヤー演算すべてのニューラルネット
コサイン類似度埋め込み比較検索、RAG
SVDモデル圧縮LoRA、量子化
偏微分勾配計算逆伝播
連鎖律自動微分PyTorch autograd
勾配降下法パラメータ最適化Adam、SGD
Softmax確率分布変換分類、Attention
クロスエントロピー損失関数LLM、分類器
ガウス分布ノイズモデリングDDPM、VAE
ベイズの定理事後確率推論ベイジアンML
KL Divergence分布の差の測定VAE、RLHF
エントロピー不確実性の測定Temperature、情報量

学習ロードマップ

[1週目] 線形代数の基礎
  → ベクトル、行列の掛け算、転置、逆行列
  → numpyで直接実装

[2週目] 微積分 + 最適化
  → 偏微分、連鎖律、勾配降下法
PyTorch autogradの理解

[3週目] 確率 + 統計
  → 条件付き確率、ベイズ、分布
  → softmax、cross-entropyの実装

[4週目] 情報理論 + 実践
  → エントロピー、KLダイバージェンス
  → nanoGPT/DDPMのコードから数学を見つける

おすすめリソース

  • 3Blue1Brown(YouTube) — 線形代数/微積分の直感的な可視化
  • Mathematics for Machine Learning(無料教材) — 数学 → MLへの橋渡し
  • Andrej Karpathyのmicrograd — 逆伝播をゼロから
  • Stanford CS229 — 確率/統計 + ML数学
  • Ian GoodfellowのDeep Learning Book — Ch.2〜4(無料オンライン)

クイズ — AI数学(クリックして確認!)

Q1. ニューラルネットワークにおける行列の掛け算の役割は? ||入力ベクトルに重み行列を掛けて次のレイヤーの出力を計算。1回の行列の掛け算 = 1レイヤーの線形変換||

Q2. LoRAがSVDと関連する理由は? ||SVDは大きな行列を小さな行列の積に分解。LoRAは重み行列の変化量(ΔW)を低ランク行列2つ(A, B)の積で近似し、パラメータを大幅に削減||

Q3. 逆伝播(Backpropagation)の数学的基礎は? ||連鎖律(Chain Rule)。合成関数の微分を各段階の微分の積に分解。Lossから各パラメータまで勾配を逆方向に伝播||

Q4. Softmax関数の役割と数値安定性のトリックは? ||実数ベクトル(logits)を確率分布に変換(合計=1、すべて正)。トリック:入力から最大値を引く — expのオーバーフロー防止||

Q5. クロスエントロピーが損失関数として優れている理由は? ||正解の確率が1に近づけばloss → 0、0に近づけばloss → ∞。誤った予測に強いペナルティを与え、学習が効率的||

Q6. Diffusion Modelでガウス分布が使われる理由は? ||Forwardプロセスで画像にガウスノイズを段階的に追加。ガウスは数学的に扱いやすく(閉形式解)、中心極限定理により自然なノイズモデル||

Q7. Temperatureが高いとGPT出力のエントロピーはどうなる? ||増加。logitsをTで割るとsoftmax分布が平坦になり(均等分布に近づく)、不確実性が増加 → 多様な出力を生成||

Q8. RLHFにおけるKL Divergenceペナルティの目的は? ||強化学習で更新されたモデル(π_new)が元のモデル(π_ref)から離れすぎないよう制約。報酬ハッキング防止 + 既存能力の保持||

関連シリーズとおすすめ記事

参考資料