reading this record
code is folded so the argument and saved outputs come first. expand any cell to inspect the implementation. saved outputs are historical evidence from the source notebook, not a new run or independent verification.
Bigram language model¶
Prepare the Tiny Shakespeare token stream. The next step is to build context-window (x, y) samples.
inspect code · cell 01
In [1]:
import sys
from pathlib import Path
repo_root = Path.cwd()
if not (repo_root / "data").exists():
repo_root = repo_root.parent
sys.path.insert(0, str(repo_root))
from src.dataset import get_batch, load_tiny_shakespeare_tokens, split_token_stream
from src.tokenizer import bpe_decode
Load and encode Tiny Shakespeare¶
inspect code · cell 02
In [2]:
tokens, vocab, _ = load_tiny_shakespeare_tokens(repo_root / "data")
Split the token stream¶
inspect code · cell 03
In [3]:
train_tokens, validation_tokens = split_token_stream(tokens)
Sanity check¶
inspect code · cell 04
In [4]:
print(f"Total tokens: {len(tokens):,}")
print(f"Training tokens: {len(train_tokens):,}")
print(f"Validation tokens: {len(validation_tokens):,}")
# print(bpe_decode(train_tokens[:100], vocab))
inspect code · cell 05
In [5]:
import random
# Set parameters for batching
seed = 42
random.seed(seed)
block_size = 8
batch_size = 32
def sample_batch(split):
return get_batch(
split,
train_tokens,
validation_tokens,
block_size,
batch_size,
)
inspect code · cell 06
In [6]:
import torch
from torch import nn
vocab_size = len(vocab)
n_embd = 32
class BigramLanguageModel(nn.Module):
def __init__(self, vocab_size, n_embd):
super().__init__()
self.token_embedding_table = nn.Embedding(vocab_size, n_embd)
self.lm_head = nn.Linear(n_embd, vocab_size)
def forward(self, idx, targets=None):
x = self.token_embedding_table(idx)
logits = self.lm_head(x)
if targets is None:
loss = None
else:
B, T, V = logits.shape
logits = logits.view(B * T, V)
targets = targets.view(B * T)
loss = nn.functional.cross_entropy(logits, targets)
return logits, loss
def generate(self, idx, max_new_tokens):
for _ in range(max_new_tokens):
logits, _ = self(idx)
logits = logits[:, -1, :]
probs = torch.softmax(logits, dim=-1)
idx_next = torch.multinomial(probs, num_samples=1)
idx = torch.cat((idx, idx_next), dim=1)
return idx
inspect code · cell 07
In [7]:
model = BigramLanguageModel(vocab_size, n_embd)
optimizer = torch.optim.AdamW(model.parameters(), lr=1e-3)
for step in range(1000):
x_batch, y_batch = sample_batch("train")
x_batch = torch.tensor(x_batch, dtype=torch.long)
y_batch = torch.tensor(y_batch, dtype=torch.long)
logits, loss = model(x_batch, y_batch)
optimizer.zero_grad(set_to_none=True)
loss.backward()
optimizer.step()
if step % 100 == 0:
print(f"Step {step}: loss = {loss.item():.4f}")
inspect code · cell 08
In [8]:
import math
model.eval()
with torch.no_grad():
xb, yb = sample_batch("validation")
xb = torch.tensor(xb, dtype=torch.long)
yb = torch.tensor(yb, dtype=torch.long)
_, val_loss = model(xb, yb)
print("validation loss:", val_loss.item())
print("log vocab size:", math.log(len(vocab)))
model.train()
Out[8]:
inspect code · cell 09
In [9]:
context = torch.tensor([[0]], dtype=torch.long)
out = model.generate(context, max_new_tokens=100)
print(bpe_decode(out[0].tolist(), vocab, errors="replace"))