In this article
Start here: the learning path
- ML coding round
- An ML coding round is an interview in which a candidate writes short, runnable machine learning code, such as a layer, a loss or a decoding loop, and explains it while writing.
- From scratch
- Writing code "from scratch" means building a routine from arrays and basic operations instead of calling the library function that already does the job, so the interviewer can see that you understand what the library does.
- Tensor shape
- A tensor's shape is the tuple of its dimension sizes, for example batch, heads, sequence and embedding.
- Logits
- Logits are the raw, unnormalised scores a model outputs before softmax turns them into probabilities.
The path below is ordered by dependency, so each step uses only what the steps before it taught. Follow it top to bottom once, then return to whichever step an interviewer made you stumble on. Across the 284 interviewer questions we collected for this round (counted 2026-10-24), 42 mention attention, 42 mention writing something from scratch, 14 mention the key-value (KV) cache, 11 mention gradients or the backward pass and 10 ask you to debug code 1.
Text description of the diagram
- The path has seven steps ordered by dependency.
- Tensor shapes come first because every later topic is a shape problem.
- Debugging is the last step: finding the planted bug in a transformer or a training loop.
- Tensor shapes and broadcasting. Learn to write the shape of every intermediate value as a comment.
- A stable softmax. The smallest task that exposes numerical thinking.
- Scaled dot-product attention. The task that appears most often in our collected questions.
- Masks, heads and cross-attention. Attention with the real-world variations.
- Losses and gradient descent. Cross-entropy, a training step, the learning rate.
- Decoding and the KV cache. How a trained model produces text, step by step.
- Debugging. Finding the planted bug in a transformer or a training loop.
- AI engineer coding interview questions: LRU cache, async batching and retries
- How do you implement scaled dot-product attention from scratch?
- How do you write multi-head attention in PyTorch from scratch?
- Gradient descent from scratch, with warmup and cosine learning-rate schedules
- Cross-attention vs self-attention, and what the causal mask does
- How do you implement the KV cache in a transformer decoder?
- How do you write k-means and k-NN from scratch in NumPy, step by step?
- What is a numerically stable softmax, and how do you implement it?
- How do you write the CLIP loss in PyTorch?
- Code review interview questions: what to check, feedback and AI-written code
- How do you implement LLM sampling from logits?
- PyTorch and NumPy idioms interviewers test: broadcasting and views, step by step
- MLP forward and backward passes by hand with the chain rule, step by step
- Linear and logistic regression from scratch by gradient descent, step by step
- FlashAttention from scratch: how it works and how to code it
- Positional encodings from scratch: sinusoidal and rotary (RoPE), step by step
- How to build GPT from scratch, step by step in PyTorch
- How do you implement 2D convolution from scratch?
- Beam search explained: how it works, and when it beats greedy decoding
- Decision trees with Gini splits and naive Bayes from scratch, step by step
- What is FSDP? Fully sharded data parallel, explained
- How do you build a toy LLM inference engine?
- Evaluation metrics from scratch in Python, step by step: F1, AUC and NDCG
- How do you implement LoRA in a coding interview?
- How do you code a minimal agent loop and a minimal RAG pipeline?
- How do you implement grouped query attention (GQA)?
- The transformer debugging round, step by step
- The 45-minute notebook round: what to do, minute by minute
- What is the data-parsing round in a coding interview?
- How do you implement a BPE tokenizer from scratch?
- Debugging training loops and pipelines that fail silently, step by step
- Domain-flavoured implementation tasks in ML coding interviews, step by step
- GPU kernel questions in ML coding rounds: coalescing and fusion, step by step
- What is self-attention, and how does it work step by step?
- Why are attention scores divided by sqrt(d_k) in a transformer?
- How would you write a KNN search using base Python?
- How would you critique this regression model?
- What are some differences between TensorFlow and PyTorch?
What an ML coding round is and what it scores
An ML coding round tests whether you can turn an equation into working array code under time pressure. The task is small by design: it fits on one screen and can grow, with the interviewer adding a requirement once the first version runs.
Reported practice differs by employer, so treat any single description as one data point. The questions we collected describe tasks such as implementing attention, writing a backward pass, parsing and summarising data, and debugging a training loop; they mention different lengths: three describe a one-hour round, one a 50-minute round in three parts, and one a task finished in 15 minutes 1, so ask how long you have. Correct shapes, vectorised code instead of Python loops, a quick test on a tiny input and narration that explains why each step exists are habits worth building for any version of the round. Employers differ, so ask at the start which libraries are allowed.
NumPy and PyTorch appear together for a practical reason. NumPy shows that you know the arithmetic; PyTorch shows that you know the framework's conventions, such as which functions take logits and which take probabilities. Prepare in NumPy first, then translate.
Knowing it is half the job. Saying it is the other half.
Interview Clozo is an AI interview copilot for real interview rounds: it hears the question and puts a strong answer on your screen, and you say it in your own words. See the features or how it works.
Start Free Trial →Questions interviewers ask on PyTorch and ML coding
The questions fall into three families: implement a named layer or algorithm, debug code that is wrong, and extend working code under a new constraint. These are ten that recur, in our own words, each with a one-line answer.
- Implement multi-head attention without
nn.MultiheadAttention. Project to queries, keys and values, split the embedding into heads, run scaled dot-product attention per head, merge and project. See scaled dot-product attention below. - Add a KV cache to a given transformer. Append the new key and value to stored tensors along the sequence axis and attend over all of them. See the KV cache explainer.
- Write top-k sampling from logits. Keep the k largest logits, set the rest to negative infinity, apply softmax, sample.
- Write a stable softmax and the cross-entropy gradient. Subtract the row maximum; the gradient with respect to the logits is the probabilities minus the one-hot target. See cross-entropy loss.
- Implement a two-dimensional (2D) convolution with padding and stride. Pad the input, slide the window by the stride, and multiply and sum at each position.
- Compute precision and recall without scikit-learn. Count true positives, false positives and false negatives from the predictions, then divide. See the confusion matrix page.
- Find the planted bug in a transformer. Check shapes, then masks, then the loss, then the update. See the debugging round.
- Explain how autograd works. PyTorch records each operation in a graph as you compute, and the backward call walks that graph in reverse applying the chain rule. See gradient descent.
- Explain
viewagainstreshape.viewneeds compatible memory layout and shares storage;reshapereturns a view when it can and copies when it must. - Show the loss decreasing on a toy problem. Fix the seed, overfit one tiny batch and print the loss every few steps.
A wider list of answered questions for AI engineers is in the AI engineer question list, and the round's place in a full loop is in interview loops. For the deeper theory behind these tasks, start at deep learning and FlashAttention.
The numerical traps that are easy to miss
Two numerical traps are easy to miss; the numbers below come from code run for this guide.2
Overflow in softmax. Softmax is . For the three scores 1000, 1001 and 1002, the textbook version overflows and returns nan (not a number) for all three entries. Subtracting the maximum first returns 0.09, 0.2447 and 0.6652 2. The SciPy documentation says its implementation "uses shifting to avoid overflow" (SciPy v1.18.0 documentation, read October 2026), which is the same trick.
The scale in attention. The dot product of two random vectors with unit-variance entries has a spread that grows with the vector length. In the run, the unscaled dot-product spread (its standard deviation) was 4.0 at dimension 16, 7.99 at 64 and 15.97 at 256, and dividing by the square root of the dimension brought each back to 1.0 2. The original Transformer paper (Vaswani et al., 2017) introduced this scaling, and the PyTorch default scale is the same , where is the query's last dimension (PyTorch 2.14 documentation, read October 2026).
Text description of the diagram
- Unscaled dot-product spread was 4.0 at dimension 16, 7.99 at 64 and 15.97 at 256.
- Dividing by the square root of the dimension gave a spread of 1.0 at every dimension.
- At dimension 64 the average largest attention weight was 0.877 unscaled and 0.362 scaled.
The widening matters because a wide score makes the softmax nearly one-hot. In an illustrative simulation of eight keys at dimension 64, the average largest attention weight was 0.877 without the scale and 0.362 with it 2. A one-hot row passes almost no gradient to the other keys.
| Fact | Value | Source |
|---|---|---|
| Default attention scale in PyTorch | , E = last dimension of the query | PyTorch 2.14 documentation, October 2026 |
| Causal mask in PyTorch attention | lower triangle kept, future positions set to negative infinity | PyTorch 2.14 documentation, October 2026 |
| Boolean attention mask meaning | True means the position takes part in attention | PyTorch 2.14 documentation, October 2026 |
| Cross-entropy input | unnormalised logits, not probabilities | PyTorch 2.14 documentation, October 2026 |
The topics in this path
Implement scaled dot-product attention from scratch
Scaled dot-product attention computes, for each query, a weighted average of the values, where the weights are a softmax over the query's scaled dot products with every key. The shapes carry the whole answer: queries are batch by query length by embedding, keys match them, so the scores come out as query length by key length, and the output has the value dimension.
Write the shape beside every line, divide the scores by the square root of the key dimension, apply the mask before the softmax, and then multiply by the values. Test on a three-token input where you can work out the answer by hand, and check that every row of weights sums to one. The Transformers hub explains why attention exists.
Causal masks, encoder against decoder, and cross-attention
A causal mask stops a position from reading later positions by setting those scores to negative infinity before the softmax, so their weights become exactly zero. PyTorch documents that behaviour for is_causal=True and raises an error if you pass both a mask and that flag. An encoder lets every position see every other; a decoder uses the causal mask because it generates left to right.
Cross-attention is the same code with queries from one sequence and keys and values from another, so the two lengths can differ and the score matrix is not square. The usual trap is a mask built for a square case being applied to a rectangular one, which fails on shape or silently masks the wrong positions. Print the mask for a four-token input before you trust it.
Multi-head attention in PyTorch
Multi-head attention splits the embedding into several smaller heads, runs attention in each, and joins the results, so each head can attend to a different pattern. With embedding size 64 and 8 heads, each head has size 8. The mistake interviewers watch for is a reshape that mixes up the head and sequence dimensions. Say the reshape out loud: batch, sequence, heads, head size, then transpose the heads forward so attention runs over the sequence axis. After attention, transpose back, reshape to batch, sequence, embedding, and apply the output projection. A reliable test is that with one head the function must equal plain attention. Variants such as grouped-query attention share keys and values across heads and are asked as follow-ups.
Numerically stable softmax
This is the task shown above: subtract the row maximum, exponentiate, divide. State the axis, because softmax over the wrong dimension returns numbers that look plausible and are wrong. The follow-up is why the answer does not change, and the one-line proof is that the shift multiplies numerator and denominator by the same constant. A second follow-up is the log-softmax, which subtracts the log of the sum of exponentials instead and is what cross-entropy uses internally. FP16 (half precision) overflows sooner than 32-bit floats, which the fp16 against bf16 page explains. Test with the 1000, 1001, 1002 case from the worked numbers above 2 and with a row of equal values, which must return equal probabilities.
Gradient descent from scratch and choosing a learning rate
A gradient-descent step subtracts the learning rate times the gradient from each parameter. Show it on one parameter first, then vectorise over all of them. The gradient descent page covers the maths; in this round you also show that the loss falls on a toy problem, which is what the research-paper task in our collected questions asks for.
Start with a linear fit, print the loss at steps 0, 10 and 100, and say what you would change if it rose: a smaller learning rate, a bug in the sign of the gradient, or unscaled inputs. For classification, PyTorch's cross-entropy function takes logits and class indices (or class probabilities) and ignores target value -100 by default (PyTorch cross-entropy documentation, read October 2026); see cross-entropy loss.
Implement the KV cache
A KV cache stores each processed token's attention keys and values so that generation does not recompute them for every new token. The coding task is to keep a cache of keys and values per layer, append the new token's key and value along the sequence axis, and let the new query attend over the whole cache.
The bug to avoid is concatenating on the wrong axis, which leaves the shapes valid for a moment and then breaks the next step. Check that generating five tokens with the cache gives the same output as recomputing everything without it, which is the standard correctness test. The concept is in the KV cache explainer and the serving context for a large language model (LLM) in LLM inference, where memory limits make the cache matter.
The debugging round
A debugging round hands you broken code, such as a transformer with one planted fault or a training loop that does not learn, and asks you to find it. Work in a fixed order: shapes first, then masks, then the loss, then the update.
Typical faults are a softmax over the wrong axis, a mask applied after the softmax, probabilities passed to a loss that expects logits, gradients that are never zeroed between steps, and a forgotten switch between training and evaluation mode. Narrate the hypothesis before each check, run the smallest input that shows the fault, and fix one thing at a time so you know which change worked. In our collected questions, 10 of the 284 questions ask for this skill directly 1.
Follow-up questions
Do I have to implement algorithms from scratch without scikit-learn?
It depends on the round, so ask at the start. A from-scratch task usually bans the library function that solves the problem, while allowing the array library; computing precision and recall from the four counts, as the confusion matrix explainer shows, is a typical example.
Which parts can I use PyTorch for and which must be NumPy?
Ask the interviewer which tensor library is allowed, then state the choice aloud. If both are allowed, write in NumPy first because the arithmetic is more visible.
What happens if my code does not run?
Say what you expect it to do, check shapes on a tiny input, and read the error line by line. Narrating the debugging process shows the skill the round is testing.
Why does my softmax return NaN?
The exponentials overflowed. Subtract each row's maximum before exponentiating, as shown in the worked numbers above. The same overflow limit is lower in half precision, covered in fp16 against bf16, and a loss that takes the log of a zero probability gives the same symptom, which cross-entropy loss explains.
How do I show the loss decreasing when I implement part of a paper?
Fix the random seed, train on a tiny batch you can overfit, and print the loss every few steps. A loss that falls on one batch proves the gradient path works; the update rule is derived on the gradient descent page.
- Practise correct shapes, stable numerics and clear narration; how the round is scored differs by employer, so ask.
- Comment the shape of every intermediate value.
- Subtract the row maximum in every softmax.
- Divide attention scores by the square root of the key dimension, and know why.
- Test on a tiny input, and show the loss falling on one batch.
Sources
- Attention Is All You Need (arXiv 1706.03762 v7) — arXiv (Vaswani et al.), 12 Jun 2017
- torch.nn.functional.scaled_dot_product_attention (PyTorch 2.14 documentation) — PyTorch documentation, 24 Oct 2026
- torch.nn.functional.cross_entropy (PyTorch 2.14 documentation) — PyTorch documentation, 24 Oct 2026
- scipy.special.softmax (SciPy v1.18.0 documentation) — SciPy documentation, 24 Oct 2026