Chapter 1: LLM のための数学基礎
LLM(大規模言語モデル)の中身は、突き詰めると 「数を並べて、掛けて、足して、確率に変える」 という数学の積み重ねでできています。Transformer も Attention も、特別な魔法ではなく、ここで学ぶベクトル・行列・指数関数・確率といった基礎の組み合わせです。
この章では、LLM をゼロから理解・実装するために必要な数学を、前提知識ゼロからていねいに積み上げていきます。各トピックは次の流れで説明します。
- 直感 … その概念が「何のためにあるのか」をイメージでつかむ
- 数式 … 正確な定義を式で押さえる
- 具体例 … 実際の数値で手を動かして確かめる
- コード … NumPy / PyTorch でどう書くかを見る
- LLM での使われ方 … その数学が LLM のどこに出てくるかをつなぐ
このドキュメントは、書籍 「作ってわかる大規模言語モデルの仕組み」 の輪読会メモをベースに構成しています。書籍で扱うソースコードはこちらです。
この章で学ぶこと
この章で扱うのは、厳密には 「数学そのもの」 と、「その数学を使って作られる関数・扱うための道具」 の2種類です。両者は別物ですが、LLM を理解・実装するにはどちらも欠かせないので、区別したうえでまとめて学びます。
LLM を支える数学そのもの
- スカラー・ベクトル・行列・テンソル … データを数で表現する入れ物
- 内積とコサイン類似度 … 「2つのデータがどれくらい似ているか」を測る
- 行列積・転置 … 大量の内積を一度に計算する/次元を入れ替える
- 線形変換と非線形変換 … テンソルを掛けて空間を変形する
- 総和記号 Σ と指数関数 exp … 後述の softmax を組み立てる部品
- 微分・偏微分・三角関数 … 学習(最適化)と幾何の道具
数学を使って作られる「関数」(数学そのものではなく、上記の数学を組み合わせた応用)
- 判別関数・シグモイド・softmax … exp や Σ を組み合わせ、出力を「確率」に変える関数
数学を扱うための「道具」(数学ではなくライブラリ・実装テクニック)
- NumPy によるテンソル操作・ブロードキャスト … 数学をコードで計算するためのライブラリ機能
(応用)内積アテンション
- 以上の数学・関数がどう組み合わさって Transformer になるか
この数学が、LLM のどこで使われるのか(全体マップ)
「数学を勉強しても、結局 LLM のどこで使うの?」という疑問が一番のつまずきポイントです。そこで先に、各数学基礎が LLM を構成するどの要素に効いてくるのかを一覧にしておきます。各節の最後にも「LLM とのつながり」を書いていますが、まずはこの地図を頭に入れてから読み進めると、迷子になりません。
種別 … 🔢 数学そのもの/🧩 数学で作る関数/🛠️ 数学を扱う道具
| 種別 | この章で学ぶ項目 | LLM を作る要素 | 何のために使うか |
|---|---|---|---|
| 🔢 | ベクトル・行列・テンソル | 単語埋め込み(embedding) / 入力・中間表現の入れ物 | 単語や文章を「数の並び」に変え、計算できるようにする |
| 🔢 | 内積・コサイン類似度 | Attention の関連度スコア / 単語の意味の近さ | 「どの単語とどの単語が関係するか」を数値で測る |
| 🔢 | 行列積・転置 | Self-Attention の / 全結合層 | 大量の単語ペアの関連度(内積)を一度に計算する |
| 🔢 | 線形変換(重み行列) | Q・K・V の生成 ほか / 各層の変換 | 入力を別の表現空間へ変換する。学習対象そのもの |
| 🔢 | 非線形( など) | 活性化関数 / モデルの表現力 | 直線では表せない複雑な関係を学習可能にする |
| 🔢 | Σ・指数関数 exp | softmax の中身 | スコアを「すべて正・合計1」の確率へ変える部品 |
| 🔢 | 微分・偏微分 | 学習(勾配降下・誤差逆伝播) | 損失を小さくする方向(傾き)を求めてパラメータを更新 |
| 🔢 | 三角関数(sin・cos) | 位置エンコーディング / 内積の証明・回転 | 「何番目の単語か」という位置情報を波の形で表す |
| 🧩 | softmax | 出力層(次の単語の予測) / Attention の注目度 | 数万語の候補を確率分布に変える/注目度を確率化する |
| 🧩 | シグモイド・判別関数 | 2値分類 / 各種ゲート | 出力を 0〜1 の確率に押し込め、はい/いいえを判定 |
| 🛠️ | NumPy・ブロードキャスト・転置操作 | 実装全般(PyTorch / NumPy) | 次元を合わせてバグなくテンソルを掛け合わせる |
表の 🧩 softmax・シグモイド は、数学そのものではなく 🔢 の数学(exp や Σ)を組み合わせて作った関数です。また 🛠️ NumPy は数学ではなく、その計算をコードで実行するためのライブラリ(道具)です。この章ではタイトルこそ「数学基礎」ですが、LLM 実装に直結するこれらの関数・道具もあわせて扱います。
🔢 の数学はおおむね 「データを数にする → 関連度を測る → 変換する → 確率の部品にする → 学習する」 という LLM の処理の流れの順に並んでいます。「いま地図のどこを学んでいるのか」を意識すると、Transformer 全体像の中での位置づけが見えてきます。
はじめに:本書の流れと読み進め方
LLM の教科書は、おおまかに次のリズムで進みます。
説明 → 関数 → 目的変数
LLM が扱うのは「文章」「言葉」という、本来コンピュータには直接計算できないものです。そこで、
- まず対象(言葉・文章)を 説明(どういうデータか)し、
- それを 関数 で処理できる形に変え、
- 最終的に予測したい 目的変数(次の単語など)にたどり着く
という流れになります。説明パートで言葉に対する説明が多くなるのは、言葉をベクトル(数の並び)という変数に変換するためです。言葉を数にできれば、あとはこの章で学ぶ数学がすべて使えるようになります。
- 進む頻度は遅くて良いので、理解しながら進むことを優先してください。
- 各節の 数式とアーキテクチャ図を中心に読み込み、「なぜその構造が必要なのか」という理論的背景に注目すると、自分の言葉で説明できるようになります。
- 後続の章で出てくる Transformer の理解が最重要です。本章の数学は、そこを重点的に理解するための土台になります。
1. 数とデータの表現:スカラー・ベクトル・行列・テンソル
LLM の計算は、すべて「数の入れ物」の上で行われます。まずはその入れ物の種類を整理しましょう。
1.1 スカラー・ベクトル・行列
- スカラー(scalar) … ただ1つの数値。例:
- ベクトル(vector) … 数を一列に並べたもの。例: 、
- 行列(matrix) … 数を縦・横の格子状に並べたもの。
ベクトルは 座標 だと思うとイメージしやすいです。 は「横に3、縦に2 進んだ点」を表し、原点からその点に向かう矢印(向きと長さを持つ量)とも見なせます。LLM では、1つの単語を「数百〜数千個の数を並べたベクトル」で表します。これが 単語埋め込み(word embedding) です。
import numpy as np
scalar = 3 # スカラー
vector = np.array([3, 2]) # ベクトル(1次元配列)
matrix = np.array([[3, 2],
[1, 5]]) # 行列(2次元配列)
1.2 テンソルと「階数(rank)」
テンソル(tensor) は、スカラー・ベクトル・行列をすべて含む、より一般的な概念です。「何方向に数が並んでいるか」を 階数(rank, 次元数) という言葉で区別します。
| 名前 | 階数 | イメージ | 例 |
|---|---|---|---|
| スカラー | 0 | 点(数1つ) | |
| ベクトル | 1 | 線(数の一列) | |
| 行列 | 2 | 面(縦×横) | |
| 3階テンソル | 3 | 立体(縦×横×奥行き) | 行列を奥行き方向に重ねたもの |
| n 階テンソル | n | それ以上の多次元 | 4階・5階…と無限に拡張可 |
図にすると、「数の並べ方の方向(階数)」が1つずつ増えていくイメージです。
ポイントは、行列に「奥行き」を足したものが3階テンソルで、そこからさらに方向を足していけば4階・5階…といくらでも拡張できる、ということです。階数に上限はありません。
LLM では、たとえば「バッチ × 系列長 × 埋め込み次元」のような3階テンソルが日常的に登場します(複数の文章を、それぞれ複数の単語に分け、各単語をベクトルで表す、という3方向の入れ物です)。
PyTorch や TensorFlow といったライブラリは、こうした テンソルの演算(加算・乗算など)を効率的に行うために作られています。名前にそのまま「Tensor」が入っているのはそのためです。LLM の実装とは、つまるところ「テンソルをどう作り、どう掛け合わせるか」を記述する作業だと言えます。
import torch
# バッチ2 × 系列長3 × 埋め込み次元4 の3階テンソル
x = torch.randn(2, 3, 4)
print(x.shape) # torch.Size([2, 3, 4])
2. ベクトルの内積とコサイン類似度
ここは LLM 理解の 心臓部 です。「内積」という1つの計算が、のちほど Attention の「単語同士の関連度」に直結します。
2.1 内積の定義
2つのベクトルの 内積(dot product) は、対応する要素どうしを掛けて、すべて足し合わせた値です。結果は1つの数(スカラー)になります。
具体例で確かめましょう。
このように、ベクトルという「並んだ数」を1つの数(スカラー)にまとめるのが内積です。
内積は対応する要素を掛けるので、要素数(次元)が同じベクトルどうしでないと計算できません。2次元ベクトルと3次元ベクトルの内積は定義できない、という点に注意してください。
a = np.array([3, 2])
b = np.array([1, 4])
print(np.dot(a, b)) # 11
print(a @ b) # 11(@ は行列積・内積の演算子)
2.2 準備:三角関数 cos・sin とは
このあと、いきなり (コサイン)という記号が出てきます。その前に、三角関数の (コサイン)と (サイン)がそもそも何者なのかを、LLM で使ううえでいちばん役立つ形でおさえておきましょう。
ひとことで言うと「角度 → 座標」の変換器
と は、むずかしく考えなくて大丈夫です。ざっくり言うと、
と は、「角度」を「座標(数値)」に変換してくれる関数
です。「どっちを向いているか(角度)」を「数」に変えてくれるので、ベクトルの向きを数式で計算できるようになります。
単位円で見るのがいちばんわかりやすい
ここでは 単位円(半径 1 の円) を使った見方が一番役に立ちます。原点を中心とした半径 1 の円の上で、横軸(右方向)から反時計回りに角度 だけ回った点を考えます。このとき、
- その点の 横方向(x 座標)の位置 =
- その点の 縦方向(y 座標)の位置 =
です。つまり、角度 の方向を向いた「長さ 1 の矢印」の先端の座標が、ちょうど になります。
代表的な値(−1 〜 1 の範囲で変化する)
角度 を変えると、 と は から の間で変化します。
| 角度 | (右) | (上) | (左) | (下) |
|---|---|---|---|---|
| (横の位置) | ||||
| (縦の位置) |
- (真右を向く)→ 横いっぱいなので 、縦は動いてないので
- (真上を向く)→ 横は 、縦いっぱいで
ここで特に注目してほしいのが の動き です。同じ向き()なら 、直角()なら 、反対向き()なら 。この「同じ向きほど大きく、反対向きほど小さくなる」性質が、次の節でそのまま 「2つのベクトルがどれくらい似た方向か」の目盛りになります。
三角関数を「直角三角形の辺の比」で習った人も多いはずです(、)。これは上の単位円の見方と同じことを言っています。斜辺の長さを にすると、隣辺(横)がちょうど 、対辺(縦)が になり、単位円の座標とぴったり一致します。
これで準備OK。では、この がどうやって内積と結びつくのかを見ていきましょう。
2.3 内積が表すもの:似ている度合い
「対応要素を掛けて足す」だけの内積が、なぜそんなに重要なのでしょうか。答えは、内積が「2つのベクトルがどれくらい同じ方向を向いているか」を測っているからです。
内積には、もう1つの顔があります。2つのベクトルの 長さ(大きさ) と、それらが なす角 を使って、次のように書けます。
この式は記号が多くて身構えてしまうかもしれませんが、3つのパーツの掛け算なだけです。1つずつ意味を見ていきましょう。
| 記号 | 読み方 | 意味 |
|---|---|---|
| a ドット b | 内積(2.1 で計算した「対応要素の積の和」) | |
| a の大きさ | ベクトル の 長さ(矢印の長さ) | |
| b の大きさ | ベクトル の 長さ | |
| コサイン シータ | 2つのベクトルが なす角 のコサイン(=向きの揃い具合) |
ベクトルの「長さ」 とは
ベクトルの長さ(大きさ)は、原点からその点までの距離のことです。三平方の定理(ピタゴラスの定理)で計算できます。たとえば なら、
横に 3・縦に 2 進んだ点までの、まっすぐな距離というわけです。
式全体が言っていること
この式は、内積が 「2つの矢印がどれだけ長いか」×「どれだけ同じ方向を向いているか」 で決まる、と言っています。
- … 2つのベクトルの長さの掛け算(どちらも長いほど大きい)
- … 向きの揃い具合(同じ向きで 、直角で 、反対向きで )
つまり、両方が長くて、かつ同じ方向を向いているほど、内積は大きくなるということです。逆に、どんなに長いベクトルでも直角()なら、内積は になります。
ここから「長さ」の影響を取り除いて、純粋に「向きの揃い具合」だけを取り出したいときは、両辺を長さで割って だけにします(これが後で出てくる コサイン類似度 です)。
先ほど計算した内積の値(11 や 14)そのものが になるわけではありませんが、こうして を突き止めるために絶対に必要な主役が内積なのです。
の値は角度によって次のように変わります。
| 2つのベクトルの関係 | 角度 | 内積の符号 | |
|---|---|---|---|
| 同じ向き | (最大) | 大きな正 | |
| 直角(無関係) | |||
| 反対向き | (最小) | 大きな負 |
つまり、
内積が大きい(正)ほど、2つのデータは似た方向を向いている(=似ている)。 0 に近いほど無関係(直交)。負なら反対方向を向いている。
これが「内積 = 2つのデータがどれくらい似ているか(同じ方向を向いているか)を判定する道具」と言われる理由です。LLM では、これがそのまま「単語と単語がどれくらい関連しているか」の計算に使われます。
2.4 なぜ内積が になるのか(単位円による証明)
「内積 = 長さ × 長さ × 」がどこから来るのか、単位円(半径1の円) を使って確かめてみましょう。さきほど準備した三角関数( は角度 の点の座標)を使うと、ここを理解できて「内積=類似度」が腑に落ちます。
長さが 1 の2つのベクトル を考えます。長さ1のベクトルの先端は必ず単位円の上にあるので、それぞれの角度を とすると、座標は次のように書けます。
この2つの内積を、定義(対応要素の積の和)どおりに計算します。
ここで、三角関数の 加法定理
という関係が成り立ちます。右辺が、すぐ上で計算した内積 とぴったり同じ形ですね。したがって、
つまり、長さ1のベクトルどうしの内積は、その2つのベクトルの間の角度のコサインそのものになります。 は2つのベクトルがなす角ですから、これで「内積が に等しい」ことが示せました。
「加法定理を急に出されても…」と思うかもしれません。これは丸暗記する公式ではなく、ちゃんと証明できます。ここでは 「図形をくるっと回転させても、2点間の距離は変わらない」 という、当たり前の事実だけを使って導いてみます。使う道具は次の3つだけです。
- 単位円上の角度 の点の座標は (2.2 で見たとおり)
- 半径が 1 なので、三平方の定理から (横² + 縦² = 半径²)
- 2点間の距離は三平方の定理で計算できる(横の差² + 縦の差²)
証明のイメージは下の図がすべてです。同じ2点 を「回転前」と「回転後」で見ても、2点を結ぶ線(距離 )の長さは変わらない——これだけです。回転前は座標 から、回転後は片方が に来た形から、それぞれ同じ を計算して見比べます。
緑の点線(距離 )が①と②で同じ長さである、というのが証明の核心です。では、その を実際に2通りで計算してみましょう。
ステップ1:2点の距離を座標から計算する
その前に、そもそも 「2点間の距離」をどう計算するか を確認します。もう一度、上の 図① を見てください。2点 を結ぶ距離 (緑の点線)は、横のズレ(x 座標の差)と 縦のズレ(y 座標の差)を2辺とする 直角三角形の斜辺になっています。だから三平方の定理「斜辺² = 横² + 縦²」を使えば、距離が座標だけから計算できます。
一般に、2点 、 の距離の2乗は次のように書けます。
つまり 「横の差の2乗 + 縦の差の2乗」 です。図①では、横の差(横の点線)が 、縦の差(縦の点線)が にあたります(どちらも と の座標の差)。これを当てはめると、
ステップ2:全体を回転させてから、もう一度距離を計算する
次に、図形全体を だけ回転させます(時計回りに 回す)。回転とは「すべての点の角度を一律にずらす」操作なので、 回すというのは どの点も角度から を引く、ということです。すると、
- の角度は になります。角度 の単位円上の点は なので、
- の角度は になります。なので
そして大事なのは、回転しても2点の距離は変わらないこと(右上の図②)。なので、この回転後の2点 で距離の2乗を計算しても、ステップ1と同じ値になるはずです。やってみましょう。 と の「横の差・縦の差」を2乗して足します。
各行で何をしたかを補足します。
- 1行目 … 距離の2乗の公式「横の差の2乗 + 縦の差の2乗」をそのまま当てはめただけです。 の座標は なので、横は 、縦は になります。
- 2行目 … 1つ目のカッコを展開しています。 という公式で、 とみなしただけです。
- 3行目 … (ステップ1で使ったのと同じ性質)でまとめます。
- 4・5行目 … にして整理して完成です。
ポイントは、 という塊を 1つの角度 として扱っていることです。「角度 の単位円上の点は 、そして 」を、 に当てはめているだけなので、ステップ1とまったく同じ計算をしています(ただし片方が なのでよりシンプル)。
ステップ3:(1) と (2) は同じ距離なので等しい
(1) と (2) は、回転しても変わらない「同じ2点の距離」を別の方法で計算したものです。等しいので、
両辺を整理すると、
となり、加法定理が導けました。「回転で距離は変わらない」というだけで証明できる、というのがポイントです。
長さが1でない一般のベクトルでも、長さで割って(正規化して)長さ1に揃えれば、純粋に向きだけの比較ができます。これが コサイン類似度(cosine similarity) です。
図にすると、こういうことです。長さの違う2つのベクトル も、長さ 1 に正規化(長さで割る)して にしてしまえば、残るのは「なす角 」だけ。その が類似度です。 で割るのは、まさにこの「長さの違いを打ち消して、向きだけを見る」操作にあたります。
LLM では、単語の意味をベクトルで表します。「猫」と「犬」のベクトルは近い向き(コサイン類似度が高い)、「猫」と「方程式」は遠い向き、という具合に、ベクトルの向きで意味の近さを表現します。後半で学ぶ Attention は、この「内積で類似度を測る」操作を膨大な回数くり返しているだけ、とも言えます。
3. 行列積(matrix multiplication)
内積は「2つのデータの関係性を1つの数にまとめる計算」でした。これに対して 行列積 は、大量のデータを、大量のルールで一斉に変換・処理する計算システムです。内積を一度にまとめて計算する仕組み、と言い換えてもよいでしょう。
3.1 行列積は「内積の集まり」
行列積は、左側の行列の「行ベクトル」と、右側の行列の「列ベクトル」の内積を、出力の各位置に並べる操作です。
例として の行列どうしを掛けてみます。
図にすると、「左の行」と「右の列」の内積が、結果のその位置のセルになる、という対応です。色のついた行(青)と列(青)の内積が、結果の色のついたセル(青)になっています。
出力の各要素を見ると、
- 左上 … の1行目 と の1列目 の内積
- 右上 … の1行目 と の2列目 の内積
- 左下 … の2行目 と の1列目 の内積
- 右下 … の2行目 と の2列目 の内積
となっています。つまり 同士の行列積は、4パターンのベクトルの組み合わせの類似度(内積)を一括で計算していることと同じです。1つ1つ内積を取る代わりに、行列積を使えば「全組み合わせの内積」をまとめて求められる——これが LLM が大量の単語ペアの関連度を高速に計算できる理由です。
A = np.array([[1, 2],
[3, 4]])
B = np.array([[5, 6],
[7, 8]])
print(A @ B)
# [[19 22]
# [43 50]] ← 例: 左上 = 1*5 + 2*7 = 19
3.2 次元の整合性:掛けられる形・掛けられない形
行列積には 厳密なルール があります。
左側の行列の「列数」と、右側の行列の「行数」が一致していなければ計算できない。
の行列と の行列を掛けると、結果は の行列になります。
内側の が揃っていることが必須で、外側の と が結果の形になります。
例を挙げます。4行7列 の行列に対して掛けられるのは、
- の行列 … ✅ 計算可能(内側の 7 が一致)
- の行列 … ❌ 計算不可(左の列数 7 と右の行数 8 が不一致)
A = np.random.randn(4, 7) # 4×7
B = np.random.randn(7, 3) # 7×3
print((A @ B).shape) # (4, 3) ← OK
C = np.random.randn(8, 3) # 8×3
# A @ C → エラー(7 と 8 が一致しない)
3.3 順序は入れ替えられない(非可換)
ふつうの数の掛け算は ですが、行列積では順序を入れ替えると結果も次元も変わります。一般に、
です(そもそも が計算できない形になることも多いです)。
LLM の中では、行列(テンソル)を次々に掛けて変換を重ねていきます。そのため、いま自分が扱っているテンソルが「何行何列(何次元)なのか」を常に意識することが、実装でバグを出さない最大のコツになります。
NumPy なら .shape、PyTorch なら .size()(または .shape)で、テンソルの形をいつでも確認できます。計算が合わないときの9割は次元の不一致です。
print(A.shape) # NumPy
print(t.size()) # PyTorch
4. 線形変換と非線形変換
4.1 線形変換とは
線形変換(linear transformation) とは、文字通り「線形に変換する」こと。つまり、定数倍と足し算をくずさない(まっすぐな関係を保ったまま移す)変換のことです。「線形代数(linear algebra)」という分野の名前も、この「線形」から来ています。
そして、その線形変換を具体的に計算する手段が テンソル(行列)を掛ける操作です。
「線形」とは、ざっくり言うと 定数倍と足し算だけでできている、まっすぐな関係のことです。
- 線形の例: 、
- 実数を掛けたり足したりするだけ。変数そのものどうしを掛けていない。
- グラフはまっすぐな直線になる。
- 非線形の例:
- 変数 どうしを掛けている()。
- グラフが曲がる。
行列を掛ける操作は、ベクトル(座標)を別の座標へ「まっすぐに」移す変換なので、線形変換に分類されます。
4.2 イメージ:回転は線形変換
線形変換のわかりやすい例が 回転 です。ゲームプログラミングでキャラクターをくるっと回すとき、内部では座標ベクトルに 回転行列 を掛けています。
角度 だけ回転させる回転行列は次の形です。
ある点の座標 にこれを掛けると、
となり、回転後の新しい座標が得られます。次のフレームで表示すべき位置が、行列を1回掛けるだけで計算できるわけです。
LLM の中の「学習可能な重み行列を掛ける」という操作は、すべてこの線形変換です。たとえば後半で出てくる 、、 は、入力 に重み行列を掛けて別の空間に移す線形変換です。一方、softmax や活性化関数のような非線形な処理を間に挟むことで、モデルは直線だけでは表せない複雑な関係を学習できるようになります。「線形変換 → 非線形 → 線形変換 → …」の積み重ねがニューラルネットワークの正体です。
5. 転置とブロードキャスト(NumPy の実務)
実装では「次元が合わなくて掛けられない」場面に頻繁に出会います。それを解決する2つの道具を押さえましょう。
5.1 転置(transpose)
転置は、行列の 行と列を入れ替える 操作です。 の行列を転置すると になります。記号では 、コードでは .T と書きます。
転置は、次元が合わずに掛け算ができないときの救済手段としてよく使います。たとえば の行列と の行列は、そのままでは掛けられません(左の列数 4 と右の行数 7 が不一致)。しかし片方を転置して にすれば、 と 、あるいは と として計算できるようになります。
A = np.random.randn(7, 4)
B = np.random.randn(7, 4)
# A @ B → エラー(4 と 7 が不一致)
print((A @ B.T).shape) # (7, 7) ← B を転置して解決
print((A.T @ B).shape) # (4, 4)
後半の Attention の式 にも、この転置 が登場します。Query と Key の全組み合わせの内積を取るために、Key を転置して形を合わせているのです。
5.2 ブロードキャスト(broadcast)
ブロードキャストは、NumPy(や PyTorch)が持つ便利機能で、次元が異なる配列どうしの演算で、足りない次元をライブラリが自動でコピーして補い、計算を成立させる仕組みです。
たとえば、1行のベクトルに1つの数値を足すと、その数値が全要素に自動で加算されます。
v = np.array([1, 2, 3])
print(v + 10) # [11 12 13] ← 10 が全要素に配られる
これは便利な反面、意図しない挙動を招く落とし穴にもなります。
行ベクトルと列ベクトルのように、向きの違う配列どうしを足すと、ライブラリが両方を拡張して、思っていたより大きな行列を作ってしまうことがあります。
row = np.array([[1, 2, 3]]) # 形 (1, 3) 行ベクトル
col = np.array([[10],
[20]]) # 形 (2, 1) 列ベクトル
print(row + col)
# [[11 12 13]
# [21 22 23]] ← (2, 3) の行列に勝手に拡張された!
「ベクトルどうしを足したつもりが、行列が返ってきた」というバグはここから生まれます。演算前に必ず .shape を確認しましょう。
6. NumPy によるテンソル操作の実践
ここまでの概念を、実際に NumPy で手を動かして確かめます。LLM 実装で頻出する操作ばかりです。
NumPy は数学そのものではなく、ここまでの数学(テンソルや行列積)をコンピュータ上で計算するためのライブラリ(道具)です。LLM の実装は PyTorch などの上で書きますが、その土台となる考え方は NumPy とほぼ共通なので、まず NumPy で感覚をつかみます。
6.1 配列・ベクトルを作る
import numpy as np
# 既存の数値から配列を作る
a = np.array([1, 2, 3, 4])
# 連番を作る(0 以上 5 未満)
b = np.arange(5) # [0 1 2 3 4]
# 範囲を等分割する(開始・終了・個数を指定)
c = np.linspace(1, 10, 3) # [ 1. 5.5 10. ]
# 多次元のランダムテンソル(標準正規分布)
d = np.random.randn(2, 3) # 2×3 のランダム行列
np.linspace(1, 10, 3) は「1 から 10 までを 3個に等分割する」という意味で、結果は 1, 5.5, 10 になります(両端を含み、その間を均等に刻みます)。np.arange が「間隔を指定して連番を作る」のに対し、np.linspace は「個数を指定して均等割りする」点が違います。
6.2 要素を取り出す(スライシング・負のインデックス)
x = np.array([10, 20, 30, 40, 50])
print(x[0]) # 10 ← 先頭(インデックスは 0 から)
print(x[-1]) # 50 ← 負のインデックスは末尾から数える
print(x[1:4]) # [20 30 40] ← 1番目以上 4番目未満を切り出す
-1 は「末尾の要素」を指す便利な書き方です。系列の最後のトークンを取り出す、といった場面で多用します。
6.3 形を変える(reshape)
.reshape は、要素の総数を変えずに、テンソルの形(次元構成)だけを組み替える操作です。
y = np.arange(6) # [0 1 2 3 4 5] 形 (6,)
print(y.reshape(2, 3))
# [[0 1 2]
# [3 4 5]] 形 (2, 3) に組み替え
LLM では、たとえば Multi-Head Attention で「埋め込み次元」を「ヘッド数 × ヘッドごとの次元」に分けるときなどに reshape を多用します。
7. 総和記号 Σ と指数関数 exp
softmax を理解するために必要な2つの部品を押さえます。
7.1 総和記号 Σ(シグマ)
(シグマ)は、たくさんの項を足し合わせることを短く書くための記号です。
「 を 1 から まで動かしながら、 を全部足す」と読みます。内積の定義 にも、softmax の分母にも登場する、非常によく使う記号です。
7.2 指数関数 exp(x) = eˣ とネイピア数 e
指数関数は と書きます。ここで は ネイピア数 と呼ばれる特別な定数で、
という値です(円周率 のように、数学のあちこちに自然に現れる定数です)。
の最大の特徴は、
微分しても変わらない
ことです。式で書くと 。微分(次の節で説明する「傾き」)を取っても元の形に戻るという、極めて扱いやすい性質を持っています。このおかげで、学習(微分を使う最適化)と相性が良く、機械学習のいたるところで使われます。
指数関数のもう1つの大事な性質は、
- がどんな実数でも、 は 必ず正の数になる()
- が大きいほど は急激に大きくなる
という点です。次の softmax で、この「必ず正になる」「大小を強調する」性質が効いてきます。
import numpy as np
print(np.e) # 2.718281828459045
print(np.exp(0)) # 1.0 (e^0 = 1)
print(np.exp(1)) # 2.718...(e^1 = e)
print(np.exp([1, 2])) # [2.718... 7.389...]
8. 微分・偏微分・三角関数
8.1 微分は「傾き」
微分(differentiation) は、ある点での 傾き(変化率) を求める操作です。「 をほんの少し増やしたとき、 がどれだけ変化するか」を表します。
- 傾きが 正 … を増やすと が増える(右上がり)
- 傾きが 負 … を増やすと が減る(右下がり)
- 傾きが 0 … その点は山や谷の頂点(平ら)
LLM の 学習 は、「予測の誤差(損失)」をできるだけ小さくする作業です。これは「損失という山の斜面を、傾きの情報を頼りに谷底へ下っていく」イメージで、勾配降下法(gradient descent) と呼ばれます。その「傾き」を求めるのが微分です。つまり微分は、モデルが賢くなる仕組みそのものを支えています。
8.2 偏微分
LLM のパラメータは何億・何兆個もあります。このように 変数がたくさんある関数 で、「1つの変数だけに注目し、ほかの変数は定数とみなして微分する」のが 偏微分(partial differentiation) です。記号は のように、丸まった (ラウンドディー)を使います。
たとえば を で偏微分すると、 を定数扱いして、
となります。LLM の学習では、「各パラメータを少し動かすと損失がどう変わるか」をパラメータ1つ1つについて求める必要があり、その計算がまさに偏微分です(実際にはこれを自動で行う「自動微分」をライブラリが担います)。
8.3 三角関数
三角関数(、 など)は、角度と座標を結びつける関数です(基礎は 2.2 節「三角関数 cos・sin とは」を参照)。本章ではすでに、
- 内積が になる証明(2.4節の単位円・加法定理)
- 回転行列(4.2節)
で使いました。さらに LLM では、単語の 位置情報 をモデルに教えるための 位置エンコーディング(positional encoding) に ・ が使われます。「何番目の単語か」を波の形で表現する、という使い方です。ここでは「三角関数は内積・回転・位置の表現に顔を出す」と覚えておけば十分です。
9. 判別関数・シグモイド・softmax・2値分類
最後に、モデルの出力(ただの数値の並び)を、人間が解釈できる 確率 に変える関数を学びます。
シグモイドや softmax は、新しい数学ではありません。これまで学んだ 指数関数 exp と総和 Σ を組み合わせて作った関数です。「どんな数学を使って、どんな性質(0〜1・合計1)を作り出しているか」という視点で読むと、丸暗記せずに理解できます。
9.1 判別関数と分類
判別関数(discriminant function) は、入力がどのクラスに属するかを決めるための関数です。出力された数値(スコア)の大小で「どちらのクラスらしいか」を判定します。ただし、生のスコアは「3.0」「-1.0」のようなバラバラの値なので、そのままでは「確率」として扱えません。そこで登場するのが、スコアを 0〜1 に押し込める関数です。
9.2 シグモイド関数(2値分類)
シグモイド関数(sigmoid) は、どんな実数を入れても出力を 〜 の間に収める関数です。
- が大きい → 出力は 1 に近づく
- が小さい(大きな負)→ 出力は 0 に近づく
- → 出力はちょうど 0.5
出力を「そのクラスである確率」とみなせるので、「はい / いいえ」「スパム / 非スパム」のような 2値分類 にぴったりです。ここでも分母に指数関数 が使われています。
def sigmoid(x):
return 1 / (1 + np.exp(-x))
print(sigmoid(0)) # 0.5
print(sigmoid(2)) # 0.880...
print(sigmoid(-2)) # 0.119...
9.3 softmax 関数(多クラス分類)
2値ではなく、たくさんの選択肢の中から確率を割り当てたいときに使うのが softmax 関数 です。LLM が「次の単語」を予測するとき、語彙数(数万語)ぶんのスコアを確率分布に変えるのが、まさに softmax です。
個のスコア に対して、softmax は次のように定義されます。
やっていることは2ステップです。
- 各スコアを指数関数に通す()… これで全部 正の数 になり、大きいスコアはより強調される
- 全体の合計で割る(分母の )… これで全部足すと ちょうど 1 になる
結果として、出力は「すべて 0〜1 の値で、合計すると 1 になる」という、まさに 確率 の性質を持ちます。「合計すると 1 になる 0〜1 の値 = 確率っぽい」ので、softmax の出力は確率分布として解釈できるのです。
具体例で見てみましょう。スコア [3.0, 1.0, -1.0] に softmax をかけると、おおよそ次のようになります。
| 入力スコア | softmax 後(確率) |
|---|---|
図にすると、softmax は バラバラのスコア(左)を、合計1の確率(右)に変換する操作だとわかります。
合計はちゃんと です。指数関数を通したことで、いちばん大きかった 3.0 の項がはっきり強調されている(0.84)点に注目してください。指数の効果で 極端に大きい値が暴れるのを抑えつつ、大小関係はくっきりさせる ことができます。
def softmax(a):
e = np.exp(a - np.max(a)) # max を引くのは数値の安定化のため
return e / np.sum(e)
print(softmax(np.array([3.0, 1.0, -1.0])))
# [0.84379473 0.11419519 0.04201007]
print(softmax(np.array([3.0, 1.0, -1.0])).sum()) # 1.0
| 関数 | 用途 | 出力 |
|---|---|---|
| シグモイド | 2値分類 | 1つの確率(0〜1) |
| softmax | 多クラス分類 | 合計1の確率分布 |
LLM の出力層は softmax です。「次に来る単語」の候補すべてにスコアをつけ、softmax で確率分布に変え、その分布からサンプリング(または最大確率を選択)して次の単語を決めます。そして次の節で見るように、Attention の内部でも softmax が「どの単語にどれだけ注目するか」を確率に変えるために使われています。
10. (応用)内積アテンション — 数学から Transformer への橋渡し
ここまで学んだ数学が、どう Transformer の中核 Attention(アテンション) になるのかを見てみましょう。Attention は新しい魔法ではなく、これまでの部品の組み合わせです。
| これまで学んだ数学 | Attention での役割 |
|---|---|
| 内積(2節) | 単語どうしの 関連度スコア を測る |
| 行列積(3節) | 全単語ペアのスコアを 一括計算 する |
| 線形変換・重み行列(4節) | 入力から Q・K・V を 作り出す |
| 転置(5節) | で形を合わせる |
| softmax(9節) | スコアを 注目度(確率) に変える |
10.1 Q・K・V と内積アテンションの式
Attention は Query(クエリ)・Key(キー)・Value(バリュー) の3つで動きます。検索にたとえると分かりやすいです。
- Query … 「いま、自分が探している情報」(検索語)
- Key … 「各トークンがどんな情報を持っているか」(検索対象のタグ)
- Value … 「実際に取り出して混ぜ合わせる中身」(検索結果の本文)
基本の流れはこうです。
- Query と各 Key の 内積を取り、関連度スコアを計算する()
- スコアを softmax にかけて、合計1の 注目度(attention weight) に変える
- 注目度を重みにして、Value を重み付き平均する
これを行列でまとめて書いたのが、有名な Scaled Dot-Product Attention の式です。
- … Query と Key の全組み合わせの内積(=関連度スコアの行列)。Key を転置して形を合わせています。
- で割る(scaled)… は Key の次元数。次元が大きいほど内積の値も大きくなりがちで、そのまま softmax に入れると値の差が極端になり、ほぼ1つの単語だけに注目が集中してしまいます。 で割ってスコアを適度に抑え、softmax が極端になりすぎるのを防ぎ、学習を安定させます。
- softmax … スコアを注目度(確率)に変換。
- 最後に を掛ける … 注目度で Value を重み付き平均する。
10.2 Self-Attention と Multi-Head
Self-Attention では、Q・K・V を 同じ入力(同じ単語列の埋め込み )から作ります。ただしそのまま使うのではなく、学習可能な重み行列(4節の線形変換)で3通りに変換します。
同じ単語でも「探す側」「照合される側」「取り出される中身」で役割が違うため、別々の行列で別々の表現に変換するわけです。これにより、文中の各単語が同じ文中の他の単語を参照しあうことができます。
さらに Multi-Head Attention では、Attention を複数の「ヘッド」に分けて並行に計算します。1つの Attention だと1種類の関係しか捉えにくいですが、複数のヘッドがそれぞれ「文法的な関係」「意味的な関係」「近くの単語」「遠くの単語」といった異なる観点を担当することで、文脈を多面的に理解できます。
理解度チェック:内積アテンション
ここまでの数学が身についていれば、内積アテンションは自分の言葉で説明できるはずです。次の問いに答えてみてください(解答は下の折りたたみにあります)。
【基礎】
- Query / Key / Value の役割は何ですか? それぞれを一言で説明してください。
- なぜ Query と Key の内積を計算するのですか? 内積が大きいとき、何を意味しますか?
- 内積アテンションで計算される「スコア」とは何ですか? の意味を説明してください。
- なぜ attention score に softmax をかけるのですか? softmax の前と後で、値の意味はどう変わりますか?
- Value はどのタイミングで使われますか? Query と Key でスコアを計算したあと、Value に対して何をしますか?
- Attention weight が大きい Value は、最終出力にどう影響しますか? ある単語への weight が 0.8 のとき、それは何を意味しますか?
【Self-Attention】
- Self-Attention では、Q / K / V はどこから作られますか? 同じ入力から作られる理由を説明してください。
- Scaled Dot-Product Attention の「scaled」とは何ですか? で、なぜ で割るのでしょうか?
- 内積アテンションと「単語同士の関連度」はどう関係していますか?「私は猫が好き」で「好き」が「猫」に強く attention するとは、どういう意味ですか?
- 内積アテンションの出力は、元の Value のうち1つだけを選ぶものですか? それとも複数を混ぜ合わせたものですか? 理由も説明してください。
【発展】
- なぜ単純な内積ではなく、学習可能な重み行列で Q, K, V に変換してから attention を計算するのでしょうか?
- Multi-Head Attention では、なぜ attention を複数の head に分けるのでしょうか? 1つの attention だけでは何が足りないのでしょうか?
- 1〜6 に答えられれば → 基本理解 OK
- 7〜10 に答えられれば → Self-Attention の流れも理解できている
- 11〜12 に答えられれば → Transformer の理解に進める状態
解答を見る
1. Query / Key / Value の役割は何ですか?
Query は「今、自分が探している情報」、Key は「各トークンがどんな情報を持っているか」、Value は「実際に取り出して混ぜ合わせる中身の情報」を表します。たとえるなら、Query = 検索語、Key = 検索対象のタグ、Value = 検索結果として取り出す本文、です。
2. なぜ Query と Key の内積を計算するのですか?
内積を計算すると、両者がどれくらい似た方向を向いているかを測れます。内積が大きいほど「この Query は、この Key に強く関係している」と解釈でき、トークン同士の関連度スコアとして使えます。
3. 内積アテンションで計算される「スコア」とは何ですか?
は、Query と Key の関連度を表す値です。ある単語が「どの単語を参考にすべきか」を決めるために、Query とすべての Key の内積を取ります。スコアが高い単語ほど、後で強く参照されます。
4. なぜ attention score に softmax をかけるのですか?
softmax をかけることで、スコアを合計が1になる重みに変換します。softmax 前はただの関連度スコア(例:猫 3.0、私 1.0、が −1.0)ですが、softmax 後は確率のように解釈できる attention weight(例:猫 0.84、私 0.11、が 0.05)になります。これにより「どのトークンをどれくらい参照するか」を表せます。
5. Value はどのタイミングで使われますか?
Query と Key から attention weight を計算したあとに使われます。流れは、(1) Query と Key の内積を計算 → (2) softmax で attention weight に変換 → (3) attention weight で Value を重み付き平均、です。Value は最終出力を作るために混ぜ合わせる情報です。
6. Attention weight が大きい Value は、最終出力にどう影響しますか?
大きく反映されます。ある単語への weight が 0.8 なら、その単語の Value をかなり強く参照しているという意味です。最終出力は のように計算されます。
7. Self-Attention では、Q / K / V はどこから作られますか?
同じ入力ベクトル列から作られます。各トークンの埋め込みから、学習可能な重み行列で と変換します。同じ入力から作るのは、文中の各単語が同じ文中の他の単語を参照するためです。
8. Scaled Dot-Product Attention の「scaled」とは何ですか?
内積スコアを ( は Key の次元数)で割ることです。次元数が大きいほど内積の値も大きくなりやすく、そのまま softmax に入れると値の差が極端になり、ほぼ1つのトークンだけに注意が集中します。 で割ることで、softmax が極端になりすぎるのを防ぎ、学習を安定させます。
9. 内積アテンションと「単語同士の関連度」はどう関係していますか?
Query と Key の内積が単語同士の関連度を表します。「私は猫が好き」で「好き」が「猫」に強く attention する場合、「好き」の意味を作るときに「猫」の情報を強く参照している、という意味です。「好き」は何が好きなのか対象を理解する必要があるため、「猫」に高い weight が付くのは自然です。
10. 内積アテンションの出力は、Value のうち1つだけを選ぶものですか?
1つだけを選ぶものではありません。複数の Value を重み付き平均して混ぜ合わせたものです(例:)。softmax は複数のトークンに重みを分配するため、出力は複数の情報を合成したベクトルになります。
11. なぜ学習可能な重み行列で Q, K, V に変換するのでしょうか?
入力をそのまま使うと、すべての用途で同じ表現しか使えません。しかし同じ単語でも「何を探す側か」「何と照合される側か」「実際に取り出される情報か」で役割が違います。学習可能な行列で =探すための表現、=照合されるための表現、=出力に混ぜるための表現、に変換することで、モデルは「どの観点で関連を見るべきか」をタスクに合わせて学習できます。
12. Multi-Head Attention では、なぜ複数の head に分けるのでしょうか?
1つの attention だけだと1種類の関係しか見にくくなります。文の中には、主語と述語、修飾語と被修飾語、直前の単語、遠く離れた単語、文脈上重要な単語など、いろいろな関係があります。Multi-Head Attention では複数の head がそれぞれ異なる観点(例:head1=文法、head2=意味、head3=近くの単語、head4=遠くの単語)で attention を計算します。つまり Multi-Head Attention は、複数の視点から文脈を理解するための仕組みです。
まとめ
この章で学んだ内容は、すべて LLM の中で生きています。「数学そのもの」「数学で作る関数」「数学を扱う道具」を区別しておさらいしましょう。
🔢 数学そのもの
- ベクトル・テンソル … 言葉を数で表す入れ物
- 内積・コサイン類似度 … 単語どうしの関連度を測る
- 行列積・転置 … 大量の関連度を一括計算する
- 線形変換 … 重み行列で表現を作り変える
- 指数関数・Σ … 確率を作るための部品
- 微分・偏微分 … 学習(最適化)を可能にする
🧩 数学で作る関数
- シグモイド・softmax … exp と Σ を組み合わせ、スコアを確率(注目度)に変える
🛠️ 数学を扱う道具
- NumPy / PyTorch … 以上の計算をコードで実行するためのライブラリ
これらが組み合わさると、内積アテンション → Self-Attention → Multi-Head Attention → Transformer という LLM の中核へとつながります。次章以降では、この土台の上に実際の LLM を一歩ずつ組み立てていきます。