Model Adaptation · 2 to 3 hours
Validate a fine-tuning dataset and plan SFT
Implement a split validator for a fine-tuning dataset, predict its results on fixture cases, find the leaks it cannot catch, and plan a supervised fine-tuning experiment against a prompted baseline.
Implementation and experiment design
3 h
Used in interview rounds:
Live Coding (Medium)Overview
Plan a reproducible supervised fine-tuning (SFT) experiment and implement a small dataset-integrity check. You implement the validator, predict its results on fixtures, and work out what it cannot detect, because it does not replace semantic, tokenizer, or privacy review.
Study time: 2 to 3 hours
Interview format: Hands-on validator implementation with fixture predictions, then an experiment design discussion
By the end you can
- Implement a dataset validator that enforces a stated contract in a fixed check order with exact error messages
- Group examples by exercise family and explain which leaks exact-match checks cannot detect
- Inspect chat-template rendering, loss masks, and truncation before training
- Design a blinded, paired comparison against a prompted baseline with a predefined decision rule and family-level uncertainty
- Verify an exported model on the serving path and keep a rollback path
Prerequisites
Helpful background: Python and basic tokenization knowledge. TypeScript helps for reading the second reference implementation; the practice problem runs in JavaScript or Python.
This topic covers choosing between prompting, retrieval, and fine-tuning, validating training data, and proving that specialization helped. Training infrastructure and distributed training are out of scope.
The hint dataset
Worked example
Suppose a coding-practice platform shows a hint when a learner's submission fails its tests. The hints come from an open-weight model the platform hosts, driven by a prompt; this prompted model is the baseline. Reviewers report hints that are too vague or give away the solution, and the team wants to know whether supervised fine-tuning (SFT) on reviewed examples does better.
| Item | Value (synthetic) |
|---|---|
| Reviewed examples | 1,200 conversations |
| Exercise families | 40; a family is one exercise plus its variants and paraphrases |
| Split plan | By family: train 28 families (840 conversations), validation 6 (180), test 6 (180) |
| Record | id, group_id (the exercise family), and messages |
| Messages | An optional system message, a user message (exercise, failing submission, test output, requested help level), and a final assistant hint written or approved by a reviewer |
| Release concerns | Hint usefulness, correctness, and answer disclosure |
Write an experiment contract
Define the behavior, baseline, population, metrics, and decision rule before training. For the hint writer, a useful objective is expert-rated hint quality without giving away solutions. Track correctness and answer disclosure separately; one blended score can hide a serious regression.
Record the baseline model revision, prompt, inference configuration, dataset manifest, split assignment, and evaluation rubric. Use identical evaluation cases across candidates. Define how ambiguous cases and reviewer disagreements will be adjudicated.
Build a dataset manifest
Each example needs a stable ID, a grouping key, provenance, and review status. Group by the smallest unit that must not cross partitions, such as an exercise family including paraphrases and variants. If examples connect through multiple sources, form connected groups before splitting.
Deduplicate before assignment, then freeze the assignment. Exact normalized text checks catch only simple duplication. Similarity search, source tracing, and manual inspection are needed for near duplicates and semantic leakage.
A split proportion is not a quality guarantee. Small or imbalanced slices may require targeted collection. A deterministic hash split is convenient, but it does not ensure representative distributions or adequate sample counts.
The validator contract
The validator checks a narrow contract: system, user, and assistant messages ending in one target assistant response, in splits the caller has already assigned.
validateSplits(splits) in TypeScript or JavaScript, and validate_splits(splits) in Python, checks the input in this order and stops at the first failure with the message shown (an Error in JavaScript, a ValueError in Python):
splitsis an object (adictin Python, never an array or list) whose keys are exactlytrain,validation, andtest: "Expected train, validation, and test".- The splits are processed in the order train, validation, test. Each split is checked completely, row by row in list order, before the next split starts.
- The split is a non-empty list: "Every split must be a nonempty list".
- The row is an object: "Record must be an object". Its
idandgroup_idare strings that are not empty or whitespace-only: "Missing identifier". - The
idhas not appeared earlier in any split: "Duplicate ID". - The
group_idhas not appeared in a different split: "Group crosses splits". messagesis a list of at least 2 items: "Expected conversation and target". Each message, in order, is an object ("Message must be an object") whoseroleissystem,user, orassistant("Unsupported role") and whosecontentis a string that is not empty or whitespace-only ("Empty message").- The last message has role
assistant: "Final message must be the target". At least one earlier message has roleuser: "Missing user input". - The normalized conversation has not appeared in a different split: "Conversation crosses splits". To normalize, keep each message's role, split its content on whitespace, and rejoin the pieces with single spaces. The conversation key is the canonical JSON string of the resulting list of
[role, content]pairs. Every message counts, including system messages. Nothing else changes: case and punctuation are kept.
Rule 9 compares splits with each other only. Deduplicating conversations inside one split is a separate step before training, not part of this contract.
A valid dataset returns the row count of each split as { train, validation, test }.
The base dataset below is valid. Its user messages are shortened; real ones would carry the exercise, the failing submission, and its test output. In TypeScript and JavaScript the row field is groupId; Python keeps group_id.
| Split | id | group_id | Messages, in order |
|---|---|---|---|
| train | tr-1 | family-loops | user: "Why does my loop never end?" / assistant: "Check the loop condition." |
| train | tr-2 | family-recursion | system: "Give one hint, never the full solution." / user: "My recursive function overflows the stack." / assistant: "Look for the case that stops the recursion." |
| validation | va-1 | family-sorting | user: "My sort returns the list unchanged." / assistant: "Check whether each swap writes back to the list." |
| test | te-1 | family-graphs | user: "My BFS visits some nodes twice." / assistant: "Check when you mark a node as visited." |
Code exercise
Split validator
Implement validateSplits (Python: validate_splits) for the contract above. Apply the rules in the stated order, fail with the first broken rule's exact message, and return the row count of each split when the dataset is valid.
Expected
The linked practice problem calls validateSplits(splits) in JavaScript and validate_splits(splits) in Python. A valid input returns an object (a dict in Python) with the keys train, validation, and test, each holding that split's row count. A failure throws an Error (Python: raises ValueError) whose message contains the rule's text exactly. JavaScript rows use groupId; Python rows use group_id. The tests cover a valid dataset, one failing input per rule, whitespace-only identifiers and content, whitespace-equivalent conversations in different splits, a conversation repeated within one split, and inputs where the order of checks decides the message.
Limitations
The validator reads one in-memory dataset against a narrow contract: system, user, and assistant messages ending in one target. Tool-call datasets need a richer schema. Whitespace normalization suits prose, but whitespace can carry meaning in code, so a production code dataset needs field-aware duplicate detection. Passing the validator says nothing about whether the targets are correct, safe to train on, or free of private data.
Predict the validator's results
Predict the outcome
Predict the validator's results
Each case starts from the base dataset and applies the listed change (F12 applies two). Using only the rules, predict whether the validator returns counts or fails, and with which message. Then name the rule that decides it.
| Case | Change to the base dataset | Result | Deciding rule |
|---|---|---|---|
| F1 | None: the base dataset as shown | ? | ? |
| F2 | Append `tr-3` to train, with `group_id` `family-loops` and the same two messages as `tr-1` | ? | ? |
| F3 | Add a fourth top-level key, `dev`, holding an empty list | ? | ? |
| F4 | Replace the test split with an empty list | ? | ? |
| F5 | Set `tr-2`'s `id` to `" "` (three spaces) | ? | ? |
| F6 | Change `te-1`'s `id` to `tr-1` | ? | ? |
| F7 | Change `va-1`'s `group_id` to `family-loops` | ? | ? |
| F8 | Insert a message with role `tool` and content `Expected [1, 2, 3]` between `va-1`'s user and assistant messages | ? | ? |
| F9 | Set the content of `tr-2`'s assistant message to `" "` (two spaces) | ? | ? |
| F10 | Replace `va-1`'s messages with `tr-2`'s system message followed by `va-1`'s assistant message | ? | ? |
| F11 | Append `te-2` to test, with `group_id` `family-imported`, user content `"Why does my loop never end?"`, and assistant content `"Check the\tloop condition."` (two spaces after Check, a tab after the) | ? | ? |
| F12 | Replace the test split with an empty list and change `va-1`'s `id` to `tr-1` | ? | ? |
Find what the validator misses
A dataset can pass every rule above and still leak. The exercise below applies the manifest section to how the full hint dataset was assembled.
Diagnose
Find what the validator misses
The full hint dataset (1,200 conversations from 40 families) passes the validator. In blind pairwise ratings on the test split, reviewers prefer the fine-tuned candidate's hints by a wide margin, more than the validation pilot suggested. The team's notes on how the dataset was assembled:
- Reviewers wrote or approved a hint for each learner submission. Each row's
group_idis its exercise family. - To add volume, a model paraphrased reviewed user messages. An import script gave each paraphrase a
group_idtaken from its import batch name, not from the original family. - Several families were written from one instructor walkthrough, and different excerpts of it appear in user messages across those families.
- The test split's reference hints were written first. The same reviewers then wrote training targets in a matching style, sometimes adapting a test reference hint to a similar exercise.
- For each note, decide whether it can let test information reach training, and explain why the validator still accepts the dataset.
- For each leak, propose a control and say where it runs: the manifest, the split assignment, review, or evaluation.
- Say what the test result can and cannot support until the controls are in place, and what you would rerun.
Inspect the tokenized data
Render examples with the selected model's chat template and inspect the decoded tokens. Confirm role boundaries, end-of-turn tokens, and the absence of duplicated special tokens. Make sure the inference prompt ends where the model should begin its answer.
Decide which tokens contribute to loss. For response learning, prompt tokens may be masked while assistant targets contribute. In multi-turn conversations, decide whether all assistant turns or only selected targets should train. Verify the mask against actual token positions rather than relying on a configuration name: framework support varies with the template, and some assistant-only options work only when the template marks the assistant spans.
Check truncation before launching a run. A sequence limit that removes the answer creates unusable supervision; one that removes the problem statement changes the task. Report how many examples were truncated, discarded, or shortened.
Loss and weighting
For the set of target token positions T, a common objective is the mean negative log-likelihood of the targets:
Loss over target positions T:
L = -(1 / |T|) * sum over t in T of log p(target_t | preceding tokens)
Mask padding and any context tokens the objective excludes. Compare token-weighted and example-weighted reporting, because long responses can dominate a token average. Suppose a batch holds a 20-token hint and a 180-token explanation. A token-weighted mean gives the explanation 180 / 200 = 90% of the weight; an example-weighted mean gives each example 50%. If one task produces longer answers, a mixed dataset may overweight it without anyone choosing that.
The objective measures how well the model predicts the supplied targets. It cannot establish that those targets are factually correct or appropriate for learners.
Select the adaptation method
Choose a method your serving stack can reproduce. An experiment that only works in the training framework is not yet a deployable improvement.
Run a controlled pilot
- Freeze the baseline and split manifest.
- Inspect rendered examples, masks, token lengths, and sensitive fields.
- Train briefly on a small subset to detect data and optimization failures.
- Evaluate checkpoints on validation cases, including regressions.
- Change one interpretable factor at a time when diagnosing problems.
- Select the candidate using the predefined rule, then run the held-out evaluation.
Track learning rate, batch token counts, accumulation, effective update count, seed, precision, optimizer, adapter configuration, and checkpoint. Effective batch size can differ when examples have unequal lengths; record token throughput as well as example counts.
Diagnose common failures
| Symptom | Possible cause | Useful check |
|---|---|---|
| Loss improves but hints worsen | Wrong targets or proxy objective | Blind expert review of changed cases |
| Output contains role markers | Template or special-token mismatch | Decode train and inference sequences |
| Model never finishes | End-token mismatch or missing targets | Inspect endings and generation configuration |
| Validation is unusually strong | Duplicate or source leakage | Audit family and source assignments |
| Rare tasks regress | Dataset imbalance | Slice metrics and sampling distribution |
| Training runs out of memory | Long sequences or activation peaks | Profile token lengths and peak memory |
| Candidate varies sharply across runs | Small data or unstable optimization | Repeat promising configurations with controlled seeds |
Evaluate paired outputs
Use the same inputs for baseline and candidate. Blind the model identity and randomize response order for human comparisons. Separate ties, both-wrong cases, and missing outputs. For automated code evaluation, isolate execution and distinguish test coverage from general correctness.
Calculate uncertainty at the unit of independence. If many cases come from one exercise family, resample families rather than treating every paraphrase as independent. Keep per-slice results alongside aggregate scores.
An illustrative gate might require improved hint usefulness, no material correctness regression, and no increase in answer disclosure beyond a specified tolerance. Choose tolerances from product needs and sample uncertainty; this guide does not prescribe universal numeric thresholds.
Package the result
Save the base model and adapter identifiers, tokenizer, template, inference settings, dataset and code hashes, evaluation report, and deployment dependencies. Record which checkpoint was selected and why. Verify the exported artifact on the serving path using the same evaluation cases.
Maintain a known-good release and a rollback path. A successful training job is an intermediate artifact, not an automatic production promotion.
Completion checklist
- I can reproduce every fixture result, including the case where split order decides the message.
- I can name three leaks the validator accepts and a control for each.
- I can trace one reviewed example from raw record to rendered training tokens and its loss mask.
- I can state the baseline, metrics, decision rule, and unit of independence before training starts.
- I can explain why a trained checkpoint must be re-evaluated on the serving path before release.
Sources
- Hugging Face TRL: SFT Trainer Describes conversational datasets of role and content messages, automatic chat-template application, and options that compute loss only on assistant messages or completions; check option names against the installed version.
- Hugging Face Transformers: Chat templates Explains how role and content messages become a model-specific token sequence, why mismatched control tokens hurt quality, how a generation prompt ends the input where the assistant reply begins, and the risk of duplicated special tokens.
- scikit-learn: Cross-validation for grouped data Explains why dependent samples from one group must not appear in both training and evaluation data when measuring generalization to unseen groups.
The coding-practice platform, hint dataset, fixtures, experiment rules, and rubric are original synthetic teaching material about a fictional application. Study times and readiness thresholds are editorial estimates. The validator is executable; model training and GPU benchmarks are outside this guide's verification scope. Product and library details are as of November 2026.
Related on CodeSnatch
Lessons, problems, and articles that build the skills this question uses.
System Design
Explains shingling and MinHash for near-duplicate text, the similarity check that exact normalized keys cannot replace.
AI Engineering
Builds a sliced evaluation set and explains paired baseline and candidate comparisons, habits the SFT experiment plan needs.
Practice problem
Practice merging records that share any key into connected groups, the step a manifest needs before splits are assigned.
AI Engineering
Practice fixing metrics, tolerances, and a decision rule in an experiment contract before any result is read.
Practice the interview question
Answer it in your own words before you look at the reference answer.
