This interview guide is for educational and informational purposes only. It is designed to help readers prepare, but it does not guarantee any interview result, hiring decision, offer, or outcome. Interview questions, hiring criteria, and preferred answers can vary by employer, interviewer, industry, location, and time. The examples and explanations reflect the authors' research and judgment, are provided without warranties of any kind, and should not be treated as the only correct approach. Diagrams are simplified illustrations intended to highlight the main components and their interactions; actual systems and implementations may be more complex. Alternative approaches may be equally valid or better suited to a particular question, context, or interviewer. To the fullest extent permitted by applicable law, the author, contributors, and publisher are not liable for decisions made, actions taken, or losses incurred based on this guide.
Identity, Image, and Privacy Notice
To respect individual privacy, some names, profile photographs, avatars, biographical details, and other identifying information displayed in this guide may be replaced with pseudonyms, licensed stock images, illustrative avatars, composite images, or representative descriptions. Unless a person is expressly identified as an actual contributor, a displayed name, image, or profile should not be understood as depicting or identifying a specific candidate, interviewer, employee, or other real individual. These representations are provided for editorial and illustrative purposes only and do not imply endorsement, employment, participation, or affiliation with this guide or any company mentioned in it. Any resemblance to an actual person is coincidental.
Company Notice
This guide is an independent educational resource and is not affiliated with, endorsed by, sponsored by, or approved by the company named in this guide. Company names are used only to identify interview experiences commonly reported by candidates. Interview practices can change without notice, and inclusion of company-specific content does not mean these questions are official, complete, or guaranteed to be asked. To the fullest extent permitted by law, the author, contributors, and publisher are not responsible for outcomes related to use of this material.
Questions or comments?
Contact us for general questions, or share feedback, technical corrections, and comments with the community.
11. Implement masked cross-entropy with label smoothing and optional temperature scaling in NumPy.CodingMediumOpenai
i Question Details
Preserve batch and class shapes, validate labels, masks, dtypes, finite logits, smoothing and temperature ranges, use stable normalization, exclude masked rows correctly, support reduction choices, derive the gradient, and test boundary cases.
Short Interview Answer (30-60 seconds)
I validate the logits, labels, mask, smoothing value, temperature, and reduction mode first. Then I divide the logits by temperature and compute a numerically stable log-softmax. I create smoothed target distributions, calculate one cross-entropy loss per row, and remove masked rows from the result. The reduction can be none, sum, or mean. The gradient is the masked softmax-minus-target term divided by temperature. Time is O(NC), and auxiliary space is O(NC).
The input contains a batch of class scores, one correct class index for each row, and a Boolean mask that says which rows count. We may also soften the target labels and change how sharp the predicted probabilities are. The goal is to calculate cross-entropy safely, ignore masked rows, support three reduction choices, and return a gradient with the same (N, C) shape as the logits. Stable log-softmax is useful because it avoids overflow when logits are large.
Useful Questions to Ask the Interviewer
Should a masked row contribute zero to both the loss and the gradient?
For mean reduction, should the denominator be only the number of unmasked rows?
Should reduction="none" return one masked loss value for every batch row?
How to Explain It in an Interview
1. Validate the input and output contract
The logits have shape (N, C), where N is the batch size and C is the number of classes. Labels have shape (N,), and every label must be an integer from 0 through C - 1. The mask also has shape (N,) and must be Boolean. Logits must use a floating-point dtype and contain only finite values. Smoothing must be in [0, 1). Temperature must be finite and greater than 0. Reduction must be "none", "sum", or "mean". The loss is a scalar for sum or mean and an (N,) array for none. The gradient always has shape (N, C).
2. Scale the logits and normalize them safely
I divide the logits by the temperature: z = logits / temperature. A smaller positive temperature makes the probability distribution sharper. A larger temperature makes it softer. Next I compute log-softmax. I subtract the maximum value in each row before exponentiating. This does not change the normalized probabilities, but it prevents large exponential values from overflowing. The resulting log_probs array keeps shape (N, C).
3. Build the label-smoothed targets
I first create a one-hot target matrix with shape (N, C). Then I use y = (1 - smoothing) * one_hot + smoothing / C. This spreads a small amount of target probability uniformly across all classes. For smoothing=0.1 and C=3, a row whose correct class is class 0 becomes approximately [0.933333, 0.033333, 0.033333]. Every target row still sums to 1.
4. Walk through the concrete example
The example uses N=2, C=3, smoothing=0.1, and temperature=2.0. The logits are [[2.0, 0.0, -1.0], [0.5, 1.5, 0.0]]. The labels are [0, 2]. The mask is [True, False]. Temperature scaling gives [[1.0, 0.0, -0.5], [0.25, 0.75, 0.0]]. Stable log-softmax gives approximately [[-0.464369, -1.464369, -1.964369], [-1.231838, -0.731838, -1.481838]]. The smoothed targets are approximately [[0.933333, 0.033333, 0.033333], [0.033333, 0.033333, 0.933333]]. The per-row losses are approximately [0.547702, 1.448504]. The second row is masked, so the masked losses become [0.547702, 0.0]. Mean reduction divides by one valid row, so the final mean loss is approximately 0.547702, which rounds to 0.548 as shown in the diagram.
5. Apply the mask and reduction
The code multiplies each per-row loss by the Boolean mask. A False entry therefore contributes zero. For reduction="none", the function returns the masked (N,) loss array. For "sum", it adds the masked losses. For "mean", it divides their sum by the number of True mask entries. It uses max(valid_count, 1), so an all-false mask produces a zero loss instead of division by zero.
6. Derive the gradient
Let p = softmax(logits / temperature), and let y be the smoothed target. Before masking and reduction scaling, the derivative with respect to the original logits is (p - y) / temperature. The 1 / temperature factor comes from z = logits / temperature. Multiplying by mask[:, None] makes every masked gradient row exactly zero. Sum and none reductions need no extra divisor. Mean reduction also divides by max(valid_count, 1). The final gradient has shape (N, C).
7. Explain correctness, complexity, and boundary cases
The method is correct because each predicted row and each smoothed target row are probability distributions over the same C classes. Stable log-softmax calculates the normalized log probabilities without changing their mathematical value. Cross-entropy then compares the target and predicted distributions class by class. Masking is applied before reduction, so excluded rows affect neither the returned loss nor the gradient. The code performs a constant number of operations over N by C arrays, so time is O(NC). Its main temporary arrays are also size N by C, so auxiliary space is O(NC). Useful tests include no smoothing, smoothing near 1, small and large positive temperatures, an all-false mask, C=1, invalid labels, invalid shapes, non-finite logits, and finite-difference gradient checks.
Key Insight / Why This Solution Works
The key idea is to keep the class-wise calculations in arrays of shape (N, C). First scale logits by temperature. Then compute stable log-softmax by subtracting each row maximum before exponentiating. Build one-hot targets and apply y = (1 - smoothing) * one_hot + smoothing / C. Calculate one cross-entropy value per row, then apply the Boolean row mask. The central invariant is that p and y always describe probability distributions across the same C classes for each batch row. Masking changes only whether a row contributes. It never changes the class dimension. The gradient follows from softmax cross-entropy and is scaled by both temperature and the selected reduction.
Code
import numpy as np
defmasked_cross_entropy(
logits: np.ndarray,
labels: np.ndarray,
mask: np.ndarray,
smoothing: float = 0.0,
temperature: float = 1.0,
reduction: str = "mean",
) -> tuple[float | np.ndarray, np.ndarray]:
"""Compute masked label-smoothed cross-entropy and d(loss)/d(logits)."""# Validate that the three main inputs are NumPy arrays with the expected rank.ifnotisinstance(logits, np.ndarray) or logits.ndim != 2:
raise ValueError("logits must be a 2D NumPy array")
ifnotisinstance(labels, np.ndarray) or labels.ndim != 1:
raise ValueError("labels must be a 1D NumPy array")
ifnotisinstance(mask, np.ndarray) or mask.ndim != 1:
raise ValueError("mask must be a 1D NumPy array")
n, c = logits.shape
# Cross-entropy needs at least one class column.if c <= 0:
raise ValueError("logits must contain at least one class")
# Every batch row must have exactly one label and one mask value.if labels.shape != (n,):
raise ValueError("labels must have shape (N,)")
if mask.shape != (n,):
raise ValueError("mask must have shape (N,)")
# Logits must use floating-point arithmetic for normalization and gradients.ifnot np.issubdtype(logits.dtype, np.floating):
raise TypeError("logits must have a floating-point dtype")
# Labels are class indices, so they must use an integer dtype.ifnot np.issubdtype(labels.dtype, np.integer):
raise TypeError("labels must have an integer dtype")
# The mask must be explicitly Boolean so row inclusion is unambiguous.if mask.dtype != np.bool_:
raise TypeError("mask must have Boolean dtype")
# NaN or infinity would make the probability calculation invalid.ifnot np.all(np.isfinite(logits)):
raise ValueError("logits must contain only finite values")
# Every label must identify a valid class column.if np.any(labels < 0) or np.any(labels >= c):
raise ValueError("labels must be in [0, C-1]")
# The diagram uses smoothing in [0, 1).ifnot np.isfinite(smoothing) ornot0.0 <= smoothing < 1.0:
raise ValueError("smoothing must be in [0, 1)")
# Temperature is a divisor, so it must be finite and strictly positive.ifnot np.isfinite(temperature) or temperature <= 0.0:
raise ValueError("temperature must be finite and greater than 0")
# Normalize the reduction name before checking the supported choices.ifnotisinstance(reduction, str):
raise TypeError("reduction must be a string")
reduction = reduction.lower()
if reduction notin {"none", "sum", "mean"}:
raise ValueError("reduction must be 'none', 'sum', or 'mean'")
# Step 1: divide by temperature to control probability sharpness.
z = logits / temperature
# Step 2: subtract each row maximum before exponentiation for stability.
row_max = np.max(z, axis=1, keepdims=True)
shifted = z - row_max
exp_shifted = np.exp(shifted)
logsumexp = np.log(np.sum(exp_shifted, axis=1, keepdims=True))
log_probs = shifted - logsumexp
# Step 3: create one-hot targets with the same (N, C) shape as logits.
one_hot = np.zeros_like(log_probs)
one_hot[np.arange(n), labels] = 1.0# Step 4: mix the one-hot target with a uniform distribution over C classes.
targets = (1.0 - smoothing) * one_hot + smoothing / c
# Step 5: sum the class contributions to obtain one loss for each row.
per_row_loss = -np.sum(targets * log_probs, axis=1)
# Step 6: excluded rows contribute exactly zero to the loss.
masked_loss = per_row_loss * mask
valid_count = int(mask.sum())
# Step 7: return the loss in the requested reduction form.if reduction == "none":
loss: float | np.ndarray = masked_loss.copy()
elif reduction == "sum":
loss = float(masked_loss.sum())
else:
# An all-masked batch has zero numerator. Using 1 avoids division by zero.
loss = float(masked_loss.sum() / max(valid_count, 1))
# Stable log probabilities can be exponentiated to recover softmax p.
probabilities = np.exp(log_probs)
# Temperature scaling adds the 1 / temperature factor to dL/dlogits.
gradient = (probabilities - targets) / temperature
# Expand the row mask across classes so excluded rows have zero gradient.
gradient *= mask[:, None]
# Mean reduction averages only over the included rows.if reduction == "mean":
gradient /= max(valid_count, 1)
# Return the requested loss and an (N, C) gradient.return loss, gradient
defmain() -> None:
# Run the exact concrete example used by the approved diagram.
logits = np.array(
[[2.0, 0.0, -1.0], [0.5, 1.5, 0.0]],
dtype=np.float64,
)
labels = np.array([0, 2], dtype=np.int64)
mask = np.array([True, False], dtype=np.bool_)
loss, gradient = masked_cross_entropy(
logits=logits,
labels=labels,
mask=mask,
smoothing=0.1,
temperature=2.0,
reduction="mean",
)
# Only row 0 contributes because row 1 is masked out.print(f"loss = {loss:.6f}")
print("gradient =")
print(gradient)
if __name__ == "__main__":
main()
Time & Space Complexity
Let N be the number of batch rows and C be the number of classes. Temperature scaling, stable log-softmax, target construction, loss calculation, and gradient calculation each work over an N by C array. This gives O(NC) time. The implementation also creates several N by C arrays, including log probabilities, probabilities, one-hot targets, and smoothed targets. Therefore auxiliary space is O(NC). The mask and per-row loss require only O(N) additional memory and do not change the overall bound.
Where it is used
This pattern is useful in classification training when some rows in a batch should be ignored, such as padded sequence positions or invalid examples. Label smoothing can reduce overly confident targets. Temperature scaling controls how sharp or soft the predicted class distribution is. A NumPy version is also useful as a small reference implementation for checking the behavior and gradients of a larger machine-learning framework implementation.
Why Interviewers Ask This
This problem checks whether a candidate can turn a machine-learning formula into careful NumPy code. It tests array-shape reasoning, numerical stability, validation, masking rules, reduction behavior, and gradient derivation together. It also shows whether the candidate understands label smoothing and temperature scaling instead of treating cross-entropy as a black box. Boundary cases such as invalid labels, non-finite logits, or an all-masked batch reveal whether the implementation has a clear and consistent contract.
Common interview mistakes
One common mistake is computing softmax directly from large logits instead of subtracting the row maximum first. Another is using the wrong label-smoothing formula. With the shown formula, the uniform smoothing mass is smoothing / C for every class. Candidates also sometimes divide mean loss by the full batch size instead of the number of true mask entries. Another mistake is masking the loss but forgetting to mask the gradient. It is also easy to forget the 1 / temperature factor in the gradient or to return the wrong shape for reduction="none".
Interview tip
Explain the implementation in the same order as the data flow: validate, scale by temperature, compute stable log-softmax, create smoothed targets, calculate per-row loss, apply the mask, reduce, and derive the gradient. When discussing the gradient, explicitly explain where the 1 / temperature factor comes from. That shows you understand the chain rule instead of only memorizing the softmax cross-entropy derivative.
Interviewer may ask next
What changes when reduction is "none" instead of "mean"?
The loss becomes the masked per-row vector with shape (N,) instead of one scalar. False mask entries are zero. The gradient still has shape (N, C). It is mask[:, None] * (p - y) / temperature, with no division by the valid-row count. The time complexity stays O(NC), and the auxiliary space stays O(NC).
How would you test that the analytic gradient is correct?
I would use a deterministic finite-difference check on a small input. For one logit at a time, I would add and subtract a tiny value, recompute the scalar mean loss, and estimate the derivative from the loss difference. I would compare that value with the analytic gradient. Masked rows should stay zero. Testing every logit this way takes many extra forward evaluations, so it is appropriate for tests rather than training.
12. Derive sharded matrix multiplication and backpropagation for a column-sharded weight matrix across P devices.CodingHardOpenai
i Question Details
State every local tensor shape, forward ownership, output assembly, gradients for input and local weights, required collectives, communication volume, uneven-shard handling, pseudocode, and tests against the unsharded computation.
Short Interview Answer (30-60 seconds)
I split W by columns, so device p owns W_p with shape [K, N_p]. X is replicated. Each device computes Y_p = X @ W_p, then an all-gather concatenates those column blocks into Y. In backprop, I scatter dY using the same columns. Each device computes local dW_p and partial dX_p. An all-reduce sum combines the dX_p tensors. With balanced shards, matrix work is O(BKN/P) per device. Local storage follows the shard shapes plus replicated X and dX.
We have one input matrix X and one weight matrix W. W is split by columns across P devices. Each device stores only its own columns. All devices use the same X. In the forward pass, every device computes its own output columns. We join those pieces to form Y. During backpropagation, we split dY by the same columns. Each device calculates its local weight gradient and one partial input gradient. We then add the partial input gradients across devices.
Useful Questions to Ask the Interviewer
Should the final Y and dX be replicated on every device, as shown in the diagram?
Should I handle the case where N is not divisible by P?
Should I use the communication-volume model shown in the diagram for broadcast, all-gather, scatter, and ring-style all-reduce?
How to Explain It in an Interview
1. Define the tensor shapes and ownership
Let X have shape [B, K]. Let W have shape [K, N]. Device p owns a contiguous block of columns W_p with shape [K, N_p]. The shard widths satisfy sum_p N_p = N.
X is available on every device with shape [B, K]. Device p owns the same global column range in W_p, Y_p, dY_p, and dW_p. This ownership rule is the main invariant of the algorithm.
2. Compute the forward pass
Device p performs the local matrix multiplication
Y_p = X @ W_p.
The shapes are [B, K] @ [K, N_p] -> [B, N_p]. Each device therefore computes only the output columns that belong to its local weight shard.
After the local matrix multiplications finish, an all-gather collects Y_0, Y_1, ..., Y_{P-1}. The pieces are concatenated in column order:
Y = concat([Y_0, Y_1, ..., Y_{P-1}], dim=1).
The result has shape [B, N]. In the diagram, the full Y is assembled on the devices. If X was initially available on only one device, X is broadcast once before these local computations.
3. Split the upstream gradient
Backpropagation starts with dY of shape [B, N]. The diagram scatters dY by columns using exactly the same boundaries that were used for W.
Device p receives dY_p with shape [B, N_p]. Matching these boundaries is essential because dY_p belongs to the output columns produced by W_p.
4. Compute dW_p and partial dX_p locally
Each device computes its local weight gradient:
dW_p = X^T @ dY_p.
The shapes are [K, B] @ [B, N_p] -> [K, N_p]. This result has the same shape and owner as W_p, so dW_p stays local.
Each device also computes a partial input gradient:
dX_p = dY_p @ W_p^T.
The shapes are [B, N_p] @ [N_p, K] -> [B, K]. Every shard contributes to the gradient of the full input X.
5. Reduce the input gradient
The complete input gradient is
dX = sum_p dX_p.
The diagram uses an all-reduce with SUM for this operation. The result has shape [B, K]. It is replicated after the reduction.
This reduction is different from the forward all-gather. Y_p blocks represent different columns, so they are concatenated. dX_p tensors represent overlapping contributions to the same entries, so they are added.
6. Handle communication and uneven shards
Using the communication accounting shown in the diagram, broadcasting X costs ((P-1)/P) * B * K elements per device. All-gathering Y costs ((P-1)/P) * B * N elements. Scattering dY costs ((P-1)/P) * B * N elements. A ring-style all-reduce of dX costs 2 * ((P-1)/P) * B * K elements per device. These are communication-volume formulas for the illustrated collective model. Exact network traffic can depend on the collective implementation and topology.
If N is not divisible by P, define q = floor(N/P) and r = N mod P. Give q+1 columns to the first r devices and q columns to the other devices. Prefix sums define each device's start and end columns. Every local formula remains valid with that device's N_p. When N < P, some devices can own zero columns.
7. Verify against an unsharded reference
Compute Y_ref = X @ W without sharding. The concatenated sharded output must match Y_ref within floating-point tolerance.
For backpropagation, compute dW_ref = X^T @ dY and dX_ref = dY @ W^T. Every local dW_p must match the corresponding column slice of dW_ref. The all-reduced dX must match dX_ref.
The tests in the diagram include normal cases, uneven N, a numerical gradient check on small sizes, seeded deterministic runs, and edge cases such as B=1, K=1, N<P, and large P.
Key Insight / Why This Solution Works
Column sharding works because each output column depends only on the matching column of W and the shared input X. Device p can therefore compute its block Y_p independently from W_p. The central invariant is that W_p, Y_p, dY_p, and dW_p always refer to the same global column range. Forward assembly uses concatenation because the local outputs contain different columns. In backpropagation, dW_p stays local because it has the same ownership as W_p. Each dX_p covers the full [B, K] input shape, so these tensors overlap and must be summed with an all-reduce.
Code
from __future__ import annotations
from dataclasses import dataclass
from typing import TypeAlias
Matrix: TypeAlias = list[list[float]]
@dataclass(frozen=True)classForwardCache:
# Cache the tensors needed by backpropagation.# X is replicated, while each entry of w_local represents one device's W_p.
x: Matrix
w_local: list[Matrix]
defshape(matrix: Matrix) -> tuple[int, int]:
# Return matrix dimensions and reject ragged input because matrix# multiplication requires every row to have the same number of columns.
rows = len(matrix)
cols = len(matrix[0]) if rows else0ifany(len(row) != cols for row in matrix):
raise ValueError("Matrix rows must all have the same length")
return rows, cols
deftranspose(matrix: Matrix) -> Matrix:
# Swap rows and columns. Backpropagation needs X^T and W_p^T.
rows, cols = shape(matrix)
return [[matrix[row][col] for row inrange(rows)] for col inrange(cols)]
defmatmul(left: Matrix, right: Matrix) -> Matrix:
# Perform ordinary matrix multiplication after checking the inner dimensions.
left_rows, left_cols = shape(left)
right_rows, right_cols = shape(right)
if left_cols != right_rows:
raise ValueError(
f"Incompatible shapes: {(left_rows, left_cols)} and {(right_rows, right_cols)}"
)
# Each result cell is one row-by-column dot product.return [
[sum(left[i][k] * right[k][j] for k inrange(left_cols)) for j inrange(right_cols)]
for i inrange(left_rows)
]
defcolumn_shard_sizes(total_columns: int, devices: int) -> list[int]:
# Use the diagram's uneven-shard rule. Every device first gets floor(N/P)# columns. The first N % P devices receive one additional column.if devices <= 0:
raise ValueError("devices must be positive")
if total_columns < 0:
raise ValueError("total_columns cannot be negative")
base = total_columns // devices
remainder = total_columns % devices
return [base + (1if p < remainder else0) for p inrange(devices)]
defshard_columns(matrix: Matrix, sizes: list[int]) -> list[Matrix]:
# Split a full matrix into contiguous column blocks using prefix boundaries.
rows, cols = shape(matrix)
ifany(width < 0for width in sizes):
raise ValueError("Shard widths cannot be negative")
ifsum(sizes) != cols:
raise ValueError("Shard sizes must sum to the matrix column count")
shards: list[Matrix] = []
start = 0for width in sizes:
end = start + width
shards.append([row[start:end] for row in matrix])
start = end
return shards
defconcat_columns(shards: list[Matrix]) -> Matrix:
# Concatenate Y_p blocks in device order. This is the local simulation# of the diagram's all-gather along the output-column dimension.ifnot shards:
raise ValueError("At least one shard is required")
row_count = shape(shards[0])[0]
ifany(shape(shard)[0] != row_count for shard in shards):
raise ValueError("All column shards must have the same row count")
result: Matrix = []
for row_index inrange(row_count):
row: list[float] = []
for shard in shards:
row.extend(shard[row_index])
result.append(row)
return result
defall_reduce_sum(partials: list[Matrix], output_shape: tuple[int, int]) -> Matrix:
# Sum overlapping dX_p tensors. This models an all-reduce with SUM.
rows, cols = output_shape
result: Matrix = [[0.0for _ inrange(cols)] for _ inrange(rows)]
for partial in partials:
if shape(partial) != output_shape:
raise ValueError("Every dX partial must have shape [B, K]")
for i inrange(rows):
for j inrange(cols):
result[i][j] += partial[i][j]
return result
defforward(x: Matrix, w_local: list[Matrix]) -> tuple[Matrix, ForwardCache]:
# X is replicated on all devices. Each device computes only the output# columns owned by its local weight matrix W_p.ifnot w_local:
raise ValueError("At least one weight shard is required")
batch, input_features = shape(x)
del batch # The row count is carried by X and does not need a separate variable.for shard in w_local:
shard_rows, _ = shape(shard)
if shard_rows != input_features:
raise ValueError("Every W_p must have shape [K, N_p]")
y_local: list[Matrix] = [matmul(x, shard) for shard in w_local]
# Concatenation in device order simulates the forward all-gather.
y = concat_columns(y_local)
return y, ForwardCache(x=x, w_local=w_local)
defbackward(dy: Matrix, cache: ForwardCache) -> tuple[Matrix, list[Matrix]]:
# Recover the replicated X and the original column-sharded weights.
x = cache.x
w_local = cache.w_local
batch, input_features = shape(x)
# Scatter dY by exactly the same column widths used for W.
shard_sizes = [shape(shard)[1] for shard in w_local]
if shape(dy) != (batch, sum(shard_sizes)):
raise ValueError("dY must have global shape [B, N]")
dy_local = shard_columns(dy, shard_sizes)
x_t = transpose(x)
dw_local: list[Matrix] = []
dx_local: list[Matrix] = []
for w_shard, dy_shard inzip(w_local, dy_local, strict=True):
_, local_columns = shape(w_shard)
# dW_p = X^T @ dY_p. It has shape [K, N_p] and stays with W_p.
dw_local.append(matmul(x_t, dy_shard))
if local_columns == 0:
# When N < P, a device may own no columns. It then contributes# zero to dX and owns an empty [K, 0] dW shard.
dx_local.append([[0.0for _ inrange(input_features)] for _ inrange(batch)])
else:
# dX_p = dY_p @ W_p^T. Every non-empty shard contributes to# the same global [B, K] input-gradient tensor.
dx_local.append(matmul(dy_shard, transpose(w_shard)))
# Sum every partial dX_p. This simulates all-reduce(SUM).
dx = all_reduce_sum(dx_local, (batch, input_features))
return dx, dw_local
defmatrices_close(
left: Matrix,
right: Matrix,
tolerance: float = 1e-9,
) -> bool:
# Compare sharded and reference calculations with floating-point tolerance.if shape(left) != shape(right):
returnFalse
rows, cols = shape(left)
returnall(abs(left[i][j] - right[i][j]) <= tolerance for i inrange(rows) for j inrange(cols))
defrun_reference_test(
x: Matrix,
w: Matrix,
dy: Matrix,
devices: int,
) -> None:
# Build contiguous W_p shards using the same boundaries that will also# be used to scatter dY during backpropagation.
_, total_columns = shape(w)
sizes = column_shard_sizes(total_columns, devices)
w_local = shard_columns(w, sizes)
# Run the sharded forward path and assemble the full Y.
y, cache = forward(x, w_local)
# Run the sharded backward path and all-reduce the partial dX tensors.
dx, dw_local = backward(dy, cache)
# Compute the direct unsharded reference requested by the diagram.
y_ref = matmul(x, w)
dw_ref = matmul(transpose(x), dy)
dx_ref = matmul(dy, transpose(w))
dw_ref_local = shard_columns(dw_ref, sizes)
# The distributed decomposition is correct only if all results match# the corresponding unsharded computation.assert matrices_close(y, y_ref)
assert matrices_close(dx, dx_ref)
assertlen(dw_local) == len(dw_ref_local)
for actual, expected inzip(dw_local, dw_ref_local, strict=True):
assert matrices_close(actual, expected)
defmain() -> None:
# The diagram supplies symbolic shapes rather than fixed numeric values.# These deterministic fixtures test the same forward/backward equations.
x: Matrix = [
[1.0, 2.0, -1.0],
[0.5, -2.0, 3.0],
]
w: Matrix = [
[1.0, 0.0, 2.0, -1.0, 3.0],
[2.0, 1.0, 0.0, 4.0, -2.0],
[-1.0, 3.0, 1.0, 0.5, 2.0],
]
dy: Matrix = [
[1.0, -1.0, 0.5, 2.0, 0.0],
[0.0, 2.0, -1.0, 1.5, 3.0],
]
# N=5 and P=2 exercises the diagram's uneven-shard rule: [3, 2].
run_reference_test(x, w, dy, devices=2)
# P>N exercises the diagram's N<P edge case. Some devices own zero columns.
run_reference_test(x, w, dy, devices=7)
print("All sharded results match the unsharded reference.")
if __name__ == "__main__":
main()
Time & Space Complexity
Device p owns N_p columns. Its forward matrix multiplication costs O(B*K*N_p). Computing dW_p costs O(B*K*N_p). Computing dX_p also costs O(B*K*N_p). For balanced shards, N_p is about N/P, so each main matrix multiplication costs O(B*K*N/P) per device. Across all devices, total arithmetic remains O(B*K*N) for each such matrix multiplication. Per-device tensor storage includes W_p and dW_p of shape [K, N_p], Y_p and dY_p of shape [B, N_p], and replicated X and dX of shape [B, K]. If full Y and full dY are materialized on a device, each also needs [B, N] storage. The single-process Python simulator stores all shards together, so its process-wide memory is larger than the real per-device view.
Where it is used
This pattern is used in tensor-parallel neural-network layers when a wide weight matrix is split across accelerators by its output columns. It is useful for large linear layers and projections because each device stores and multiplies only its local weight block while collective operations assemble or reduce the tensors that cross shard boundaries.
Why Interviewers Ask This
This question tests whether you can turn matrix calculus into a correct distributed implementation. The interviewer is checking tensor-shape reasoning, shard ownership, gradient derivation, and collective communication. They want to see that you concatenate independent output-column blocks, sum overlapping input-gradient contributions, and keep local weight gradients on their owners. They also test whether you can reason about uneven partitions, communication volume, executable code, and comparison with an unsharded reference.
Common interview mistakes
One mistake is to all-reduce the forward Y_p tensors. That would add different output columns together. Column-sharded Y_p blocks must be concatenated with an all-gather. Another mistake is to scatter dY using boundaries that do not match W. This gives the wrong local dW_p. A third mistake is forgetting that all dX_p tensors have shape [B, K] and overlap, so they must be summed. Candidates may also communicate dW_p unnecessarily even though it stays with W_p. Finally, N<P must not break the implementation. A zero-column shard contributes an empty dW_p and a zero dX_p.
Interview tip
Draw the column ownership first. Keep N_p beside every local tensor. If W_p, Y_p, dY_p, and dW_p all use the same N_p, while each dX_p returns to [B, K], the correct all-gather, scatter, and all-reduce operations follow naturally.
Interviewer may ask next
What changes when N is not divisible by P?
The equations do not change. Let q = floor(N/P) and r = N mod P. Give q+1 columns to the first r devices and q columns to the others. Prefix sums define each device's exact column range. W_p, Y_p, dY_p, and dW_p must all use that same range. Local matrix work becomes O(B*K*N_p). The total arithmetic across devices stays O(B*K*N). The main tradeoff is small load imbalance because some devices own one extra column. If N<P, some devices own zero columns and contribute zero to dX.
Can we avoid replicating the full Y or dX on every device?
Yes, if the next operation accepts a sharded tensor. For Y, the program can keep Y_p local instead of performing the forward all-gather. For dX, a reduce-scatter can replace a replicated all-reduce when the next stage expects a partitioned input gradient. The local matrix equations do not change, so correctness is preserved. Balanced arithmetic remains O(B*K*N/P) per device. The benefit is lower replicated memory and potentially less communication. The tradeoff is that later layers must understand and preserve the new shard layout.
13. Which infrastructure problem would you be most excited to explore first, and why?BehavioralEasyOpenai
i Question Details
Connect one AI-infrastructure problem to evidence from your past work, the users or systems affected, the technical depth you want to own, what you would learn first, and why the problem is more compelling than adjacent opportunities.
Interview tip:
Use STAR to structure your answer: briefly explain the Situation and Task, make Action the most detailed part, and finish with the Result. For example, describe a previous AI project where production reliability became important, explain which users and systems were affected, show how you investigated failures and improved visibility, and connect that experience to the infrastructure problem you would explore first. Explain what you would learn first, what technical depth you want to own, and why this problem matters more to you than nearby opportunities.
Situation
In my last role, I worked on an AI application that depended on several services before a user received a final response. The flow included application logic, model calls, data retrieval, and supporting services. When something failed or became slow, it was not always clear where the problem started. That made reliability especially important because users experienced the whole system as one product.
Task
I was responsible for improving how we understood and handled these failures. My goal was not only to fix individual incidents. I wanted us to see where time was being spent, where errors were happening, and how each part of the AI request affected the final user experience.
Action
I first traced the request from the user entry point through each major service. I added structured logs so important events were recorded in a consistent form. I also connected related events with a request identifier. This made it easier to follow one request across the system. I then separated model failures from retrieval failures, application errors, and timeouts. That distinction mattered because each type of failure needed a different response. I worked with the team to define useful signals for latency, errors, and dependency health. I also reviewed failed requests instead of looking only at successful averages. That helped us understand what users actually experienced when the system behaved badly. This work made me especially interested in AI inference reliability and observability. Observability means having enough useful signals to understand what a running system is doing and why it is failing. If I explored an infrastructure problem first, I would choose this area. I would start by learning how requests move through the inference stack, where delays and failures appear, how capacity is managed, and how engineers connect infrastructure signals to model behavior. I want to understand this deeply enough to own the path from a production symptom to the underlying cause. I find this more compelling than adjacent areas because better models provide limited value when the serving system is hard to understand, unstable, or too slow for users.
Result
The team gained a clearer way to investigate production problems and make reliability decisions from evidence instead of guesses. I learned that AI quality depends on more than the model itself. The infrastructure around the model strongly shapes whether users receive a dependable experience. That is why inference reliability and observability would be the infrastructure problem I would be most excited to explore first.
Why Interviewers Ask This
Interviewers ask this question to understand what technical problems genuinely motivate the candidate and whether that interest is supported by real experience. A strong answer shows clear priorities, curiosity, technical ownership, awareness of user impact, and a thoughtful reason for choosing one infrastructure problem over other valuable opportunities.
Interviewer may ask next
What would you try to learn first if you started working on inference reliability?
I would first map the full request path from the application to the model serving layer and back to the user. I would learn which components can add delay or fail, what signals already exist, and which failures are hardest to diagnose. That would give me a concrete picture of the system before I proposed changes.
Why would you choose observability and reliability before another infrastructure problem such as cost optimization?
Cost is important, but I would first want enough visibility to understand how the system behaves. Without that visibility, it is easy to optimize the wrong part of the system or create a reliability problem while reducing cost. My past work showed me that clear production signals create a strong base for later work on performance, capacity, and cost.
14. What would make an AI infrastructure role a poor fit for you?BehavioralMediumOpenai
i Question Details
State material scope, operating-model, location, ethical, or ownership constraints honestly; distinguish preferences from non-negotiables; connect them to how you do strong work; and explain how you would test mutual fit without inventing company details.
Interview tip:
Use STAR to structure your answer: briefly explain the Situation and Task, make Action the most detailed part, and finish with the Result. For example, describe a previous AI infrastructure project that helped you understand which working conditions are preferences and which are true constraints, how ownership and reliability affected your ability to do strong work, and how you would ask clear questions during an interview to test mutual fit.
Situation
During a previous project, I worked on infrastructure that supported an AI service in production. The work included deployment, monitoring, reliability, and coordination with engineers who depended on the platform. That experience helped me understand the conditions I need to do strong infrastructure work.
Task
My responsibility was to help keep the service reliable while improving the underlying systems. I also needed to understand which parts I could own directly and which decisions required help from other teams.
Action
I learned to separate preferences from true constraints. I prefer a role where I can spend meaningful time improving systems, automating repeated work, and solving reliability problems instead of only reacting to tickets. That is a preference because I can still handle operational work when production needs it. A more important constraint is ownership without authority. I would be uncomfortable being responsible for the reliability or safety of an AI system while lacking the access, information, or decision making ability needed to address known problems. I also would not be comfortable being asked to ignore or hide a serious reliability or safety concern. On the project, I handled these issues by making ownership clear, documenting risks, discussing unclear responsibilities early, and working with the appropriate teams before making production changes. I would use the same approach when evaluating a new role. I would ask how infrastructure ownership is divided, how incidents are handled, how much work is planned engineering versus reactive support, how engineers raise reliability or safety concerns, and what decisions the role can make directly. Those questions would help both sides decide whether the operating model is a good match.
Result
The project became easier to operate because responsibilities and escalation paths were clearer. I also learned that a demanding infrastructure role itself is not a poor fit for me. A poor fit would be a role where I am expected to own important production outcomes but cannot meaningfully improve the systems or address serious risks. I would rather identify that mismatch during the interview process than discover it after joining.
Why Interviewers Ask This
Interviewers ask this question to understand whether the candidate knows the conditions they need to perform well and can discuss constraints without sounding rigid. A strong answer separates preferences from true limits, shows mature judgment about ownership and ethics, and explains how the candidate would test mutual fit through clear questions.
Interviewer may ask next
How would you tell whether heavy operational work is a temporary need or a sign that the role is a poor fit?
I would ask what creates the operational load and what the team does after repeated incidents. If engineers are expected to automate recurring work, improve monitoring, and remove common failure causes, I would see the operational work as part of owning a production system. If the same manual work continues without time or authority to improve it, I would be more concerned about the fit.
What would you do if you joined and later found that ownership was less clear than you expected?
I would first clarify the specific responsibilities with my manager and the teams involved. I would document where decisions or access were unclear and explain how that affected reliability or safety work. I would then propose a clearer ownership and escalation model. If the team was willing to improve the situation, I would work through it. If I remained accountable for important outcomes without the ability to address known problems, I would consider that a serious fit issue.
15. Tell me about a project you decided not to ship.BehavioralHardOpenai
i Question Details
Choose an AI-enabled project with meaningful sunk cost, define the release criteria and disqualifying evidence, explain your personal decision and stakeholder communication, the user or safety risk avoided, what happened to the work, and what the team learned.
Interview tip:
Use STAR to structure your answer: briefly explain the Situation and Task, make Action the most detailed part, and finish with the Result. For example, describe an AI enabled project with significant engineering investment, the release criteria you set, the evidence that made you stop the launch, how you explained the decision to stakeholders, the user risk you avoided, what happened to the work, and what the team learned.
Situation
In my last role, my team was building an AI assistant to help engineers investigate production incidents and suggest possible recovery actions. We had already invested significant engineering time in retrieval, model integration, evaluation, and the user experience. The project looked useful in normal test cases, so there was pressure to move toward release.
Task
I was responsible for the AI evaluation and for giving my recommendation on whether the system was ready. Before the final release review, I defined clear criteria with the team. The assistant needed to base important recommendations on retrieved operational guidance. It also needed to avoid giving a confident action when the available evidence was weak or conflicting. I treated confident but unsupported recovery advice as disqualifying evidence because an engineer could follow it during a stressful incident.
Action
I expanded our evaluation beyond the clean examples we had used during development. I added cases with incomplete context, similar incident symptoms, conflicting documents, and older guidance mixed with newer guidance. In those cases, I found that the assistant could sometimes retrieve relevant information but still combine it into an unsafe recommendation. The answer sounded convincing even when the evidence did not support the action. I reproduced the failures and reviewed them with the team so we could separate isolated model mistakes from a deeper system problem. I concluded that the failure was serious because the product was helping users make production changes, not just answering a low risk question. I recommended that we not ship the assistant in its current form. Some stakeholders were concerned because we had already spent substantial time on it. I explained the decision using concrete failure examples and the release criteria we had agreed on earlier. I focused the discussion on user impact rather than on the amount of work already spent. I also proposed a path that preserved useful parts of the project. We kept the evaluation framework, retrieval work, and interface components. We stopped the release work and used those pieces to improve a narrower tool that surfaced evidence for engineers without automatically recommending a recovery action.
Result
We did not release the original assistant, which avoided putting engineers in a position where they might trust unsupported operational advice during an incident. The work was not discarded. Several parts became useful foundations for safer internal tools and future evaluation work. The main lesson our team learned was that sunk cost should not decide whether an AI system ships. Clear release criteria should be agreed on early, and evidence from realistic failure cases should be strong enough to stop a launch.
Why Interviewers Ask This
Interviewers ask this question to test whether a candidate can make a difficult decision when time and effort have already been invested. A strong answer shows that the candidate uses evidence, defines clear release standards, protects users, communicates difficult decisions clearly, and finds value in work even when the original product should not launch.
Interviewer may ask next
How did you handle stakeholders who wanted to ship because so much work had already been completed?
I brought the discussion back to the release criteria we had agreed on before the decision. I showed concrete cases where the assistant gave a confident action that was not supported by the available evidence. I explained that the engineering effort already spent could not reduce the risk to users. I also made the decision easier to accept by showing which parts of the work we could reuse in a safer product.
What would you do differently if you were starting the same project again?
I would create the difficult evaluation cases earlier, before the system became heavily developed. I would also define the conditions that would stop a release at the beginning of the project. That would expose the hardest safety problem sooner and reduce the chance that sunk cost affects the final decision.
Disclaimer: This interview guide is for educational and informational purposes only. It is designed to help readers prepare, but it does not guarantee any interview result, hiring decision, offer, or outcome. Interview questions, hiring criteria, and preferred answers can vary by employer, interviewer, industry, location, and time. The examples and explanations reflect the authors' research and judgment, are provided without warranties of any kind, and should not be treated as the only correct approach. Diagrams are simplified illustrations intended to highlight the main components and their interactions; actual systems and implementations may be more complex. Alternative approaches may be equally valid or better suited to a particular question, context, or interviewer. To the fullest extent permitted by applicable law, the author, contributors, and publisher are not liable for decisions made, actions taken, or losses incurred based on this guide.
Company Notice: This guide is an independent educational resource and is not affiliated with, endorsed by, sponsored by, or approved by the company named in this guide. Company names are used only to identify interview experiences commonly reported by candidates. Interview practices can change without notice, and inclusion of company-specific content does not mean these questions are official, complete, or guaranteed to be asked. To the fullest extent permitted by law, the author, contributors, and publisher are not responsible for outcomes related to use of this material.