Covers the full ML engineer loop. Every entry is written as the sentence you speak in the room. Questions are collapsed by default so you test recall instead of re-reading. The amber cards are traps — questions where the obvious answer is wrong.
Read the question, answer it out loud, then open the card. If you couldn't say it, you don't have it.
Hit Mark for review inside a card, then filter to just those on the second pass.
Skim Ground floor, then attention, then LLMs & agents, then RAG. Skip the rest.
Read this first
This matters more than any single concept, because you are always one question from the edge of what you know, and how you behave at that edge is most of what is being measured.
A gap is not what loses an interview. Everyone has gaps. What loses it is the gap turning into silence, because silence reads as this person cannot think when they are stuck — a far worse signal than this person has not memorised the softmax scaling.
Go quiet. Guess confidently and get caught. Start apologising for your background. One honest "I don't know" costs almost nothing; three minutes of visible panic costs the interview.
A week on the paper and one midnight pass on fundamentals is backwards. The paper is the visible thing, so it attracts the time — but an interviewer can only test what they can ask in sixty seconds, which means fundamentals get asked far more often than papers do. Fundamentals first, always, then the specific material.
Ground floor
These get asked because they're assumed. Fumbling "what's a token" is worse than missing something advanced, because it reads as not having the ground under you. Each of these should take you one sentence.
The unit of text a language model actually reads. Text is split into sub-word pieces by an algorithm like byte-pair encoding: start from characters and repeatedly merge the most frequent adjacent pair until you reach a fixed vocabulary size.
Common words end up as a single token; rare ones break into several. Rough rule: one token is about four characters of English, so roughly 0.75 words.
No. "Tokenising" might be three tokens; "the" is one; an emoji can be several; and a space usually attaches to the front of the following token.
Two consequences worth naming, because they explain real behaviour:
A learned vector that represents a discrete thing — a token, a patch, an entire image — as a point in a continuous space. The embedding table is a lookup: token id in, vector out.
The useful property is that geometry carries meaning: similar things land near each other, so distance and direction in that space are informative. That's what makes "representation" a meaningful word rather than jargon.
No. Logits are the raw, unbounded real-valued outputs of the final layer — they can be negative and they don't sum to anything in particular. Softmax turns them into probabilities.
Why it matters practically: loss functions in PyTorch like CrossEntropyLoss expect
logits, not softmaxed probabilities, because they apply the softmax internally in a numerically
stable way. Applying softmax yourself first is a common and quiet bug.
No — it exponentiates first, then normalises. That's the whole character of it: the exponential amplifies differences, so a modest lead in the logits becomes a large lead in probability. Plain normalisation would preserve the ratios and would break on negative values.
The exponential is also why large logits saturate it, which is the reason attention divides by √dk.
Parameters are learned by the optimiser from data — the weights and biases. Hyperparameters are set by you before or around training — learning rate, batch size, number of layers, weight decay, how long to train.
The clean test: did gradient descent choose it? Then it's a parameter. Learning rate is a hyperparameter, which is why it's tuned on the validation set and not learned.
A tensor is just an n-dimensional array. Scalar, vector, matrix, then everything above. A batch of RGB images is a 4D tensor shaped (batch, channels, height, width).
A channel is one feature map at every spatial position — the 3 of RGB at the input, and after a conv layer, one channel per learned filter. "64 channels" means 64 different learned feature detectors, each giving a value at every location.
No — it computes the gradients. Backprop is the chain rule applied efficiently backwards through the computation graph, producing ∂loss/∂parameter for every parameter. The optimiser (SGD, Adam) is what then uses those gradients to change the weights.
In PyTorch this is literally two calls: loss.backward() then
optimizer.step(). Separating them cleanly sounds precise; conflating them sounds vague.
The vector of partial derivatives of the loss with respect to every parameter. It points in the direction of steepest increase of the loss, which is why you step in the negative gradient direction.
Practically: each component says "if I nudge this one weight up slightly, how much does the loss change, and in which direction".
Batch is the group of samples processed together in one step. Iteration (or step) is one forward-backward-update cycle, so one weight update. Epoch is one full pass through the training set.
1000 samples at batch size 100 gives 10 iterations per epoch. People mix up epoch and iteration constantly.
A single linear layer. The composition of linear maps is itself linear, so stacking fifty of them gives you exactly the expressive power of one — you've just factorised one matrix into fifty, at enormous cost.
Non-linearity is the entire reason depth buys anything. ReLU is the standard choice because it's cheap and doesn't saturate for positive inputs, unlike sigmoid and tanh which squash into a bounded range and kill gradients at the extremes.
Training: forward pass, loss, backward pass, update. Gradients are computed and stored, so memory use is much higher. Dropout is active, BatchNorm uses batch statistics.
Inference: forward pass only. No gradients (torch.no_grad()), dropout
off, BatchNorm using running averages. This is what model.eval() switches — forgetting it is
a classic bug that shows up as noisy or shifted predictions.
| Term | In one sentence |
|---|---|
| Ground truth / label | The correct answer you train against. In imaging it's an annotation someone drew, which means it carries human error and inter-observer variability. |
| Feature | Any measured or computed quantity the model uses. A feature map is the spatial grid of one such quantity inside a CNN. |
| Latent / representation | An internal learned encoding — not the input, not the output, the compressed thing in between. |
| Architecture vs model vs checkpoint | Architecture is the structure; model is architecture plus trained weights; checkpoint is the saved weights file. |
| Pre-training vs fine-tuning | Pre-training learns general representations on a large corpus; fine-tuning adapts that model to your specific task with far less data. |
| Zero-shot / few-shot | Performing a task with no examples, or a handful given in the prompt — with no weight updates in either case. |
| End-to-end | One model trained jointly from raw input to final output, with gradients flowing all the way through, rather than separately trained stages bolted together. |
| Inductive bias | Assumptions baked into the architecture. Convolution assumes locality and translation equivariance; attention assumes very little, which is why it needs more data. |
| Paradigm | What the supervision is |
|---|---|
| Supervised | Human-provided labels for every example. |
| Unsupervised | No labels at all — clustering, density estimation. |
| Self-supervised | Labels manufactured from the data itself (predict the masked part, match two views). Then fine-tune on a small labelled set. |
| Semi-supervised | A small labelled set plus a large unlabelled one, used together. This is the family your own registration work sits in. |
| Reinforcement | No labels, only a reward signal from acting in an environment. |
| Task | Output |
|---|---|
| Classification | One label for the whole input. Is there a tumour? |
| Regression | A continuous number. How large is it? |
| Detection | Boxes plus labels. Where are the lesions? |
| Segmentation | A label per pixel or voxel. Which exact voxels are tumour? |
| Registration | A transformation aligning one image to another. |
| Generation | A new sample from the learned distribution. |
The one that cost you
Three learned projections, one similarity matrix, one weighted average.
Every token produces three vectors by multiplying its embedding with three learned matrices: a query, a key and a value. The query is what this token is looking for, the key is what it advertises about itself, the value is what it passes on if attended to.
To get the new representation of a token, you dot its query with every token's key. That gives a score per token. You divide by the square root of the key dimension, softmax the scores so they become weights summing to one, then take the weighted sum of all the value vectors. So each token's output is a mixture of every token's value, weighted by how much it cares about them.
That's one head. Multi-head does several in parallel with different projections, concatenates them, and passes the result through one more linear layer, so different heads can specialise.
Dot products of two random vectors of dimension dk have variance proportional to dk, so the standard deviation grows like √dk. Large scores push the softmax toward a one-hot distribution, where it saturates and its gradient goes to almost zero.
Say "so the gradients don't vanish" — that is the point. Dividing by √dk keeps the score variance near 1, where softmax still learns.
Because the variance scales with dk, so the standard deviation — the scale the numbers actually live on — scales with √dk. You normalise by the standard deviation, not the variance. Dividing by dk would over-shrink the scores and flatten the attention distribution toward uniform.
O(n²d) time and O(n²) memory in the sequence length n. QKᵀ multiplies n × dk by dk × n, producing an n × n matrix.
That quadratic cost is why long context was hard, why flash attention exists (same maths, tiled so the n × n matrix is never materialised), and why linear-attention approximations get written.
No — roughly the same. The usual setup is dk = dmodel / h, so each head works in a smaller subspace and the total work across h heads matches one full-width head.
You get specialisation for free. That is the actual argument for multi-head, not extra capacity.
Equivariant, not invariant. Shuffle the input tokens and you get the same outputs, shuffled the same way. An invariant function would return an identical result regardless of order.
This is exactly why positional encoding has to be added separately — attention has no notion of order on its own.
In self-attention, Q, K and V all come from the same sequence — tokens attend to each other. In cross-attention, Q comes from one sequence while K and V come from another.
Cross-attention is how a decoder reads an encoder, and how modules in a perception stack read a shared feature map: the queries are the module's own, the keys and values come from the shared representation.
Before the softmax, you add −∞ to the scores at positions a token isn't allowed to see — future positions in a language model, or padding. After softmax those weights are exactly zero.
This is what makes a decoder autoregressive, and it is why training is parallel across all positions while generation is sequential: during training the whole target sequence is present and masked, but at inference each token must exist before the next can attend to it.
No. The softmax, the scaling and the weighted sum are all parameter-free. Every learned parameter in an attention block sits in the projections WQ, WK, WV and the output projection WO.
Good follow-on: the mixing operation itself is fixed; what the model learns is what to compare and what to pass along.
Give both reasons. Path length: every position reaches every other in one step rather than through n sequential hops, so long-range dependencies don't have to survive a chain of updates. Parallelism: it's a couple of matrix multiplications, so training parallelises across the sequence, which an RNN fundamentally cannot.
Everything around attention
Separately, because attention is permutation-equivariant. Three flavours worth naming:
Naming RoPE signals you're current.
LayerNorm normalises across the feature dimension within one token, independently of other samples. BatchNorm normalises each feature across the batch, so it depends on batch composition, needs running statistics at inference, and breaks with variable sequence lengths and small batches.
You want inference to be independent of what else is in the batch. Hence LayerNorm. RMSNorm is the cheaper modern variant that drops mean-centring.
The original paper was post-norm: LayerNorm(x + sublayer(x)). Current
practice is pre-norm: x + sublayer(LayerNorm(x)).
It matters a lot. Pre-norm leaves a clean identity path down the residual stream, so deep stacks train stably without delicate learning-rate warmup. Post-norm at depth is notoriously fragile.
The feed-forward network, not attention. The FFN expands to roughly 4× the model dimension and back, which is about 8d² parameters per block, against roughly 4d² for the four attention projections.
Almost everyone guesses attention, because that's the part that gets talked about.
No. It only helps autoregressive generation. During training the full sequence is available at once and processed in parallel, so there is nothing to cache across steps.
Worth adding: for long contexts the KV cache, not the weights, is usually what fills the GPU — it grows with sequence length × layers × heads.
Cut the image into patches (16×16, say), linearly embed each patch as a token, add positional embeddings, run a standard transformer. The only real change from text is how you make tokens.
Today's interview
A language model maps text to text in one pass, so it can't take actions, can't reach anything it wasn't trained on, and can't tell whether it was right. An agent wraps it in a loop with tools: the model proposes an action, the harness executes it, the result comes back, and the model decides what to do next. The point is the feedback — it means the model only has to be approximately right each step rather than correct first time. That's a much lower bar, which is why it works.
A bare LLM has to know the answer. An agent only has to recognise a good answer when it sees one, and be able to try again. Writing correct SQL from scratch is hard; noticing your query returned zero rows is easy. Wherever checking is cheaper than solving, the loop converts a knowledge problem into a search problem.
95% reliable per step, twenty steps blind → about 36% success. Errors multiply. A 95%-per-step model with a working feedback loop beats a 99%-per-step model without one on any long task.
No. The harness does. The model emits a structured request — a name and JSON arguments — and ordinary code decides whether to honour it, validates the arguments, executes, and appends the result to the context.
This is the most important architectural fact about agents and the one that makes safety tractable. If asked about safety, the answer is structural: you constrain what the harness will execute, not what the model is willing to ask for.
No. Greedy decoding removes sampling noise from the model, but an agent trajectory still varies: tool results change, timing and ordering change, floating-point non-determinism on GPU changes logits at the margin, and one different token early sends the whole trajectory elsewhere.
Which is exactly why you run each case several times and report the spread, not a single number. Most published agent work is weak here — saying so is cheap and marks you out.
No. If you wrote the order of operations in advance, it's a pipeline. It becomes an agent when the model chooses what to do next and when to stop, at runtime.
That's the cleanest available definition of "agentic", and a good answer to a vague question.
The exponential of the mean cross-entropy loss. Loosely: how many tokens the model is effectively choosing between at each step. Lower is better.
And the thing to add: perplexity measures prediction, not truthfulness. A model can get dramatically better at predicting the next token and no better at being right.
| Option | What it is | When |
|---|---|---|
| Prompting | Instructions and examples in context | Default. Always try first |
| RAG | Retrieve documents, put them in context | Knowledge lives in documents and changes |
| LoRA / PEFT | Freeze the base, train small low-rank matrices | Behaviour change, thousands of examples |
| Full fine-tune | Update everything | Rarely worth it; can degrade general ability |
Not really — it's much better for behaviour, format and style than for facts. Facts injected by fine-tuning are learned unreliably, can't be updated without retraining, and don't come with a citation. Retrieval is the right tool for knowledge.
A common follow-up is whether RAG is a kind of fine-tuning. It isn't — the weights never change.
No. Three reasons: attention is quadratic so cost rises sharply; the KV cache grows linearly and fills memory; and retrieval quality degrades in the middle of very long contexts, the "lost in the middle" effect.
The engineering answer is to keep structured state outside the model and feed it a compact view.
Task success rate, number of tool calls, cost, latency, and variance across repeated runs. Overlap metrics and LLM-as-judge both have problems — judges favour longer answers and their own outputs.
And the structural point: you need baselines that each kill one alternative explanation — a human reference for scale, the existing product for commercial relevance, a one-shot LLM to isolate whether the loop does anything, and random search on a matched budget to isolate whether the LLM's prior does anything.
An agent optimising against a scored objective will satisfy what is scored and wreck what is not. So a benchmark needs two tiers: the criteria the agent is scored on, and guard metrics it is never told about. If the scored tier improves while the guard tier degrades, the agent is gaming, not solving.
Four parts: an LLM that decides, tools it can call, a loop that feeds results back, and state it carries between steps. The defining property is that the model chooses the next step, not the programmer.
You describe each tool with a name, description and a JSON schema for its arguments. The model outputs a structured call; your code validates it, runs the tool and returns the result as a new message. Tool descriptions are prompts — vague ones cause wrong tool choices.
About 36% (0.95²⁰). Errors compound across steps, which is why long agent runs fail more than single-step demos suggest. Fixes: fewer, more capable tools, verification steps, and checkpoints where the agent checks its own progress.
Short-term: the context window — summarise or trim as it fills. Long-term: write facts or past episodes to a store (vector database, files) and retrieve them when relevant. Memory is just retrieval; the same pitfalls as RAG apply.
Not by default. Multi-agent setups multiply cost and latency and add hand-off errors. They pay off when work is genuinely parallel (an orchestrator dispatching independent research threads) or when separate roles (critic, verifier) catch real mistakes. Start with one agent and good tools.
Looping on the same action, calling tools with hallucinated arguments, stopping too early and claiming success, losing the goal in a long context, and over-trusting tool output. Guard with step limits, schema validation, explicit success checks and traces you actually read.
Least privilege on tools, sandboxes for code execution, human approval for irreversible actions (payments, deletion, sending messages), and treating all retrieved or tool-returned text as data, not instructions — the defence against prompt injection.
The Model Context Protocol: an open standard for exposing tools and data sources to LLM applications, so a tool is written once and used by any compatible client — rather than writing a custom integration per app.
Route easy steps to smaller models, cache repeated tool calls and prompts, cap steps and tokens, run independent tool calls in parallel, and measure cost per completed task, not per call.
Usually not. If the steps are known, a deterministic workflow that calls the LLM where needed is cheaper, faster and easier to test. Use agents where the path genuinely depends on what's found along the way.
Grounding models in your data
RAG retrieves relevant documents at query time and gives them to the model as context, so it answers from your data instead of from memory. Offline you chunk the documents, embed them and store them in an index. At query time you embed the question, retrieve the closest chunks — usually hybrid with keyword search — rerank them, and have the model answer with citations. It gives you fresh, private, checkable knowledge without retraining, and when it goes wrong, it's usually retrieval, so you evaluate that separately.
Retrieval-augmented generation: fetch relevant documents at query time and put them in the prompt, so the model answers from them rather than from memory.
It gives fresh knowledge without retraining, access to private data, citations you can check, and fewer hallucinations. The weights never change.
Indexing (offline): load documents → clean → chunk → embed each chunk → store vectors plus metadata in an index.
Query (online): rewrite the query if needed → embed it → retrieve top-k (dense, often hybrid with BM25) → rerank → assemble the prompt with the chunks → generate with citations → optionally verify the answer is grounded.
It's a trade-off. Small chunks retrieve precisely but lose context; large chunks keep context but dilute the embedding and waste prompt space. Typical starting point: a few hundred tokens with 10–20% overlap.
Better: split on structure (headings, paragraphs, code blocks) rather than fixed length, and tune chunk size against retrieval recall on your own eval set.
No. One embedding has to summarise the whole chunk, so a large chunk covering several topics matches none of them well. You also fit fewer distinct chunks in the prompt. A common fix is small-to-big: retrieve on small chunks, then pass their parent section to the model.
Sparse (BM25): keyword matching. Great for exact terms, names, IDs, error codes. Misses synonyms.
Dense: embedding similarity. Captures meaning and paraphrase. Weak on rare exact tokens.
Hybrid runs both and fuses the rankings, usually with reciprocal rank fusion. It reliably beats either alone, which is why it's the production default.
A bi-encoder embeds query and document separately, so document vectors are precomputed and search is fast. Used for first-stage retrieval.
A cross-encoder reads query and document together and outputs a relevance score. Far more accurate but must run per pair, so it's used to rerank the top 20–100 candidates.
Approximate nearest neighbour search. HNSW builds a layered proximity graph and greedily walks it; IVF clusters vectors and searches only the nearest clusters; product quantisation compresses vectors to save memory. You trade a little recall for orders-of-magnitude speed.
No. For thousands of chunks, brute-force cosine similarity in NumPy is fine. Postgres with pgvector or plain Elasticsearch/BM25 often suffice. A dedicated vector DB pays off at scale or when you need filtering, updates and replication.
Cosine ignores vector length; dot product doesn't. If embeddings are L2-normalised they're identical. Use what the embedding model was trained with.
Query rewriting (turn a chatty follow-up into a standalone question using chat history), multi-query (generate several phrasings and merge results), HyDE (generate a hypothetical answer and embed that, since answers look more like documents than questions do), and metadata filters (date, product, permissions).
Split it into two problems, because they fail differently.
Retrieval: recall@k, MRR, nDCG against a labelled set of question → relevant-chunk pairs. Generation: faithfulness (is every claim supported by the retrieved context?), answer relevance, and correctness against references. Tools like RAGAS automate parts with LLM judges; spot-check them by hand.
Retrieval. Check whether the right chunk was in the top-k at all. If it wasn't, no prompt change will help — fix chunking, embeddings, hybrid search or the query. If it was retrieved and the answer is still wrong, then look at the prompt, context order and the model.
Models use information at the start and end of a long context better than the middle. So stuffing 50 chunks can hurt. Retrieve fewer, rerank, and put the most relevant chunks first or last.
Instruct the model to answer only from the provided context and to say when it can't; require citations per claim; set a retrieval score threshold below which you abstain; and run a groundedness check on the output.
No. Putting everything in context costs far more per query, is slower, still suffers lost-in-the-middle, and can't cover corpora larger than the window or enforce per-user access. Long context and RAG are complementary: retrieve, then use the long window to include more of what you retrieved.
Agentic RAG: the model decides when and what to search, possibly several times. GraphRAG: build a knowledge graph of entities for multi-hop and global questions. Contextual retrieval: prepend a short document-level summary to each chunk before embedding so it keeps its context. Self-RAG / corrective RAG: the model grades retrieved documents and re-retrieves if they're poor.
RAG for knowledge: facts that change, need citations, or are private. Fine-tuning for behaviour: format, style, domain jargon, tool use. They combine well — fine-tune the model to use retrieved context better.
Prompt injection through retrieved documents (a page saying 'ignore previous instructions'), and access-control leaks — retrieving chunks the user isn't allowed to see. Enforce permissions as filters at retrieval time, not in the prompt, and treat retrieved text as data.
Where the embeddings live
A vector database stores embeddings and finds the ones nearest to a query vector. Exact search compares against every vector, which is fine up to around a million but too slow beyond. So we use approximate nearest-neighbour indexes. IVF clusters the vectors and searches only the closest clusters. HNSW builds a layered graph and walks greedily towards the query — fast and accurate, but memory-hungry. Product quantisation compresses vectors so billions fit in memory. Each trades a little recall for speed, so I measure recall against exact search and tune parameters like nprobe or efSearch. The database part adds metadata filtering, updates, hybrid search and scaling.
Stores embeddings with their IDs and metadata, and answers k-nearest-neighbour queries fast. On top of the index it adds what a library like FAISS doesn't: inserts, updates and deletes, metadata filtering, persistence, replication, access control and often hybrid keyword search.
That's flat (brute-force) search: exact, simple, O(n·d) per query. It's fine — even on a GPU it's fast — up to roughly a million vectors. Beyond that, or at high query rates, latency and cost push you to approximate indexes.
Some true neighbours get missed. Measure recall@k: the fraction of the exact top-k (from a flat search) that the index returns. Tune for a target like 95% recall at your latency budget, on your own data and queries.
Run k-means to partition vectors into nlist clusters (Voronoi cells). Each vector is stored in its cluster's inverted list. At query time, find the nprobe nearest centroids and search only those lists. Raising nprobe raises recall and latency.
Its true nearest neighbours may sit in the next cluster and are missed. That's exactly why nprobe > 1 is used. It also explains IVF's recall ceiling at small nprobe.
Hierarchical Navigable Small World: a multi-layer graph where each vector links to its nearest neighbours. Upper layers are sparse, with long-range links; the bottom layer has everything. A search enters at the top, greedily moves to the closest neighbour, drops a layer, and repeats. Roughly logarithmic search time with very high recall.
M: links per node — more means better recall and more memory. efConstruction: candidate list size while building — slower build, better graph. efSearch: candidate list size per query — the main recall-versus-latency dial at query time.
No. It's excellent for millions of vectors in RAM, but it stores the full vectors plus the graph, builds slowly, and handles deletes poorly. At billions of vectors, IVF with product quantisation or disk-based indexes like DiskANN are cheaper.
Split each vector into m sub-vectors, learn 256 centroids per sub-space with k-means, and store each sub-vector as its 1-byte centroid ID. A 128-float vector (512 bytes) becomes 8–16 bytes. Distances are approximated with lookup tables. Often combined with IVF as IVF-PQ.
Re-rank: retrieve a larger candidate set with the compressed codes, then compute exact distances on the full vectors for those candidates only. Most production systems do this.
Use whatever the embedding model was trained with. On L2-normalised vectors all three give the same ranking, so normalising lets you use the fastest inner-product index. Mixing metrics silently degrades results.
Post-filtering — search, then drop rows that fail the filter — can return far fewer than k results when the filter is selective. Pre-filtering restricts the search to matching rows but can break graph traversal. Good databases filter during traversal, or partition by common filters such as tenant.
No. For thousands to a few million vectors, NumPy, FAISS in memory, or pgvector in an existing Postgres is often simplest. A dedicated database (Milvus, Qdrant, Weaviate, Pinecone) pays off with scale, heavy filtering, multi-tenancy or frequent updates.
Every stored vector must be re-embedded and the index rebuilt — vectors from different models live in different spaces and can't be compared. Store the source text and the model version with each vector, and plan migrations as a full re-index.
Graph indexes mark deleted nodes as tombstones and degrade over time, so they need periodic rebuilds. Many systems write new vectors to a small fresh segment searched alongside the main index, then merge in the background.
FAISS: a library, fastest and most flexible, no persistence or filtering. pgvector: when you already run Postgres and want joins and transactions. Qdrant, Milvus, Weaviate: when you need a service with filtering, scaling and updates. Benchmark recall and latency on your own data — published benchmarks rarely match your workload.
Asked more often than papers
Bias is error from the model being too simple to represent the truth. Variance is error from the model being too sensitive to the particular training sample. High bias underfits, high variance overfits.
The caveat worth adding: very overparameterised networks break the simple story — they can interpolate the training data and still generalise. One sentence of that signals you've read past the textbook.
It reduces variance, not bias. If the model is too simple for the problem, infinite data won't help — you'll just fit the wrong function very precisely.
So "get more data" is the right answer to a variance problem and the wrong answer to a bias problem, and distinguishing them is the actual skill.
No — L1 does. L2 shrinks weights smoothly toward zero but essentially never to exactly zero, because its gradient vanishes as the weight does. L1's gradient has constant magnitude, so it pushes weights all the way to zero and keeps them there.
No. At inference dropout is disabled and activations are scaled so the expected value matches training (or, with inverted dropout, the scaling happened during training and nothing changes at test). Leaving it on is a classic bug that shows up as noisy, irreproducible predictions.
Related: BatchNorm also behaves differently — training uses batch statistics,
inference uses running averages. Forgetting model.eval() breaks both.
Train fits parameters. Validation selects hyperparameters and decides when to stop. Test is touched once, at the end.
The subtlety: validation is a form of fitting too. Every hyperparameter you choose on it leaks a little information, which is exactly why the test set has to stay sealed.
With a softmax output, MSE gives small gradients when the model is confidently wrong, so it learns slowly in exactly the situation you care about. Cross-entropy's gradient stays proportional to the error.
Cross-entropy is also the maximum-likelihood objective for a categorical distribution, so it's the principled choice rather than a trick.
Where careless answers show
Unknowable without the class balance. If 99.9% of voxels are background, predicting "nothing everywhere" scores 99.9% and finds no lesions at all.
The right move is to change the metric, not celebrate: precision and recall, or Dice, or PR-AUC.
Not under heavy imbalance. ROC-AUC is the probability a random positive ranks above a random negative. With vast numbers of negatives, the false-positive rate stays tiny even when the absolute number of false positives is enormous, so the curve looks great while the model is unusable.
Precision-recall AUC is the right choice there, because precision is sensitive to the number of false positives rather than their rate.
Dice = 2|A∩B| / (|A|+|B|). It is exactly F1 computed over voxels, with the segmentation's predicted and true masks as the two sets.
And the relation worth knowing: Dice = 2·IoU / (1 + IoU), so Dice is always ≥ IoU, and they rank models identically. If someone quotes one and you think in the other, you can convert.
Hard Dice isn't — it's computed on thresholded binary masks, and thresholding has zero gradient. Soft Dice is: you use the predicted probabilities directly instead of thresholding, which makes it differentiable and usable as a loss.
That's why the standard recipe is cross-entropy plus soft Dice: cross-entropy gives well-behaved per-voxel gradients, Dice optimises the overlap you're actually judged on.
Dice is a volume-overlap measure, so it is dominated by the bulk of a large structure. A segmentation can score 0.95 Dice while having one badly wrong protrusion, and Dice barely notices.
Hausdorff measures the worst boundary error, so it catches exactly that. Report both — and prefer the 95th-percentile Hausdorff, since the raw maximum is hostage to a single outlier voxel.
Training behaviour
Adam keeps running averages of the gradient (first moment) and its square (second moment), and divides the step by the square root of the second moment — so every parameter gets its own effective learning rate, larger where gradients have been consistently small.
AdamW decouples weight decay from the gradient update, which is the correct formulation and is now standard.
No. Adam converges faster and is far more forgiving about learning rate, but well-tuned SGD with momentum frequently generalises better on vision tasks, and most ImageNet-era benchmarks were set with it.
The honest answer: Adam is the right default, SGD+momentum is worth trying if you have the budget to tune it and generalisation is what you care about.
Depends which kind — and this is the trap.
Clip by norm is what people usually mean and usually want.
Nothing learns. Every unit in a layer computes the same output and receives the same gradient, so they stay identical forever. Symmetry never breaks, and a layer of n units has the expressive power of one.
The whole point of random initialisation is symmetry breaking. Xavier/Glorot for symmetric activations, He/Kaiming for ReLU — which accounts for ReLU zeroing half the inputs, so it uses a larger variance.
Early in training the gradients are large and the adaptive moment estimates in Adam are still poorly conditioned, so full-size steps destabilise the model — especially with post-norm, where there's no clean identity path. Ramping the learning rate up over the first few hundred steps avoids that.
Pre-norm reduces the need for it considerably, which is part of why pre-norm won.
No. Too low and you train impractically slowly, and you may settle into a sharp minimum nearby rather than escaping to a flatter one. Gradient noise is partly a feature — it's one reason smaller batches often generalise better.
Learning rate is the hyperparameter that matters most, and it's the first thing to check when a run misbehaves.
In this order:
Leading with the single-batch test marks you as someone who has actually debugged training.
Imaging
Equivariant, not invariant — the classic trap. Shift the input and the feature maps shift with it. Convolution does not discard position.
Partial invariance comes later, from pooling and from a global pooling or flatten at the end. And for segmentation you specifically want equivariance, because the output is spatial.
k × k × Cin × Cout + Cout for a 2D layer (the trailing term is the bias). In 3D it's k³ × Cin × Cout + Cout.
The two things people forget: the Cin factor — each kernel spans all input channels — and the bias. Note the count is independent of input spatial size, which is the weight-sharing property.
It has no spatial extent, so it mixes channels — a learned linear projection applied at every spatial position. That's how you change channel count cheaply, which is what the bottleneck in a ResNet block is for.
Inserting gaps in the kernel enlarges the receptive field without adding parameters and without downsampling. That's exactly the trade segmentation needs: you want wide context but cannot afford to throw away the resolution that locates the boundary.
nnU-Net. Not a new architecture — an automatic configuration pipeline that picks patch size, spacing, normalisation and augmentation from the dataset's own properties. It remains a hard baseline to beat, and saying "I'd start from nnU-Net" is a sign of someone who knows the field.
Full 3D uses real spatial context but is memory-hungry, so you train on patches. 2D slice-by-slice is cheap but ignores through-plane context. 2.5D feeds a few adjacent slices as channels as a compromise.
The detail that sounds like experience: patch-based 3D training forces tiny batch sizes, which is why you use GroupNorm or InstanceNorm rather than BatchNorm.
Two things with confusingly similar names
A vision transformer is a transformer — attention, queries, keys, values — applied to image patches. A spatial transformer has no attention in it at all; it is a differentiable warping module from 2015, two years before "Attention Is All You Need". They share a word and nothing else. If someone uses the bare phrase "spatial transformer", they mean the warping module.
No — that property is lost. Patches are cut on a fixed grid, so shifting the image by less than one patch width changes the tokenisation entirely: different pixels land in different patches and every token changes. A CNN's kernel slides, so a shift just shifts the feature map.
ViTs recover approximate invariance from data and augmentation rather than from architecture. That is exactly the inductive-bias trade, and it is the reason the data requirement is higher.
(H/P) × (W/P), plus one if there's a CLS token. A 224×224 image at patch size 16 gives 14×14 = 196 patches, 197 tokens.
It matters because attention is quadratic in token count. Halving the patch size to 8 quadruples the tokens and multiplies attention cost by sixteen. In 3D it's (H/P)(W/P)(D/P), which is why volumetric transformers almost always use windowed or hierarchical attention rather than global.
Not without handling the positional embeddings. The patch embedding itself is fine — it's applied per patch, so more patches just means more tokens, and attention handles variable length.
But learned positional embeddings are one vector per position for a fixed grid. At a new resolution there are positions you have no embedding for. The standard fix is to interpolate the positional embeddings onto the new grid, which is what every fine-tuning recipe does.
Two ways to get a single vector for the whole image. The CLS token is an extra learned token prepended to the sequence; it attends to everything and its final state is used as the image representation. Global average pooling just averages all the patch tokens.
They perform comparably. CLS is inherited from BERT; GAP is simpler and common in later work.
Jaderberg et al., 2015. This is the module that makes your own work possible, so be able to explain it precisely — it's the piece that turns registration into something a network can be trained on end to end.
A differentiable module that applies a geometric transformation to a feature map. Three parts: a localisation network predicts the transformation parameters, a grid generator turns those into sampling coordinates for every output position, and a sampler reads the input at those coordinates using bilinear interpolation.
Because every step is differentiable, you can put it in the middle of a network and train the whole thing by backpropagation. Its original purpose was letting a network learn to crop, rotate and rectify its own input — a learned, differentiable form of spatial attention.
No, and this is the heart of it. Nearest-neighbour is piecewise constant in the sampling coordinate — nudge the coordinate a little and the output doesn't change at all until it jumps to a different pixel. The derivative is zero almost everywhere and undefined at the jumps. No gradient reaches the deformation field, so nothing trains.
Bilinear interpolation blends the four surrounding pixels by distance, so the output varies smoothly with the coordinate and the derivative exists. That single property is what makes learned registration possible.
Follow the loop: the network takes the moving and fixed images and outputs a deformation field. The spatial transformer warps the moving image by that field. The loss compares the warped image to the fixed one, plus a regularisation term on the field.
Because the warp is differentiable, the gradient of that image-similarity loss flows back through the sampler, onto the deformation field, and into the network that produced it. So you never need a ground-truth deformation — the supervision comes from how well the warped image matches, which you can always compute.
This is the bridge between the registration section and this one, and it's worth being able to say in one breath.
No. No attention, no queries, keys or values, no tokens. It's a differentiable warping module and the name is a coincidence of vocabulary.
The dates settle it: spatial transformer networks are from 2015, the attention transformer from 2017. The spatial one came first.
Mechanically none — both are sampling grids fed to the same sampler. The difference is their form and where they come from.
The original STN predicts a global, low-dimensional transformation, typically a six-parameter affine, so the whole image moves together. Deformable registration predicts a dense field — a displacement vector at every voxel, millions of parameters — usually parameterised as a velocity field and exponentiated so the result is a diffeomorphism.
Same sampler, vastly different degrees of freedom, which is exactly why the dense case needs a regularisation term and the affine case doesn't.
For the registration and synthesis conversation
No. A GAN is an implicit model — it can produce samples but gives no density. You cannot evaluate p(x), which is why GAN evaluation relies on proxy scores like FID rather than held-out likelihood.
VAEs give a likelihood bound (the ELBO); diffusion models can give one too; autoregressive models give an exact likelihood.
Because the reconstruction term is typically a per-pixel Gaussian likelihood, which is minimised by predicting the conditional mean. When several outputs are plausible, the mean of them is a blur. The KL term pushing the posterior toward the prior adds to it.
No, it's the slowest of the three at inference, because sampling is tens to hundreds of sequential forward passes. GANs and VAEs generate in one. Distillation and flow-matching approaches exist to cut the step count, but the baseline fact stands.
This is a real engineering consideration, not trivia — it's why a deformation-based method can beat a diffusion method on wall-clock by an order of magnitude.
GANs. The generator discovers a narrow set of outputs that reliably fool the discriminator and stops covering the rest of the distribution. Samples look fine individually and lack diversity collectively.
It's a consequence of the adversarial objective: nothing in it requires covering the data distribution, only fooling the current discriminator.
Foundation models
This is the standard probing question, because the loss alone appears to have a trivial solution — output the same vector always.
It doesn't collapse because of asymmetry: an extra predictor head on the online branch only, a stop-gradient on the target branch, and the target being an exponential moving average of the online network. The target is therefore always a lagged version of the student, and the dynamics never settle on the constant.
VICReg and Barlow Twins solve the same problem differently: explicit variance and covariance terms that make collapse directly costly.
No. Contrastive methods (SimCLR, MoCo) need them, and their quality depends heavily on having many — hence large batches or a memory bank. Non-contrastive methods (BYOL, VICReg, Barlow Twins) and masked modelling (MAE, BERT) need none.
Not blindly. Two concrete examples worth having:
Useful medical-specific ones instead: elastic deformation, bias-field simulation, anisotropic resampling, simulated motion artefact.
Your own territory — be exact
A transformation, not an image. The warped image is a by-product you get by applying it. This matters because the transformation is the object you analyse — which is exactly what the synthetic-ageing work does: it generates, transports and decomposes deformation fields.
A smooth, invertible transformation with a smooth inverse. It preserves topology — no tearing, no folding, structures stay connected and in the same order. It also gives inverse consistency, so registering A→B and B→A agree.
In practice you get it by parameterising a stationary velocity field and exponentiating it (scaling and squaring), which guarantees a diffeomorphism by construction. That representation is also what makes parallel transport possible — velocity fields live in a vector space where you can do algebra.
Unsupervised. People assume supervised because it's a neural network predicting a deformation, but there are no ground-truth correspondences. The loss is computed by warping the moving image with a differentiable spatial transformer and comparing it to the fixed one — the same similarity-plus-regularisation objective classical methods optimise, just amortised into a network.
Inference drops from minutes of iterative optimisation to one forward pass.
Not necessarily. Label overlap is an indirect proxy. A deformation can push labels into agreement while being locally implausible — folded, wildly non-uniform, or compressing tissue in ways no anatomy does.
That's why you report regularity alongside overlap: fraction of negative Jacobian determinants, and the distribution of the Jacobian. Volunteering this is the most sophisticated thing you can say about registration evaluation.
Synthetic pairs give exact ground-truth correspondence, which real pairs never have; real pairs keep the model honest about actual anatomical variation. Neither alone is sufficient.
"How exactly did you generate the synthetic supervision?" — the most likely question and the most interesting part. Have the real answer: how the fields were produced, why they were plausible, and the weakness if there is one. If they were smoother than real anatomical variation, say so yourself. Volunteering it is worth more than a clean story.
Where domain maturity shows
No — it still helps, even though the domain is completely wrong, because low-level features (edges, textures, gradients) transfer. The gains are smaller than in natural images and mostly show up when labelled data is scarce.
The better answer: self-supervised pre-training on unlabelled scans from your own domain tends to help more, which is the whole commercial argument for foundation models in a medical imaging company.
Held-out performance on a different site, scanner or hospital. Anything less is a weaker claim than it sounds — a random split within one dataset mostly measures how consistent that dataset's acquisition is.
The 3D analogue of a pixel — "volume element". A scan is a grid of them, each holding one intensity at a position, each a small cuboid of tissue.
The detail worth adding: voxels are frequently anisotropic — MRI often has thicker slices than in-plane resolution, say 0.9 × 0.9 × 3 mm. That's why resampling matters before registration, and why 3D convolutions with isotropic kernels can be the wrong assumption.
Before deep learning
Still asked everywhere, because tabular data is still most of industry — and gradient boosting still wins on it.
Linear regression predicts a continuous value and minimises squared error. Logistic regression predicts a probability by passing a linear score through a sigmoid, and minimises log loss.
Despite the name, logistic regression is a classifier. Its decision boundary is linear.
Yes — log loss is convex in the weights, so there's a single global minimum. MSE through a sigmoid would not be convex, which is another reason cross-entropy is used.
Recursively split the data on the feature and threshold that most reduce impurity (Gini or entropy for classification, variance for regression), until a stopping rule. Interpretable, handles mixed types, no scaling needed — and overfits badly alone.
Random forest: many deep trees trained in parallel on bootstrap samples with random feature subsets, averaged. Reduces variance.
Gradient boosting (XGBoost, LightGBM, CatBoost): shallow trees trained sequentially, each fitting the residual gradient of the previous ensemble. Reduces bias. Usually more accurate, more sensitive to tuning.
No. Splits are thresholds on one feature at a time, so any monotonic transform leaves the tree unchanged. Scaling matters for distance- and gradient-based models: kNN, SVM, linear models with regularisation, neural nets.
Bagging: train independent models on resampled data and average — cuts variance. Boosting: train models in sequence, each correcting its predecessor — cuts bias.
It finds the hyperplane with the maximum margin to the nearest points (the support vectors), with a soft-margin penalty for violations.
The kernel trick computes inner products in a high-dimensional feature space without ever building it, so a linear separator there is a non-linear one in input space (RBF kernel is the usual default).
Pick k centroids, assign each point to its nearest, move each centroid to the mean of its points, repeat until stable.
Failures: you must choose k (elbow, silhouette); sensitive to initialisation (use k-means++); assumes roughly spherical, equal-size clusters; sensitive to scale and outliers.
No — training is free; inference is expensive. It just stores the data. Every prediction scans for neighbours, O(n·d) naively. And it degrades in high dimensions, where all distances become similar (curse of dimensionality).
Finds orthogonal directions of maximum variance — the eigenvectors of the covariance matrix (computed via SVD in practice) — and projects onto the top k.
Caveats: linear only, and variance ≠ importance for your label. Standardise first, or large-scale features dominate.
No. Each component is a linear combination of all original features. It's dimensionality reduction, not feature selection — you lose interpretability.
It assumes features are conditionally independent given the class. Almost never true, yet it works surprisingly well for text classification because ranking, not calibrated probability, is what matters.
The maths under everything
Short, sharp questions where hesitation is visible.
P(A|B) = P(B|A)·P(A) / P(B). Posterior ∝ likelihood × prior.
The classic trap: a 99%-accurate test for a 1-in-1000 disease. A positive result still means only about a 9% chance of disease, because false positives from the huge healthy population swamp the true positives.
MLE picks parameters that maximise the likelihood of the data. MAP maximises likelihood × prior. L2 regularisation is MAP with a Gaussian prior; L1 is MAP with a Laplace prior. That link is the line that impresses.
The probability of seeing data at least this extreme if the null hypothesis were true.
It is not the probability the null is true, and not the probability your result is a fluke.
Type I: false positive — rejecting a true null (rate α). Type II: false negative — missing a real effect (rate β). Power is 1 − β.
Means of many independent samples are approximately normal regardless of the underlying distribution. It's why confidence intervals and A/B tests on averages work.
No. Pearson correlation only measures linear dependence. y = x² over symmetric x has zero correlation and is completely dependent on x.
Entropy H(p) is the expected surprise of a distribution. Cross-entropy H(p,q) is the expected surprise when coding p with q. KL(p‖q) = H(p,q) − H(p): the extra cost. Minimising cross-entropy loss is minimising KL to the data distribution.
No — it isn't symmetric and doesn't satisfy the triangle inequality. KL(p‖q) ≠ KL(q‖p); forward KL is mode-covering, reverse KL is mode-seeking.
Where most of the work is
First ask why they're missing — at random, or informative? Options: drop, impute (mean/median, model-based), or add a missingness indicator feature. Tree boosters handle NaN natively.
One-hot for low cardinality. Target encoding or learned embeddings for high cardinality — with target encoding done out-of-fold, or you leak the label.
Information at training time that won't be available at prediction time. Examples: fitting a scaler on the full dataset before splitting; features computed from the future; duplicate or same-patient rows across splits; target-encoding without folds.
Symptom: validation scores too good to be true.
No — fit on train only, then apply to validation and test. Fitting on everything leaks test statistics into training.
In rough order: use the right metric (PR-AUC, F1, recall at fixed precision); class-weighted loss or focal loss; resampling (undersample majority, oversample minority / SMOTE); tune the decision threshold. Don't resample the test set.
No. Split by time — train on the past, validate on the future — or you leak future information. Use rolling/expanding-window validation.
Split into k folds, train on k−1, validate on the rest, rotate, average. Use grouped CV when samples share an entity (patient, user), and time-based CV for temporal data. Too expensive for large deep models — use a single held-out set.
Language, before and after LLMs
Modern NLP turns text into sub-word tokens, maps them to vectors, and runs a transformer over them. Encoders like BERT see the whole sentence and are best for understanding tasks — classification, entity extraction, embeddings for search. Decoders like GPT see only the left context and are built to generate. Older methods still matter: TF-IDF and BM25 are strong, cheap baselines, especially for exact terms. And for evaluation, overlap metrics like BLEU and ROUGE don't measure correctness, so for clinical text you check factual consistency separately.
Start from characters (or bytes). Repeatedly find the most frequent adjacent pair in the training corpus and merge it into a new token, until the vocabulary reaches a target size (often 32k–200k).
Common words become single tokens; rare words split into pieces, so there is no out-of-vocabulary problem. WordPiece (BERT) and SentencePiece/Unigram are close relatives.
Words: huge vocabulary, and any unseen word becomes an unknown token. Characters: tiny vocabulary but very long sequences, which is expensive for attention and makes the model learn spelling from scratch. Sub-words sit in between.
They never see the letters. 'strawberry' arrives as two or three sub-word tokens, so the character count isn't directly in the input. The same root cause hurts arithmetic on long numbers and rhyming.
Bag-of-words counts word occurrences, ignoring order. TF-IDF weights each count by how rare the word is across documents, so 'the' counts for little and 'myocarditis' for a lot.
Still useful: as a fast, explainable baseline for classification, and as BM25 in search, where it routinely beats dense retrieval on exact terms.
Train a shallow network to predict a word from its neighbours (CBOW) or neighbours from a word (skip-gram), using negative sampling to keep it cheap. Words appearing in similar contexts end up with similar vectors — the distributional hypothesis.
The famous result: king − man + woman ≈ queen.
No — one vector per word type, a blend of all its senses. That's the core limitation contextual models (ELMo, BERT) fixed: the vector depends on the sentence.
BERT is an encoder trained with masked language modelling: it sees the whole sentence in both directions. Great for understanding tasks — classification, NER, embeddings.
GPT is a decoder trained on next-token prediction with a causal mask: it only sees the left context. Built for generation. T5 and BART are encoder–decoder, suited to translation and summarisation.
Hide about 15% of tokens and train the model to predict them from both sides. (BERT replaces 80% of those with [MASK], 10% with a random token, 10% unchanged, so the model can't rely on seeing [MASK] at fine-tune time.)
Not naturally. It was never trained to produce text left to right, so there's no clean way to sample a sequence. It's an encoder; use it to read, not to write.
Fine-tune an encoder with a contrastive objective on pairs that should match (question–answer, paraphrases), pulling them together and pushing in-batch negatives apart. Mean-pool the token vectors. Sentence-BERT is the classic recipe; modern embedding models follow it.
Usually not. Without contrastive fine-tuning, raw BERT embeddings cluster in a narrow cone and cosine similarity between them is poorly informative — often worse than averaged GloVe vectors.
Tagging spans like PERSON, DRUG, DATE. Framed as token classification with BIO tags (Begin, Inside, Outside), so 'acute myeloid leukaemia' becomes B-DISEASE I-DISEASE I-DISEASE. Evaluated with span-level F1, not token accuracy.
BLEU (n-gram precision, translation), ROUGE (n-gram recall, summarisation), BERTScore (embedding similarity), and increasingly LLM-as-judge plus human evaluation.
All the overlap metrics reward copying the reference's words, not being right.
No. A summary can share most of its words with the reference and still flip a negation or invent a number. For anything clinical, check factual consistency separately (entailment models, QA-based checks, or expert review).
The exponential of the average negative log-likelihood per token: roughly, how many tokens the model is choosing between at each step. Lower is better. Only comparable between models with the same tokeniser and the same test text.
Not necessarily. Perplexity measures how well the model predicts text, not whether its answers are correct. Domain-adapting a model can cut perplexity sharply while factual QA barely moves — measure the task directly.
Prompt an LLM when labels are scarce, the task changes often, or you need it working today. Fine-tune a small encoder when you have a few thousand labels, need low latency and cost at scale, or need stable, auditable behaviour. A common path: use the LLM to label data, then distil into a small model.
Abbreviations and ambiguity ('MS' = multiple sclerosis or mitral stenosis), negation ('no evidence of pneumonia'), copy-pasted note bloat, de-identification requirements, and a vocabulary general tokenisers split badly. Negation handling alone breaks many naive keyword systems.
Pre-transformer, still asked
Backprop through time multiplies by the same recurrent Jacobian at every step, so gradients shrink or blow up exponentially with length — vanishing/exploding gradients.
A separate cell state updated additively, gated by forget, input and output gates. The additive path lets gradients flow across many steps — the same idea as a residual connection. GRU is a simpler two-gate variant.
It mitigates them, it doesn't eliminate them. Very long dependencies are still hard, and training remains sequential — which is why attention won.
During training, feed the ground-truth previous token rather than the model's own prediction. Fast and stable, but creates exposure bias: at inference the model sees its own mistakes, which it never trained on.
Greedy: argmax each step, fast, repetitive. Beam: keep top-k partial sequences, good for translation. Sampling with temperature / top-k / top-p (nucleus): diverse, better for open-ended generation.
Beyond prompting
Pre-training: next-token prediction on trillions of tokens. Supervised fine-tuning: instruction–response pairs. Preference tuning: RLHF (reward model + PPO) or DPO directly on preference pairs. Each stage changes behaviour far more than knowledge.
RLHF trains a reward model on human preferences, then optimises the policy against it with RL plus a KL penalty to the reference model. DPO skips the reward model and RL loop, optimising a closed-form classification loss on preferred vs rejected pairs. DPO is simpler and stabler; RL-based methods remain strong for reasoning.
Freeze W; learn a low-rank update ΔW = BA with rank r ≪ d. Trains a tiny fraction of parameters and can be merged into W after training, so no inference latency cost. QLoRA does this on a 4-bit-quantised base.
Loss falls as a smooth power law in parameters, data and compute. Chinchilla showed many models were under-trained: compute-optimal is roughly ~20 tokens per parameter. In practice, models are now over-trained past that because inference cost dominates.
No. 8-bit is usually near-lossless and 4-bit (GPTQ, AWQ) costs little for large models. Damage concentrates in outlier activations, which is what those methods handle.
Chunk documents → embed → store in a vector index. At query time, embed the query, retrieve top-k by similarity (often hybrid with BM25, then rerank with a cross-encoder), put them in the prompt, generate with citations.
Most RAG failures are retrieval failures, so evaluate retrieval (recall@k) separately from generation.
Replace the FFN with many expert FFNs and a router that sends each token to the top-k. Total parameters are huge, but compute per token is that of a small model. Costs: load balancing, all-expert memory footprint, communication.
They are trained to produce probable text, not true text, and the objective rewards fluent guessing over saying 'I don't know'. Mitigations: retrieval grounding, calibrated abstention training, verification tools.
A small draft model proposes several tokens; the big model verifies them in one parallel pass and accepts the matching prefix. Same output distribution, 2–3× faster, because generation is memory-bandwidth-bound, not compute-bound.
Training big things
Data: replicate the model, split the batch, all-reduce gradients. Tensor: split individual matrices across GPUs. Pipeline: split layers across GPUs, micro-batch to fill the bubble. Big runs combine all three.
Optimiser state, gradients and finally the parameters themselves across data-parallel workers, gathering them only when needed. Adam's states are 2× the parameters in fp32, so sharding them is the big memory win.
Compute in fp16/bf16 for speed and memory, keep a fp32 master copy of weights for the update. fp16 needs loss scaling to avoid gradient underflow; bf16 has fp32's exponent range, so it usually doesn't.
Store only some activations in the forward pass and recompute the rest during backward. Trades roughly 30% more compute for a large memory saving.
Mathematically for the gradient, yes — but not for BatchNorm, whose statistics still come from the small micro-batch. Also, the LR schedule must be in terms of optimiser steps.
Rule of thumb ~16 bytes per parameter for mixed-precision Adam (weights, grads, fp32 master, two moments) ≈ 112 GB before activations. That's why you shard or use LoRA.
Common at product companies
Collaborative: learn from the user–item interaction matrix (users like you liked this). Content-based: match item features to user profiles. Hybrids are standard; content features solve cold start.
Candidate generation narrows millions of items to hundreds cheaply — two-tower embeddings with approximate nearest neighbour search. Ranking scores those with a heavy model using rich features. Often a re-ranking stage adds diversity and business rules.
New users: popularity, onboarding questions, contextual features. New items: content features, exploration (bandits).
No. Logged data is biased by the old system's choices, and offline metrics correlate weakly with engagement. Ship via an online A/B test.
Enough to not be lost
Agent, environment, state, action, reward, policy π(a|s), value function V(s) (expected return from s), Q(s,a) (expected return taking a in s), discount factor γ.
Exploit what you know pays off, or explore to find better. ε-greedy, UCB and Thompson sampling are the standard answers; multi-armed bandits are the simplest setting.
Value-based (Q-learning, DQN) learns Q and acts greedily — good for discrete actions. Policy gradient (REINFORCE, PPO) optimises the policy directly — handles continuous actions. Actor-critic combines both.
On-policy learns from data generated by the current policy (PPO). Off-policy can learn from any data, e.g. a replay buffer (Q-learning, DQN) — more sample efficient, less stable.
Largely — it clips the probability ratio so one update can't move the policy too far, a cheap approximation of TRPO's trust region. It's also why PPO was the default RL algorithm for RLHF.
Getting it into production
Data drift (input distribution shifts), concept drift (the input–label relationship changes), training–serving skew (features computed differently online), silent upstream breakages, and feedback loops where the model shapes its own future training data.
Input feature distributions (PSI, KS test), prediction distribution and confidence, latency and error rates, and delayed labels or proxy metrics once they arrive.
Batch: precompute predictions on a schedule — cheap, simple, stale. Online: predict on request — fresh, latency-bound, needs a feature store for consistent features.
When features at serving time are computed differently from training. The fix is one shared feature definition — a feature store — and logging serving features for retraining.
Offline eval → shadow mode (run alongside, don't serve) → canary to a small slice → A/B test on the business metric → gradual ramp, with automatic rollback triggers.
No. Check practical effect size, guard metrics, novelty effects, peeking (repeated checking inflates false positives), and sample ratio mismatch.
Version code, data and config together; fix seeds; log environment and hardware; track runs (MLflow, W&B). Even then GPU non-determinism means report variance across seeds.
The open-ended round
The framework matters more than the specific answer. Say the steps out loud.
1. Clarify the business goal and constraints (latency, scale). 2. Frame as an ML task and pick an offline metric tied to the business metric. 3. Data: sources, labels, leakage risks. 4. Features and model: start with a simple baseline. 5. Serving: batch or online, latency budget. 6. Evaluation: offline then A/B. 7. Monitoring and iteration.
Extreme imbalance, adversarial and drifting distribution, delayed labels, asymmetric costs. Tree boosting on behavioural features as baseline, a threshold tuned to the cost ratio, human review queue, frequent retraining.
Retrieval (BM25 + dense embeddings) then learning-to-rank (pointwise / pairwise / listwise) with click data, corrected for position bias. Metrics: NDCG, MRR offline; CTR and success online.
No — start with a simple baseline. It sets the bar, exposes data problems early, and is often good enough. Interviewers listen for this explicitly.
Increasingly asked
Global: feature importance, partial dependence. Local: SHAP (Shapley-value attributions), LIME, and for images saliency / Grad-CAM. Note they explain the model, not the world, and can be unstable.
Demographic parity (equal positive rate across groups), equalised odds (equal TPR and FPR), calibration within groups. Except in trivial cases they can't all hold at once — you have to choose.
No. Proxies (postcode, name, purchase history) reintroduce it. You need the attribute to even measure fairness.
A calibrated model's 0.8 predictions are right 80% of the time. Check with a reliability diagram / ECE. Fix post hoc with temperature scaling, Platt scaling, or isotonic regression on held-out data.
Knowing when the model doesn't know
Uncertainty comes in two kinds: aleatoric, noise in the data, and epistemic, the model not having seen enough. Softmax scores aren't trustworthy uncertainty. Deep ensembles are the strong baseline, MC dropout the cheap one. Conformal prediction is the practical tool: it wraps any trained model, uses a held-out calibration set, and outputs prediction sets or intervals with a guaranteed coverage — say 90% — as long as the test data looks like the calibration data. The guarantee is on average, not per case, and it breaks under distribution shift.
Aleatoric: noise in the data itself — a blurry scan, an ambiguous label. More data doesn't remove it.
Epistemic: the model's ignorance — inputs unlike the training data. More data, or the right data, does reduce it. Out-of-distribution inputs should show high epistemic uncertainty.
Not reliably. Softmax outputs are often overconfident, and a network can output 0.98 on an input unlike anything it was trained on. Calibration fixes the first problem on in-distribution data; it doesn't fix the second.
Keep dropout switched on at test time, run the same input several times, and treat the spread of predictions as uncertainty. Cheap — no retraining — and an approximation to Bayesian inference. In practice it usually underestimates uncertainty compared with ensembles.
Train several models from different random initialisations and average them. Disagreement between members is a strong uncertainty signal, and accuracy usually improves too. The cost is N times the training and inference.
A wrapper that turns any model's outputs into prediction sets (classification) or intervals (regression) with a guaranteed coverage rate, using a held-out calibration set. No assumptions about the model or the data distribution beyond exchangeability.
Hold out n calibration examples. Score each with a nonconformity score, e.g. 1 − p(true class). Take q̂ as the ⌈(n+1)(1−α)⌉/n quantile of those scores. For a new input, include every class whose score is ≤ q̂.
Result: the set contains the true label with probability at least 1 − α.
No — the guarantee is marginal: 90% averaged over the population. A subgroup can be under-covered while the average holds. Conditional coverage per input is impossible in general; Mondrian (group-wise) conformal gives coverage per chosen group, such as per site or per class.
No. It's post-hoc. It needs only a calibration set the model didn't train on. That makes it practical for an already-deployed or third-party model.
Score with the absolute residual |y − ŷ| on the calibration set, take the quantile q̂, and predict ŷ ± q̂. Those intervals are the same width everywhere; conformalised quantile regression (CQR) starts from quantile predictions so intervals widen where the data is noisier.
Distribution shift. The guarantee assumes calibration and test data are exchangeable. A new scanner, a new truck model or seasonal drift violates that. Fixes: recalibrate on recent data, weighted conformal under covariate shift, or adaptive conformal for time series.
Baselines: maximum softmax probability, energy score, Mahalanobis distance in feature space, ensemble disagreement. Evaluate with AUROC between in- and out-of-distribution sets — ideally with realistic shifts, not just a different dataset.
Let the model abstain when uncertain and hand those cases to a human. Plot a risk–coverage curve: error rate on accepted cases against the fraction accepted. In medicine and safety-critical systems, this is how uncertainty becomes a workflow.
Not necessarily. A model predicting the base rate for every input is perfectly calibrated and useless. You want calibration and sharpness — confident where it can be, uncertain where it should be.
Give an interval on remaining useful life instead of a point estimate, so maintenance is scheduled against the lower bound. Conformal intervals on RUL are a clean, defensible answer — and flag the distribution-shift caveat for new vehicle variants.
Sensors over time
For forecasting I always start from a naive baseline and gradient boosting on lag features, and only add deep models if they beat them on a time-based split. Anomaly detection is mostly unsupervised: model what normal looks like and flag large deviations, then set the threshold by how many alerts people can act on. For remaining useful life, I split by machine, not by window, cap the target early in life when there's no degradation to see, treat machines that haven't failed yet as censored, and penalise late predictions more than early ones.
Trend, seasonality (fixed-period patterns) and residual noise. Many classical models assume stationarity — constant mean and variance — so you difference or detrend first.
Naive (tomorrow = today) and seasonal naive (next Monday = last Monday). They're often surprisingly hard to beat, and reporting against them is what makes a result credible. MASE is literally error scaled by the naive forecast's error.
Often not, on typical industrial tabular time series. LightGBM on lags, rolling means and calendar features is a very strong baseline. Deep models (TCN, LSTM, PatchTST) and foundation models (Chronos, TimesFM) earn their place with many related series or long, rich histories.
MAE (robust, interpretable), RMSE (punishes large misses), MASE (scale-free, comparable across series). Avoid MAPE when values approach zero — it explodes and is asymmetric.
Point: one reading far off. Contextual: normal value, wrong context (high load at 3 a.m.). Collective: each point fine, the sequence abnormal (a vibration pattern).
Model normal behaviour and flag deviation: robust statistics (median and MAD rather than mean and std), Isolation Forest, forecast residuals, or autoencoder reconstruction error. Then set the threshold by the alert rate operators can actually handle.
No. A powerful autoencoder can generalise well enough to reconstruct anomalies too, and if training data contains unlabelled anomalies it learns them as normal. Keep it constrained and clean the training set.
Precision and recall at the operating threshold, precision@k, and the lead time — how early before the failure it fired. Labels are scarce, so get domain experts to review the top alerts.
It inflates scores badly. Point adjustment counts a whole anomalous segment as detected if any single point in it is flagged, so even random scores can look strong. Several published benchmarks were shown to be largely artefacts of it.
Predict how many cycles or hours a component has before failure, from sensor history. Classic benchmark: NASA C-MAPSS turbofan engines, with run-to-failure trajectories.
Early in life a healthy engine shows no degradation, so predicting 300 vs 250 cycles is guesswork. A piecewise-linear target — constant (e.g. 125) until degradation begins, then decreasing — matches what the sensors can actually reveal and trains better.
By unit. Random windows from the same engine in both sets leak that engine's specific behaviour, and test scores look far better than they will in the field.
They're right-censored: you know they lasted at least this long. Dropping them biases the model towards short lives. Survival analysis — Cox models, Weibull, deep survival models — uses them properly.
Fixed-length sliding windows; per-sensor normalisation, ideally per operating condition (load, ambient temperature) so the model sees degradation rather than regime changes; drop constant sensors.
Making models small and fast
There are four levers. Quantisation stores weights and activations in fewer bits — int8 is usually near-lossless with post-training calibration, and 4-bit needs smarter methods or quantisation-aware training. Pruning removes weights, and only structured pruning reliably speeds things up. Distillation trains a small model to imitate a large one. Then compilers like TensorRT fuse operations and pick fast kernels. FLOPs aren't latency, so I always benchmark on the target device and re-check accuracy after each step.
Map floating-point values to low-bit integers with a scale and zero point: q = round(x/s) + z, and back as x ≈ s(q − z). Weights shrink 4× from fp32 to int8, and integer arithmetic is cheaper on hardware that supports it.
Symmetric fixes the zero point at 0 — simpler, good for weights. Asymmetric adds a zero point — better for skewed activations like post-ReLU. Per-channel gives each output channel its own scale and is noticeably more accurate for weights.
Post-training quantisation: quantise a trained model using a small calibration set to pick ranges. Fast, no retraining; fine for int8 on most CNNs.
Quantisation-aware training: insert fake-quantise ops during fine-tuning so the model learns to tolerate rounding (straight-through estimator for gradients). Needed when PTQ loses too much, typically at 4 bits or for small, sensitive models.
No. Only if the hardware and runtime have int8 kernels for your ops. Weight-only quantisation mainly saves memory and bandwidth — which is why it speeds up LLM decoding, which is memory-bound — but adds dequantisation overhead on compute-bound workloads.
A few activation channels have huge outliers, so one scale per tensor wastes almost all the integer range on them. Fixes: LLM.int8 (keep outliers in fp16), SmoothQuant (shift the difficulty from activations into weights), GPTQ and AWQ (error-aware weight quantisation to 4 bits).
Unstructured removes individual weights — high sparsity, little accuracy loss. Structured removes whole channels, heads or layers — the model genuinely gets smaller and faster.
Usually not at all on GPUs. Dense hardware still multiplies the zeros unless you have sparse kernels or special patterns (e.g. NVIDIA 2:4 sparsity). Structured pruning is what gives reliable speed-ups.
Train a small student to match a large teacher's soft output distribution, with temperature T > 1 to expose the 'dark knowledge' in wrong-class probabilities, usually mixed with the normal hard-label loss.
ONNX is a portable model format. Runtimes like TensorRT, OpenVINO or TFLite then optimise the graph for a target: fuse ops (conv + BN + ReLU into one kernel), choose fast kernels, use fp16/int8, plan memory. Often a 2–5× gain without touching accuracy.
Latency: time for one request (watch p95/p99, not just the mean). Throughput: requests per second. Batching raises throughput and usually hurts latency. Real-time perception cares about latency; offline scoring cares about throughput.
Not necessarily. Latency depends on memory traffic, kernel efficiency and parallelism too. Depthwise convolutions have few FLOPs but low arithmetic intensity, so they can run slower than expected. Always measure on the target device.
On the target hardware, with warm-up runs, synchronised timing on GPU, realistic input sizes and batch, many iterations, and percentiles reported. And check accuracy after every optimisation step.
Fixed memory and power budgets, int8 NPUs with limited operator support, deterministic real-time deadlines, and safety certification. Design for the accelerator's supported ops from the start, rather than converting at the end.
Name the levers precisely: 'post-training int8 quantisation with per-channel weights and a 500-image calibration set, exported through ONNX to TensorRT, latency from X to Y ms on Z hardware, accuracy drop under N points'. Precision of vocabulary is what interviewers listen for.
Perception for vehicles
Each sensor fails differently: cameras tell you what something is but not how far, radar gives range and velocity through bad weather but is sparse, and LiDAR gives precise 3D shape. Fusion can happen on raw data, on features, or on object lists. Today's default is feature-level fusion in a bird's-eye-view grid. For tracking, a Kalman filter predicts each object forward and corrects with every sensor's measurement, weighted by its noise. And none of it works without precise calibration and time sync.
Camera: rich appearance, cheap, poor direct depth and weak at night or in glare. Radar: measures range and radial velocity directly, works in rain and fog, but sparse and low angular resolution. LiDAR: precise 3D points, degrades in heavy rain or fog, expensive, sparse at long range.
It transmits frequency-modulated chirps. The frequency difference between sent and received signal gives range; the phase change across successive chirps gives radial velocity (Doppler); differences across the antenna array give angle. FFTs turn these into a range–Doppler–angle cube, then detections.
No. They're far sparser and noisier, with multipath ghosts and clutter, but each point carries velocity, which LiDAR doesn't. 4D imaging radar adds elevation and density, narrowing the gap.
Early: combine raw data — most information, needs exact calibration and sync. Late: each sensor detects objects, then merge lists — modular and robust to a sensor failing, but loses evidence that was weak in each sensor alone. Mid/feature-level, typically in BEV, is the current default.
Bird's-eye view: a top-down grid around the car. LiDAR and radar map into it naturally; camera features are 'lifted' into it using predicted depth (Lift-Splat-Shoot) or attention (BEVFormer). A shared BEV space makes fusion, planning and multi-camera reasoning simple.
Calibration: intrinsics for each camera, extrinsics (rotation and translation) between every sensor. Time synchronisation: at 30 m/s, 30 ms of misalignment is almost a metre. Plus ego-motion compensation for LiDAR sweeps.
No. A miscalibrated or unsynchronised sensor injects confident errors, and a fusion network can learn to over-rely on one modality and fail when it drops out. Train with sensor dropout and test degraded modes.
Track a state (position, velocity) and its uncertainty P. Predict with a motion model — uncertainty grows. Update with a measurement — the Kalman gain weights prediction against measurement by their uncertainties, and P shrinks. Fusion falls out naturally: each sensor has its own noise covariance R.
Only for linear dynamics with Gaussian noise. For non-linear motion or measurements (radar's polar coordinates, turning cars) you use the extended KF (linearise) or unscented KF (sigma points), or particle filters for multi-modal beliefs.
Compute a cost between each track's prediction and each detection (distance, IoU, Mahalanobis), discard implausible pairs with a gate, and solve the assignment with the Hungarian algorithm. Unmatched detections start tentative tracks; unmatched tracks age out.
Points are unordered. PointNet applies a shared MLP per point then a symmetric max-pool, so order doesn't matter. Faster in practice: voxelise or group into vertical pillars (PointPillars) and use 2D/3D convolutions.
3D mAP over IoU or centre-distance thresholds; nuScenes adds NDS, combining mAP with errors in translation, scale, orientation, velocity and attributes. Tracking uses AMOTA and ID switches.
Depth is ambiguous from one image, and depth error grows with distance. Multi-view geometry, temporal stereo and learned depth help; radar or LiDAR resolve it directly — which is the core argument for fusion.
Pre-trained once, adapted everywhere
Vision foundation models are pre-trained on huge data and adapted cheaply. CLIP learns a shared space for images and text with a contrastive loss, which gives zero-shot classification. DINOv2 learns strong visual features with no labels, and SAM produces masks from a click. A vision-language model plugs a vision encoder into an LLM through a small projector. A VLA uses the same recipe but outputs actions instead of text — usually as action chunks, because a large model can't run a fast control loop on its own.
Pre-trained on huge, broad data, usually with self-supervision or weak supervision from captions, and then adapted to many tasks with little labelled data. Examples: CLIP and SigLIP (image–text), DINOv2 (self-supervised features), SAM (segmentation).
An image encoder and a text encoder map a batch of image–caption pairs into one space. A symmetric contrastive (InfoNCE) loss raises the similarity of the true pairs — the diagonal — and lowers all others. SigLIP replaces the softmax with a per-pair sigmoid loss, which scales better.
Embed text prompts like 'a photo of a {class}' for every class, embed the image, and pick the closest. No training on those classes. Prompt wording and ensembling several templates noticeably change accuracy.
No — notably weak. Contrastive training rewards matching the gist of a caption, so 'three dogs left of a cat' and 'a cat left of three dogs' embed similarly. Many VLMs inherit this weakness from their CLIP-style encoder.
A pre-trained vision encoder (ViT, often CLIP/SigLIP), a projector (MLP or Q-Former) mapping patch features into the LLM's token space, and an LLM. Training: first align the projector on caption data with both models frozen, then instruction-tune on visual Q&A. LLaVA is the reference design.
Usually not. Images are resized to the encoder's input size (e.g. 336 or 448 px), so small text and fine defects vanish. Higher-resolution models tile the image into crops, at a large token cost.
Segment Anything: a heavy image encoder run once per image, plus a light prompt encoder and mask decoder that respond to points, boxes or masks in real time. Trained on over a billion masks. Gives class-agnostic masks — it knows 'an object is here', not what it is.
Detecting objects named by arbitrary text, not a fixed class list. Grounding DINO and OWL-ViT align region features with text embeddings. Useful for rare classes and for auto-labelling data.
Vision-language-action model: a VLM whose output is robot or vehicle actions. RT-2 and OpenVLA discretise actions into tokens the LLM predicts; π0 adds a flow-matching action head for smooth continuous control. Trained on large teleoperation datasets such as Open X-Embodiment.
Not directly. A forward pass takes far too long. Systems predict action chunks — several future steps at once — or use a slow VLM for high-level decisions with a fast low-level controller underneath.
Linear probe on frozen features: fastest, tests representation quality, fine for small data. Full fine-tuning: best when data is plentiful and the domain differs. LoRA or adapters: most of the gain at a fraction of the cost.
Partly. Features trained on web photos often miss what matters in X-rays or weld defects. Domain-specific pre-training (e.g. on medical images) or fine-tuning usually wins; always compare against a supervised baseline.
Describing objects that aren't in the image, driven by language priors ('a kitchen usually has a fridge'). Measured with benchmarks like POPE; reduced with grounding, better data and preference tuning.
Learning to act from demonstrations
Behaviour cloning is supervised learning on expert demonstrations — simple, but the policy drifts: one mistake takes it to a state the expert never showed it, and errors compound. DAgger fixes that by having the expert label the states the policy actually visits. When the expert's actions are multimodal, you need a policy that models a distribution, like a diffusion policy. Offline RL learns from logged data without exploring, but has to stop the value function from trusting actions it has never seen. In practice you pre-train by imitation and fine-tune with RL.
Supervised learning on expert demonstrations: input the state, predict the expert's action. Simple, stable, needs no reward function. The default first step for driving and robot manipulation.
Compounding error from covariate shift. The policy trains on expert states but runs on its own; one small mistake leads to an unfamiliar state, where it makes a bigger mistake. Error grows roughly with the square of the horizon.
Only partly. More perfect demos still never show the policy how to recover from being off course. You need recovery data: DAgger, noise injected into the expert's driving (as in DART), or deliberate perturbations.
Roll out the current policy, have the expert label the states it actually visits, add those to the dataset, retrain, repeat. It trains on the policy's own state distribution, cutting error growth to roughly linear. The cost: an expert available to label on demand.
Mean-squared-error regression averages the modes — passing an obstacle on the left or the right becomes driving straight into it. Use policies that model a distribution: mixtures, discretised actions, diffusion policies, or action chunking with a generative head (ACT).
Inverse RL infers the reward the expert seems to optimise, then runs RL on it — generalises better than copying actions but is expensive. GAIL trains a discriminator to tell expert from policy behaviour and uses it as the reward, adversarially.
Learn a policy from a fixed dataset of logged experience with no further interaction — attractive when exploring is dangerous or expensive, as with real vehicles or patients.
No — it usually fails. The Q-function overestimates the value of actions absent from the data, and the policy chases those errors, with no environment to correct them. Offline methods constrain this: CQL penalises unseen actions, IQL avoids querying them, BCQ stays close to the logged behaviour.
Q(s, a) = r + γ · max over a′ of Q(s′, a′): the value of an action is the immediate reward plus the discounted value of the best next action. Q-learning moves Q towards this target.
An experience replay buffer to break correlation between consecutive samples, and a target network updated slowly so the regression target doesn't chase itself. Double DQN then reduced overestimation.
Soft actor-critic: off-policy actor-critic for continuous actions that maximises reward plus policy entropy, so it keeps exploring and is robust. A standard choice for robotic control in simulation.
The agent maximises the reward you wrote, not what you meant — circling to collect points, exploiting a simulator bug. Shaped or learned rewards are especially prone. Mitigate with careful reward design, constraints and watching actual behaviour, not just the curve.
Domain randomisation of physics, sensor noise and visuals so reality looks like one more variation; system identification to match simulator parameters; then fine-tuning on a small amount of real data.
Model-free learns a policy or value directly from experience — simple, sample-hungry. Model-based learns the dynamics and plans with them (MPC, Dreamer) — far more sample-efficient, but model errors compound over long plans.
Imitation when good demonstrations exist and a reward is hard to write — usually the case for driving. RL when a reward is clear and you can simulate cheaply. In practice: pre-train by imitation, then fine-tune with RL or offline RL.