Module 7
Training Language Models at Scale
Pretraining via next-token prediction, training loops, learning rate scheduling, and checkpointing.
Learning Objectives
- Understand pretraining vs fine-tuning
- Implement efficient training loops
- Apply learning rate schedules (warmup + decay)
- Implement checkpointing and evaluation
7.1 Pretraining: Next-Token Prediction
The pretraining task: given tokens [t₁, t₂, …, t_n], predict [t₂, t₃, …, t_{n+1}]. This self-supervised objective can be applied to any text corpus without labels.
Haskell — sliding-window training examples
-- Sliding window over tokenised text
createTrainingExamples :: Int -> [Int] -> [([Int], Int)]
createTrainingExamples contextLength tokens =
[ (take contextLength (drop i tokens), tokens !! (i + contextLength))
| i <- [0 .. length tokens - contextLength - 1] ]
-- Batch structure
data Batch = Batch
{ inputIds :: Matrix Int
, targetIds :: Vector Int
, attentionMask :: Matrix Bool
}
createBatches :: Int -> Int -> [([Int], Int)] -> [Batch]
createBatches batchSize contextLength examples =
map createBatch (chunksOf batchSize examples)
where
createBatch batch =
Batch { inputIds = map fst batch
, targetIds = map snd batch
, attentionMask = trueMatrix (length batch) contextLength }
7.2 Training Loop
Haskell — training state and step
data TrainingState = TrainingState
{ model :: LanguageModel
, optimizer :: AdamOptimizer
, step :: Int
, loss :: Double
}
-- One training step
trainStep :: TrainingState -> Batch -> TrainingState
trainStep state batch =
let logits = forward (model state) (inputIds batch)
batchLoss = crossEntropyLoss logits (targetIds batch)
gradients = backprop (model state) logits (targetIds batch)
updatedModel = updateParameters (optimizer state) (model state) gradients
in state { model = updatedModel
, step = step state + 1
, loss = batchLoss }
-- Full training run
train :: LanguageModel -> Int -> [Batch] -> LanguageModel
train model numEpochs batches =
let initialState = TrainingState
{ model = model, optimizer = adamOptimizer, step = 0, loss = 0 }
finalState = foldl (\s _ -> trainEpoch s batches) initialState [1..numEpochs]
in model finalState
trainEpoch :: TrainingState -> [Batch] -> TrainingState
trainEpoch = foldl trainStep
7.3 Learning Rate Scheduling
Learning rates that are too high lead to unstable training; too low leads to slow convergence. The original Transformer paper uses a warmup-then-decay schedule:
lr = d_model^(-0.5) · min(step^(-0.5), step · warmup_steps^(-1.5))
Typical values:
warmup_steps = 4000
peak LR ≈ 6e-4 (for d_model = 512)
Haskell — LR schedule
learningRateSchedule
:: Double -- d_model
-> Int -- warmup_steps
-> Int -- current_step
-> Double
learningRateSchedule d_model warmupSteps currentStep =
let step = fromIntegral currentStep
warmup = fromIntegral warmupSteps
factor = d_model ** (-0.5)
schedule = min (step ** (-0.5)) (step * warmup ** (-1.5))
in factor * schedule
7.4 Checkpointing
Haskell — save and load checkpoints
saveCheckpoint :: FilePath -> LanguageModel -> Int -> IO ()
saveCheckpoint path model step = do
encodeFile (path ++ "/model_" ++ show step ++ ".model") model
writeFile (path ++ "/metadata.txt") (show step)
loadCheckpoint :: FilePath -> IO (LanguageModel, Int)
loadCheckpoint path = do
step <- read <$> readFile (path ++ "/metadata.txt")
model <- decodeFile (path ++ "/model_" ++ show step ++ ".model")
return (model, step)
-- Evaluation
evaluate :: LanguageModel -> [Batch] -> Double
evaluate model batches =
let losses = map (computeBatchLoss model) batches
in average losses
Reading & Viewing
Essential reading
Attention Is All You Need — Training details section (2017)
Section 5 covers the exact LR schedule and training settings.
arxiv.org/abs/1706.03762
Language Models are Unsupervised Multitask Learners (GPT-2, 2019)
Details on large-scale pretraining.
PDF link
Reference implementation
nanoGPT
Minimal ~400-line PyTorch GPT. Excellent reference when implementing your Haskell version.
github.com/karpathy/nanoGPT