DS 207 · Introduction to NLP · Jan 2026 · Assignment 3
About this study: This dashboard visualizes a hyperparameter search conducted using Optuna, an open-source framework that automates the trial-and-error process of finding optimal model settings. By intelligently sampling combinations of parameters over multiple trials, Optuna maps the trade-offs between our SFT and DPO performance metrics while keeping us within strict computational constraints.
Figure 1 — Each point is one Optuna trial. Amber diamonds mark the Pareto front.
What is a Pareto front? When optimising two objectives at once (here, SFT Pass@5 and DPO Pass@5), there is usually no single "best" configuration that wins on both simultaneously. Instead, we get a frontier of trade-offs.
A trial belongs to the Pareto front if no other trial is strictly better on both metrics at the same time. In other words, to improve SFT beyond a Pareto point, you must accept a lower DPO score, and vice versa. The frontier therefore represents the full set of optimal trade-offs found during the search: configurations where you are genuinely choosing between objectives rather than leaving performance on the table.
Full parameter table
| Parameter | Optuna name | Range / Values |
|---|---|---|
| MAX_NEW_TOKENS | fixed | 256 |
| TEMPERATURE | temperature | [0.6, 1.4] |
| TOP_P | fixed | 0.95 |
| REPETITION_PENALTY | repetition_penalty | [1.0, 1.3] |
| SFT · MAX_LENGTH | fixed | 512 |
| SFT · LEARNING_RATE | sft_lr | [5e-5, 8e-4] (log) |
| SFT · NUM_EPOCHS | sft_epochs | [1, 5] integer |
| SFT · BATCH_SIZE | sft_batch_size | {4, 8, 16} |
| SFT · WEIGHT_DECAY | sft_weight_decay | [0.0, 0.2] |
| DPO · MAX_LENGTH | fixed | 512 |
| DPO · LEARNING_RATE | dpo_lr | [1e-7, 1e-5] (log) |
| DPO · NUM_EPOCHS | dpo_epochs | [1, 10] integer |
| DPO · BETA | dpo_beta | [0.05, 0.4] |
| DPO · BATCH_SIZE | dpo_batch_size | {4, 8} |
| DPO · WEIGHT_DECAY | dpo_weight_decay | [0.0, 0.2] |
These parameters control how the model samples its output tokens. They apply equally during SFT evaluation and DPO evaluation, so they directly affect every Pass@5 score you measure.
Temperature scales the model's raw output probabilities (logits) before sampling. A value of 1.0 leaves probabilities unchanged; below 1.0 sharpens the distribution so the top token dominates; above 1.0 flattens it so rare tokens get more chances. For Pass@5 (where you generate five independent answers and check if any is correct), a little randomness is your friend: it lets the model explore different solution paths across the five samples.
Too Low (below ~0.6)
The model becomes near-deterministic. All five samples look almost identical, so if the first attempt is wrong, the remaining four probably are too, wasting your Pass@5 budget entirely.
Too High (above ~1.4)
The probability distribution is so flat that the model treats nonsense tokens as plausible. Arithmetic steps start hallucinating: you get creative stories about numbers rather than structured reasoning.
Range rationale: [0.6, 1.4] is what we found to be a practical bracket for sampling diversity without sacrificing coherence. The 0.6 floor ensures at least some variation between samples; the 1.4 ceiling keeps outputs from degenerating into noise.
Repetition penalty down-weights any token that has already appeared in the generated text, making it less likely to be chosen again. A value of 1.0 applies no penalty at all; values above 1.0 increase the suppression proportionally. For math reasoning, this primarily prevents the model from getting stuck in looping phrases like "so we add 4, so we add 4, so we add 4…"
At 1.0 (No Penalty)
Small language models can fall into repetition loops when confused, cycling through the same phrase until the generation budget runs out and producing zero useful content in those samples.
Too High (above ~1.3)
Even perfectly natural word reuse (e.g. "the total is… the answer is…") gets penalised. The model is forced into awkward paraphrases that break the clean step-by-step structure needed for math.
Range rationale: [1.0, 1.3] is a narrow, conservative window that we think is appropriate here. Math solutions inherently repeat structural keywords ("so", "therefore", "the answer"); the goal is to catch genuine infinite loops without penalising those legitimate repetitions.
SFT teaches the base model to produce step-by-step math reasoning by training it on (question, correct solution) pairs. The parameters below control the speed, duration, and regularisation of that training process.
sft_lr
search: [5e-5, 8e-4] log-scale
Learning rate is the "step size" of the optimizer: after computing how the loss changes with respect to each weight, the model moves lr × gradient in the corrective direction. We search this in log-space because the right order of magnitude matters far more than a precise decimal value. For instance, 1e-4 and 2e-4 behave similarly, while 1e-4 and 1e-3 can behave very differently.
Too Low (below ~5e-5)
Training crawls. In only a few epochs over a small dataset, the model's weights barely shift away from their pre-trained state, resulting in underfitting: it outputs fluent text but not math reasoning.
Too High (above ~8e-4)
Weights update in huge leaps, violently overwriting pre-trained representations. This often causes "catastrophic forgetting" (the model loses its language abilities entirely), or the training loss diverges to NaN within the first epoch.
Range rationale: [5e-5, 8e-4] is what we considered a practical window for fine-tuning a small transformer on this task. Pre-trained weights are delicate; the goal is to nudge them, not overwrite them.
sft_epochs
search: [1, 5] integer
Each epoch is one complete pass through the training dataset. More epochs mean more opportunities to learn the target format, but with diminishing returns and increasing risk of memorisation.
Too Few (1 Epoch)
The model sees each example only once. It will likely pick up the broad response format (numbered steps, boxed answers) but fail to internalise the arithmetic reasoning structure deeply enough to generalise.
Too Many (beyond ~5 Epochs)
The model begins memorising specific solutions from the training set rather than learning the underlying reasoning strategy. Overfitting causes it to fail on unseen problems at evaluation time.
Range rationale: [1, 5] reflects both the small size of the GSM-8K training split we use and our Colab T4 (16 GB) session budget. With a compact dataset, the model tends to saturate quickly; beyond epoch 5, you are mostly memorising rather than generalising, and the time cost becomes hard to justify given the session limits.
sft_batch_size
search: {4, 8, 16}
Batch size determines how many training examples the model processes before computing a gradient update. Larger batches produce smoother, more reliable gradient estimates; smaller batches are noisier but update more frequently, and can act as an implicit regulariser.
Very Small (1–2)
Gradients are so noisy they can point in wildly wrong directions. Training becomes unstable and slow, which is why we excluded these values from the search.
Very Large (32+)
Requires substantially more GPU memory, which quickly hits the ceiling of the Colab T4 (16 GB VRAM). Very large batches can also lead the optimiser to "sharp" minima that generalise poorly.
Range rationale: {4, 8, 16} covers what we think is a practical range given the strict 16 GB GPU memory limit of a Colab T4 and the 512-token sequence length fixed for SFT. All three are powers of two for memory alignment efficiency.
sft_weight_decay
search: [0.0, 0.2]
Weight decay (L2 regularisation) adds a small penalty proportional to the magnitude of each weight to the loss, nudging the optimiser to prefer smaller, more diffuse parameter values. This discourages the model from leaning too hard on any single feature, which can help generalisation.
At 0.0 (None)
No regularisation. The model is free to fit the training data as tightly as possible, which is fine given plenty of data and a conservative learning rate, but risky otherwise.
Too High (above ~0.2)
The regularisation term dominates the learning signal, shrinking weights aggressively. The model underfits and cannot retain enough information to perform the task.
Range rationale: [0.0, 0.2] felt like a practical window to us for this task. 0.0 is a perfectly valid choice (no regularisation at all), and we found values beyond 0.2 tend to over-constrain the model without clear benefit.
DPO fine-tunes the SFT model further using preference pairs (question, chosen answer, rejected answer), nudging it towards solutions that are more likely to be correct. Because we start from an already-adapted model, the updates here need to be much smaller and more careful than during SFT.
dpo_lr
search: [1e-7, 1e-5] log-scale
DPO uses a learning rate orders of magnitude smaller than SFT, typically somewhere in the 1e-7 to 1e-5 range rather than 1e-4 to 1e-3. This is intentional: DPO is meant to make subtle preference-driven adjustments on top of an already capable model, not rewrite it from scratch.
Too Low (at or below 1e-8)
At 1e-8 or below, the weight updates per step become so tiny that the model weights are effectively frozen; you are training for epochs and achieving almost nothing. The DPO model ends up indistinguishable from the SFT checkpoint it started from.
Too High (above ~1e-5)
The preference signal is applied too forcefully. The model can "forget" the step-by-step reasoning structure that SFT painstakingly instilled, and often collapses to a single response style regardless of the input.
Range rationale: [1e-7, 1e-5] spans three orders of magnitude specifically to explore the boundary between "barely moving" and "moving enough to matter". Because any individual step is so small at the lower end, the model needs more passes to accumulate a meaningful signal, which is why the DPO epoch range extends to 10 rather than 5.
dpo_epochs
search: [1, 10] integer
The DPO epoch range is intentionally wider than SFT's [1, 5] to compensate for the much lower learning rate. When each individual update is tiny (e.g. at lr=1e-7), the model needs many more passes through the preference data before the signal accumulates into a meaningful shift in behaviour.
Too Few (1 Epoch)
With a low learning rate, a single epoch barely moves the policy. The DPO model behaves almost identically to the SFT model, offering no improvement from preference learning.
Too Many (beyond ~10 Epochs)
Extended DPO training can over-optimise for the specific preference pairs in the dataset, causing reward hacking: the model learns to "look like" a correct answer superficially without actually getting the arithmetic right.
Range rationale: [1, 10] is set wide to cover both ends of the LR spectrum. At a higher LR (1e-5), a single epoch may already be enough; at a lower LR (1e-7), you need closer to 10 epochs for meaningful change. Keeping the upper bound at 10 also kept total DPO training time within our Colab T4 (16 GB) session budget.
dpo_beta
search: [0.05, 0.4]
Beta (β) is the KL-regularisation coefficient in the DPO objective. It controls how strongly the training penalises the policy model for drifting too far from the reference model (the SFT checkpoint). A higher β keeps the new policy close to the reference; a lower β allows larger deviations in pursuit of the preference signal.
Too Low (β close to 0)
The KL penalty effectively vanishes. The model is free to stray arbitrarily far from the SFT checkpoint chasing the preference signal, often collapsing to degenerate outputs or mode-dropping (generating only a narrow style of answer).
Too High (β close to 1+)
The KL penalty dominates. The model is so strongly anchored to the reference that it can barely incorporate the preference signal at all; DPO training becomes a no-op.
Range rationale: [0.05, 0.4] is what we considered a practical range where the KL constraint is meaningful but not overwhelming. Values around 0.1–0.2 appear frequently in DPO literature for small-scale experiments, which informed our choice of upper bound.
dpo_batch_size
search: {4, 8}
DPO requires both the live policy model and the frozen reference (SFT) model to be in memory simultaneously during training, roughly doubling the GPU footprint compared to SFT. This is why the batch size options here are smaller than in SFT; 16 would exceed the strict 16 GB VRAM limit of the Colab T4 under the dual-model load.
Range rationale: {4, 8} are the options we found fit comfortably within the Colab T4's 16 GB memory budget given the dual-model overhead of DPO and the 512-token sequence length.
dpo_weight_decay
search: [0.0, 0.2]
Same L2 regularisation mechanism as in SFT. During DPO, with such a small learning rate, weight decay plays a more prominent relative role; even a modest value of 0.05 can meaningfully constrain the magnitude of the preference-driven updates.
Range rationale: [0.0, 0.2] mirrors the SFT range. 0.0 is a perfectly reasonable default when the learning rate alone provides sufficient implicit regularisation, and we saw little benefit from going beyond 0.2.