AFS / 01INTERVIEW FIELD MANUAL Reading ↗

VOLUME II · TECHNICAL QUESTION BANK

技术题库

34 道可运行 Live coding,33 道 ML fundamentals,13 个 scientific ML case,以及 research deep dive。每道题都保留证据边界、答案、测试、评分点与追问。

● LILA CONFIRMED ◆ OBSERVED ELSEWHERE ◇ PRACTICE VARIANT

显示全部章节

AI for Science 技术面试题库:证据驱动版

状态:2026-08-21 核验版。题库依据已打开并核验的候选人面经、Lila Sciences 第一方招聘沟通及官方职位描述重构。除“已观察题”明确标注外,所有 prompt 都是能力覆盖练习,不是 Lila Sciences 原题,也不应被宣传为原题。

如何使用这本题库

这不是一份按热门模型堆砌的清单。它按证据强度和能力依赖排序:先能独立写对 tensor、loss、MLP 和训练循环,再进入 attention、VAE 或 diffusion 的最小组件;先能定义数据、标签、切分和指标,再讨论蛋白生成模型。

证据等级

  • A,直接且重复:Lila 第一方范围明确支持,或两个以上高度相关岗位的候选人直接报告重复出现。
  • B,直接但单点:一个高度相关岗位出现,有其他相邻岗位提供方法印证。
  • C,明确可能但未观察到原题:例如 Lila 招聘沟通确认 VAE“could”,但没有候选人公开报告具体 VAE coding 题。
  • D,基础覆盖变体:没有作为原题出现,但它是已观察任务不可绕过的基础,例如从“训练一个模型”和“解释 loss”推导出的手写 MSE。

优先级

优先级 含义 建议题库占比
P0 所有后续题的基础,也是多个近邻岗位重复观察到的主体 50%
P1 高相关 scientific ML 与生成模型小组件 30%
P2 用于覆盖明确提到但证据较弱的高级模型 15%
P3 低迁移扩展;基础稳定后再做 5%

P0 / P1 / P2 学习地图

P0  tensor 与 shape
       ├─ loss:MSE、BCE、cross entropy
       ├─ module:linear、MLP、activation
       ├─ optimization:zero_grad、backward、step
       ├─ loop:train、eval、no_grad、batch aggregation
       ├─ metrics:accuracy、precision/recall、Jaccard
       ├─ algorithms:pairwise distance、KNN、K-means
       └─ reasoning:baseline、split、leakage、imbalance、debug

      P1  scientific ML
       ├─ variable-length sequence、padding mask、pooling
       ├─ scaled dot-product attention、position
       ├─ protein design:generate、filter、rank、test
       ├─ uncertainty、diversity、active learning
       └─ research deep dive、code ownership、failure analysis

      P2  advanced minimal components
       ├─ VAE reparameterization 与 KL
       └─ diffusion q_sample、timestep broadcast、noise loss

主要观察来源

PyTorch 语义依据

本题库已核对 PyTorch stable 文档中的 nn.ModuleMSELossBCEWithLogitsLossCrossEntropyLosstorch.no_gradtorch.topktorch.softmaxtorch.optim.SGDReLUSigmoidLayerNormtorch.utils.data。本次发布的 34 个 Python 代码块已在 PyTorch 2.8.0 环境逐块运行通过;依赖版本变化后应重新验证。


Part I: Live Coding

LC01: Implement MSE from Scratch

Tags: P0, tensor, loss, 5–10 minutes
Evidence / Derivation: D. Derived jointly from the Tempus “train a model” task, the PathAI custom loss question, the Deep Genomics loss follow-up, and Lila’s stated focus on basic ML. No source claims this is an actual Lila question.

Prompt

Implement mse_loss(pred, target, reduction) without calling torch.nn.functional.mse_loss. Support none, sum, and mean. Both inputs must have the same shape.

Clarify First

  • Is broadcasting allowed? Not here, so that shape bugs cannot pass silently.
  • How should an empty tensor behave? Here mean follows whatever PyTorch produces; production code may reject that case separately.
  • Is the dtype guaranteed to be floating point? Confirm this during the interview.

Approach

Validate the shape and the reduction first, then compute the elementwise squared error. none returns the tensor as is, sum aggregates it, and mean averages over every element. Avoid Python loops and keep the autograd graph intact.

Reference Implementation and Minimal Tests

import torch
      
      def mse_loss(pred: torch.Tensor, target: torch.Tensor, reduction: str = "mean"):
          if pred.shape != target.shape:
              raise ValueError(f"shape mismatch: {pred.shape} vs {target.shape}")
          if reduction not in {"none", "sum", "mean"}:
              raise ValueError(f"unsupported reduction: {reduction}")
          loss = (pred - target).square()
          if reduction == "none":
              return loss
          if reduction == "sum":
              return loss.sum()
          return loss.mean()
      
      pred = torch.tensor([1.0, 3.0], requires_grad=True)
      target = torch.tensor([2.0, 1.0])
      assert torch.allclose(mse_loss(pred, target, "none"), torch.tensor([1.0, 4.0]))
      assert torch.allclose(mse_loss(pred, target), torch.tensor(2.5))
      assert torch.allclose(mse_loss(pred, target, "sum"), torch.tensor(5.0))
      mse_loss(pred, target).backward()
      assert torch.allclose(pred.grad, torch.tensor([-1.0, 2.0]))

Scoring Rubric (10 points): formula 3; reduction 2; shape and argument checks 2; no detach and no loops 1; tests and gradient 2.
Likely Follow-ups: Why is MSE sensitive to outliers? When would you use MAE or Huber? When do a batch mean and a per sample mean differ?
Common Failure Modes: forgetting to square the error; averaging only over the batch dimension; calling .item() and breaking the gradient; letting [B,1] and [B] broadcast silently.

LC02: Implement Huber Loss from Scratch

Tags: P0, loss, robust regression, 10–15 minutes
Evidence / Derivation: D. A robust regression variant of the observed loss choice follow-ups, not a publicly reported question.

Prompt

Implement an elementwise Huber loss: quadratic where the absolute error is at most delta, linear beyond it, then take the mean.

Clarify First: whether the two branches must join continuously; that delta must be positive; whether an elementwise result is expected.
Approach: compute abs_error and switch between the quadratic and the linear regime with torch.where. The two branches agree in both value and first derivative at delta.

Reference Implementation and Minimal Tests

import torch
      
      def huber_loss(pred, target, delta=1.0):
          if pred.shape != target.shape:
              raise ValueError("pred and target must have the same shape")
          if delta <= 0:
              raise ValueError("delta must be positive")
          error = pred - target
          abs_error = error.abs()
          quadratic = 0.5 * error.square()
          linear = delta * (abs_error - 0.5 * delta)
          return torch.where(abs_error <= delta, quadratic, linear).mean()
      
      p = torch.tensor([0.0, 3.0], requires_grad=True)
      t = torch.tensor([0.5, 0.0])
      expected = torch.tensor((0.5 * 0.5**2 + (3.0 - 0.5)) / 2)
      assert torch.allclose(huber_loss(p, t), expected)
      huber_loss(p, t).backward()
      assert p.grad is not None
      assert torch.isfinite(p.grad).all()

Scoring Rubric: piecewise formula 4; continuity 2; vectorization 1; argument checks 1; tests 2.
Likely Follow-ups: How do you choose delta? How does this differ from gradient clipping?
Common Failure Modes: forgetting the delta factor in the linear region; a discontinuous boundary; branching on a tensor with a plain Python if.

LC03: Batched Linear Regression Module

Tags: P0, nn.Module, shape, 10–15 minutes
Evidence / Derivation: D. Lila explicitly names basic ML, and the Merck and Microsoft cases include predictive models, but no source reports this exact prompt.

Prompt

Without using nn.Linear, implement a linear regression module that maps [B,D] to [B,1]. The parameters must be discoverable by an optimizer.

Clarify First: whether a bias is needed; whether the input is always two dimensional; any initialization requirement.
Approach: subclass nn.Module and register the weight and the bias as nn.Parameter, then do the matrix multiply in forward. Keep the output at [B,1] so it cannot broadcast by accident against a [B] target.

Reference Implementation and Minimal Tests

import torch
      from torch import nn
      
      class LinearRegressor(nn.Module):
          def __init__(self, in_features):
              super().__init__()
              self.weight = nn.Parameter(torch.randn(in_features, 1) * 0.01)
              self.bias = nn.Parameter(torch.zeros(1))
      
          def forward(self, x):
              if x.ndim != 2 or x.shape[1] != self.weight.shape[0]:
                  raise ValueError("x must have shape [batch, in_features]")
              return x @ self.weight + self.bias
      
      model = LinearRegressor(3)
      x = torch.randn(5, 3)
      y = model(x)
      assert y.shape == (5, 1)
      assert len(list(model.parameters())) == 2
      y.sum().backward()
      assert model.weight.grad.shape == (3, 1)

Scoring Rubric: parameter registration 3; correct shapes 3; forward 2; tests and gradient 2.
Likely Follow-ups: Why does a plain tensor not show up in parameters()? How would you initialize the weights?
Common Failure Modes: transposing the weight shape; omitting super().__init__(); creating a new Parameter on every forward; squeezing the output unintentionally.

LC04: Binary Logits and Numerically Stable BCE

Tags: P0, classification, numerical stability, 15–20 minutes
Evidence / Derivation: B. The Merck vaccination classification case, the Microsoft classification case, and PathAI ML fundamentals. The specific stable formula is a practice variant.

Prompt

Implement binary cross entropy with logits, without applying a sigmoid first, and return the mean loss. Then write the prediction function.

Clarify First: labels are 0/1 floats; logits and targets share a shape; the threshold is a logit of 0.
Approach: use the stable identity max(x,0) - x*y + log1p(exp(-abs(x))). At prediction time, logit >= 0 is equivalent to a probability of at least 0.5.

Reference Implementation and Minimal Tests

import torch
      
      def stable_bce_with_logits(logits, targets):
          if logits.shape != targets.shape:
              raise ValueError("shape mismatch")
          targets = targets.to(logits.dtype)
          positive_part = logits.clamp_min(0)
          correction = torch.log1p(torch.exp(-logits.abs()))
          losses = positive_part - logits * targets + correction
          return losses.mean()
      
      def binary_predict(logits):
          return (logits >= 0).long()
      
      logits = torch.tensor([-100.0, 0.0, 100.0], requires_grad=True)
      targets = torch.tensor([0.0, 1.0, 1.0])
      loss = stable_bce_with_logits(logits, targets)
      reference = torch.nn.functional.binary_cross_entropy_with_logits(logits, targets)
      assert torch.allclose(loss, reference)
      assert binary_predict(logits).tolist() == [0, 1, 1]
      loss.backward()
      assert torch.isfinite(logits.grad).all()

Scoring Rubric: stable formula 4; no duplicate sigmoid 2; shape and dtype 1; prediction 1; extreme value tests 2.
Likely Follow-ups: How would you add pos_weight? How does probability calibration differ from the classification threshold?
Common Failure Modes: applying a sigmoid before a logits loss; overflowing with a direct log(sigmoid(x)); passing integer targets without converting them.

LC05: Multiclass Cross Entropy

Tags: P0, classification, shape, 15–20 minutes
Evidence / Derivation: D. Drawn from the classification cases and ML fundamentals; not a publicly reported exact prompt.

Prompt

Given logits [B,C] and class indices [B], implement the mean cross entropy without F.cross_entropy.

Clarify First: targets are class indices rather than one-hot vectors; whether class weights are needed; whether an ignore index exists.
Approach: build a stable log-softmax with logsumexp, then gather the log probability of the correct class.

Reference Implementation and Minimal Tests

import torch
      
      def cross_entropy_from_logits(logits, targets):
          if logits.ndim != 2 or targets.shape != (logits.shape[0],):
              raise ValueError("expected logits [B,C] and targets [B]")
          if targets.dtype != torch.long:
              raise TypeError("targets must be torch.long")
          log_probs = logits - torch.logsumexp(logits, dim=1, keepdim=True)
          rows = torch.arange(logits.shape[0], device=logits.device)
          return -log_probs[rows, targets].mean()
      
      logits = torch.tensor([[2.0, 0.0], [0.0, 2.0]], requires_grad=True)
      targets = torch.tensor([0, 1])
      actual = cross_entropy_from_logits(logits, targets)
      expected = torch.nn.functional.cross_entropy(logits, targets)
      assert torch.allclose(actual, expected)
      actual.backward()
      assert logits.grad.shape == logits.shape

Scoring Rubric: stable log-softmax 3; correct gather 3; shape and dtype 2; tests 2.
Likely Follow-ups: How would you write label smoothing? How would you handle one-hot targets?
Common Failure Modes: taking the softmax over the wrong dimension; applying a softmax to probabilities again; a target of shape [B,1]; using a naive log(exp().sum()).

LC06: Two-Layer MLP

Tags: P0, MLP, PyTorch, 10–20 minutes
Evidence / Derivation: A. A Zoom Research Scientist candidate directly reports a PyTorch MLP, and Lila explicitly names basic PyTorch.

Prompt

Implement a two-layer MLP: input [B,D], hidden width H, and C output logits. Explain clearly why the forward pass does not apply a softmax.

Clarify First: the task is classification; the output must be logits; the activation is ReLU; whether dropout is required.
Approach: Linear(D,H) → ReLU → Linear(H,C). The classification loss consumes logits, so the model itself does not apply a softmax.

Reference Implementation and Minimal Tests

import torch
      from torch import nn
      
      class MLP(nn.Module):
          def __init__(self, in_dim, hidden_dim, out_dim):
              super().__init__()
              self.fc1 = nn.Linear(in_dim, hidden_dim)
              self.fc2 = nn.Linear(hidden_dim, out_dim)
      
          def forward(self, x):
              if x.ndim != 2:
                  raise ValueError("x must be [batch, features]")
              hidden = self.fc1(x)
              activated = torch.relu(hidden)
              return self.fc2(activated)
      
      model = MLP(4, 8, 3)
      x = torch.randn(5, 4)
      logits = model(x)
      assert logits.shape == (5, 3)
      loss = torch.nn.functional.cross_entropy(logits, torch.tensor([0, 1, 2, 1, 0]))
      loss.backward()
      for parameter in model.parameters():
          assert parameter.grad is not None

Scoring Rubric: module structure 3; shapes 2; correct logits 2; gradient test 2; clarity of explanation 1.
Likely Follow-ups: How many parameters does it have? What are the strengths and weaknesses of ReLU? When would you add normalization or dropout?
Common Failure Modes: applying a final sigmoid or softmax and then using cross entropy; dropping the activation; confusing the input and output dimensions.

LC07: Residue-Level MLP and Flattening

Tags: P0, shape, protein overlay, 15–20 minutes
Evidence / Derivation: D. A safe transfer of the observed MLP question onto biological sequences; not claimed to be an actual question.

Prompt

Given per sequence residue embeddings [B,L,D], use one shared MLP to produce C class logits at every position, shape [B,L,C].

Clarify First: parameters are shared across positions; padding stays out of the loss for now; no loop over L is needed.
Approach: nn.Linear already acts on the last dimension, so no manual flattening is required. Keep the batch and length dimensions intact.

Reference Implementation and Minimal Tests

import torch
      from torch import nn
      
      class ResidueClassifier(nn.Module):
          def __init__(self, d_model, hidden, classes):
              super().__init__()
              self.fc1 = nn.Linear(d_model, hidden)
              self.fc2 = nn.Linear(hidden, classes)
      
          def forward(self, x):
              if x.ndim != 3:
                  raise ValueError("x must be [B,L,D]")
              hidden = self.fc1(x)
              activated = torch.relu(hidden)
              return self.fc2(activated)
      
      x = torch.randn(2, 7, 5)
      model = ResidueClassifier(5, 9, 4)
      out = model(x)
      assert out.shape == (2, 7, 4)
      out.sum().backward()
      assert model.fc1.weight.grad is not None

Scoring Rubric: understanding that Linear acts on the last dimension 4; output shape 2; no loop over positions 2; tests 2.
Likely Follow-ups: How would you exclude padded positions from the loss? How would you obtain a sequence level prediction?
Common Failure Modes: a reshape that mixes the batch and length dimensions; building a separate layer per position; returning [B,C,L] without saying so.

LC08: One Correct Training Step

Tags: P0, training loop, autograd, 15–20 minutes
Evidence / Derivation: A. The Tempus train a model test, the TORC training loop comprehension question, and Cohere model training.

Prompt

Given model, optimizer, x, y, implement one classification training step that returns the loss as a Python float. No gradient accumulation.

Clarify First: the loss is cross entropy; one call handles exactly one batch; the return value may be detached.
Approach: set train mode, clear the gradients, run the forward pass, compute the loss, call backward, step the optimizer, and only then convert the loss to a Python number.

Reference Implementation and Minimal Tests

import torch
      from torch import nn
      
      def train_step(model, optimizer, x, y):
          model.train()
          optimizer.zero_grad()
          logits = model(x)
          loss = torch.nn.functional.cross_entropy(logits, y)
          loss.backward()
          optimizer.step()
          return loss.detach().item()
      
      torch.manual_seed(0)
      model = nn.Linear(3, 2)
      optimizer = torch.optim.SGD(model.parameters(), lr=0.1)
      x = torch.randn(8, 3)
      y = torch.randint(0, 2, (8,))
      before = model.weight.detach().clone()
      value = train_step(model, optimizer, x, y)
      assert isinstance(value, float)
      assert not torch.equal(before, model.weight.detach())
      for parameter in model.parameters():
          assert parameter.grad is not None

Scoring Rubric: correct ordering 5; mode 1; detached return value 1; parameter update test 2; explanation 1.
Likely Follow-ups: When would you use zero_grad(set_to_none=True)? How would you implement gradient accumulation?
Common Failure Modes: forgetting zero_grad; calling .item() before backward; putting optimizer.step() before backward; rebuilding the optimizer on every step.

LC09: A Complete Train and Eval Epoch

Tags: P0, training loop, evaluation, 20–30 minutes
Evidence / Derivation: A. Supported jointly by the training and debugging questions at TORC, Wayve, NVIDIA, and Tempus.

Prompt

Implement a reusable Meter, then write train_epoch(model, batches, optimizer) and evaluate(model, batches) for multiclass classification. Both epoch functions return the sample weighted average cross entropy and accuracy.

Clarify First: each batch is (x, y) and is already on the correct device; the last batch may be smaller; cross entropy uses its default batch mean; an empty iterator returns (nan, nan).
Approach: keep aggregation in one Meter. Multiply each batch mean by its batch size so every sample has equal weight. Keep training and evaluation explicit: training performs the optimizer step, while evaluation uses model.eval() and torch.inference_mode().

Reference Implementation and Minimal Tests

import torch
      import torch.nn.functional as F
      
      class Meter:
          def __init__(self):
              self.loss_sum = 0.0
              self.correct = 0
              self.n = 0
      
          def update(self, loss, logits, y):
              bs = y.shape[0]
              self.loss_sum += loss.detach().item() * bs
              self.correct += (logits.argmax(dim=1) == y).sum().item()
              self.n += bs
      
          def result(self):
              if self.n == 0:
                  return float("nan"), float("nan")
              return self.loss_sum / self.n, self.correct / self.n
      
      def train_epoch(model, batches, optimizer):
          model.train()
          meter = Meter()
          for x, y in batches:
              optimizer.zero_grad(set_to_none=True)
              logits = model(x)
              loss = F.cross_entropy(logits, y)
              loss.backward()
              optimizer.step()
              meter.update(loss, logits, y)
          return meter.result()
      
      @torch.inference_mode()
      def evaluate(model, batches):
          model.eval()
          meter = Meter()
          for x, y in batches:
              logits = model(x)
              loss = F.cross_entropy(logits, y)
              meter.update(loss, logits, y)
          return meter.result()
      
      model = torch.nn.Linear(2, 2)
      batches = [(torch.randn(3, 2), torch.tensor([0, 1, 0])),
                 (torch.randn(1, 2), torch.tensor([1]))]
      optimizer = torch.optim.SGD(model.parameters(), lr=0.1)
      weight_before_train = model.weight.detach().clone()
      train_loss, train_acc = train_epoch(model, batches, optimizer)
      assert train_loss >= 0
      assert 0 <= train_acc <= 1
      assert not torch.equal(weight_before_train, model.weight)
      
      weight_before_eval = model.weight.detach().clone()
      eval_loss, eval_acc = evaluate(model, batches)
      assert eval_loss >= 0
      assert 0 <= eval_acc <= 1
      assert torch.equal(weight_before_eval, model.weight)
      empty_loss, empty_acc = evaluate(model, [])
      assert empty_loss != empty_loss  # NaN
      assert empty_acc != empty_acc  # NaN

Scoring Rubric: reusable meter and sample weighted aggregation 3; correct training step 2; evaluation mode and inference context 2; accuracy 1; empty input and tests 2.
Likely Follow-ups: What changes if the loss reduction is sum? How would you support mixed precision?
Common Failure Modes: averaging batch means instead of weighting by batch size; forgetting zero_grad; building a graph during evaluation; forgetting model.eval(); dividing by zero on empty input.

LC10: Mini-Batch Iterator without DataLoader

Tags: P0, batching, indexing, 10–15 minutes
Evidence / Derivation: D. A building block of the observed end to end model training tasks.

Prompt

Given x and y with a matching first dimension, write a generator that yields mini-batches. Support shuffling and an incomplete final batch.

Clarify First: whether the last batch should be dropped; how randomness is controlled; whether the data already sits on the target device.
Approach: build an index order, slice it by batch size, and use the same indices for both x and y.

Reference Implementation and Minimal Tests

import torch
      
      def iterate_minibatches(x, y, batch_size, shuffle=True, generator=None):
          if x.shape[0] != y.shape[0]:
              raise ValueError("x and y must have equal first dimension")
          if batch_size <= 0:
              raise ValueError("batch_size must be positive")
          n = x.shape[0]
          if shuffle:
              order = torch.randperm(n, generator=generator, device=x.device)
          else:
              order = torch.arange(n, device=x.device)
          for start in range(0, n, batch_size):
              idx = order[start:start + batch_size]
              yield x[idx], y[idx]
      
      x = torch.arange(15).reshape(5, 3)
      y = torch.arange(5)
      batches = list(iterate_minibatches(x, y, 2, shuffle=False))
      batch_sizes = []
      target_batches = []
      for batch_x, batch_y in batches:
          batch_sizes.append(batch_x.shape[0])
          target_batches.append(batch_y)
      assert batch_sizes == [2, 2, 1]
      assert torch.equal(torch.cat(target_batches), y)

Scoring Rubric: synchronized indexing 3; last batch 2; shuffling 2; argument checks 1; tests 2.
Likely Follow-ups: How do you keep this reproducible across epochs? What happens if the data is on CPU and the indices are on GPU?
Common Failure Modes: shuffling x and y separately; dropping the final batch; a batch size of zero breaking the range.

LC11: Accuracy, Precision, and Recall

Tags: P0, metrics, class imbalance, 15–20 minutes
Evidence / Derivation: A. Tempus asks candidates to design metrics for an ML pipeline, and Flagship discusses skewed data directly.

Prompt

Given binary logits and 0/1 labels, compute accuracy, precision, and recall. Return 0 when a denominator is zero.

Clarify First: the threshold is a probability of 0.5, that is a logit of 0; the positive class is 1; the outputs are Python floats.
Approach: threshold to get the predictions, then count TP, FP, FN, and the total number correct. Handle zero denominators explicitly.

Reference Implementation and Minimal Tests

import torch
      
      def binary_metrics(logits, labels):
          if logits.shape != labels.shape:
              raise ValueError("shape mismatch")
          pred = logits >= 0
          truth = labels.bool()
          tp = (pred & truth).sum().item()
          fp = (pred & ~truth).sum().item()
          fn = (~pred & truth).sum().item()
          correct = (pred == truth).sum().item()
          n = labels.numel()
          if n == 0:
              accuracy = 0.0
          else:
              accuracy = correct / n
          precision_denominator = tp + fp
          if precision_denominator == 0:
              precision = 0.0
          else:
              precision = tp / precision_denominator
          recall_denominator = tp + fn
          if recall_denominator == 0:
              recall = 0.0
          else:
              recall = tp / recall_denominator
          return {"accuracy": accuracy, "precision": precision, "recall": recall}
      
      m = binary_metrics(torch.tensor([2.0, 1.0, -1.0, -2.0]), torch.tensor([1, 0, 1, 0]))
      assert m == {"accuracy": 0.5, "precision": 0.5, "recall": 0.5}
      z = binary_metrics(torch.tensor([-2.0]), torch.tensor([0]))
      assert z["precision"] == 0.0
      assert z["recall"] == 0.0

Scoring Rubric: confusion counts 4; threshold 1; zero denominator 2; shape 1; tests 2.
Likely Follow-ups: Why is accuracy misleading under severe imbalance? How do macro and micro averaging differ?
Common Failure Modes: treating logits as probabilities and thresholding at 0.5; swapping precision and recall; hiding a definitional problem behind an epsilon.

LC12: Jaccard Coefficient

Tags: P0, metric, set operation, 10–15 minutes
Evidence / Derivation: B. A PathAI candidate directly reports being asked to code the Jaccard coefficient.

Prompt

Implement the Jaccard coefficient of two boolean tensors, that is intersection over union. When both are empty sets, return 1 by the convention stated in the question.

Clarify First: the convention for an empty union; whether the metric covers the whole tensor or each sample; whether the inputs can be cast to bool.
Approach: validate the shapes, cast to bool, and count the logical and and the logical or separately.

Reference Implementation and Minimal Tests

import torch
      
      def jaccard(a, b):
          if a.shape != b.shape:
              raise ValueError("shape mismatch")
          a = a.bool()
          b = b.bool()
          intersection = (a & b).sum()
          union = (a | b).sum()
          if union.item() == 0:
              return torch.tensor(1.0, device=a.device)
          return intersection.float() / union.float()
      
      first = torch.tensor([1, 1, 0])
      second = torch.tensor([1, 0, 1])
      expected = torch.tensor(1 / 3)
      assert torch.allclose(jaccard(first, second), expected)
      assert jaccard(torch.zeros(3), torch.zeros(3)).item() == 1.0

Scoring Rubric: formula 4; bool conversion 1; empty case 2; shape 1; tests 2.
Likely Follow-ups: How would you compute this in a batched way? How does it relate to Dice and F1?
Common Failure Modes: dividing the intersection by the length; treating addition as union; ignoring the empty union convention.

LC13: Masked Mean Pooling

Tags: P0, mask, sequence, 15–20 minutes
Evidence / Derivation: B. Derived from the shared needs of protein sequence settings, attention masks, and the Latent positional embedding follow-up; not a publicly reported question.

Prompt

Given embeddings [B,L,D] and a mask [B,L] marking the valid positions, return the masked mean [B,D]. A fully padded sequence returns a zero vector.

Clarify First: True in the mask means valid; how a fully padded row is defined; the dtype and device should follow the input.
Approach: expand the mask to [B,L,1], sum over L after masking, and clamp the denominator to at least 1.

Reference Implementation and Minimal Tests

import torch
      
      def masked_mean(x, mask):
          if x.ndim != 3 or mask.shape != x.shape[:2]:
              raise ValueError("expected x [B,L,D] and mask [B,L]")
          weights = mask.to(x.dtype).unsqueeze(-1)
          total = (x * weights).sum(dim=1)
          count = weights.sum(dim=1).clamp_min(1)
          return total / count
      
      x = torch.tensor([[[1., 2.], [3., 4.], [100., 100.]],
                        [[8., 9.], [7., 6.], [5., 4.]]])
      mask = torch.tensor([[1, 1, 0], [0, 0, 0]], dtype=torch.bool)
      out = masked_mean(x, mask)
      assert torch.allclose(out[0], torch.tensor([2., 3.]))
      assert torch.equal(out[1], torch.zeros(2))

Scoring Rubric: shapes 3; mask broadcasting 3; fully padded case 2; tests 2.
Likely Follow-ups: How would you handle a masked max? What order information does mean pooling discard?
Common Failure Modes: dividing by a fixed L; unsqueezing the wrong dimension; zeroing the padding but still dividing by L.

LC14: Pairwise Squared Distances

Tags: P0, tensor, broadcasting, 15–20 minutes
Evidence / Derivation: A. The PathAI KNN question directly requires nearest neighbors between two 2D arrays, and the Zoom K-means question depends on the same distance matrix.

Prompt

Given a query [M,D] and a reference [N,D], return the squared Euclidean distances [M,N] without an M by N double loop.

Clarify First: whether broadcasting is allowed; whether the data size can hold an [M,N,D] intermediate; this version uses the algebraic expansion to keep intermediate memory low.
Approach: use ||q||² + ||r||² - 2 q rᵀ, then clamp to remove the tiny negative values that floating point error can produce.

Reference Implementation and Minimal Tests

import torch
      
      def pairwise_sq_dist(query, reference):
          if query.ndim != 2 or reference.ndim != 2:
              raise ValueError("expected [M,D] and [N,D]")
          if query.shape[1] != reference.shape[1]:
              raise ValueError("expected [M,D] and [N,D]")
          q2 = query.square().sum(dim=1, keepdim=True)
          r2 = reference.square().sum(dim=1).unsqueeze(0)
          cross_term = 2 * query @ reference.T
          distances = q2 + r2 - cross_term
          return distances.clamp_min(0)
      
      q = torch.tensor([[0., 0.], [1., 1.]])
      r = torch.tensor([[1., 0.], [2., 2.]])
      expected = torch.tensor([[1., 8.], [1., 2.]])
      assert torch.allclose(pairwise_sq_dist(q, r), expected)

Scoring Rubric: algebraic formula 4; shapes 2; numerical clamp 1; argument checks 1; tests 2.
Likely Follow-ups: How does the memory of direct broadcasting compare with the matrix formula? How would you write cosine distance?
Common Failure Modes: dropping the square; getting the matmul dimensions wrong; returning [N,M]; taking the square root of the distance matrix before sorting it.

LC15: K-Nearest Neighbors

Tags: P0, KNN, top-k, 20–25 minutes
Evidence / Derivation: B. A PathAI candidate directly reports a 60 minute KNN assessment.

Prompt

Building on LC14, return the indices and squared distances of the k nearest references for each query.

Clarify First: the valid range of k; whether the query itself must be excluded; ties need not have a stable order.
Approach: compute the [M,N] distances, then apply topk(k, largest=False) along the last dimension.

Reference Implementation and Minimal Tests

import torch
      
      def knn(query, reference, k):
          if not 1 <= k <= reference.shape[0]:
              raise ValueError("k out of range")
          q2 = query.square().sum(dim=1, keepdim=True)
          r2 = reference.square().sum(dim=1).unsqueeze(0)
          cross_term = 2 * query @ reference.T
          distances = (q2 + r2 - cross_term).clamp_min(0)
          values, indices = torch.topk(distances, k, dim=1, largest=False, sorted=True)
          return values, indices
      
      ref = torch.tensor([[0., 0.], [2., 0.], [5., 0.]])
      query = torch.tensor([[1., 0.], [4., 0.]])
      dist, idx = knn(query, ref, 2)
      assert idx.tolist() == [[0, 1], [2, 1]]
      assert torch.allclose(dist, torch.tensor([[1., 1.], [1., 4.]]))

Scoring Rubric: distances 3; topk 3; k check 1; shape 1; tests 2.
Likely Follow-ups: What if N is very large? How would you exclude a self neighbor?
Common Failure Modes: letting topk default to the largest values; sorting along the query dimension; k out of range; misunderstanding the tie order.

LC16: K-Means Assignment Step

Tags: P0, K-means, tensor, 15–20 minutes
Evidence / Derivation: A. A Zoom Research Scientist candidate directly reports a K-means implementation question, and it recurs across several general ML interview reports.

Prompt

Given data [N,D] and centroids [K,D], return the nearest cluster id [N] for every point.

Clarify First: squared Euclidean distance is used; ties may follow the default argmin result.
Approach: build the [N,K] distance matrix and take argmin along the K dimension.

Reference Implementation and Minimal Tests

import torch
      
      def assign_clusters(x, centroids):
          if x.ndim != 2 or centroids.ndim != 2:
              raise ValueError("incompatible shapes")
          if x.shape[1] != centroids.shape[1]:
              raise ValueError("incompatible shapes")
          differences = x[:, None, :] - centroids[None, :, :]
          distances = differences.square().sum(dim=-1)
          return distances.argmin(dim=1)
      
      x = torch.tensor([[0., 0.], [1., 0.], [9., 0.]])
      c = torch.tensor([[0., 0.], [10., 0.]])
      assert assign_clusters(x, c).tolist() == [0, 0, 1]

Scoring Rubric: broadcast shapes 4; argmin dimension 2; checks 2; tests 2.
Likely Follow-ups: How would the matrix formula from LC14 reduce memory? What is the time complexity?
Common Failure Modes: taking argmin over the wrong dimension; reducing over the D dimension as well; taking the square root of the distances.

LC17: K-Means Centroid Update and Empty Clusters

Tags: P0, K-means, edge case, 20–25 minutes
Evidence / Derivation: A. From the same K-means family. The empty cluster case is a frequent follow-up rather than observed question text.

Prompt

Given x [N,D], assignments [N], and the previous centroids [K,D], compute the new centroids. An empty cluster keeps its old value.

Clarify First: the empty cluster policy; whether the assignments are guaranteed to lie in [0,K); whether full vectorization is required.
Approach: accumulate the per cluster feature sums and counts with index_add_, divide for the nonempty clusters, and keep the cloned old values for the empty ones.

Reference Implementation and Minimal Tests

import torch
      
      def update_centroids(x, assignments, old_centroids):
          k, d = old_centroids.shape
          if x.shape[1] != d or assignments.shape != (x.shape[0],):
              raise ValueError("incompatible shapes")
          sums = torch.zeros_like(old_centroids)
          sums.index_add_(0, assignments, x)
          counts = torch.zeros(k, dtype=x.dtype, device=x.device)
          point_counts = torch.ones(
              x.shape[0],
              dtype=x.dtype,
              device=x.device,
          )
          counts.index_add_(0, assignments, point_counts)
          new = old_centroids.clone()
          nonempty = counts > 0
          new[nonempty] = sums[nonempty] / counts[nonempty, None]
          return new
      
      x = torch.tensor([[0., 0.], [2., 0.], [10., 0.]])
      a = torch.tensor([0, 0, 1])
      old = torch.tensor([[1., 1.], [9., 1.], [99., 99.]])
      new = update_centroids(x, a, old)
      assert torch.allclose(new, torch.tensor([[1., 0.], [10., 0.], [99., 99.]]))

Scoring Rubric: sums and counts 4; empty clusters 3; shape and device 1; tests 2.
Likely Follow-ups: What other empty cluster policies exist? How would you write this with scatter_add?
Common Failure Modes: dividing by zero for an empty cluster; creating a CPU tensor and causing a device mismatch; overwriting the old centroids in place and making debugging harder.

LC18: Full K-Means with a Fixed Iteration Count

Tags: P0, algorithm composition, 25–30 minutes
Evidence / Derivation: A. The Zoom K-means question directly. This is the version that fits a half hour ceiling.

Prompt

Initialize from the first k data points, run a fixed number of assignment and update rounds, and return the centroids and assignments. An empty cluster keeps its old center.

Clarify First: k-means++ is not needed; the iteration count is fixed and no tolerance is required; the input has at least k points.
Approach: compose the assignment and update steps, then reassign once after the loop so the returned assignments match the final centers.

Reference Implementation and Minimal Tests

import torch
      
      def kmeans(x, k, iterations=10):
          if x.ndim != 2:
              raise ValueError("invalid input")
          if not 1 <= k <= x.shape[0]:
              raise ValueError("invalid input")
          if iterations < 0:
              raise ValueError("invalid input")
          centroids = x[:k].clone()
          for _ in range(iterations):
              differences = x[:, None, :] - centroids[None, :, :]
              distances = differences.square().sum(dim=-1)
              assignment = distances.argmin(dim=1)
              sums = torch.zeros_like(centroids)
              counts = torch.zeros(k, dtype=x.dtype, device=x.device)
              sums.index_add_(0, assignment, x)
              point_counts = torch.ones(
                  x.shape[0],
                  dtype=x.dtype,
                  device=x.device,
              )
              counts.index_add_(0, assignment, point_counts)
              nonempty = counts > 0
              centroids[nonempty] = sums[nonempty] / counts[nonempty, None]
          final_differences = x[:, None, :] - centroids[None, :, :]
          final_distances = final_differences.square().sum(dim=-1)
          return centroids, final_distances.argmin(dim=1)
      
      x = torch.tensor([[0.], [1.], [9.], [10.]])
      centroids, assignment = kmeans(x, 2, 5)
      assert torch.allclose(centroids.sort(dim=0).values, torch.tensor([[0.5], [9.5]]))
      assert set(assignment.tolist()) == {0, 1}

Scoring Rubric: correct loop 3; assignment and update 3; edge checks 1; final consistency 1; tests 2.
Likely Follow-ups: What convergence criterion would you use? How much does initialization affect the result? How would you batch or chunk this?
Common Failure Modes: returning the stale assignments; NaN from an empty cluster; initializing from a view of x that a later in place write corrupts.

LC19: Amino Acid Tokenization and One-Hot Encoding

Tags: P1, protein overlay, indexing, 15–20 minutes
Evidence / Derivation: D. The Lila role involves biological sequences. This exercise only adds the domain framing and does not claim a publicly reported question.

Prompt

Encode a list of equal length sequences over the 20 standard amino acid characters into integer ids [B,L] and one-hot vectors [B,L,20]. Raise on an unknown character.

Clarify First: whether padding or unknown tokens are needed; this version handles only equal length standard characters.
Approach: build a fixed alphabet mapping and convert the ids character by character, using the PyTorch API for the one-hot step. A Python loop is reasonable here for string parsing; the model computation itself needs no loop.

Reference Implementation and Minimal Tests

import torch
      
      AMINO_ACIDS = "ACDEFGHIKLMNPQRSTVWY"
      AA_TO_ID = {}
      for amino_acid_id, amino_acid in enumerate(AMINO_ACIDS):
          AA_TO_ID[amino_acid] = amino_acid_id
      
      def encode_proteins(sequences):
          if not sequences:
              raise ValueError("sequences must be nonempty and equal length")
          sequence_length = len(sequences[0])
          for sequence in sequences:
              if len(sequence) != sequence_length:
                  raise ValueError("sequences must be nonempty and equal length")
          token_rows = []
          try:
              for sequence in sequences:
                  token_ids = []
                  for amino_acid in sequence:
                      token_ids.append(AA_TO_ID[amino_acid])
                  token_rows.append(token_ids)
          except KeyError as exc:
              raise ValueError(f"unknown amino acid: {exc.args[0]}") from exc
          ids = torch.tensor(token_rows, dtype=torch.long)
          one_hot = torch.nn.functional.one_hot(
              ids,
              num_classes=len(AMINO_ACIDS),
          ).float()
          return ids, one_hot
      
      ids, one_hot = encode_proteins(["ACD", "WYA"])
      assert ids.shape == (2, 3)
      assert one_hot.shape == (2, 3, 20)
      assert torch.equal(one_hot.sum(-1), torch.ones(2, 3))

Scoring Rubric: stable vocabulary 2; id shape 2; one-hot 2; input validation 2; tests 2.
Likely Follow-ups: How would you design the padding and unknown tokens? What is the trade-off between one-hot and a learned embedding?
Common Failure Modes: building the vocabulary from a set and getting an unstable order; building a tensor directly from unequal lengths; treating padding as a real amino acid.

LC20: Sequence Classifier with Padding

Tags: P1, embedding, mask, MLP, 25–30 minutes
Evidence / Derivation: B. Combines three supported capabilities, the MLP, sequences, and masking, into one half hour scientific ML question.

Prompt

Given padded token ids [B,L] and a valid mask [B,L], use an embedding, a masked mean, and an MLP to produce sequence level logits [B,C].

Clarify First: whether the padding id is reserved; a fully padded row returns a zero pooled vector; the output is logits.
Approach: embed to [B,L,D], average under the mask to [B,D], then pass through a two-layer MLP.

Reference Implementation and Minimal Tests

import torch
      from torch import nn
      
      class SequenceClassifier(nn.Module):
          def __init__(self, vocab_size, embed_dim, hidden_dim, classes, padding_idx=0):
              super().__init__()
              self.embedding = nn.Embedding(vocab_size, embed_dim, padding_idx=padding_idx)
              self.fc1 = nn.Linear(embed_dim, hidden_dim)
              self.fc2 = nn.Linear(hidden_dim, classes)
      
          def forward(self, token_ids, mask):
              if token_ids.shape != mask.shape:
                  raise ValueError("token_ids and mask must have shape [B,L]")
              x = self.embedding(token_ids)
              weights = mask.to(x.dtype).unsqueeze(-1)
              pooled = (x * weights).sum(1) / weights.sum(1).clamp_min(1)
              hidden = self.fc1(pooled)
              activated = torch.relu(hidden)
              return self.fc2(activated)
      
      model = SequenceClassifier(21, 8, 12, 3)
      tokens = torch.tensor([[1, 2, 3, 0], [4, 5, 0, 0]])
      mask = tokens != 0
      logits = model(tokens, mask)
      assert logits.shape == (2, 3)
      logits.sum().backward()
      assert model.embedding.weight.grad is not None

Scoring Rubric: embedding 2; masked pooling 3; MLP and logits 2; shape 1; tests 2.
Likely Follow-ups: What are the limits of mean pooling? How would you switch to attention pooling? Does the padding embedding get updated?
Common Failure Modes: averaging over the padding; a final softmax; a mask with the wrong device or dtype; dividing by zero on a fully padded row.

LC21: Scaled Dot-Product Attention

Tags: P1, attention, shape, 20–25 minutes
Evidence / Derivation: A. A Cohere candidate reports an attention module implementation, Mistral and xAI candidates report PyTorch multi-head attention, BigHat reports a Transformer implementation discussion, and Lila explicitly says Transformers could be covered.

Prompt

Given q [B,Lq,D], k [B,Lk,D], and v [B,Lk,Dv], implement scaled dot-product attention without masking, returning both the output and the weights.

Clarify First: the softmax runs along the key dimension; the scale is sqrt(D); no dropout is needed.
Approach: q @ k.transpose(-2,-1) gives [B,Lq,Lk]. Divide by the scale, take the softmax along the last dimension, then multiply by v.

Reference Implementation and Minimal Tests

import math
      import torch
      
      def scaled_dot_product_attention(q, k, v):
          if q.ndim != 3 or k.ndim != 3 or v.ndim != 3:
              raise ValueError("q, k, v must be rank 3")
          if q.shape[0] != k.shape[0]:
              raise ValueError("incompatible shapes")
          if k.shape[:2] != v.shape[:2]:
              raise ValueError("incompatible shapes")
          if q.shape[-1] != k.shape[-1]:
              raise ValueError("incompatible shapes")
          scores = q @ k.transpose(-2, -1) / math.sqrt(q.shape[-1])
          weights = torch.softmax(scores, dim=-1)
          output = weights @ v
          return output, weights
      
      q = torch.tensor([[[1., 0.]]])
      k = torch.tensor([[[1., 0.], [0., 1.]]])
      v = torch.tensor([[[2., 0.], [0., 4.]]])
      out, weights = scaled_dot_product_attention(q, k, v)
      assert out.shape == (1, 1, 2)
      assert weights.shape == (1, 1, 2)
      assert torch.allclose(weights.sum(-1), torch.ones(1, 1))

Scoring Rubric: score shape 3; scale 2; softmax dimension 2; output 1; tests 2.
Likely Follow-ups: Why scale at all? What is the time and space complexity? Can Dv differ from D?
Common Failure Modes: transposing the wrong dimension; taking the softmax over queries; forgetting the scale; passing integer tensors and hitting a dtype problem.

LC22: Attention with a Padding Mask

Tags: P1, attention, mask, 20–25 minutes
Evidence / Derivation: B. A safe combination of the observed attention implementation with variable length biological sequences.

Prompt

Extend LC21 with a key validity mask [B,Lk] so that False positions receive no attention weight. Assume every sequence has at least one valid key.

Clarify First: the mask applies to keys only; True means valid; an all False row is undefined input.
Approach: broadcast the mask to [B,1,Lk], fill the invalid scores with the smallest finite value of the dtype, then take the softmax.

Reference Implementation and Minimal Tests

import math
      import torch
      
      def masked_attention(q, k, v, key_valid):
          if key_valid.shape != k.shape[:2]:
              raise ValueError("each sequence needs at least one valid key")
          if not key_valid.any(dim=1).all():
              raise ValueError("each sequence needs at least one valid key")
          scores = q @ k.transpose(-2, -1) / math.sqrt(q.shape[-1])
          invalid = ~key_valid.bool().unsqueeze(1)
          fill_value = torch.finfo(scores.dtype).min
          scores = scores.masked_fill(invalid, fill_value)
          weights = torch.softmax(scores, dim=-1)
          output = weights @ v
          return output, weights
      
      q = torch.randn(1, 2, 3)
      k = torch.randn(1, 3, 3)
      v = torch.randn(1, 3, 4)
      out, w = masked_attention(q, k, v, torch.tensor([[1, 1, 0]], dtype=torch.bool))
      assert out.shape == (1, 2, 4)
      assert torch.equal(w[..., 2], torch.zeros(1, 2))
      assert torch.allclose(w.sum(-1), torch.ones(1, 2))

Scoring Rubric: mask semantics 2; broadcasting 3; masking before the softmax 2; edge check 1; tests 2.
Likely Follow-ups: How would you handle query padding? Why can you not simply zero the weights after the softmax?
Common Failure Modes: inverting the mask; filling with zero instead of a large negative value; masking after the softmax without renormalizing; getting NaN or uniform weights for a fully padded row.

LC23: Causal Self-Attention Mask

Tags: P1, Transformer, mask, 15–20 minutes
Evidence / Derivation: A for the attention family; this causal variant is a practice extension. A protein encoder does not necessarily need a causal mask.

Prompt

Given self-attention scores [B,L,L], build a causal mask so that position i cannot see any j > i, and return the softmax weights.

Clarify First: a position may see itself; the mask is identical across the batch; padding masks are out of scope here.
Approach: build an invalid mask that is True strictly above the diagonal, broadcast it over the batch, then apply a masked fill.

Reference Implementation and Minimal Tests

import torch
      
      def causal_weights(scores):
          if scores.ndim != 3 or scores.shape[-2] != scores.shape[-1]:
              raise ValueError("scores must be [B,L,L]")
          length = scores.shape[-1]
          future = torch.triu(
              torch.ones(length, length, dtype=torch.bool, device=scores.device),
              diagonal=1,
          )
          fill_value = torch.finfo(scores.dtype).min
          masked = scores.masked_fill(future.unsqueeze(0), fill_value)
          return torch.softmax(masked, dim=-1)
      
      scores = torch.zeros(1, 3, 3)
      w = causal_weights(scores)
      assert torch.equal(w[0, 0], torch.tensor([1., 0., 0.]))
      assert w[0, 1, 2].item() == 0.0
      assert torch.allclose(w.sum(-1), torch.ones(1, 3))

Scoring Rubric: mask orientation 4; diagonal rule 2; device 1; softmax 1; tests 2.
Likely Follow-ups: Why do an encoder and an autoregressive decoder use different masks? How would you combine this with a padding mask?
Common Failure Modes: masking the past with a lower triangle; masking the diagonal too; a mask shape that only happens to work at batch size 1.

LC24: Multi-Head Split and Merge

Tags: P1, attention, reshape, 20–25 minutes
Evidence / Derivation: A. Mistral and xAI candidates directly report implementing multi-head attention from scratch. For a half hour question, split and merge is the safer granularity.

Prompt

Implement split_heads mapping [B,L,D] → [B,H,L,D/H] and its inverse merge_heads. D must be divisible by H.

Clarify First: the head dimension goes before the sequence dimension; whether the returned tensor must be contiguous.
Approach: view out the head dimension and transpose L and H. The inverse transposes back, then reshapes after making the tensor contiguous.

Reference Implementation and Minimal Tests

import torch
      
      def split_heads(x, heads):
          if x.ndim != 3 or x.shape[-1] % heads != 0:
              raise ValueError("D must be divisible by heads")
          b, length, dim = x.shape
          reshaped = x.reshape(b, length, heads, dim // heads)
          return reshaped.transpose(1, 2)
      
      def merge_heads(x):
          if x.ndim != 4:
              raise ValueError("x must be [B,H,L,Dh]")
          b, heads, length, head_dim = x.shape
          transposed = x.transpose(1, 2).contiguous()
          return transposed.reshape(b, length, heads * head_dim)
      
      x = torch.arange(2 * 3 * 8).reshape(2, 3, 8)
      h = split_heads(x, 4)
      assert h.shape == (2, 4, 3, 2)
      assert torch.equal(merge_heads(h), x)

Scoring Rubric: split shape 3; transpose 2; inverse 3; tests 2.
Likely Follow-ups: Why does each head scale by sqrt(D/H)? Why is contiguous so often needed after a transpose?
Common Failure Modes: reshaping straight to [B,H,L,Dh] without swapping dimensions; scrambling elements on merge; not checking divisibility.

LC25: Sinusoidal Positional Encoding

Tags: P1, position, tensor construction, 20–25 minutes
Evidence / Derivation: B. A Latent Labs candidate was directly asked about positional embeddings; this specific sinusoidal implementation is a practice variant.

Prompt

Implement a sinusoidal positional encoding [L,D] for length L and dimension D. Even columns use sine and odd columns use cosine. Odd values of D must work.

Clarify First: the classic base of 10000 is used; the output is float; no batch dimension is needed.
Approach: build positions as [L,1] and an exponent vector over the even dimensions. Form the angles, assign the two column families separately, and truncate the cosine columns when D is odd.

Reference Implementation and Minimal Tests

import math
      import torch
      
      def sinusoidal_encoding(length, dim, device=None):
          if length < 0 or dim <= 0:
              raise ValueError("invalid length or dimension")
          position = torch.arange(length, device=device, dtype=torch.float32).unsqueeze(1)
          even = torch.arange(0, dim, 2, device=device, dtype=torch.float32)
          exponent = -math.log(10000.0) * even / dim
          frequencies = torch.exp(exponent)
          angles = position * frequencies
          pe = torch.zeros(length, dim, device=device)
          pe[:, 0::2] = torch.sin(angles)
          if dim > 1:
              pe[:, 1::2] = torch.cos(angles[:, :pe[:, 1::2].shape[1]])
          return pe
      
      pe = sinusoidal_encoding(4, 5)
      assert pe.shape == (4, 5)
      assert torch.allclose(pe[0, 0::2], torch.zeros(3))
      assert torch.allclose(pe[0, 1::2], torch.ones(2))

Scoring Rubric: frequencies 3; sine and cosine layout 3; odd D 2; tests 2.
Likely Follow-ups: How do learned positions, RoPE, and sinusoidal encodings differ? Why does a sequence model need positional information at all?
Common Failure Modes: using the column index instead of the even index in the exponent; an assignment mismatch when D is odd; broadcasting the position and feature dimensions incorrectly.

LC26: VAE Reparameterization

Tags: P2, VAE, stochastic, 10–15 minutes
Evidence / Derivation: C. The Lila recruiting conversation confirms only that a VAE “could” appear, and no specific VAE coding question at an adjacent company was verified.

Prompt

Given mu and logvar, sample z during training and return mu during evaluation. Accepting a generator argument is out of scope.

Clarify First: logvar is the log variance, not the log standard deviation; the train and eval semantics; the shapes match exactly.
Approach: std=exp(0.5*logvar), eps=randn_like(std), z=mu+std*eps.

Reference Implementation and Minimal Tests

import torch
      
      def reparameterize(mu, logvar, training=True):
          if mu.shape != logvar.shape:
              raise ValueError("shape mismatch")
          if not training:
              return mu
          std = torch.exp(0.5 * logvar)
          noise = torch.randn_like(std)
          return mu + std * noise
      
      torch.manual_seed(0)
      mu = torch.zeros(10000)
      logvar = torch.zeros(10000)
      z = reparameterize(mu, logvar)
      assert abs(z.mean().item()) < 0.05
      assert abs(z.std().item() - 1.0) < 0.05
      assert torch.equal(reparameterize(mu, logvar, training=False), mu)

Scoring Rubric: the 0.5 factor 3; randn_like 2; a gradient preserving expression 2; eval semantics 1; tests 2.
Likely Follow-ups: Why does this allow backpropagation? Must evaluation always return mu?
Common Failure Modes: treating exp(logvar) as the standard deviation; drawing a sample from Normal(mu,std) that cannot be reparameterized; using torch.randn(mu.shape) and causing a device mismatch.

LC27: Diagonal Gaussian KL

Tags: P2, VAE, loss, 10–15 minutes
Evidence / Derivation: C. The same low confidence minimal VAE coverage as LC26.

Prompt

Implement the KL divergence of q(z|x)=N(mu, diag(exp(logvar))) against a standard normal prior. Sum over the latent dimension first, then average over the batch.

Clarify First: the latent dimension is the last one; the input is [B,Z]; the return value is a scalar mean.
Approach: per sample, -0.5 * sum(1 + logvar - mu² - exp(logvar)).

Reference Implementation and Minimal Tests

import torch
      
      def gaussian_kl(mu, logvar):
          if mu.shape != logvar.shape or mu.ndim < 2:
              raise ValueError("expected matching [B,...,Z] tensors")
          variance = logvar.exp()
          per_element = 1 + logvar - mu.square() - variance
          per_sample = -0.5 * per_element.sum(dim=-1)
          return per_sample.mean()
      
      mu = torch.zeros(4, 3, requires_grad=True)
      logvar = torch.zeros(4, 3, requires_grad=True)
      kl = gaussian_kl(mu, logvar)
      assert torch.allclose(kl, torch.tensor(0.0))
      kl.backward()
      assert mu.grad is not None
      assert logvar.grad is not None
      mu2 = torch.ones(2, 3)
      assert torch.allclose(gaussian_kl(mu2, torch.zeros_like(mu2)), torch.tensor(1.5))

Scoring Rubric: formula 5; reduction 2; shape 1; tests 2.
Likely Follow-ups: Why is the KL nonnegative? How does beta-VAE change it? What is posterior collapse?
Common Failure Modes: flipping the sign; dropping exp(logvar); averaging over the batch and the latent dimension together and changing the scale.

LC28: Diffusion Forward Noising

Tags: P1, diffusion, broadcasting, 15–20 minutes
Evidence / Derivation: A/B. BigHat discusses a diffusion implementation in an antibody design case, Latent uses a protein diffusion take-home, and Lila confirms diffusion “could” appear. This q_sample is a safe reconstruction of a small component.

Prompt

Given a clean sample x0 [B,...], noise of the same shape, and a per sample alpha_bar_t [B], produce x_t.

Clarify First: alpha_bar_t has already been gathered by timestep; it lies in [0,1]; x0 may have any rank.
Approach: reshape [B] to [B,1,...,1], then apply sqrt(alpha_bar)*x0 + sqrt(1-alpha_bar)*noise.

Reference Implementation and Minimal Tests

import torch
      
      def q_sample(x0, noise, alpha_bar_t):
          if x0.shape != noise.shape or alpha_bar_t.shape != (x0.shape[0],):
              raise ValueError("incompatible shapes")
          if ((alpha_bar_t < 0) | (alpha_bar_t > 1)).any():
              raise ValueError("alpha_bar_t must be in [0,1]")
          batch_size = x0.shape[0]
          extra_dimensions = x0.ndim - 1
          broadcast_shape = (batch_size,) + (1,) * extra_dimensions
          a = alpha_bar_t.reshape(broadcast_shape)
          a = a.to(dtype=x0.dtype, device=x0.device)
          return a.sqrt() * x0 + (1 - a).sqrt() * noise
      
      x0 = torch.tensor([[1., 2.], [3., 4.]])
      noise = torch.tensor([[10., 20.], [30., 40.]])
      out = q_sample(x0, noise, torch.tensor([1.0, 0.0]))
      assert torch.equal(out[0], x0[0])
      assert torch.equal(out[1], noise[1])

Scoring Rubric: formula 4; generic broadcasting 3; range and shape checks 1; boundary tests 2.
Likely Follow-ups: How does alpha_bar relate to alpha and beta? Why is t sampled randomly during training?
Common Failure Modes: multiplying [B] against [B,L,D] directly and broadcasting incorrectly; confusing alpha with alpha_bar; forgetting the square root on both coefficients.

LC29: Diffusion Noise Prediction Loss

Tags: P1, diffusion, training objective, 20–25 minutes
Evidence / Derivation: Same as LC28. This is the minimal training objective of a full diffusion pipeline, not an exact prompt from any report.

Prompt

Given a model, x0, a batch of timesteps t, and the cumulative schedule alpha_bars [T], sample the noise, build x_t, have the model predict the noise, and return the MSE.

Clarify First: the model takes (x_t,t) and returns the same shape; t is long and within range; only the training loss is required, not a sampler.

Approach: gather alpha_bars[t], draw the noise, build xt, then compare the prediction against the true noise with MSE.

Reference Implementation and Minimal Tests

import torch
      from torch import nn
      
      def diffusion_noise_loss(model, x0, t, alpha_bars):
          if t.dtype != torch.long or t.shape != (x0.shape[0],):
              raise ValueError("t must be long [B]")
          if (t < 0).any() or (t >= alpha_bars.numel()).any():
              raise ValueError("t out of range")
          noise = torch.randn_like(x0)
          batch_size = x0.shape[0]
          extra_dimensions = x0.ndim - 1
          broadcast_shape = (batch_size,) + (1,) * extra_dimensions
          a = alpha_bars[t].to(x0).reshape(broadcast_shape)
          xt = a.sqrt() * x0 + (1 - a).sqrt() * noise
          predicted = model(xt, t)
          if predicted.shape != noise.shape:
              raise ValueError("model output shape mismatch")
          error = predicted - noise
          return error.square().mean()
      
      class ZeroNoiseModel(nn.Module):
          def forward(self, xt, t):
              return torch.zeros_like(xt)
      
      torch.manual_seed(0)
      x0 = torch.randn(3, 4)
      timesteps = torch.tensor([0, 1, 2])
      alpha_bars = torch.tensor([0.9, 0.5, 0.1])
      loss = diffusion_noise_loss(ZeroNoiseModel(), x0, timesteps, alpha_bars)
      assert loss.ndim == 0
      assert loss.item() >= 0
      assert torch.isfinite(loss)

Scoring Rubric: schedule gather 2; broadcasting 2; q_sample 2; target and loss 2; validation and tests 2.
Likely Follow-ups: How does predicting x0 or v differ? Should different values of t be weighted? Where does conditional input enter?
Common Failure Modes: using one t for the whole batch; taking the loss against x0 instead of the noise; wasting time on a reverse sampler; not checking the model output shape.

LC30: Training Loop Code Review and Fix

Tags: P0, debugging, code comprehension, 25–30 minutes
Evidence / Derivation: A. TORC, Wayve, NVIDIA, PathAI, and Tempus all provide signal on reading, debugging, or coding style in a training loop.

Prompt

Review the following faulty logic, whose stated goal is to “train a binary classification model”: the model outputs logits, yet a sigmoid is applied before binary_cross_entropy_with_logits; gradients are never cleared; validation runs in train mode and builds a graph. Write separate, correct training and evaluation epoch functions.

Clarify First: each batch is (x, y); the target can be reshaped to match the logits; the loss must be averaged with sample weighting.
Approach: keep the logits and make training and evaluation separate entry points. The training function owns zero_grad/backward/step; the evaluation function owns eval and inference mode. Both accumulate the detached batch loss times the batch size.

Reference Implementation and Minimal Tests

import torch
      import torch.nn.functional as F
      
      def train_binary_epoch(model, batches, optimizer):
          model.train()
          loss_sum = 0.0
          sample_count = 0
          for x, y in batches:
              optimizer.zero_grad()
              logits = model(x)
              targets = y.to(logits.dtype).reshape_as(logits)
              loss = F.binary_cross_entropy_with_logits(logits, targets)
              loss.backward()
              optimizer.step()
              batch_size = x.shape[0]
              loss_sum += loss.detach().item() * batch_size
              sample_count += batch_size
          if sample_count == 0:
              raise ValueError("empty epoch")
          return loss_sum / sample_count
      
      @torch.inference_mode()
      def evaluate_binary_epoch(model, batches):
          model.eval()
          loss_sum = 0.0
          sample_count = 0
          for x, y in batches:
              logits = model(x)
              targets = y.to(logits.dtype).reshape_as(logits)
              loss = F.binary_cross_entropy_with_logits(logits, targets)
              batch_size = x.shape[0]
              loss_sum += loss.item() * batch_size
              sample_count += batch_size
          if sample_count == 0:
              raise ValueError("empty epoch")
          return loss_sum / sample_count
      
      model = torch.nn.Linear(2, 1)
      batches = [(torch.randn(3, 2), torch.tensor([0, 1, 0])),
                 (torch.randn(1, 2), torch.tensor([1]))]
      optimizer = torch.optim.SGD(model.parameters(), lr=0.1)
      weight_before_train = model.weight.detach().clone()
      train_loss = train_binary_epoch(model, batches, optimizer)
      assert train_loss >= 0
      assert not torch.equal(weight_before_train, model.weight)
      
      weight_before_eval = model.weight.detach().clone()
      gradient_before_eval = model.weight.grad.detach().clone()
      eval_loss = evaluate_binary_epoch(model, batches)
      assert eval_loss >= 0
      assert torch.equal(weight_before_eval, model.weight)
      assert torch.equal(gradient_before_eval, model.weight.grad)

Scoring Rubric: spotting the double sigmoid 2; clearing gradients 2; separate train and eval modes plus inference mode 2; aggregation 2; tests 2.
Likely Follow-ups: Why can stale .grad values still exist after validation? How would you prove that no new graph was built? How would you add gradient clipping?
Common Failure Modes: assuming eval() disables autograd; setting the grads to None before validation and then writing a broken test; thresholding logits at 0.5.

LC31: ReLU Forward and Local Backward

Tags: P0, activation, manual gradient, 10–15 minutes
Evidence / Derivation: D. A Zoom Research Scientist candidate was asked about ReLU and its weaknesses, while first-person research interview retrospectives include manual backward passes. This exact two-part prompt is a bounded fundamentals variant.

Prompt

Given x and an upstream gradient grad_out of the same shape, implement ReLU’s forward output and its local backward gradient. Define the derivative at zero as zero. Do not use custom autograd.

Clarify First: elementwise ReLU only; no in-place update; x and grad_out are floating tensors of the same shape.
Approach: clamp the forward value at zero, then multiply the upstream gradient by the positive-input mask.

Reference Implementation and Minimal Tests

import torch
      
      def relu_forward_backward(x, grad_out):
          if x.shape != grad_out.shape:
              raise ValueError("shape mismatch")
          y = x.clamp_min(0)
          grad_x = grad_out * (x > 0).to(grad_out.dtype)
          return y, grad_x
      
      x = torch.tensor([-2.0, 0.0, 3.0])
      grad_out = torch.tensor([1.0, 2.0, -4.0])
      y, grad_x = relu_forward_backward(x, grad_out)
      assert torch.equal(y, torch.tensor([0.0, 0.0, 3.0]))
      assert torch.equal(grad_x, torch.tensor([0.0, 0.0, -4.0]))
      
      x_auto = x.clone().requires_grad_()
      torch.relu(x_auto).backward(grad_out)
      assert torch.equal(grad_x, x_auto.grad)

Scoring Rubric: forward 3; chain rule 3; zero convention 1; shape check and tests 3.
Likely Follow-ups: Why can ReLU units die? How would LeakyReLU change the mask?
Common Failure Modes: returning only the binary mask; forgetting grad_out; assigning derivative one at zero without stating it.

LC32: Linear Layer Forward and Local Backward

Tags: P0, linear layer, manual gradient, 15–20 minutes
Evidence / Derivation: D. Manual backward passes and neural-layer forward/backward exercises appear in Silvia Sapora’s and Nishanth Kumar’s first-person research interview retrospectives. This exact linear-layer prompt is a short practice reconstruction.

Prompt

For x [B,D], weight [D,H], bias [H], and upstream grad_out [B,H], return y, grad_x, grad_weight, and grad_bias. Do not call autograd inside the implementation.

Clarify First: the batch gradient is summed for parameters; no activation or loss is included.
Approach: apply the chain rule to y = x @ weight + bias; the three gradients are matrix products plus a batch sum.

Reference Implementation and Minimal Tests

import torch
      
      def linear_forward_backward(x, weight, bias, grad_out):
          if x.ndim != 2 or weight.ndim != 2:
              raise ValueError("invalid parameter shapes")
          if bias.shape != (weight.shape[1],):
              raise ValueError("invalid parameter shapes")
          expected_grad_shape = (x.shape[0], weight.shape[1])
          if x.shape[1] != weight.shape[0]:
              raise ValueError("incompatible shapes")
          if grad_out.shape != expected_grad_shape:
              raise ValueError("incompatible shapes")
          y = x @ weight + bias
          grad_x = grad_out @ weight.T
          grad_weight = x.T @ grad_out
          grad_bias = grad_out.sum(dim=0)
          return y, grad_x, grad_weight, grad_bias
      
      x = torch.tensor([[1.0, 2.0], [-1.0, 3.0]], requires_grad=True)
      weight = torch.tensor([[2.0, -1.0], [0.5, 4.0]], requires_grad=True)
      bias = torch.tensor([0.25, -0.5], requires_grad=True)
      grad_out = torch.tensor([[1.0, -2.0], [3.0, 0.5]])
      y, grad_x, grad_weight, grad_bias = linear_forward_backward(
          x,
          weight,
          bias,
          grad_out,
      )
      (x @ weight + bias).backward(grad_out)
      assert y.shape == grad_out.shape
      assert torch.allclose(grad_x, x.grad)
      assert torch.allclose(grad_weight, weight.grad)
      assert torch.allclose(grad_bias, bias.grad)

Scoring Rubric: forward 2; grad_x 2; parameter gradients 4; shapes and tests 2.
Likely Follow-ups: Compose this with LC31 for one MLP block. Why is grad_bias summed over the batch?
Common Failure Modes: transposing the wrong operand; averaging parameter gradients without being asked; omitting the batch dimension check.

LC33: Collate Variable-Length Protein Sequences

Tags: P1, data loading, padding mask, 15–20 minutes
Evidence / Derivation: A for the capability. An OpenAI Research Engineer candidate reports implementing a data loader and tests; a Rakuten Research Scientist candidate reports a large-JSON data loader. Protein collation is the shorter domain transfer.

Prompt

Implement collate_sequences(batch, pad_id=0) for a nonempty list of (token_ids, label) pairs. Each sequence is a nonempty 1D LongTensor. Return padded tokens [B,Lmax], a boolean valid-token mask, and labels [B].

Clarify First: all sequences share dtype and device; labels are integer classes; this question stops at collation, not a full Dataset or worker pipeline.
Approach: compute lengths, allocate once, fill each row, and obtain the mask by comparing positions with lengths.

Reference Implementation and Minimal Tests

import torch
      
      def collate_sequences(batch, pad_id=0):
          if not batch:
              raise ValueError("empty batch")
          sequences, labels = zip(*batch)
          length_values = []
          for sequence in sequences:
              if sequence.ndim != 1 or sequence.numel() == 0:
                  raise ValueError("sequences must be nonempty 1D tensors")
              length_values.append(sequence.numel())
          device = sequences[0].device
          lengths = torch.tensor(length_values, device=device)
          max_length = int(lengths.max().item())
          tokens = torch.full(
              (len(sequences), max_length),
              pad_id,
              dtype=sequences[0].dtype,
              device=device,
          )
          positions = torch.arange(max_length, device=device)
          valid = positions[None, :] < lengths[:, None]
          for row, sequence in zip(tokens, sequences):
              row[:sequence.numel()] = sequence
          return tokens, valid, torch.tensor(labels, device=tokens.device)
      
      batch = [(torch.tensor([4, 8, 2]), 1), (torch.tensor([7]), 0)]
      tokens, valid, labels = collate_sequences(batch)
      assert torch.equal(tokens, torch.tensor([[4, 8, 2], [7, 0, 0]]))
      assert torch.equal(valid, torch.tensor([[True, True, True], [True, False, False]]))
      assert torch.equal(labels, torch.tensor([1, 0]))

Scoring Rubric: lengths and allocation 3; padding 2; mask 2; labels and tests 2; empty-batch check 1.
Likely Follow-ups: When would IterableDataset be preferable for large JSON? How should padding interact with attention?
Common Failure Modes: confusing valid and padding masks; padding labels; implementing multiprocessing before the collate function works.

LC34: LayerNorm over the Last Dimension

Tags: P1, normalization, tensor operations, 10–15 minutes
Evidence / Derivation: D. A Mistral candidate reports debugging a pre-norm Transformer, but no verified source reports this exact LayerNorm implementation prompt. It is a bounded prerequisite exercise, checked against the official PyTorch semantics.

Prompt

Implement layer_norm_last(x, gamma, beta, eps) using basic tensor operations. Normalize only the last dimension, use the population variance, and preserve every leading dimension.

Clarify First: gamma and beta have shape [D], where D = x.shape[-1]; return the same shape as x.
Approach: retain the last dimension while computing mean and variance, multiply by rsqrt(var + eps), then apply the affine parameters by broadcasting.

Reference Implementation and Minimal Tests

import torch
      
      def layer_norm_last(x, gamma, beta, eps=1e-5):
          if x.ndim == 0:
              raise ValueError("invalid shapes")
          if gamma.shape != (x.shape[-1],):
              raise ValueError("invalid shapes")
          if beta.shape != gamma.shape:
              raise ValueError("invalid shapes")
          mean = x.mean(dim=-1, keepdim=True)
          var = (x - mean).square().mean(dim=-1, keepdim=True)
          normalized = (x - mean) * torch.rsqrt(var + eps)
          return normalized * gamma + beta
      
      x = torch.tensor([[1.0, 3.0], [2.0, 2.0]])
      gamma = torch.tensor([2.0, 0.5])
      beta = torch.tensor([0.25, -1.0])
      actual = layer_norm_last(x, gamma, beta)
      expected = torch.nn.functional.layer_norm(x, (2,), gamma, beta)
      assert torch.allclose(actual, expected)
      assert torch.allclose(actual[1], beta)

Scoring Rubric: reduction axes 3; population variance and epsilon 2; affine broadcast 2; shape check and tests 3.
Likely Follow-ups: How does LayerNorm differ from BatchNorm? What changes for RMSNorm?
Common Failure Modes: normalizing across the batch; using sample variance; dropping leading dimensions.

Live Coding Coverage Audit

Capability Questions
tensors, shapes, broadcasting LC01, LC03, LC07, LC13–LC15, LC21–LC25, LC28–LC34
loss LC01–LC05, LC27, LC29
MLP and modules LC03, LC06–LC07, LC20, LC31–LC32
activation and manual backward LC31–LC32
training loop LC08–LC10, LC30
data batching and collation LC10, LC33
normalization LC34
metrics LC11–LC12
KNN and K-means LC14–LC18
protein sequence overlay LC07, LC13, LC19–LC20
attention and masks LC21–LC24
minimal VAE LC26–LC27
minimal diffusion LC28–LC29

The advanced models do not outnumber the fundamentals. The VAE appears in only two questions, both at evidence grade C, and diffusion covers only the forward pass and the noise loss, with no full sampler. LC31–LC34 add missing fundamentals as separate 10–20 minute components rather than one oversized exercise.


第二部分:ML fundamentals

本部分默认是口头题。答案给出应覆盖的逻辑,不要求逐字背诵。好的回答要先说明假设,再给结论,并能连接到数据、实验和失败模式。

MF01:MSE、MAE 和 Huber 应怎样选择?

标签:P0、loss
证据:PathAI custom loss、Deep Genomics loss 追问、Tempus model-training task;具体三者比较是基础覆盖变体。

答案

  • MSE 对应高斯噪声下的负对数似然,平滑、梯度随误差增大,但离群值影响大。
  • MAE 对应 Laplace 风格噪声,较稳健,但零点不可导,实践中可用次梯度。
  • Huber 在小误差区用二次项、大误差区用线性项,在稳定优化和离群值稳健之间折中。
  • 选择不能只看 loss 名称,应看标签生成机制、异常值是否真实、评价指标和输出尺度。
  • 必须报告与业务/科学目标一致的 validation metric,不能只比较 training loss。

追问:多任务标签尺度差异怎么办?heteroscedastic noise 怎么建模?
常见误区:把 MAE 说成“总是更好”;没有先检查 outlier 是错误还是重要信号。

MF02:为什么 BCEWithLogitsLoss 通常优于 sigmoid 后 BCE?

标签:P0、numerical stability
证据:分类与训练题的基础覆盖;PyTorch 官方语义已核对。

答案

  • logits loss 将 sigmoid 和 log-loss 合并,用稳定的 log-sum-exp 形式避免概率接近 0 或 1 时的下溢和 log(0)
  • model forward 保留 logits,训练 loss 直接接 logits;需要概率时再 sigmoid。
  • 二分类默认 threshold 0.5 概率等价于 threshold 0 logit。
  • class imbalance 可用 pos_weight 或 sample weights,但要区分训练权重与最终 calibration。

追问:多标签与多分类的 loss shape 有何差异?
常见误区:模型 sigmoid 一次,loss 内又 sigmoid;预测 logits 用 0.5 threshold。

MF03:cross entropy 接收什么输入?

标签:P0、classification、shape
证据:分类 case 和 PathAI ML fundamentals 的前置覆盖。

答案

  • 标准多分类输入是未归一化 logits [B,C],target 通常是 long 类别索引 [B]
  • 它等价于 log-softmax 加 negative log likelihood;不应在前面再次 softmax。
  • softmax 沿 class dimension;每个样本 target 必须在合法类别范围内。
  • class weights、ignore index、label smoothing 都会改变 loss 语义,应说明 reduction。

追问:soft targets 或 one-hot targets 怎么处理?
常见误区:把 target 设成 float class id;沿 batch 维 softmax。

MF04:bias 和 variance 分别是什么?

标签:P0、generalization
证据:BenevolentAI 和 InstaDeep 候选人直接报告 bias-variance/regularization。

答案

  • bias 高意味着模型或假设过于受限,训练集和验证集都表现差。
  • variance 高意味着模型对训练样本扰动敏感,训练表现好而验证表现差。
  • 更多数据通常主要降低 variance;更强模型可降低 bias,但可能提高 variance。
  • regularization、early stopping、合理 feature 和更简单模型可降低 variance。
  • 实际诊断应看 learning curves、不同 split 的方差和误差分解,而不是只看一次分数。

追问:标签噪声造成的不可约误差在哪里?
常见误区:把所有 validation gap 都归因于模型太复杂,忽略 leakage 或 distribution shift。

MF05:L1、L2、dropout 和 early stopping 有何区别?

标签:P0、regularization
证据:PathAI regularization、BenevolentAI regularization。

答案

  • L1 倾向稀疏权重;L2 惩罚权重幅度,常改善条件和稳定性。
  • 优化器中的 weight decay 与把 L2 加入 loss 在某些优化器下并不完全等价,回答时应谨慎。
  • dropout 在训练时随机屏蔽 activation,评估时关闭;它改变表示学习噪声和 ensemble-like 行为。
  • early stopping 以 validation 表现限制有效训练时长。
  • 选择要根据模型、数据规模和 failure mode,通过 validation 验证,而非把所有方法同时打开。

追问:为什么 model.eval() 对 dropout 重要?
常见误区:把 regularization 当成修复数据泄漏的方法。

MF06:训练集、验证集和测试集各自做什么?

标签:P0、evaluation
证据:Zendesk 直接问 train/validation/test;多个 case 重复要求 evaluation。

答案

  • training 用于拟合参数。
  • validation 用于选择模型、超参数、threshold 和停止时机。
  • test 在模型与分析决策锁定后做最终、尽量一次性的估计。
  • 如果反复根据 test 改模型,test 已成为 validation,结果偏乐观。
  • scientific ML 还要明确 split unit,例如病人、蛋白家族、实验批次或时间,而不只是行随机切分。

追问:交叉验证何时适合?外部 validation 有什么价值?
常见误区:同一 donor/sequence family 同时出现在 train 与 test。

MF07:生命科学数据最常见的 leakage 有哪些?

标签:P0、scientific data
证据:由 DNA/protein/RNA/assay case 的 evaluation 要求推导;没有来源声称这是 Lila 原题。

答案

  • 同一病人、样本、蛋白家族或近同源序列跨 split。
  • 在全数据上先做 feature selection、normalization 或 label-derived preprocessing。
  • assay batch、site、plate 或时间与标签高度相关,模型学到批次而非生物机制。
  • 使用预测时不可获得的 future information。
  • 重复测量或高度相似候选未按 group split。
  • 应用 group、time、family/scaffold 或 external split,并做 nearest-neighbor/batch baseline 检查。

追问:怎样量化 train/test similarity?
常见误区:只说“shuffle 后 random split”。

MF08:类别极不平衡时如何评估?

标签:P0、metrics
证据:Flagship 直接问 skewed data;Tempus 直接问 pipeline metrics。

答案

  • accuracy 往往失真,应报告 precision、recall、PR-AUC,并根据用途选择 operating point。
  • ROC-AUC 可补充,但阳性极少时 PR 曲线更直接反映 precision/recall trade-off。
  • threshold 应在 validation 上选择,不能在 test 上调。
  • 检查 calibration、不同亚组表现和置信区间。
  • 训练可使用重采样、class weighting 或适当 loss,但 evaluation 应反映真实 prevalence。

追问:screening 更重 recall 还是 precision?取决于哪个下游成本?
常见误区:只报 F1,不解释成本;把重新平衡后的验证集当真实部署分布。

MF09:calibration 与 discrimination 有何不同?

标签:P1、uncertainty、evaluation
证据:Tempus metrics、Merck interpretability、Latent 大规模筛选的安全推导。

答案

  • discrimination 衡量排序或区分类别的能力,例如 AUROC。
  • calibration 衡量预测概率是否与实际频率一致,例如预测 0.8 的样本约 80% 为正。
  • 好排序不保证好 calibration,反之亦然。
  • 检查 reliability diagram、Brier score、expected calibration error;可在独立 validation 上做 temperature scaling 等校准。
  • 选实验候选时,calibration 影响预期命中率和资源分配。

追问:distribution shift 后 calibration 会怎样?
常见误区:把 softmax 最大值直接称为可信概率。

MF10:p-value 是什么,不是什么?

标签:P0、statistics
证据Genentech Data Scientist 候选人直接被问 p-value 定义。

答案

  • 在零假设及检验假设成立的条件下,p-value 是观察到当前或更极端统计量的概率。
  • 它不是零假设为真的概率,也不是效应有实际意义的概率。
  • p-value 受样本量、检验选择和多重比较影响。
  • 应同时报告 effect size、confidence interval、实验设计和假设检查。
  • 在大量基因或候选测试中必须处理 multiple testing,例如控制 FDR。

追问:统计显著与科学显著如何区分?
常见误区:说 p=0.03 表示零假设只有 3% 概率为真。

MF11:为什么 RNA count data 常不用普通 Gaussian regression?

标签:P0、omics、distribution
证据:insitro 直接 RNA-seq scenario 和分布/统计检验追问。

答案

  • counts 非负、离散,variance 常随 mean 改变,且存在不同 library size 和 overdispersion。
  • Poisson 可作起点,但真实 RNA counts 经常 variance 大于 mean,因此 negative binomial 更常见。
  • normalization/offset、batch effects、零值结构和 biological replicates 都会影响建模。
  • 深度模型仍需匹配合理 likelihood 或 transformation,不能因为用了神经网络就忽略观测分布。
  • 最终检验应考虑 multiple testing 和 effect size。

追问:zero inflation 是否总需要?怎样区分 technical zero 和 biological zero?
常见误区:未经处理直接对 raw counts 用 MSE。

MF12:SGD、momentum 和 Adam 怎样选择?

标签:P0、optimization
证据:PathAI optimization、多个 training-loop 面经。

答案

  • SGD 使用当前 batch gradient,简单、内存低,但对学习率和条件数敏感。
  • momentum 平滑更新并加速持续方向。
  • Adam 对每个参数维护一阶和二阶矩估计,常在稀疏或不同尺度梯度下更快得到可用结果,但泛化和 weight decay 语义需要验证。
  • 选择必须与学习率 schedule、batch size、normalization 和训练预算一起调。
  • 不论优化器都应监控 train/validation curves、gradient norm 和数值稳定性。

追问:AdamW 为什么单独处理 weight decay?
常见误区:说 Adam 永远优于 SGD。

MF13:second-order optimization 是什么?

标签:P1、optimization
证据:PathAI 2025 Machine Learning Engineer 直接出现 second-order optimization。

答案

  • 一阶方法只用 gradient;二阶方法还利用 Hessian 或其近似描述曲率。
  • Newton step 理想形式为 H^{-1}g,可改善不同方向条件差异。
  • 对大型神经网络,完整 Hessian 的计算、存储和求逆通常不可行。
  • 实践中使用 quasi-Newton、Gauss-Newton、natural-gradient 或 Hessian-vector product 等近似。
  • 回答应比较每步成本、内存、收敛速度和非凸问题,而不是只说迭代次数更少。

追问:为什么 Hessian 可能非正定?
常见误区:把 Adam 的二阶矩估计等同于严格 Newton method。

MF14:vanishing 和 exploding gradients 为什么发生?

标签:P0、deep learning
证据:InstaDeep AI Research Intern 直接问 vanishing gradients;training debugging 提供印证。

答案

  • 深层网络或长序列的 chain rule 连乘 Jacobian,奇异值持续小于 1 会消失,大于 1 会爆炸。
  • sigmoid/tanh 饱和、差初始化和长 recurrent path 会加重问题。
  • residual connections、合适初始化、normalization、非饱和 activation、gradient clipping 可缓解不同部分。
  • clipping 只限制爆炸,不解决长期信息传播和模型结构问题。
  • 诊断应看不同层 gradient norms、activation distribution 和训练曲线。

追问:为什么 residual connection 有帮助?
常见误区:把所有不收敛都称为 vanishing gradient。

MF15:为什么 initialization 重要?

标签:P0、optimization、MLP
证据:MLP/训练实现的必要前置;具体题为基础覆盖。

答案

  • 若 activation/gradient variance 随层数快速缩小或放大,训练不稳定。
  • Xavier 类初始化适合保持前后方差,常与 tanh/线性假设关联;He 类初始化考虑 ReLU 丢弃约一半 activation。
  • 所有权重初始化成相同值会破坏神经元之间的 symmetry breaking。
  • bias 常可从零开始,但具体架构和 normalization 可能改变选择。
  • 初始化不是独立超参数,应结合 activation、normalization、residual scaling 和 precision。

追问:为什么全零初始化 linear regression 可以,但两层 MLP 不行?
常见误区:背名称却无法解释 variance。

MF16:BatchNorm 和 LayerNorm 的核心区别?

标签:P1、normalization、Transformer
证据:Transformer/MLP fundamentals 的前置覆盖;没有来源报告这是 Lila 原题。

答案

  • BatchNorm 使用 batch 统计,训练和评估行为不同,并维护 running statistics。
  • LayerNorm 对单个样本的 feature dimension 归一化,不依赖其他 batch 样本,训练/评估公式通常一致。
  • 小 batch、变长 sequence 和 autoregressive 场景下 LayerNorm 更自然;CNN 大 batch 常用 BatchNorm。
  • normalization 影响优化、scale 和一定程度的 regularization,但不能替代良好数据 preprocessing。
  • 必须能指出具体 tensor 上归一化哪些维度。

追问:pre-norm 和 post-norm Transformer 有何训练差异?
常见误区:只说一个用于 CV、一个用于 NLP,而不解释统计维度。

MF17:什么是 representation learning,为什么重要?

标签:P1、representation
证据Flagship Machine Learning Scientist 候选人直接被问“what is representation learning and why is it important”。

答案

  • representation learning 让模型从数据学习对任务有用的 feature,而不是完全依赖人工设计。
  • 好表示保留下游相关信息,同时对无关变化具有适当不变性。
  • 在生命科学中,大量无标签 sequence/structure 可用于 pretraining,少量实验标签用于 transfer。
  • 评价不能只做漂亮可视化,应包含 linear probe、few-shot、transfer、retrieval、OOD 和 downstream performance。
  • 表示可能编码 batch、species 或实验来源等 confounder,因此要做 probing 和分层 evaluation。

追问:什么是不良 representation collapse?
常见误区:把 embedding 维度大等同于表示好。

MF18:怎样评估一个 protein embedding?

标签:P1、protein、evaluation
证据:Lila biological-sequence foundation models、Flagship representation learning;具体题为安全重构。

答案

  • 先明确用途:function、localization、structure、binding、generation conditioning 或 retrieval。
  • 固定 embedding 做 linear probe,区分表示质量与下游模型容量。
  • 使用 family/homology-aware split,避免近重复导致虚高。
  • 比较简单 baseline,例如 one-hot、k-mer、已知 descriptor 和较小 pretrained model。
  • 做 data-efficiency curve、OOD family performance、calibration 和 error analysis。
  • 检查 embedding 是否主要反映 length、species 或 dataset source。

追问:per-residue 与 sequence embedding 怎样分别评估?
常见误区:只用 t-SNE 图得出科学结论。

MF19:self-attention 在计算什么?

标签:P1、Transformer
证据:Cohere attention implementation、Mistral/xAI MHA、BigHat Transformer、Zoom Transformer。

答案

  • 每个 query 与 keys 做相似度,除以 sqrt(d_k),经 mask 和 softmax 得权重,再对 values 加权求和。
  • self-attention 中 Q/K/V 来自同一序列的不同线性投影;cross-attention 则 query 和 key/value 来源不同。
  • multi-head 让不同子空间学习不同关系,但不是严格保证每头具有人类可解释语义。
  • 标准 dense attention 对 sequence length 的时间和 attention-map memory 为二次复杂度。
  • padding 和 causal mask 的语义必须明确。

追问:为什么 Q 和 K 不直接共享?attention weight 能否解释因果重要性?
常见误区:把 attention 说成简单“找最相似 token”,忽略 learned projections 和 value aggregation。

MF20:为什么 Transformer 需要 positional information?

标签:P1、position
证据:Latent Labs 直接追问 positional embedding。

答案

  • 无位置编码的 self-attention 对输入排列具有 permutation-equivariant 性,不能区分相同 token 集合的不同顺序。
  • absolute learned/sinusoidal encoding 注入位置;relative bias 或 RoPE 更直接编码相对关系。
  • 选择影响长度外推、计算实现和适合的 inductive bias。
  • 蛋白 sequence 中相对距离有意义,但 sequence distance 不等同于三维距离。
  • padding position 不应被当作真实 residue,仍需 mask。

追问:sinusoidal encoding 为什么可能支持长度外推?
常见误区:认为 token embedding 本身自动包含位置。

MF21:VAE 的 ELBO 由什么组成?

标签:P2、VAE
证据:Lila 只确认 VAE“could”;无已核验 direct VAE question。

答案

  • encoder 近似 posterior q(z|x),decoder 定义 p(x|z),prior 常取标准正态。
  • ELBO 包含 reconstruction expectation 减去 KL(q(z|x)||p(z));最大化 ELBO 等价于最小化 reconstruction loss 加 KL。
  • reconstruction 项形式取决于观测 likelihood,不能一概用 MSE。
  • reparameterization 将随机性移到独立 noise,使参数可获得低方差 pathwise gradient。
  • KL 促进潜在空间接近 prior,但过强会牺牲重构或造成 posterior collapse。

追问:为什么 ELBO 是 log likelihood 的下界?
常见误区:把 VAE 说成普通 autoencoder 加随机噪声。

MF22:什么是 posterior collapse?

标签:P2、VAE failure mode
证据:VAE 最小覆盖的自然追问,非已观察原题。

答案

  • 当强 decoder 能在忽略 z 的情况下解释数据时,q(z|x) 可能退化接近 prior,KL 接近零,latent 不携带输入信息。
  • 诊断可看 per-dimension KL、mutual-information proxy、latent ablation 和 reconstruction 对 z 的敏感性。
  • 缓解方法包括 KL warm-up/annealing、free bits、限制 decoder、调整 beta 或训练 schedule。
  • 不能只把 KL 非零当成功,还要验证 latent 对生成和下游任务有用。

追问:KL 太大和太小分别意味着什么?
常见误区:看到好 reconstruction 就认为 VAE 学到了良好 latent。

MF23:diffusion 的 forward 和 reverse process 分别是什么?

标签:P1、diffusion
证据:BigHat diffusion implementation discussion、Latent protein diffusion take-home、Lila“could”。

答案

  • forward process 按预定 schedule 逐步向数据加噪,通常能直接从 x0 采样任意 xt。
  • reverse process 学习从 noisy sample 逐步去噪,近似数据生成方向。
  • 常见训练目标让网络预测 noise、x0 或 velocity;它们有不同参数化和加权性质。
  • 生成需要多步 reverse sampling,通常比单次 autoregressive/decoder forward 昂贵。
  • 对 protein sequence、coordinates 或 discrete states,noise process 和 equivariance/约束必须与数据类型匹配。

追问:为什么训练可随机一个 t 而不用展开完整链?
常见误区:把 forward noising 也描述成 learned process。

MF24:noise schedule 会影响什么?

标签:P2、diffusion
证据:diffusion 最小组件的自然追问;非公开原题。

答案

  • schedule 决定不同 timestep 的 signal-to-noise ratio、任务难度和训练权重分布。
  • 太快破坏 signal 可能让中间状态难学;太慢增加冗余步骤和 sampling cost。
  • linear、cosine 或 learned schedule 的效果依赖数据和参数化。
  • 应同时检查 per-timestep loss、sample quality、diversity、constraint satisfaction 和 compute。
  • 对不同模态,噪声空间必须有合理语义;坐标和离散序列不能机械套用同一 Gaussian process。

追问:alpha、beta 和 cumulative alpha-bar 的关系?
常见误区:只背 schedule 名称,不解释 SNR。

MF25:何时选 autoregressive、VAE 或 diffusion?

标签:P1、generative-model choice
证据:Generate protein generative-model case、BigHat Transformer/diffusion、Latent diffusion;VAE 证据较弱。

答案

  • 先按数据和决策目标定义:需要 likelihood、快速 sampling、global structure、controllable latent、inpainting 还是约束生成。
  • autoregressive 模型 likelihood 自然但 sampling 顺序化,错误可能累积。
  • VAE sampling 快且有显式 latent,但可能有 posterior collapse 和较模糊/弱表达 decoder trade-off。
  • diffusion 通常训练稳定、可做条件生成,但采样昂贵,noise process 设计重要。
  • 必须比较简单 retrieval、mutation、ranking 或 optimization baseline;生成模型不是默认答案。
  • 最终选择应以有效、novel、diverse、synthesizable、experimentally successful 为依据。

追问:一百万候选的生成速度怎样影响选择?
常见误区:按论文新旧选择模型。

MF26:epistemic 和 aleatoric uncertainty 有何区别?

标签:P1、uncertainty
证据:Lila Residency 直接涉及 uncertainty;Latent/BigHat screening 与 Lila closed loop 支持迁移。

答案

  • aleatoric 来自观测本身的随机性或噪声,更多数据不一定消除。
  • epistemic 来自模型对数据空间认知不足,相关区域增加数据通常可降低。
  • heteroscedastic likelihood 可表示输入相关 aleatoric;ensembles、Bayesian approximations 等可估 epistemic 的一部分。
  • softmax entropy 不能自动分解两者,也可能在 OOD 上过度自信。
  • active learning 更关心“获得标签后可能减少的未知”,但还要结合 diversity 和实验成本。

追问:怎样验证 uncertainty 真的有用?
常见误区:把模型间 disagreement 当成真实世界 uncertainty 的完美估计。

MF27:active learning 的基本闭环是什么?

标签:P1、closed-loop discovery
证据:Lila 官方 closed-loop职责;Lila Residency active-learning 内容;BigHat/Latent 筛选场景。

答案

  • 从初始 labeled set 训练模型和校准 uncertainty。
  • 在 candidate pool 上计算 utility,例如 uncertainty、expected improvement、diversity、cost 和 feasibility。
  • 批量选择时避免高度相似候选,处理实验容量和 controls。
  • 获得标签后更新数据、重训并在固定 evaluation set 上判断是否真正改进。
  • 明确 stopping criterion、探索/利用平衡和 selection bias。
  • 与 random、diversity-only、greedy-score baseline 比较。

追问:为什么只选最不确定样本可能失败?
常见误区:忽略 OOD junk 和实验失败概率。

MF28:数据很少、很 skewed、非常大时策略为何不同?

标签:P0、data regime
证据:Flagship 直接问 scarce、skewed、large edge cases。

答案

  • 数据少:简单 baseline、强 validation、transfer/self-supervision、合理 prior、数据质量和主动采样优先。
  • skewed:分层 split、cost-aware metrics、sampling/weighting、threshold 与 calibration。
  • 数据大:streaming、distributed sampling、mixed precision、compute-efficient model、代表性 evaluation 和 data curation。
  • 三种 regime 可能同时出现,例如总体大但阳性少;回答要拆分瓶颈。
  • 每项方法都要说明它解决的是 variance、bias、compute、label scarcity 还是评价问题。

追问:大数据为什么仍可能 overfit?
常见误区:三个场景都回答“加 regularization”。

MF29:multimodal fusion 有哪些基本选择?

标签:P1、multimodal
证据:Lila 官方职责明确 biological sequences、molecular structures 和 multimodal experimental data;未观察具体面试题。

答案

  • early fusion 在表示层较早拼接或交互,能学跨模态关系,但要求对齐和缺失处理。
  • late fusion 独立建模后组合 prediction,模块清晰、对缺失更稳健,但跨模态细粒度交互有限。
  • cross-attention 或 shared latent 提供中间融合。
  • 训练数据常不是所有模态齐全,应区分 missing-at-random、systematic missingness 和 modality dropout。
  • 评价要包含完整模态、缺失模态、单模态 baseline 和跨数据源 OOD。

追问:如何证明第二个 modality 提供互补信息而非 batch shortcut?
常见误区:只把所有 embedding concatenate,不检查对齐和 missingness。

MF30:baseline 和 ablation 分别回答什么?

标签:P0、research reasoning
证据:Deep Genomics 设计选择、BigHat case、Genentech/Flagship research discussion。

答案

  • baseline 回答“复杂方法是否优于合理的简单选择”。
  • ablation 回答“复杂系统的哪个组件造成改进”。
  • baseline 应包括 trivial predictor、classical model、强现有方法和成本匹配版本。
  • ablation 应一次改变明确因素,保持 data split、compute、tuning budget 和 evaluation 一致。
  • 需要多 seed、置信区间或统计检验判断差异是否稳健。
  • 负结果也应报告,防止只展示成功配置。

追问:如果 ablation 组合有 interaction 怎么办?
常见误区:把较少训练步数的模型叫公平 ablation。

MF31:distribution shift 和 OOD 有哪些类型?

标签:P1、robustness
证据:scientific case、跨实验/数据源 evaluation 和 closed loop 的安全推导。

答案

  • covariate shift:输入分布变,条件关系可能近似不变。
  • label shift:类别先验变。
  • concept shift:输入到标签的关系变,是更困难的情况。
  • scientific ML 中还包括新 protein family、assay protocol、lab/site、species 和时间变化。
  • 评价应有 group/time/external split,按 shift source 分层报告,并检查 calibration。
  • OOD detection 不是万能保险,真正困难的是未知 shift 和标签定义变化。

追问:重加权在哪些假设下有效?
常见误区:把任意 validation drop 都叫 OOD。

MF32:怎样系统调试“训练 loss 不下降”?

标签:P0、debugging
证据:TORC、Wayve、NVIDIA、PathAI debugging 和 Tempus coding-style review。

答案

  1. 先在极小数据上尝试 overfit,验证 pipeline 有学习能力。
  2. 检查 labels、shapes、dtype、mask、normalization 和 batch 对齐。
  3. 检查 forward output、loss range、gradient 是否 None/NaN/zero、参数是否真的在 optimizer 中。
  4. 检查 zero_grad/backward/step 顺序、learning rate 和 frozen parameters。
  5. 与恒定/linear baseline 比较,并可构造人工可学习数据。
  6. 逐层记录 activation 和 gradient norm,缩小问题范围。
  7. 最后才尝试更复杂 architecture 或大规模 hyperparameter search。

追问:训练下降但 validation 不升,排查顺序怎样改变?
常见误区:第一反应是换 Transformer 或增加 compute。

MF33:输出层应该使用什么激活函数?

标签:P0、activation、output semantics
证据DeepL Research Scientist 直接出现 0.5 到 5000 的正值回归输出激活题;Pinterest MLE 直接问 ReLU 与 sigmoid。

答案

  • 先看目标的数学 support 和 loss,而不是按模型名称选激活。
  • 单标签多分类通常输出 logits,直接交给 cross entropy;二分类或多标签也保留 logits,交给 logits-based BCE,推理时再 sigmoid。
  • 无界回归通常用线性输出。严格正值可比较 Softplus、对数目标或其他有物理意义的参数化。
  • ReLU 能保证非负,但负半轴梯度为零且会输出精确的零;它不是所有正值回归的默认答案。
  • 真正有上下界 [a,b] 时可用 a + (b-a) * sigmoid(z);边界若只是样本范围,就不应硬编码。
  • 对 0.5 到 5000 这类跨数量级目标,还应比较目标缩放或 log-space 建模,并在原始尺度报告误差。

追问:为什么训练时常传 logits 而不是概率?Softplus 在大正数区有什么性质?
常见误区:把“目标当前都为正”误当作“真实过程有已知硬上界”;不考虑目标尺度。

Fundamentals 覆盖审计

  • P0 基础:MF01–MF16 中 14 题,加 MF28、MF30、MF32–MF33。
  • 表示与 Transformer:MF17–MF20。
  • VAE:MF21–MF22,仅 P2。
  • Diffusion:MF23–MF24,加模型选择 MF25。
  • Scientific ML:MF26–MF31。
  • 实际题数:33。

第三部分:Scientific ML case study

每个 case 都按同一骨架回答:科学目标、decision、数据生成过程、split、baseline、模型与 loss、evaluation、实验闭环、风险。面试官可能在任一节点改变约束。以下 prompt 均为证据驱动的练习重构,只有明确标注的核心问题来自候选人报告;完整答案不是来源提供的标准答案。

CS01:一百万个 protein designs,实验预算一千

标签:P0、protein design、ranking、uncertainty、30–45 分钟
证据Latent Labs 候选人直接报告“如何产生一百万个 protein designs”及 protein-diffusion take-home。预算和完整约束是练习重构。

Prompt

为一个目标功能产生约一百万条蛋白候选,但第一轮只能实验测试 1,000 条。说明端到端方案。

结构化答案

  1. 先定义 decision:不是最大化模型分数,而是在 1,000 个实验名额内最大化有效 hit 数、信息增益或二者组合。先确认功能、结构、表达、稳定性、安全性和可合成约束。
  2. 定义候选空间:确定是 de novo、固定 scaffold 局部 redesign、还是已有 family 的 mutation。搜索空间和可接受 novelty 完全不同。
  3. 数据审计:训练标签来自何种 assay,是否跨 batch/lab,negative 如何产生,是否有 censoring、replicates 和失败实验。把 sequence family、assay batch 和时间纳入 split。
  4. baseline:已有序列的 nearest-neighbor retrieval;规则过滤后 mutation;简单 property predictor 加 diversity;随机或 diversity-only selection。
  5. 生成:可比较 autoregressive、diffusion 或优化已有序列;条件包含目标功能和硬约束。生成前明确 validity,而不是生成后才发现不可测试。
  6. 廉价过滤:去重;非法 residue/length;motif 和 manufacturability;明显结构冲突;与训练集过近或不必要风险。每个过滤器要记录候选淘汰原因。
  7. ranking:多任务模型估计 activity、stability、expression 等;使用校准预测和 uncertainty,不把单一 score 当真值。
  8. diversity:在 embedding、sequence family 或结构层聚类;按 cluster 配额,避免 1,000 条几乎重复。
  9. selection:将 exploitation、uncertainty exploration、diversity 和实验成本合成可解释 acquisition。保留 random/control/reference sequences。
  10. evaluation:离线看 family-held-out ranking、calibration、top-k enrichment 和 diversity;实验看 hit rate、effect size、failure categories 和 coverage。
  11. 下一轮:记录所有可用结果,包括失败和 assay QC;评估模型是否因 selection bias 失真;与 random/diversity baseline 比较 active loop 的增益。

追问树

一百万候选
      ├─ 候选如何生成?
      │  ├─ 为什么不是简单 mutation?
      │  └─ 如何保证 validity 和 novelty?
      ├─ 如何缩到 1,000?
      │  ├─ hard filters
      │  ├─ prediction 与 calibration
      │  └─ uncertainty 与 diversity
      ├─ 训练集与 test 怎样 split?
      └─ 第一轮全失败怎么办?
         ├─ assay QC
         ├─ distribution shift
         └─ model/constraint diagnosis

Rubric(20 分):目标与约束 3;数据/split 3;baseline 2;生成与过滤 3;ranking/uncertainty/diversity 4;实验设计 3;failure analysis 2。
红旗:一上来只说 diffusion;按预测分数取 top 1,000;没有 controls;把 novelty 与 effectiveness 混为一谈。

CS02:为目标蛋白建立 generative model

标签:P0、problem formulation、generative modeling、30–45 分钟
证据Generate:Biomedicines 候选人报告“如何为目标蛋白设计生成模型”;BigHat 提供 Transformer/diffusion 印证。

Prompt

我们希望为一个目标蛋白功能设计新序列。你会怎样定义和训练 generative model?

结构化答案

  1. 先问“设计什么”:binder、enzyme、stability、expression 或多目标;输入是 target sequence、structure、pocket 还是 assay context。
  2. 把硬约束和软目标分开。长度、固定 residues、symmetry、chemistry 可作为硬约束;affinity、stability 等通常是带不确定性的软目标。
  3. 明确训练单位和数据 provenance,避免同一 family 或高度相似 complex 跨 split。
  4. 建立非生成 baseline:retrieval、motif graft、local mutation、discriminative predictor 加 search。复杂生成模型必须超过这些方法。
  5. 模型选择依据数据模态和 decision:autoregressive 适合序列 likelihood;diffusion 可做全局/条件去噪;VAE 提供 latent search;模型不是因“先进”而选。
  6. 训练 objective 需与数据匹配。sequence token loss 不等价于功能优化,可增加 conditional objective、ranking 或 property guidance,但警惕 reward hacking。
  7. 生成 evaluation 分解为 validity、novelty、diversity、constraint satisfaction、predicted properties、calibration 和最终 experimental success。
  8. 做 ablation:去掉 target condition、去掉结构信息、替换简单 encoder、不同 sampling temperature/guidance。
  9. 设计 wet-lab batch,包含 positives、known negatives、random generation 和 diversity strata。
  10. 对失败候选做 taxonomy:无表达、错误折叠、无功能、assay failure,从而更新数据与 objective。

追问树:数据少怎么办 → pretraining/transfer/简单 search;没有 structure 怎么办 → sequence condition/预测结构但显式传播 uncertainty;生成有效但不 novel → sampling/训练分布/评价;novel 但不工作 → reward model shift/约束缺失。
Rubric(20 分):问题定义 4;数据与 leakage 3;baseline 2;模型选择理由 3;objective 2;evaluation 3;实验与迭代 3。
红旗:用 perplexity 作为唯一成功标准;没有 discriminative/search baseline;把结构预测当无误真值。

CS03:抗体设计与复杂 assay 数据

标签:P0、antibody、multitask、noisy labels、30–45 分钟
证据BigHat Biosciences 候选人报告 therapeutic design、复杂 biological assay data 以及 diffusion/Transformer implementation 讨论。

Prompt

你获得多轮抗体设计和多个 assay 的数据。不同 assay 的噪声、缺失和规模差异很大。怎样建立模型并选择下一轮候选?

结构化答案

  1. 建立 assay schema:每列测什么、单位、detection limit、replicate、batch、protocol 版本和 missingness 原因。
  2. 区分 biological negative、technical failure 和未测试;不能把所有 missing 当 negative。
  3. 按 sequence lineage、campaign、target 和 time 分层/group split,模拟真实下一轮泛化。
  4. baseline 从 per-assay linear/tree model、simple sequence descriptors 和 nearest neighbors 开始。
  5. 多任务模型可共享 representation,但每个 assay 用匹配的 head/loss;对 count、binary、continuous/censored label 分别建模。
  6. loss weighting 不只按样本数,可考虑 measurement noise、任务重要性和梯度 scale;必须做 single-task baseline。
  7. 用 replicate 估计 noise ceiling,避免要求模型超过标签可重复性。
  8. 选择下一轮时同时考虑 target activity、developability、uncertainty、diversity、manufacturability 和实验成本。
  9. 评价不仅是平均 metric,还要看 high-value operating region、top-k enrichment、calibration、各 assay/family 子组。
  10. 闭环后检查 selection bias:新数据由旧模型选择,并非 iid;保留探索样本和随机 controls。

追问树:某 assay 只有 5% 序列测过 → missingness/selection bias;assay 互相冲突 → Pareto front/业务约束;模型只学 batch → group split/adversarial probe;如何用 uncertainty → acquisition 与 calibration。
Rubric(20 分):assay semantics 4;missing/noise 3;split 2;baseline/multitask 3;loss/evaluation 3;selection 3;bias/controls 2。
红旗:简单填零;所有 task 共用 MSE;只报平均 RMSE;忽略 experiment provenance。

CS04:closed-loop active learning

标签:P0、active learning、experimental loop、30–45 分钟
证据:Lila 官方职位明确 closed-loop discovery;Lila Residency 面经涉及 active learning/uncertainty。以下为当前岗位的迁移练习,不是公开原题。

Prompt

每轮可实验 200 个候选,总预算五轮。设计一个可判断 active learning 是否真正有效的闭环。

结构化答案

  1. 明确 utility:hit rate、best-found value、Pareto coverage、knowledge gain 或达到目标所需实验数。
  2. 固定一个不受 acquisition 影响的 evaluation/benchmark set;否则模型在自选数据上变好不代表泛化变好。
  3. 初始数据要覆盖候选空间,结合 historical data、designed diversity 和 controls。
  4. 每轮训练时记录 model/version/data lineage,做 calibration 和 OOD 检查。
  5. acquisition 至少比较 random、uncertainty-only、score-only、diversity-only 和组合方法。
  6. batch selection 要去冗余,并纳入 assay cost、feasibility、plate layout 和 controls。
  7. 预注册 stopping/kill criteria,例如连续两轮相对 random 无改善,或 uncertainty 不再校准。
  8. 所有实验结果,包括失败和 QC 状态,按可审计方式回流;technical failure 与 negative label 分开。
  9. 分析 cumulative regret、best-so-far、hit rate、coverage、calibration 和每个 hit 的成本。
  10. 用多 seed/simulation retrospective replay 评估 acquisition,但明确 historical counterfactual 的局限。

追问树:uncertainty 不可靠 → ensemble/calibration/random exploration;batch 结果要一个月 → asynchronous acquisition;实验失败率高 → success model/cost-aware utility;模型只选一种 family → diversity constraints。
Rubric(20 分):utility 3;unbiased evaluation 4;baselines 3;acquisition 3;experiment/QC 3;stopping 2;auditability 2。
红旗:没有 random baseline;只看 training loss;把 assay failure 当 negative;每轮任意改 metric。

CS05:DNA 或 protein sequence 的监督预测

标签:P0、sequence modeling、baseline、30–45 分钟
证据Deep Genomics 候选人报告 DNA sequence 数据建模并解释 model/loss/code;D. E. Shaw Research 有 protein-folding ML 问题。

Prompt

给 sequences 和功能标签,建立能泛化到新 family 的 predictor。你会怎么做?

结构化答案

  1. 明确 label 是 sequence-level、residue-level 还是 pairwise;连续、binary、count 或 censored;测量误差如何。
  2. 审计 length、alphabet、duplicates、near-duplicates、family、species、batch 和 label prevalence。
  3. 以 family/homology group split 为主,并保留 random split 作为“容易上限”诊断,不把它作为最终证明。
  4. baselines:class prevalence/mean、k-mer/handcrafted descriptors、nearest neighbor、linear/logistic regression、小 MLP/CNN。
  5. 复杂模型可用 pretrained embedding + linear probe,之后再 fine-tune;比较 data efficiency 和 compute。
  6. loss 与 label 匹配,padding mask 正确;class imbalance 使用 weights/sampling 但 evaluation 保持真实 prevalence。
  7. metrics 包括主任务 metric、calibration、family-stratified performance、confidence interval 和 error examples。
  8. probe confounders:length、species、batch、nearest-train similarity。若这些简单变量解释大部分 performance,要重新审视任务。
  9. external validation 或 prospective experiment 评估实际泛化。

追问树:sequence 很长 → truncation/local-global architecture/compute;标签只有 1,000 → frozen embeddings/simple models;新 family 崩溃 → split与representation;解释 residue → saliency需实验/perturbation验证。
Rubric(20 分):label/data 3;family split 4;baseline 3;model/loss 3;metrics 3;confounder 2;prospective validation 2。
红旗:random row split;直接 fine-tune 巨大模型;只报 AUROC;不记录 padding mask。

CS06:single-cell RNA count 数据

标签:P0、omics、statistics、30–45 分钟
证据:insitro 候选人报告 RNA-seq scenario、分布与统计检验;Cellarity 直接问 single-cell omics basics。

Prompt

给多个 donor、实验 batch 和 treatment 的 cell-by-gene count matrix,预测 treatment response,并找出稳定 biomarker。

结构化答案

  1. decision unit 是 donor 或 sample,不是单个 cell;避免把大量相关 cells 当独立样本造成伪重复。
  2. QC 包括低质量 cells、doublets、library size、mitochondrial fraction、gene filtering,但阈值应在 train 内确定并记录。
  3. count data 考虑 negative-binomial/overdispersion 或合理 transformation;保留 raw counts 供 count model。
  4. split 必须按 donor,必要时按 site/time 外推;同 donor 的 cells 不能跨 train/test。
  5. baseline 从 donor-level pseudobulk、cell-type proportions、regularized linear model 开始。
  6. 模型可分层表示 cell,再聚合 donor;或按 cell type 建 features。复杂单细胞 encoder 要与 pseudobulk baseline 公平比较。
  7. batch correction 只能用 train 信息 fit,并检查是否消除了 treatment biological signal。
  8. evaluation 包括 donor-level prediction、calibration、跨 batch/site、biomarker effect size、FDR 和稳定性。
  9. biomarker 需要独立 cohort、perturbation 或机制证据,不能只由 attention/saliency 宣称。

追问树:cell 数不同 → weighted aggregation/subsampling;batch 与 treatment 完全 confounded → 数据无法识别,需要新实验;零很多 → observation model;biomarker 不复现 → selection bias/multiple testing。
Rubric(20 分):unit-of-analysis 4;QC/distribution 3;donor split 3;baseline 2;model 2;batch/confounding 3;biomarker validation 3。
红旗:随机 split cells;把 cells 数当样本量;先全数据 batch-correct;不做 multiple testing。

CS07:sequence、structure 与 assay 的 multimodal model

标签:P1、multimodal、missing modality、30–45 分钟
证据:Lila 官方职位明确 biological sequences、molecular structures 和 multimodal experimental data。没有候选人公开报告这个 exact case。

Prompt

训练数据可能有 sequence、预测或实验 structure、assay values 和 metadata,但不是每条记录模态都齐全。怎样建模?

结构化答案

  1. 先按 prediction decision 判断测试时哪些 modality 可获得。训练时可见但部署不可见的信息不能直接作为输入。
  2. 建立 modality availability table,分析缺失是否与实验成功、target 或时间相关;missingness 本身可能泄漏标签。
  3. 每个 modality 先有单模态 baseline:sequence encoder、structure descriptor/GNN、assay/meta tabular model。
  4. 比较 late fusion、early fusion 和 cross-attention/shared latent。数据少且缺失多时 late fusion 往往更易验证;细粒度对齐充足时才证明复杂交互价值。
  5. 对缺失 modality 可用 modality dropout、mask token、mixture-of-experts 或 partial encoders;不要无标记地填零。
  6. 预测 structure 带模型误差,不能与实验 structure 混为同一真值。输入应记录来源和置信度。
  7. split 按实体/family/time,并防止同一实验的不同 modality 分散到不同 split。
  8. ablation 包括每个单模态、融合方式、缺失情境和 matched compute。
  9. metrics 分完整模态、各缺失 pattern、family/OOD、calibration 和实验 utility 报告。

追问树:structure 只在阳性样本有 → missingness leakage;测试时只有 sequence → teacher/student 或 sequence-only部署;不同模态维度差异 → projection/normalization;fusion 无提升 → redundancy/data scale/alignment。
Rubric(20 分):deployment availability 3;missingness 4;single-modality baseline 3;fusion choice 3;split 2;ablation 3;uncertainty 2。
红旗:直接 concatenate;用预测 structure 当 ground truth;没有缺失模态 evaluation。

CS08:同时面对 scarce、skewed 和 large

标签:P0、data regimes、30–40 分钟
证据Flagship Pioneering 直接问训练中 very scarce、skewed、large 等 edge cases。

Prompt

你有五千万条未标注 sequences,但只有 3,000 个实验标签,其中阳性 2%。怎样设计项目?

结构化答案

  1. 拆开三个事实:unlabeled 很大、labeled 很少、positive 很少;每个需要不同策略。
  2. 先审核 3,000 labels 的质量、replicates、selection mechanism 和 family coverage;标签质量优先于模型规模。
  3. split 以 family/time/group 为单位,确保 validation/test 含足够 positive,并给 bootstrap/CI。
  4. baselines:prevalence、nearest neighbor、handcrafted/k-mer + logistic/tree、冻结公共/自监督 embedding + linear head。
  5. 未标注数据可 self-supervised pretrain,但要与较小或现有 pretrained model 比较 data/compute efficiency。
  6. 训练可用 class weights、balanced batches 或 ranking objective;evaluation 保持原 prevalence,主看 PR-AUC、recall at precision、calibration 和 top-k enrichment。
  7. 学习曲线判断瓶颈在 labels、representation 还是 noise。
  8. 设计主动标注时结合 uncertainty、diversity、positive enrichment 和实验成本,保留 random baseline。
  9. 大数据工程仅在证明需要后扩展:streaming、dedup、distributed training、mixed precision;不能用基础设施替代科学设计。

追问树:没有阳性进 test → stratified group split/扩大 test;预训练无提升 → objective/domain mismatch;class weighting 后概率不准 → calibration;新增 200 labels 选什么 → acquisition。
Rubric(20 分):拆分 regimes 3;label audit 3;split/metrics 4;baselines 3;pretraining rationale 2;active sampling 3;compute restraint 2。
红旗:直接训练最大 Transformer;用 accuracy;balanced validation 后宣称现实表现。

CS09:预测疫苗接种可能性

标签:P1、classification、interpretability、ethics、30–40 分钟
证据Merck Machine Learning Engineer Intern 候选人直接报告 demographic/vaccine-usage 建模与解释 case。

Prompt

给 demographics 和 historical vaccine usage,预测谁可能在未来三个月接种,并解释模型。

结构化答案

  1. 明确用途:资源规划、提醒 outreach,还是风险研究。用途决定 false positive/negative 成本和是否应建个体预测。
  2. 定义 index date、prediction window 和可获得 features;未来信息、接种后记录和 healthcare-contact proxy 可能泄漏。
  3. 按时间切分,按 person group,避免一个人的历史跨 split。
  4. baselines:overall/stratified prevalence、logistic regression、regularized tree model。
  5. 处理 imbalance,评价 PR-AUC、recall/precision at operating point、calibration 和 subgroup performance。
  6. interpretability 区分 global association、local explanation 与 causal effect。feature importance 不能证明干预该 feature 会改变接种。
  7. 检查缺失数据、access-to-care、geography 和 demographic proxies 造成的公平性风险。
  8. 若用于 outreach,最好评价实际 intervention trial,而不只是 retrospective accuracy。

追问树:模型最重要 feature 是 prior visits → access proxy/leakage;不同群体 calibration 差 → subgroup calibration/data;如何解释 → coefficients/SHAP但非因果;如何上线 → monitoring/consent/intervention test。
Rubric(20 分):use-case 3;temporal target/leakage 4;baseline 2;metrics/calibration 3;interpretability 3;fairness 3;prospective test 2。
红旗:随机 row split;以敏感属性提升 accuracy 而无讨论;把解释工具当 causal analysis。

CS10:offline 指标很好,wet-lab 全部失败

标签:P0、failure analysis、experiment、30–45 分钟
证据:BigHat assay、Latent design screening、Lila closed loop 和 research-defense 记录的综合安全重构;不是公开原题。

Prompt

模型在 held-out data 上明显超过 baseline,但第一轮 100 个 top candidates 没有一个实验成功。你如何排查?

结构化答案

  1. 不先假定模型错。验证 assay QC、positive controls、plate layout、sample identity、expression/synthesis success 和 protocol drift。
  2. 审核 evaluation split:是否近重复、family leakage、batch shortcut、selection bias 或 preprocessing leakage。
  3. 比较 100 个候选与 train/validation distribution:similarity、uncertainty、length、motif、structure、manufacturability。
  4. 检查目标错位:offline label 是否只是 proxy;模型优化的 score 与实际实验 success 是否一致。
  5. 检查 top-k calibration 和 ranking,不只看全分布平均 AUROC/RMSE。
  6. 检查 filtering pipeline、单位、normalization、feature order、checkpoint、train/eval mode 和推理代码。
  7. 用 blinded known positives/negatives 重新跑完整 inference-to-assay 流程。
  8. 对失败做 taxonomy:未合成、未表达、错误折叠、无结合、assay failure。不同失败对应不同模型或流程修改。
  9. 下一轮加入更保守 near-distribution candidates、random/diverse controls,并缩小每个假设的 discriminating experiment。

追问树:controls 也失败 → assay/process;controls 成功但 designs 失败 → shift/objective;只有某 family 失败 → subgroup;代码 mismatch → reproducible inference test;下一轮怎么选 → hypothesis-driven strata。
Rubric(20 分):assay QC 3;leakage/split 3;distribution/objective 4;code pipeline 3;failure taxonomy 3;next experiment 4。
红旗:直接换模型;把 100 个失败都设为普通 negative 重训;没有 positive controls。

CS12:材料 property prediction 与候选发现

标签:P2、materials AI、transfer case、30–45 分钟
证据Schrödinger Materials Science Applications Scientist 候选人报告 computational materials science 和 ML 问题;Lila 业务跨生命与材料科学。它不是当前 life-sciences role 的主线。

Prompt

给 composition、structure、simulation 和实验 property 数据,建立模型找到满足多个性能目标的新材料。

结构化答案

  1. 明确 property、测量条件、单位、phase 和候选的可合成范围。材料“相同 composition”不一定是相同结构或工艺。
  2. 统一 provenance,区分 simulation 与 experiment,不无标记混合;记录 fidelity、temperature/pressure 和 measurement uncertainty。
  3. split 按 chemical system/composition family/time,避免相近结构跨 split。
  4. baselines:composition descriptors + linear/tree;nearest neighbor;physics/simulation baseline;简单 uncertainty model。
  5. structure model 可用图或等变表示,但必须证明结构信息相对 composition baseline 的增益。
  6. 多目标用 Pareto front 或显式 utility,纳入 stability、cost、toxicity、synthesizability。
  7. uncertainty 和 OOD 用于选择新实验,但要校准,并与 random/diversity baseline 比较。
  8. prospective batch 包含 known references、探索区域和可合成 controls;失败合成也是重要标签但需分类。

追问树:simulation bias → multifidelity;结构未知 → composition baseline/structure prediction uncertainty;极少实验标签 → transfer/physics prior;多目标冲突 → Pareto/decision weights。
Rubric(20 分):scientific semantics 4;provenance/fidelity 3;split 3;baselines 3;model 2;multiobjective 2;experiment 3。
红旗:混合 simulation/experiment 当同一标签;random split crystals;忽略 synthesis。

CS13:重复、冲突和脏实验记录

标签:P0、data quality、deduplication、30–40 分钟
证据AstraZeneca Principal Associate AI Scientist 候选人报告基于团队真实问题的数据 deduplication take-home;此处加入 assay 冲突是科学数据练习重构。

Prompt

多个历史系统合并后,同一 molecule/protein 可能有不同名字、重复实验和互相冲突的 labels。你如何构建训练表?

结构化答案

  1. 定义 entity identity 与 observation identity。相同 molecule/protein 不等于同一次 assay;重复实验不能简单删除。
  2. 建 canonicalization 规则,并保留 raw identifier、source system、conversion version 和 provenance。
  3. 使用 deterministic exact rules 处理明确重复,再用相似性/record linkage 生成候选 pair;人工审计抽样估计 precision/recall。
  4. 冲突 label 先检查单位、assay protocol、condition、time、batch、detection limit 和 transcription error。
  5. biological/technical replicates 可聚合为 mean/variance 或显式层级模型;不能只保留“最新”而无依据。
  6. split 在 dedup/grouping 后进行,保证同一实体和 replicate group 不跨 split。
  7. data pipeline 需版本化、幂等、带 invariant tests,例如 entity count、单位范围、duplicate rate 和 conflict rate。
  8. 做 sensitivity analysis:不同 dedup/aggregation 规则是否改变模型结论。
  9. 对无法解决的冲突保留 uncertainty 或排除理由,不伪造单一真值。

追问树:没有统一主键 → canonical graph/linkage;模糊匹配误合并 → threshold/audit;重复是否加权 → sampling process;冲突多数来自 batch → batch-aware model;如何测试 → golden pairs/invariants。
Rubric(20 分):identity semantics 4;provenance 3;dedup method 3;conflict handling 3;split 3;pipeline tests 2;sensitivity 2。
红旗drop_duplicates() 结束;按字符串名字判断实体;在 split 后才 dedup;冲突标签任意平均。

Case study 通用答题模板

1. Clarify:科学目标、谁做什么 decision、成功和失败的成本
      2. Data:观测单位、标签生成过程、provenance、missingness、noise
      3. Split:group/family/time/site,明确 leakage 风险
      4. Baseline:trivial、classical、nearest-neighbor、现有方法
      5. Model:为什么需要它,输入输出、loss、constraints、compute
      6. Evaluation:primary metric、calibration、subgroups、uncertainty、CI
      7. Experiment:controls、batch selection、prospective validation
      8. Failure:哪些观察能区分 data、model、objective、code、assay 问题
      9. Loop:新数据怎样回流,如何判断方法比 random/baseline 更好

Case study 覆盖审计

  • protein / antibody:CS01–CS03。
  • closed-loop discovery:CS04。
  • sequence / omics:CS05–CS06。
  • multimodal:CS07。
  • data regime:CS08。
  • clinical/pharma classification:CS09。
  • failure analysis:CS10。
  • model selection:CS11。
  • materials transfer:CS12。
  • data engineering and deduplication:CS13。
  • 实际 case 数:13。

第四部分:Research deep dive 与 technical talk

这一部分不替代完整面试指南,只把高频技术追问变成可练习题。Genentech 候选人报告五场 30 分钟 research discussion 和一小时 presentation;Flagship 报告 seminar;Cellarity、BigHat、Deep Genomics、NVIDIA 等反复出现 project/research deep dive。

RD01:你最自豪的 technical project

证据:Genentech Machine Learning Scientist 直接问题。
Prompt:用五分钟说明最自豪项目,然后接受二十分钟追问。
强答案骨架:一句话 scientific/ML problem;为什么重要;数据和约束;本人负责部分;baseline;关键方法;定量结果及 uncertainty;失败或 limitation;下一步。
追问:为什么是你的贡献?如果去掉最复杂组件?数据多十倍会怎样?哪个结果最可能不复现?
Rubric(10 分):问题 2;个人 ownership 2;证据 2;trade-off 2;limitation 2。
红旗:长背景无 decision;只说“我们”;只报最好分数。

RD02:从论文 claim 追到证据

证据:Genentech research back-and-forth、Cellarity technical questions about research。
Prompt:选择论文中最重要 claim,说明哪些实验真正支持它,哪些只相关。
强答案骨架:精确定义 claim;对应 comparison;controls;ablation;统计不确定性;替代解释;外部/前瞻验证。
追问:如果主要 ablation 无显著差异?comparison 是否 matched compute?
Rubric:claim 精确 2;证据链 3;controls 2;替代解释 2;诚实边界 1。

RD03:代码 ownership

证据:Tempus take-home coding-style review、Deep Genomics code/design discussion、Genentech code samples。
Prompt:挑一段自己的训练代码,画出 data flow 和 tensor shapes,并指出最危险的三个 bug。
强答案骨架:入口与输出;每个 boundary 的 shape/dtype/device;configuration;randomness;checkpoint;metric aggregation;tests;failure recovery。
追问:怎样证明 train/inference 一致?哪一处需要 integration test?
Rubric:data flow 2;shape 2;risk 2;tests 2;design trade-off 2。

RD04:为什么不是简单 baseline?

证据:Deep Genomics 追问设计选择;BigHat/Flagship case。
Prompt:解释采用复杂神经模型前最强 baseline,以及复杂度带来的可验证增益。
强答案骨架:baseline selection;matched split/compute/tuning;hypothesis;ablation;learning curve;wall-clock;statistical uncertainty;失败条件。
追问:baseline 调参是否公平?增益是否来自更多数据或参数?
Rubric:baseline 3;fairness 2;hypothesis 2;evidence 2;cost 1。

RD05:讲一个失败实验

证据:research-defense 和 model-debugging 记录的保守重构;不是观察到的单一原题。
Prompt:说明一次失败,如何区分 data、objective、optimization、code 和 evaluation 原因。
强答案骨架:预期;观察;候选假设;能区分假设的最小实验;结果;修复;仍不确定部分;学到的 reusable check。
追问:如果重来,第一天做什么?当时为什么没更早发现?
Rubric:failure 定义 2;hypotheses 2;discriminating tests 3;learning 2;ownership 1。

RD06:研究与岗位的迁移

证据:Flagship 直接问 research relevance;Genentech 问 JD 中技术经验。
Prompt:你的工作哪些能迁移到 biological foundation models,哪些不能?
强答案骨架:可迁移的 formulation/code/evaluation 能力;领域差距;错误类比风险;学习/合作计划;一个低成本 first experiment。
追问:没有蛋白经验为什么能胜任?哪个 assumption 最可能不成立?
Rubric:transfer 3;gap honesty 2;concrete bridge 3;first experiment 2。

RD07:向 experimental scientist 解释模型

证据:Schrödinger customer-needs discussion、Cellarity/Genentech 跨科学团队环境。
Prompt:先向 ML scientist,再向 experimental scientist,各用两分钟解释同一模型输出和 uncertainty。
强答案骨架:对 ML audience 说明训练分布、metric、calibration、shift;对实验 audience 说明该分数支持什么 decision、不支持什么,以及为何选这批候选。
追问:对方要求一个“确定答案”怎么办?
Rubric:准确 3;audience adaptation 2;uncertainty 2;actionability 2;边界 1。

RD08:复现与可审计性

证据:Tempus code style、AstraZeneca data engineering、闭环 discovery 的共同要求。
Prompt:另一位研究员下周要复现你的核心图表,需要冻结什么?
强答案骨架:raw-data snapshot/provenance;split ids;preprocessing version;environment/seed;config;checkpoint;evaluation script;exact command;expected artifacts;data-access restrictions。
追问:GPU nondeterminism 怎么处理?无法分享数据怎么办?
Rubric:data 2;code/env 2;config/seed 2;evaluation 2;limitations 2。

RD09:设计未来六个月 research agenda

证据:Flagship 围绕 startup problem 讨论、Lila 官方 model-design/closed-loop职责;具体 prompt 未观察。
Prompt:围绕低标签蛋白 function prediction 设计六个月 agenda。
强答案骨架:一个 falsifiable central hypothesis;first two-week baseline;data audit;milestones;kill criteria;highest-risk experiment;compute/experimental dependency;fallback;最终 decision。
追问:预算减半?第一项实验失败?怎样避免 agenda 只是模型列表?
Rubric:hypothesis 2;prioritization 3;kill criteria 2;resource realism 2;fallback 1。

RD10:research talk 的十分钟压缩版

证据:Genentech 一小时 presentation、Flagship seminar。
Prompt:把完整 research talk 压缩为十分钟,保留一条中心故事。
强答案骨架:1 分钟问题/impact;1 分钟 gap;2 分钟 data/setup;2 分钟方法;2 分钟关键结果/controls;1 分钟 failure/limitation;1 分钟 relevance/next step。
追问:删掉一半 slides 留什么?非本领域听众必须记住哪一句?
Rubric:story 2;technical clarity 2;evidence 2;time discipline 2;limitations/relevance 2。


出版边界与复核清单

可以明确说

  • 这些能力和题型来自已核验的近邻岗位候选人报告、Lila 第一方招聘沟通或官方岗位范围。
  • 多家公司真实出现 MLP、K-means、KNN、attention、training/debugging、metrics、regularization、statistics、scientific case 和 research talk。
  • 高级生成模型在近邻蛋白岗位出现,但半小时 live coding 的合理粒度是小组件。

不可以说

  • “这是 Lila 原题”或“Lila 一定会考”。
  • 因为 recruiter 说 Transformer/VAE/diffusion “could”,就声称三者概率相同。
  • 将本题库频次当作公司真实题型概率;研究样本来自公开自选报告,存在平台和幸存者偏差。
  • 将 CV 题型直接迁移为 Lila 主线。Lila 已明确不考 CV。

代码复核状态

  • 本文件 34 个 Python code block 已在本地逐块独立执行,当前环境全部通过。
  • 已直接核对本文件开头列出的十二个 PyTorch stable API 页面。
  • nn.Linearnn.Embeddingone_hotindex_add_logsumexpmasked_fillrandn_like 等其他 API 的代码已实际运行,但没有为每一个 API 单独打开官方文档。依赖升级后仍应按实际 PyTorch 版本复核表述与行为。
  • 示例测试是最小 sanity checks,不是完整 property-based 或跨 device/dtype test suite。

数量验收

部分 数量 最低要求
Live coding 34 30
ML fundamentals 33 30
Scientific ML case 13 12
Research deep dive / talk 10 未设最低值,作为题型地图补全

本题库与证据库分开呈现:证据库记录候选人实际遇到什么;题库记录为了训练这些能力而重构什么。两者不得在视觉或措辞上混淆。