GPTをゼロから作って理解する完全ガイド

ChatGPTを毎日使っているのに、「中身がどうなっているか全くわからない」という方は多いのではないでしょうか。ブラックボックスとして使うだけでは、プロンプトエンジニアリングの限界に気づかなかったり、AIツールの選定で迷ったりすることがあります。

OpenAIの元研究員で「nanoGPT」の作者でもあるAndrej Karpathyは、GPTをゼロからコードで実装する方法を丁寧に解説しています。本記事では、その学習アプローチをもとに、Transformerの核心を自分の手で実装しながら理解するためのロードマップをお届けします。


なぜ「作って理解する」アプローチが最強なのか

AIの論文を読んでも、「なんとなくわかった気がする」で終わってしまう経験はありませんか?理論の理解だけでは、実際のモデル動作やトラブルシューティングには限界があります。

Karpathyが提唱する**「spelled out(手順を声に出して説明しながら実装する)」**アプローチの強みは次の3点です。

  • 理解の穴を即座に発見できる:コードが動かない=理解が不完全な箇所
  • 直感的な数値感覚が身につく:テンソルの形状変化を目で追える
  • 論文と実装のギャップを埋められる:「Attention is All You Need」の数式がコードに対応する

小さなモデルでも本物のGPTと同じ原理で動く点が、この学習法の最大のメリットです。


実装の全体像:7つのステップ

GPTのスクラッチ実装は、大きく以下のステップで進みます。

ステップ1:データの準備とトークン化

まず、テキストデータ(たとえばシェイクスピア全集)を読み込み、文字レベルのトークン化を行います。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# テキストを読み込み、ユニークな文字の語彙を作成
with open('input.txt', 'r', encoding='utf-8') as f:
    text = f.read()

chars = sorted(list(set(text)))
vocab_size = len(chars)

# 文字→整数、整数→文字の変換辞書
stoi = { ch:i for i,ch in enumerate(chars) }
itos = { i:ch for i,ch in enumerate(chars) }
encode = lambda s: [stoi[c] for c in s]
decode = lambda l: ''.join([itos[i] for i in l])

ここで重要なのは、トークン化の粒度がモデルの語彙サイズと学習効率に直結するという点。実際のGPT-4はByte Pair Encoding(BPE)という手法で数万規模の語彙を扱っています。

ステップ2:バイグラムモデルで最初のベースラインを作る

最初からTransformerを実装するのではなく、まずバイグラム言語モデル(前の1文字だけを見て次の文字を予測する)をベースラインとして構築します。これにより、言語モデルの損失計算や生成ロジックの「型」を習得できます。

ステップ3:セルフアテンションの核心を実装する

GPTの心臓部がセルフアテンションです。Karpathyは4段階で段階的に実装を発展させていきます。

  1. for loopによる素朴な平均化(過去のトークンを単純平均)
  2. 行列積による高速化(同じ計算をベクトル演算で)
  3. Softmaxの導入(重みの正規化)
  4. Key・Query・Valueによる本物のアテンション

最終的なセルフアテンションの計算式は次のようになります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# 1ヘッドのセルフアテンション
class Head(nn.Module):
    def __init__(self, head_size):
        super().__init__()
        self.key   = nn.Linear(n_embd, head_size, bias=False)
        self.query = nn.Linear(n_embd, head_size, bias=False)
        self.value = nn.Linear(n_embd, head_size, bias=False)
        self.register_buffer('tril', torch.tril(torch.ones(block_size, block_size)))

    def forward(self, x):
        B, T, C = x.shape
        k = self.key(x)    # (B, T, head_size)
        q = self.query(x)  # (B, T, head_size)
        # アテンションスコアの計算(スケーリングあり)
        wei = q @ k.transpose(-2, -1) * k.shape[-1]**-0.5
        wei = wei.masked_fill(self.tril[:T, :T] == 0, float('-inf'))
        wei = F.softmax(wei, dim=-1)
        v = self.value(x)
        return wei @ v

**「なぜsqrt(head_size)で割るのか?」**という疑問が生まれたら、それが深い理解への入口です。答えは「head_sizeが大きいとdot productの分散が増大し、softmaxが極端に尖ってしまうから」。こういった「なぜ?」をコードと数式で確認できるのが、スクラッチ実装の醍醐味です。

ステップ4〜7:Transformerブロックの組み立て

セルフアテンションを理解したら、残りのピースを組み合わせます。

コンポーネント役割
マルチヘッドアテンション複数の視点から関係性を並列に学習
フィードフォワード層各トークンの情報を非線形変換
残差接続勾配消失を防ぎ、深いネットワークを安定化
LayerNorm各層の出力を正規化して学習を安定化
Dropout過学習を防ぐ正則化手法

これらを積み重ねてGPTLanguageModelクラスを完成させると、シェイクスピア風のテキストを自動生成できるようになります。


ChatGPTとの繋がりを理解する

自作のミニGPTとChatGPTの構造的な差は、ほぼスケール(規模)のみです。

  • 自作nanoGPT:数十万〜数百万パラメータ、シェイクスピアで学習
  • GPT-3:1,750億パラメータ、数千億トークンのウェブテキストで事前学習
  • ChatGPT:GPT-3.5をベースに、**RLHF(人間フィードバックからの強化学習)**でファインチューニング

RLHFとは、人間の評価者が「より良い回答」を選ぶことで、モデルを人間の好みに合わせていく手法です。ChatGPTが会話に自然に応答できるのは、この事前学習+ファインチューニングの二段階プロセスのおかげです。

スケールアップすれば、仕組みは同じ——これを理解することで、新しいLLMが登場しても「どう評価すべきか」の軸が生まれます。


実践してみよう:推奨学習ルート

実際に手を動かすためのロードマップを示します。

  1. 環境準備:Google ColabまたはローカルにPyTorchをインストール
  2. nanoGPTリポジトリをclonegithub.com/karpathy/nanoGPT
  3. チュートリアル用Colabノートブックを実行:Karpathyが公開しているノートブックをステップごとに実行
  4. 自分のデータで学習:好きなテキスト(小説・歌詞・コードなど)でモデルを訓練してみる
  5. 論文を読む:「Attention is All You Need」(2017)の数式がコードと一致する感動を味わう
1
2
3
4
5
6
7
8
# nanoGPTのセットアップ例
git clone https://github.com/karpathy/nanoGPT.git
cd nanoGPT
pip install torch numpy transformers datasets tiktoken wandb tqdm

# シェイクスピアデータの準備と学習
python data/shakespeare_char/prepare.py
python train.py config/train_shakespeare_char.py

まとめ:「作れる人」になることのメリット

GPTをスクラッチで実装できるレベルになると、次のような変化が起きます。

  • AIツールの選定眼が磨かれる:モデルの得意・不得意の理由がわかる
  • プロンプト設計が深まる:アテンション機構を意識した情報の与え方ができる
  • 技術的な会話に参加できる:エンジニアやAI研究者との議論の土台ができる

「ChatGPTを使う人」から「ChatGPTを理解して使う人」へ——その第一歩は、意外にも数百行のPyTorchコードから始まります。

まずはGoogle ColabでKarpathyのノートブックを開いて、最初のセルを実行してみましょう。 理解は、読むことではなく「動かすこと」から始まります。