第5章

CNN と Transformer — データの構造を活かす設計

画像にはCNN、自然言語にはTransformer。データの構造に応じて専用のアーキテクチャを作るのが、現代深層学習の鉄則です。 なぜそうなるのか、それぞれが何をしているのかを、インタラクティブなデモで体感します。

この章の全体像

1

専用アーキテクチャが必要

全結合層だけでは データの構造 を活かせない。画像なら空間構造、文章なら系列構造を尊重する設計が必要。

2

CNN — 画像のために

小さな カーネルが画像上を滑る。同じカーネルを使い回すことで、パラメータ激減 + 平行移動不変性。

3

Transformer — 系列のために

Self-Attention で、系列内の任意の位置間の関係を直接学習。LLM(ChatGPT, Claude)の中核技術。

💡 核心: ニューラルネットの真価は 「データの構造に合わせた設計」 で発揮される。 CNNは 空間の局所性と平行移動不変性、Transformerは 任意位置間の関係性 を、それぞれ効率よく学習できる構造を持つ。

1. なぜ専用アーキテクチャが必要か

1.1 全結合層だけの問題点

第3章 で見たMLPは、入力をフラットなベクトルとして扱うもの。 画像のような2D構造のあるデータを扱うとき、いくつかの問題があります:

📝 具体例:MLPが画像を扱うときの3つの致命傷(クリックで展開)

問題1: パラメータが爆発する

例:224×224×3 のカラー画像を全結合層で処理すると、入力は 150,528 次元のベクトル。 これを 1024 次元の中間層に繋ぐと、第1層だけで 約 1.5 億個 のパラメータが必要。 数千万のニューロンを持つ深いネットワークでは現実的でない。

📐 コラム・イン・コラム:「224×224×3 → 150,528 次元」って何? 図でゆっくり
① まず「224×224×3」の意味

カラー画像1枚は、コンピュータの中で 3つの数字の組合せ(縦×横×色チャンネル) として表されます:

  • 224:縦のピクセル数(高さ)
  • 224:横のピクセル数(幅)
  • 3:色チャンネルの数 — R(赤), G(緑), B(青)の3枚を重ねたものがカラー画像
カラー画像 = 縦224 × 横224 × 3色チャンネル 🐱 カラー画像 (224 × 224) = R 赤チャンネル 224×224 + G 緑チャンネル 224×224 + B 青チャンネル 224×224 各チャンネルは 224×224 = 50,176個 の数値 = ピクセルの明るさ それが3枚重なって 合計 50,176 × 3 = 150,528 個の数値 = 1枚のカラー画像は「150,528個の数字の塊」として表現される
② 「次元のベクトル」 = 一直線に並べた数列

全結合層(MLP)は 「縦長の一直線の数値リスト」しか受け取れません。なので画像を扱うには、3次元の数値の塊(224×224×3)を 無理矢理 1列に並べ直す 操作(フラット化)が必要:

3次元(縦×横×色) → 1次元ベクトル へのフラット化 RGB画像 224×224×3 3次元の塊 フラット化 (reshape) 150,528次元の縦ベクトル Rチャンネル50,176個 Gチャンネル50,176個 Bチャンネル50,176個 合計 150,528 個の数字が一列に 0.4 0.7 0.2 x

計算チェック:$224 \times 224 \times 3 = 50{,}176 \times 3 = 150{,}528$。1枚の画像が150,528個の数字に変換されました。

③ 「全結合層で処理する」とは — 1.5億個のパラメータの正体

全結合層(fully connected layer)は、入力の 全ての数値 が、出力の 全てのニューロン1本ずつ独立した重み線で繋がっている 層のこと:

全結合層:入力の全数値 × 出力ニューロン数 = 重み数 入力 x_1 x_2 x_3 x_150528 150,528個 各入力 → 各出力 に1本 150,528 × 1024 本 中間層 1,024個 重みの総数 = 150,528 × 1,024 = 154,140,672 ≒ 1.5億個!
④ なぜ「爆発」と表現するか
  • たった 1層目だけ で 1.5 億個のパラメータ
  • ChatGPT級の深さ(数十〜数百層)でこの調子だと、すぐ 数兆個 に到達
  • 1パラメータあたり4バイトと仮定すると、1.5億 × 4 = 600 MB がこの1層のためだけにメモリに居座る
  • 学習データもそのぶん必要(パラメータの数倍以上のデータがないと過学習)→ 現実的に学習不可能

一方、後で見る CNN なら、同じ画像を扱うのに カーネル(例:3×3×3 = 27個)を共有 するので、第1層のパラメータは 数百個〜数千個 で済みます。桁違いの効率です。

💡 1行まとめ: 「224×224×3 → 150,528 次元」は、カラー画像を全結合層が読める一直線の数字リストに変換したサイズ。 それを 1024 個のニューロンに全部繋ぐと、重みが 150,528 × 1024 = 約1.5億個 必要になる、ということ。

問題2: 空間構造が失われる

画像をフラット化すると、「ピクセル(1,1)とピクセル(1,2)が隣接している」という情報が消える。 ネットワークはそれを ゼロから学ばないといけない

問題3: 平行移動不変性がない

「猫」が画像の左にあっても右にあっても同じ「猫」と認識したい。 しかし全結合層では、各位置に独立した重みがあるので、位置が変わると別物として扱われる。

1.2 解決策 — データの構造を尊重するネット

それぞれのデータ型に合った 専用設計 を作ります:

データ型 構造 適した設計 代表アーキテクチャ
画像・盤面 2D空間(隣接性、平行移動) 畳み込み CNN
系列(文章、時系列、音声) 1D系列(順序、長距離依存) Attention Transformer
表データ 独立した特徴の組 全結合 or 決定木 MLP / XGBoost
グラフ ノードとエッジ メッセージパッシング GNN(Graph NN)

2. CNN — 画像のためのニューラルネット

CNN(Convolutional Neural Network、畳み込みニューラルネット)は、画像認識の標準アーキテクチャ。 将棋のdlshogiも、囲碁のAlphaGoも、その中核技術。

2.1 カーネル(フィルタ)の概念

CNNの主役は カーネル(kernel)と呼ばれる小さな行列(典型的には3×3や5×5)。 これが 画像上を滑り抜けながら、各位置で 畳み込み演算 を実行します。

📌 まず「カーネル」とは何か — のぞき窓のメタファー

カーネルは 「3×3 の小さな数値の表」 です。これを のぞき窓 だと思ってください:

カーネルの数値の組み合わせは「採点用テンプレート」です。例えば「縦エッジを高く採点するテンプレート」「丸い形を高く採点するテンプレート」など、目的に応じて中身を変えます。

📌 「スライドする」とは — 全位置で点数を取る

のぞき窓を 左上から右へ1マスずつ、右端まで行ったら次の行へ、と移動させて、各位置で点数を取っていきます。これが「スライド」です。

カーネルは「左上 → 右 → 下」と1マスずつ動く ① 左上の位置 入力画像(5×5)に黄=3×3カーネル 出力(0,0) ↑記録 ② 1マス右へ → 右へ1マス移動 出力(0,1) ↑記録 ③ 1行目完了→2行目へ ↓ 右端到達→次行の左端 2行目開始 1マス動くごとに1つ点数を取り、それを 出力グリッド(特徴マップ)に記録。 全位置を走査し終わると、入力画像と同じ大きさの「点数マップ」が完成。

📌 1回の点数計算で何が起きるか — 具体的な数字で

ある1つの位置で 9個の掛け算と1個の足し算 をします。例えば「縦エッジ検出カーネル」を「縦線がある画像」に当てたとき:

1位置あたりの計算:9個の掛け算 → 全部足す 画像の3×3部分 0 9 0 0 9 0 0 9 0 × 縦エッジ検出カーネル -1 2 -1 -1 2 -1 -1 2 -1 = 各セルを掛けた結果 0 18 0 0 18 0 0 18 0 Σ 合計(その位置の点数) 54 高い点数! 縦線あり 計算式: (0×−1) + (9×2) + (0×−1) + (0×−1) + (9×2) + (0×−1) + (0×−1) + (9×2) + (0×−1) = 0 + 18 + 0 + 0 + 18 + 0 + 0 + 18 + 0 = 54 ✓ 縦線が中央に揃った位置で「54」と高い点数 → 「ここに縦線あり」のシグナル 縦線がない位置でやれば 0 に近い値になり、特徴マップに「ここは縦線なし」と記録される

カーネルの中身(採点ルール) によって、検出するパターンが変わります:

これら採点ルールは 人が設計する必要はなく、CNN が学習で自動獲得する のが革命的なポイント(§2.3で詳述)。

📐 数式で見る:畳み込み演算の定義(クリックで展開)

畳み込み演算の式

$$y[i, j] = \sum_{m, n} W[m, n] \cdot x[i+m, j+n] + b$$

この式を1つずつ言葉で翻訳:

  • $y[i, j]$ :「出力マップの $(i, j)$ セルの点数」を計算しているところ
  • $W[m, n]$ :カーネルの $(m, n)$ 番目の値(採点テンプレートの1マス)
  • $x[i+m, j+n]$ :画像の $(i+m, j+n)$ ピクセルの値(カーネルが今かぶさっている画像の1マス)
  • $\sum_{m, n}$ :カーネルの全 $m, n$(3×3 なら9マス)を足し合わせる
  • $b$ :バイアス(点数全体に足す調整値)

要するに 「カーネルと画像の重ね合わせた9マスを掛けて足す」だけ。$i, j$ を1ずつ動かしながら全位置で繰り返せば、特徴マップが完成。

2.2 インタラクティブデモ:畳み込みを目で見る

インタラクティブ・デモ
3×3カーネルが画像上をスライドする様子

カーネルの値を変えると、出力画像(特徴マップ)がリアルタイムで変わります。代表的なカーネルプリセットを試してみてください。

カーネル(3×3)の値

プリセット

入力画像

※ 28×28ピクセルの白黒画像で計算しています

入力画像

*

出力(畳み込み後)

左の画像 ⊛ カーネル = 右の特徴マップ

💡 試してみる

  • エッジ検出カーネル → 物体の輪郭が浮かび上がる
  • ぼかしカーネル → 画像が滑らかになる
  • Sobel-Xカーネル → 縦方向のエッジだけ強調
  • 恒等カーネル → 画像が変わらない(中央 = 1のみ)

実際のCNNでは、このカーネルの値も学習で自動決定される。第1層はエッジ検出、深い層は形を学習する、というのを勝手に獲得するのが革命。

2.3 CNNの2つの天才的アイデア

💡 アイデア1:局所性(Local Connectivity)

出力の1点は、入力の 周辺3×3領域だけ から決まる。全結合層と違い、遠くのピクセルとは直接繋がらない。

これは 画像の特徴が局所的 だという経験則を反映している。「猫の耳」「犬の鼻」のような特徴は、近くのピクセル群で構成される。

💡 アイデア2:パラメータ共有(Weight Sharing)

同じカーネル を画像のすべての位置で使い回す。3×3 = 9個の重み + バイアス1個、これだけで画像全体を処理できる。

これにより:

📊 補足図解:パラメータ数で全結合 vs 畳み込みを比較(クリックで展開)

2.4 パラメータ数で比較

150528次元(224×224×3 画像)→ 256チャンネル の中間層を作るとき:

方式 パラメータ数 比率
全結合層(フラット化) 約 38,500,000
畳み込み層(3×3 × 256ch) 約 7,000 約 1/5500

5500倍も効率的。これがCNNが画像認識を可能にした最大の理由。

2.5 階層的な特徴学習

CNNを多層に積み重ねると、層ごとに違う抽象度の特徴 を学ぶようになります:

📝 具体例:顔認識で見る、層ごとの学習内容(クリックで展開)
層の深さ 学習される特徴 例(顔認識の場合)
第1層 エッジ・色 縦線、横線、斜め線
中間層(浅め) パターン・テクスチャ 目の形、鼻のライン
中間層(深め) 部分 目、鼻、口
最終層 全体 顔全体

誰も「目」をプログラミングしていないのに、ネットが学習で自動的に獲得する。これが End-to-end learning の威力。

3. ResNet — 深いCNNを可能にした残差接続

CNNを 深く積み重ねると、より複雑なパターンを学習できる。しかし2014年頃まで、深いCNN(30層以上)は むしろ精度が落ちる という不思議な現象がありました。

📐 数学の補足:勾配消失問題はなぜ起きるのか(クリックで展開)

① 復習:勾配とは何だったか

第4章で見たように、勾配は「重み $W$ を少し動かしたら損失 $L$ がどれくらい変わるか」を表す数値($\dfrac{\partial L}{\partial W}$)。 学習はこの勾配の 逆方向 に重みを動かすことで進みます:

$$W_{\text{new}} = W_{\text{old}} - \eta \cdot \frac{\partial L}{\partial W}$$

ここで $\eta$(学習率)は小さい正の数(典型的に 0.001 など)。

② 勾配が「消える」とどうなる?

勾配 $\dfrac{\partial L}{\partial W} \approx 0$ になると:

$$W_{\text{new}} = W_{\text{old}} - \eta \cdot 0 = W_{\text{old}}$$

重みがまったく動かない学習が止まる。これが「勾配が消えると問題」の核心です。

具体例:学習率 $\eta = 0.001$、勾配が $0.0000266$($0.9^{100}$)のとき
→新しい重みの変化量 = $0.001 \times 0.0000266 = 0.0000000266$
→小数点以下8桁の変化。事実上ゼロ。1万回ステップ回しても、ほぼ変わらない。

③ なぜ勾配が消えるのか — 掛け算の連鎖

誤差逆伝播法(§4.2 で詳述)では、各層の勾配は 後段の勾配 × 自分の偏微分 という形で繋がっています:

$$\frac{\partial L}{\partial W_1} = \frac{\partial L}{\partial y} \cdot \frac{\partial y}{\partial h_n} \cdot \frac{\partial h_n}{\partial h_{n-1}} \cdots \frac{\partial h_2}{\partial h_1} \cdot \frac{\partial h_1}{\partial W_1}$$

各偏微分項が 1未満の値(例:Sigmoid 活性化なら最大でも 0.25 程度)だと、層が深くなるほど 掛け算で指数的に小さくなる

層数各層の偏微分 $\approx 0.9$ のとき各層の偏微分 $\approx 0.5$ のとき
10層$0.9^{10} \approx 0.35$$0.5^{10} \approx 0.001$
50層$0.9^{50} \approx 0.005$$0.5^{50} \approx 10^{-15}$
100層$0.9^{100} \approx 0.0000266$$0.5^{100} \approx 10^{-30}$

100層もあると、入力側に近い $W_1$ の勾配は 事実上ゼロ になります。

④ 何が実際に起きるか — アニメで見る

下の図は 勾配が右から左に流れていく様子 をアニメーションで表現したもの。 赤いパルス(=勾配の信号)が出力側(右)で発生し、左に伝わるたびに ×0.9 されて細くなっていきます。 入力側(左端)に着くころには 事実上消滅

勾配パルスが層を遡るたびに ×0.9 で減衰していく W_1 勾配 ≈ 0 💀 学習 されない W_2 ≈ 0 😵 W_3 少しだけ 😴 W_50 中ぐらい 🙂 W_99 大きい 😊 W_100 大きい 😃 バリバリ 学習中 L 損失 🎯 逆伝播:勾配が右から左へ伝わる(途中で消える) 勾配の大きさ:

⑤ なぜそれが「有害」なのか

勾配消失が起きると、深いネットは 「浅いネットより悪化する」 という奇妙な状態になります:

  • 浅い層(入力に近い)の重みが固まる:低次の特徴(エッジなど)も学習できない
  • 固まった重みの上に組み立てられる中間〜出力層は、貧弱な特徴をベースに学習せざるを得ない
  • 結果として、深くした分は無駄+足を引っ張る(パラメータが増えただけで、初期値のランダム値が残るのと同じ)

深いネットの理論的な表現力は浅いネットより高いはずなのに、勾配消失のせいで実現できない — これが2014年頃までの深層学習の壁でした。ResNet(§3.2) が「ショートカットで勾配を直接届ける」アイデアでこの壁を打ち破ります。

💡 1行まとめ勾配が消える=重みが動かない=学習しない。 100層のNNだと入力側の重みは初期ランダム値のまま固定 → 深さの利点が活かせず、むしろ害になる。

3.2 残差接続(Residual Connection)

2015年、Microsoft の He らが ResNet を提案。アイデアは単純:

残差接続:層の入力を、層の出力にそのまま足す。

$$y = F(x) + x$$

ここで $F(x)$ は通常のCNN層、$+x$ が ショートカット(スキップ接続)。

📊 通常層 vs 残差ブロック — 1枚で見る違い

なぜ「ただ足す」だけで勾配消失が解決するのか。並べて見ると一目瞭然:

通常層と残差ブロックの構造比較 ❌ 通常の層(勾配消失する) 入力 x F(x) Conv→BN ReLU→Conv y = F(x) ∂y/∂x = ∂F/∂x ←1未満なら必ず小さくなる vs ✅ 残差ブロック(勾配が直接流れる) 入力 x F(x) Conv→BN ReLU→Conv ショートカット (skip connection) x をそのまま + y = F(x) + x ∂y/∂x = ∂F/∂x + 1 ←「+1」が勾配を保証!

🧮 数学的に何が起きているか — 「+1」の魔法

残差接続が勾配消失を解決する核心は、連鎖律で計算した勾配の式に「+1」が常に居る ことです。

順伝播 $y$逆伝播 $\dfrac{\partial y}{\partial x}$
通常層 $y = F(x)$ $\dfrac{\partial F}{\partial x}$(典型 < 1)→ 掛け算で小さくなる
残差ブロック $y = F(x) + x$ $\dfrac{\partial F}{\partial x} \mathbf{+ 1}$ → 最低でも「1」は確保

連鎖律で100層分掛け合わせるとき、通常層なら $0.9 \times 0.9 \times \cdots \times 0.9 \approx 0.0000266$ で消えてしまうのに、 残差ブロックなら各層で $(0.9 + 1) = 1.9$ になるため、100層通っても 消えるどころか膨らんでしまうくらい。

🌊 アニメで見る — 勾配が「高速道路」を通って届く

ResNet の本質は、勾配が深いところにも「高速道路(=ショートカット)」を通って直送される こと。下のアニメで見ると分かりやすい:

勾配がショートカットを通って入力側まで届く ResBlock 1 F(x)+x ResBlock 2 F(x)+x ResBlock 3 F(x)+x ResBlock 4 F(x)+x ResBlock 5 F(x)+x L 🎯 🛣️ ショートカット(勾配の高速道路) 🐢 主経路(F(x)を通る道。減衰する) 緑の高速道路(ショートカット)の勾配はほとんど減衰しない 赤の主経路(F(x))の勾配は層を通るたびに小さくなる → 「両方足したもの」が実際の勾配なので、最低でも緑のぶんは確保される どんなに深くしても勾配消失が起きない

🤔 なぜ「残差」と呼ぶのか

$y = F(x) + x$ を変形すると、$F(x) = y - x$ となります。つまり $F$ は「入力 $x$ と目標出力 $y$ の差(=残差)」を学習している ことになります。

何も学ばなくても $x$ がそのまま伝わるのが残差ブロックの「初期状態」。 これは「最悪でも前と同じ性能」を保証するので、層を増やしても精度が下がりにくい性質を持ちます。

この単純な工夫で、勾配がショートカット経由で常に流れるため、勾配消失が起きない。結果、150層、1000層といった超深層が学習可能に。

3.3 効果

ResNetは ImageNet 2015 で エラー率3.6% を達成し、人間(5%)を初めて超えました。 それ以降、深層CNNの定番アーキテクチャとして、画像認識・物体検出・将棋AI(dlshogi)まで広く使われています。

💡 意外と単純: ResNetの「革命」は、ただ 「層の入力を出力に足す」 だけの工夫。 しかしこれだけで、深層学習の歴史が決定的に変わった。

4. Transformer — Attention機構の革命

2017年、Google の論文 "Attention is All You Need"Transformer が提案されました。 自然言語処理を中心に、画像・音声・将棋まで波及した、現代AI最大の発明。 ChatGPT、Claude、Gemini はすべてTransformerベース

📚 歴史的背景:Transformer以前、RNN/LSTMが何に苦しんでいたか(クリックで展開)

4.1 RNNの限界

Transformer以前の系列モデルは RNN(リカレントNN)/ LSTM でした。 これは「1単語ずつ順番に処理」する構造:

"猫が魚を食べた"
   ↓
"猫" → 状態1 → "が" → 状態2 → "魚" → 状態3 → "を" → 状態4 → "食べた" → 状態5

問題:

  • 並列化できない(順番に処理する必要がある)
  • 長距離の依存関係が弱い(最初の単語の情報が、長い系列を経るとぼやける)
  • 学習が遅い

4.2 Attention機構の発明

Transformerの中核は Self-Attention(自己注意)。 「各単語が、他のどの単語と どれくらい関連するか を直接計算する」仕組み。

:「猫が魚を食べた」

「食べた」という単語を理解するとき:

これらの「関連度(Attention重み)」を計算し、関連度の高い単語の情報を多く取り込む。

4.3 インタラクティブデモ:Attention重みの可視化

インタラクティブ・デモ
Self-Attention で単語間の関係を見る

単語をクリックすると、その単語が 他の単語にどれくらい注目しているか(=Attention重み)が色で可視化されます。 文章を切り替えて、英語・日本語の両方で試してみてください。

文章を選択

クリックした単語からの注目度が、他の単語の背景色の濃さで表示されます。

💡 気づき

  • 「食べた」をクリックすると、主語「猫」と目的語「魚」が強く光る
  • 動詞は通常、主語と目的語に注目する
  • 実際のLLMでは、これを何百層も重ねて、複雑な意味理解を実現している

※ このデモのAttention重みは説明用に簡略化したもの。実際のLLMでは学習で決まる。

📐 数学の補足:Self-Attentionの数式(Query/Key/Value)(クリックで展開)

4.4 Self-Attentionの数式

実際の計算は、各単語に対して 3つのベクトル を作って行います:

  • Query($Q$): 「何を探しているか」
  • Key($K$): 「自分は何者か」
  • Value($V$): 「自分が持っている情報」

Attention重みは「Query と Key の類似度」で決まり、Value を重み付けして取得:

$$\text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right) V$$

Q, K, V は学習で決まる重み行列によって、入力から作られる。

4.5 Transformerが解いた3つの問題

問題 RNNの場合 Transformerの場合
並列化 不可能(順次処理) 全位置を並列計算 ✅
長距離依存 弱い(情報が薄れる) 直接アクセス可能 ✅
学習速度 遅い 桁違いに速い ✅

この3つを一気に解決したことで、大規模言語モデル(LLM)が可能になりました。 GPT-3(1750億パラメータ)以降の巨大化路線は、Transformerだから可能。

📚 歴史的背景:Transformerが侵食した領域(2017〜2023年表)(クリックで展開)

4.6 Transformerが侵食した領域

  • 2017: 機械翻訳(元論文)
  • 2018: 自然言語理解(BERT)、生成(GPT-1)
  • 2020: 画像認識(ViT, Vision Transformer)
  • 2021: タンパク質構造予測(AlphaFold2)
  • 2022: 一般人にAIが浸透(ChatGPT)
  • 2023〜: マルチモーダル(画像+言語、音声+言語)

「言語のためのアーキテクチャ」として生まれたが、今や あらゆる分野の事実上の標準 になっている。

5. 比較表 — どれをいつ使うか

アーキテクチャ 得意 仕組みの核心 代表用途
MLP 表データ、低次元入力 全結合層 NNUE評価関数、回帰、分類
CNN 画像、盤面 畳み込み + プーリング ImageNet、AlphaGo、dlshogi
RNN/LSTM 時系列、系列(旧主流) 状態の引き継ぎ 古い翻訳、音声認識(過去の主流)
Transformer 自然言語、画像、音声、何でも Self-Attention ChatGPT、Claude、ViT、AlphaFold
GNN グラフ、分子、ソーシャル メッセージパッシング 創薬、推薦システム

💡 使い分けの原則データの構造を見て、それを尊重するアーキテクチャを選ぶ。 空間構造があれば CNN、系列構造があれば Transformer、独立した特徴なら MLP。

6. JavaScriptで一気に比較(プログラマ向け)

📝 章末ボーナス:JavaScriptで4アーキテクチャを並べて見る(プログラマ向け)(クリックで展開)

ここまでの違いを JavaScript で並べると、各アーキテクチャの本質が一目で分かります。 数式や概念より、コードのほうが理解しやすいプログラマ向けの章末ボーナスです。

すべて 「forward pass(順伝播)」 の実装イメージ。 実際は tf.jsONNX などのライブラリが行列演算を最適化していますが、ここでは 構造の対比 に集中するため 素のJSで愚直に 書いています。 補助関数 matVecMul / matMul / transpose / softmax などは別途定義されている前提です。

6.1 MLP — 全結合の基本

📌 入力:1次元配列(ベクトル)
📌 演算:行列ベクトル積を直列に重ねる

// MLP の順伝播
// x:       number[]       — 入力ベクトル
// weights: number[][][]   — 各層の重み行列 [layer][out][in]
// biases:  number[][]     — 各層のバイアス [layer][out]
function mlpForward(x, weights, biases) {
  let h = x;
  for (let layer = 0; layer < weights.length; layer++) {
    h = matVecMul(weights[layer], h);         // 行列ベクトル積(全結合)
    h = h.map((v, i) => v + biases[layer][i]);
    h = h.map(v => Math.max(0, v));           // ReLU 活性化
  }
  return h;                                    // 出力ベクトル
}

全結合:出力の各成分が、入力の全成分から影響を受ける。空間構造の概念がない。

6.2 CNN — カーネルが画像上を滑る

📌 入力:3次元配列 (H × W × C_in) の画像
📌 演算:小さなカーネルを画像全体でスライド(パラメータ共有)

// 2次元畳み込みの順伝播
// x:       number[H][W][C_in]              — 画像
// kernels: number[K][K][C_in][C_out]       — 小さなフィルタ
// biases:  number[C_out]
function conv2dForward(x, kernels, biases) {
  const [H, W, Cin] = [x.length, x[0].length, x[0][0].length];
  const K = kernels.length;
  const Cout = kernels[0][0][0].length;

  const out = createZeros3D(H - K + 1, W - K + 1, Cout);

  for (let i = 0; i < H - K + 1; i++) {              // 画像をスライド
    for (let j = 0; j < W - K + 1; j++) {
      for (let cOut = 0; cOut < Cout; cOut++) {
        let sum = 0;
        // ★ 同じカーネルを画像全体で使い回す(パラメータ共有)
        for (let di = 0; di < K; di++)
          for (let dj = 0; dj < K; dj++)
            for (let cIn = 0; cIn < Cin; cIn++)
              sum += x[i + di][j + dj][cIn] * kernels[di][dj][cIn][cOut];
        out[i][j][cOut] = Math.max(0, sum + biases[cOut]);  // ReLU
      }
    }
  }
  return out;                                          // (H-K+1) × (W-K+1) × Cout
}

局所性 + パラメータ共有:カーネルは小さい範囲しか見ないが、それを 全位置で使い回す。 「猫の耳」が画像のどこにあっても認識できる平行移動不変性の正体。

6.3 RNN — 時刻ループで状態を持ち越す

📌 入力:2次元配列 (T 時刻 × n_input)
📌 演算:1時刻ずつ処理。前の状態を引き継ぐ

// RNN の順伝播
// xSequence: number[T][nInput]             — T時刻の系列
// Wxh:       number[nHidden][nInput]
// Whh:       number[nHidden][nHidden]      — 隠れ状態の自己結合
// bh:        number[nHidden]
function rnnForward(xSequence, Wxh, Whh, bh) {
  const T = xSequence.length;
  const nHidden = Wxh.length;
  let h = new Array(nHidden).fill(0);                // 初期状態
  const outputs = [];

  for (let t = 0; t < T; t++) {                      // ★ 時刻ループ(並列化不可)
    const xt = xSequence[t];
    // 前の状態 h と現在の入力 xt から、新しい状態 h を作る
    const Wxh_xt = matVecMul(Wxh, xt);
    const Whh_h  = matVecMul(Whh, h);
    h = Wxh_xt.map((v, i) => Math.tanh(v + Whh_h[i] + bh[i]));
    outputs.push(h);
  }
  return outputs;                                     // T × nHidden
}

時間軸の再帰for (let t = 0; t < T; t++) ループの中で、前の h を使って次の h を計算。 この 順次処理が必須 なので、並列化できない(=学習が遅い、長距離依存が弱い)。

6.4 Transformer (Self-Attention) — 全位置を一発で並列計算

📌 入力:系列データ(T × d_model)
📌 演算:Q・K・V から全位置間の関係を行列演算で一気に

// Self-Attention の順伝播
// xSequence: number[T][dModel]             — T位置の系列
// Wq, Wk, Wv: 重み行列(Q/K/V を作る)
function selfAttentionForward(xSequence, Wq, Wk, Wv) {
  const dK = Wq[0].length;

  // ★ 各位置の Q, K, V を一発で並列計算(時刻ループなし!)
  const Q = matMul(xSequence, Wq);                   // T × dK
  const K = matMul(xSequence, Wk);                   // T × dK
  const V = matMul(xSequence, Wv);                   // T × dV

  // ★ 全位置間の Attention スコアを行列演算で一気に
  const scores = matMul(Q, transpose(K))             // T × T
    .map(row => row.map(v => v / Math.sqrt(dK)));
  const attnWeights = scores.map(softmax);           // 行ごとに softmax

  // 重み付き和
  return matMul(attnWeights, V);                     // T × dV
}

並列化と長距離依存:時刻ループがなく、すべての位置間の関係を 1回の行列演算 で取れる。 位置 1 と位置 1000 の関係も、隣同士と同じコストで学べる(RNNなら情報が薄れる)。これが Transformer の革命。

6.5 4アーキテクチャの違いを表で

アーキテクチャ 入力の形 核心ループ 並列化 パラメータ共有
MLP (n,) 1次元 層の直列 なし
CNN (H, W, C) 2D + ch 空間スライド (i, j) ★ カーネルを位置で共有
RNN (T, n) 系列 ★ 時刻 t(順次) ×(時刻方向) ★ 重みを時刻で共有
Transformer (T, d) 系列 行列演算(ループなし) ○ 全位置同時 ★ Q/K/V重みを位置で共有

💡 プログラマ的に見る本質

  • MLP:層の直列。最もシンプル
  • CNN空間ループ(i, j)で同じカーネルを使い回す
  • RNN時刻ループ(t)で前の状態を持ち越す。並列化不可
  • Transformer:ループなし、全部 行列演算。GPUで爆速

これが「なぜTransformerが学習で速い・長文を扱えるのか」の答えです。
ループの有無と、共有される重みのスコープが、各アーキテクチャの本質。

6.6 NNUE(将棋AI)のJavaScript版

第1章第6章 で何度も登場した NNUE は、本質的に MLP です。ただし、差分更新 という工夫があります:

// NNUE の評価関数
// position:        現在の局面
// prevAccumulator: 前局面の第1層活性化(メモ化、再利用する)
function nnueEvaluate(position, prevAccumulator) {
  // ★ 工夫1: 差分更新 — 全再計算しない
  const diff = computeChangedFeatures(position);   // 変化した特徴ビットだけ抽出
  const accumulator = [...prevAccumulator];
  for (const idx of diff.added)   for (let k = 0; k < accumulator.length; k++) accumulator[k] += W1[k][idx];
  for (const idx of diff.removed) for (let k = 0; k < accumulator.length; k++) accumulator[k] -= W1[k][idx];

  // 工夫2: あとは普通のMLP(ただし整数演算 int8/int16)
  const h1 = clippedRelu(accumulator);
  const h2 = clippedRelu(vecAdd(matVecMul(W2, h1), b2));
  const h3 = clippedRelu(vecAdd(matVecMul(W3, h2), b3));
  const score = dot(W4, h3) + b4;                  // スカラー出力

  return { score, accumulator };                   // accumulator は次局面に持ち越す
}

これがCPU上で 秒間数百万局面 を可能にする理由。「1手進めるたびに変化する数ビットだけW1の列を加減算する」 という、極めてプログラマ的な工夫です。

まとめ

  1. データの構造に応じた専用設計が、深層学習の精度を決める
  2. CNN:小さなカーネルが画像上を滑る。局所性 + パラメータ共有でパラメータ激減 + 平行移動不変性
  3. ResNet:層の入力を出力に足す残差接続で、勾配消失を防ぎ深い学習を可能に
  4. TransformerSelf-Attentionで系列内の任意位置間の関係を直接学習。並列化と長距離依存を一気に解決
  5. Transformerは 言語以外にも侵食 し、現代AIの事実上の標準

次の章

ここまでで、深層学習の代表アーキテクチャがすべて出揃いました。 次は、これらを組み合わせて 新しいデータを生み出す「生成AI」 の世界を見ていきます。 ChatGPT、画像生成、Diffusion Model などの中身を理解しましょう。

(第6章「生成AI」準備中)