目次を表示する

LLM完全解説:スクラッチから理解する大規模言語モデル

線形代数 ── ベクトル・行列・テンソルが LLM の「言語」

線形代数 ── ベクトル・行列・テンソルが LLM の「言語」

LLM との関係:LLM の中で起きていることは全て行列演算。単語はベクトルに変換され、Attention は行列の掛け算、パラメータは巨大な行列として保存される。


線形代数とLLM — ベクトル・行列・テンソル・GPU並列

この章で何ができるようになるか:ベクトル・行列・テンソルの基本演算を理解し、「なぜ GPU が LLM に適しているか」を説明できるようになる。


ベクトル:単語を「方向と大きさ」で表す

LLM の世界では、単語は**ベクトル(数値の配列)**として表現される。

import numpy as np

# 「king」を 4次元ベクトルで表す(実際は 768〜4096 次元)
king  = np.array([0.9, 0.1, 0.8, 0.2])
queen = np.array([0.9, 0.1, 0.2, 0.8])
man   = np.array([0.5, 0.5, 0.9, 0.1])
woman = np.array([0.5, 0.5, 0.1, 0.9])

ベクトルの内積:「似ている」を数値化する

def dot_product(a, b):
    """内積 = 各要素の積の合計"""
    return np.dot(a, b)

# king と queen の類似度
print(np.dot(king, queen))  # 1.09(比較的高い)
print(np.dot(king, woman))  # 0.91(やや低い)

コサイン類似度:方向の近さを測る

def cosine_similarity(a, b):
    """ベクトルの「向き」の類似度。-1 〜 1"""
    return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))

print(cosine_similarity(king, queen))  # 0.78
print(cosine_similarity(king, man))    # 0.89

# 有名な例: king - man + woman ≈ queen
result = king - man + woman
print(cosine_similarity(result, queen))  # 高い類似度

これが Word2Vec(Ch.5)で実現される「単語の意味の演算」の基礎だ。


行列:データの一括変換

行列はベクトルの変換(回転・拡大・射影)を表す。LLM では重み行列(Weight Matrix)がこれに当たる。

# 入力ベクトル(4次元)を出力ベクトル(3次元)に変換
W = np.array([
    [0.1, 0.2, 0.3, 0.4],
    [0.5, 0.6, 0.7, 0.8],
    [0.9, 0.1, 0.2, 0.3],
])  # 3×4 の重み行列

x = np.array([1.0, 2.0, 3.0, 4.0])  # 4次元の入力

y = W @ x  # 行列×ベクトル = 3次元の出力
# y = [3.0, 7.0, 2.0]

行列の掛け算がなぜ重要か

LLM の 1 ステップは大きく言うと以下の繰り返しだ:

出力 = 活性化関数(重み行列 × 入力 + バイアス)
# ニューラルネットワークの1層(概念)
def linear_layer(x, W, b):
    return W @ x + b

def relu(x):
    return np.maximum(0, x)

# 2層のネットワーク
h = relu(linear_layer(x, W1, b1))    # 隠れ層
y = linear_layer(h, W2, b2)          # 出力層

バッチ行列演算:複数の入力を同時に処理

実際の LLM は複数のトークンを同時に処理する。これが行列×行列の演算になる。

# バッチサイズ 3、入力次元 4
X = np.array([
    [1.0, 2.0, 3.0, 4.0],   # トークン1
    [5.0, 6.0, 7.0, 8.0],   # トークン2
    [9.0, 1.0, 2.0, 3.0],   # トークン3
])  # 3×4

W = np.random.randn(4, 3)  # 4×3 の重み行列

Y = X @ W  # 3×3 の出力(3トークン × 3次元)
# 3トークン分の計算が1回の行列演算でまとめてできる

なぜ GPU が LLM に適しているか

CPU: 逐次処理が得意。コア数: 8〜64
GPU: 並列処理が得意。コア数: 数千〜数万

行列の掛け算は「独立した掛け算と足し算の塊」
→ 各要素の計算を別々のコアに割り当てられる
→ GPU の数千コアが同時に動く
→ CPU の100〜1000倍速い

テンソル:多次元配列

ベクトル(1次元)、行列(2次元)を一般化したものがテンソル。

import torch

# スカラー(0次元テンソル)
scalar = torch.tensor(3.14)

# ベクトル(1次元テンソル)
vector = torch.tensor([1.0, 2.0, 3.0])

# 行列(2次元テンソル)
matrix = torch.tensor([[1, 2], [3, 4], [5, 6]])

# 3次元テンソル(バッチ × シーケンス長 × 埋め込み次元)
# LLM の入力はまさにこの形
batch = torch.randn(32, 128, 768)
# 32文: 各文128トークン: 各トークン768次元のベクトル

LLM の主要なテンソル形状

入力テンソル:     (batch_size, seq_len, d_model)
                  (32, 128, 768)
                  → 32文、各文128トークン、各トークン768次元

Attention の Q/K/V: (batch_size, num_heads, seq_len, d_k)
                    (32, 12, 128, 64)
                    → 12ヘッド、各ヘッド64次元

重み行列:         (d_model, d_model) = (768, 768)
                  → パラメータ数 = 589,824(この1層だけで)

行列分解:パラメータ効率化の基礎

巨大な行列を2つの小さな行列の積で近似する手法。LoRA(Low-Rank Adaptation) の基礎。

# 元の重み行列: (768, 768) = 589,824 パラメータ
W = torch.randn(768, 768)

# 低ランク近似: W ≈ A × B
# A: (768, 8), B: (8, 768) → 768×8 + 8×768 = 12,288 パラメータ(48倍削減)
rank = 8
A = torch.randn(768, rank)
B = torch.randn(rank, 768)
W_approx = A @ B  # (768, 768) に近い行列

# LoRA: 元の重みを凍結し、差分を低ランク行列で表現
# W_new = W_frozen + A × B
# ファインチューニング時に A, B だけ更新する(元の W は変えない)

ブロードキャスティング:形状が異なるテンソルの演算

# バイアスの加算(ブロードキャスト)
X = torch.randn(32, 128, 768)  # バッチ入力
b = torch.randn(768)           # バイアス(1次元)

Y = X + b  # b が自動的に (32, 128, 768) に拡張されて加算

まとめ:線形代数と LLM の対応

線形代数の概念LLM での役割
ベクトル単語(トークン)の数値表現
内積 / コサイン類似度単語間の類似度計算
行列の掛け算ニューラルネットワークの各層の変換
テンソルバッチ処理・多次元データの表現
行列分解パラメータ効率化(LoRA)
GPU 並列計算行列演算の高速化

次章では、「次の単語の確率」を扱うための確率論と情報理論を見ていく。LLM の損失関数(交差エントロピー)と出力関数(ソフトマックス)の数学的基盤だ。