AI Hints
Home›Deep Learning Tutorial›Training Your GPT
Deep Learning · Lesson 35 of 92

Training Your GPT

Teaching it to write.

⏱ 3 min read▶ Video at 1:10:48🎓 Free
Watch this lesson in the Deep Learning courseWatch on YouTube ↗Full Deep Learning course

Everything comes together now. We give our network some of the most famous lines ever written, and we teach it to predict the next character. At the start, it will produce nothing but noise. A few minutes later, on a laptop, it will start to write.

Training Your GPT
From the video: Training Your GPT
  • Famous lines as training data
  • Predict the next character
  • From noise to writing

The training run

The data is our famous text, turned into numbers. Each batch takes random chunks, and the target is simply the same chunk, shifted one place to the right. That's the key to language models. Every position in the text is a little exercise in next character prediction. The first loss should be near the log of our vocabulary size, the sanity check that you learned earlier. Then we train, and we ask it to write. Run it. The loss falls steeply. And look at the output! It's writing words, spaces, and phrases, all learned from nothing but raw characters. Nobody taught it spelling.

train_gpt.pypython
import math
import torch
from torch import nn

class Block(nn.Module):
    def __init__(self, dim, heads):
        super().__init__()
        self.attn = nn.MultiheadAttention(dim, heads, batch_first=True)
        self.mlp = nn.Sequential(nn.Linear(dim, 4 * dim), nn.GELU(),
                                 nn.Linear(4 * dim, dim))
        self.norm1, self.norm2 = nn.LayerNorm(dim), nn.LayerNorm(dim)
    def forward(self, x, mask):
        y = self.norm1(x)
        x = x + self.attn(y, y, y, attn_mask=mask, need_weights=False)[0]
        return x + self.mlp(self.norm2(x))

class TinyGPT(nn.Module):
    def __init__(self, vocab, block_size=48, dim=64, heads=4, layers=3):
        super().__init__()
        self.block_size = block_size
        self.tok = nn.Embedding(vocab, dim)
        self.pos = nn.Embedding(block_size, dim)
        self.blocks = nn.ModuleList(Block(dim, heads) for _ in range(layers))
        self.norm = nn.LayerNorm(dim)
        self.head = nn.Linear(dim, vocab)
    def forward(self, ids):
        T = ids.size(1)
        x = self.tok(ids) + self.pos(torch.arange(T))
        mask = torch.triu(torch.ones(T, T, dtype=torch.bool), 1)
        for b in self.blocks:
            x = b(x, mask)
        return self.head(self.norm(x))

    @torch.no_grad()
    def generate(self, ids, n, temperature=1.0):
        for _ in range(n):
            logits = self(ids[:, -self.block_size:])[:, -1] / temperature
            next_id = torch.multinomial(logits.softmax(-1), 1)
            ids = torch.cat([ids, next_id], dim=1)
        return ids

text = ("to be or not to be that is the question. "
        "the slings and arrows of outrageous fortune. "
        "to sleep perchance to dream. " * 30 +
        "all the world is a stage and all the men and women merely players. "
        "the fault is not in our stars but in ourselves. " * 30)
chars = sorted(set(text))
stoi = {c: i for i, c in enumerate(chars)}
data = torch.tensor([stoi[c] for c in text])

torch.manual_seed(0)
model = TinyGPT(len(chars))
opt = torch.optim.AdamW(model.parameters(), lr=2e-3)
print(f"Vocabulary {len(chars)}, first loss should be near {math.log(len(chars)):.2f}")

for step in range(301):
    i = torch.randint(0, len(data) - 49, (32,))
    x = torch.stack([data[j:j + 48] for j in i])
    y = torch.stack([data[j + 1:j + 49] for j in i])
    loss = nn.functional.cross_entropy(model(x).transpose(1, 2), y)
    opt.zero_grad(); loss.backward(); opt.step()
    if step % 100 == 0:
        print(f"step {step}  loss {loss.item():.3f}")

out = model.generate(torch.tensor([[stoi["t"]]]), 90, temperature=0.8)
print("".join(chars[i] for i in out[0].tolist()))
Output
Vocabulary 24, first loss should be near 3.18
step 0  loss 3.373
step 100  loss 0.463
step 200  loss 0.101
step 300  loss 0.084
t is the question. the slings and arrows of outrageous fortune. to sleep perchance to dream

How it picks the next word

How does generation work? The model reads what's been written so far, and gives a chance for every possible next character. We sample one, add it to the text, and go round again. That's all. One token at a time. Chatbots do exactly this, using word pieces instead of characters, which is why you see their answers appearing bit by bit.

Temperature

One small dial changes the personality of the model. Temperature divides the scores before softmax. A low temperature makes the top choice dominate, so it's safe, but repetitive. A high one flattens the chances, so it gets creative, and then chaotic. We train a model, then generate with three temperatures. Run it. Look at the three samples. Point three repeats familiar phrases. Point eight is the sweet spot. And one point eight falls apart. Every chatbot has this dial, under the hood.

temperature.pypython
import math
import torch
from torch import nn

class Block(nn.Module):
    def __init__(self, dim, heads):
        super().__init__()
        self.attn = nn.MultiheadAttention(dim, heads, batch_first=True)
        self.mlp = nn.Sequential(nn.Linear(dim, 4 * dim), nn.GELU(),
                                 nn.Linear(4 * dim, dim))
        self.norm1, self.norm2 = nn.LayerNorm(dim), nn.LayerNorm(dim)
    def forward(self, x, mask):
        y = self.norm1(x)
        x = x + self.attn(y, y, y, attn_mask=mask, need_weights=False)[0]
        return x + self.mlp(self.norm2(x))

class TinyGPT(nn.Module):
    def __init__(self, vocab, block_size=48, dim=64, heads=4, layers=3):
        super().__init__()
        self.block_size = block_size
        self.tok = nn.Embedding(vocab, dim)
        self.pos = nn.Embedding(block_size, dim)
        self.blocks = nn.ModuleList(Block(dim, heads) for _ in range(layers))
        self.norm = nn.LayerNorm(dim)
        self.head = nn.Linear(dim, vocab)
    def forward(self, ids):
        T = ids.size(1)
        x = self.tok(ids) + self.pos(torch.arange(T))
        mask = torch.triu(torch.ones(T, T, dtype=torch.bool), 1)
        for b in self.blocks:
            x = b(x, mask)
        return self.head(self.norm(x))

    @torch.no_grad()
    def generate(self, ids, n, temperature=1.0):
        for _ in range(n):
            logits = self(ids[:, -self.block_size:])[:, -1] / temperature
            next_id = torch.multinomial(logits.softmax(-1), 1)
            ids = torch.cat([ids, next_id], dim=1)
        return ids

text = ("to be or not to be that is the question. "
        "the slings and arrows of outrageous fortune. "
        "to sleep perchance to dream. " * 30 +
        "all the world is a stage and all the men and women merely players. "
        "the fault is not in our stars but in ourselves. " * 30)
chars = sorted(set(text))
stoi = {c: i for i, c in enumerate(chars)}
data = torch.tensor([stoi[c] for c in text])

torch.manual_seed(0)
model = TinyGPT(len(chars))
opt = torch.optim.AdamW(model.parameters(), lr=2e-3)
for step in range(220):
    i = torch.randint(0, len(data) - 49, (32,))
    x = torch.stack([data[j:j + 48] for j in i])
    y = torch.stack([data[j + 1:j + 49] for j in i])
    loss = nn.functional.cross_entropy(model(x).transpose(1, 2), y)
    opt.zero_grad(); loss.backward(); opt.step()

for t in [0.3, 0.8, 1.8]:
    torch.manual_seed(1)
    out = model.generate(torch.tensor([[stoi["t"]]]), 60, temperature=t)
    print(f"temperature {t}:")
    print(" ", "".join(chars[i] for i in out[0].tolist()))
Output
temperature 0.3:
  t is not in our stars but in ourselves. all the world is a st
temperature 0.8:
  t is not in our stars but in ourselves. all the world is a st
temperature 1.8:
  t all the mefan world en hen womeousln mene. at wmerlmelyerlt

Summary

You've built and trained a real G P T. It learned to write, from characters alone, in minutes, on a laptop. What separates this from the giants? Mostly scale. Next, what it really takes to go from tiny to enormous.

Key takeaways
  • Targets are the text shifted by one
  • Generate one token at a time
  • Temperature sets creativity
Next up: From Tiny to Giant — how big models really differ