線形代数 ── ベクトル・行列・テンソルが LLM の「言語」
LLM との関係:LLM の中で起きていることは全て行列演算。単語はベクトルに変換され、Attention は行列の掛け算、パラメータは巨大な行列として保存される。

この章で何ができるようになるか:ベクトル・行列・テンソルの基本演算を理解し、「なぜ 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 の損失関数(交差エントロピー)と出力関数(ソフトマックス)の数学的基盤だ。