Hand-Written Gradients: Prefix-Product Backward Pass and Stable Cross-Entropy

Quick Overview

Write gradients by hand: the forward and backward pass of the prefix-product operation, in linear time and without division so inputs may contain zeros, and a numerically stable softmax cross-entropy loss with its gradient in NumPy. It tests the chain rule, prefix and suffix products, and gradient checking.

Hand-Written Gradients: Prefix-Product Backward Pass and Stable Cross-Entropy

Company: OpenAI

Role: Machine Learning Engineer

Category: Machine Learning

Difficulty: hard

Interview Round: Onsite

This machine learning coding round asks you to write gradients by hand, the way an automatic differentiation (autograd) engine would, without using any autograd library. ### Clarifying Questions - Can the input arrays contain zeros, or negative values? - Should the functions work on plain Python lists, NumPy arrays or batches? - For cross-entropy, are the inputs raw scores (logits) or probabilities, and is the loss averaged over the batch or summed? - What precision is expected, and how will correctness be checked? ### Part 1 — The prefix-product operation The prefix-product operation maps an array `a` of length `n` to an array `y` with `y[i] = a[0] × a[1] × … × a[i]`. Implement its forward pass, and its backward pass: given the upstream gradient `g[i] = ∂L/∂y[i]` for some scalar loss `L`, return `∂L/∂a[k]` for every `k`, applying the chain rule. Start with the special case where the loss depends only on the total product `y[n − 1]`: then the gradient for `a[k]` is the product of all the other elements. Your backward pass must run in `O(n)` time and must not divide by elements of `a`, since the input can contain zeros. ```hint Why division fails, and what replaces it Dividing the total product by `a[k]` breaks as soon as one element is zero; think about what you can accumulate from the left and from the right instead. ``` ```hint Reuse the sum from the right In the general case, the gradient for `a[k]` sums contributions from every output at or after position `k`; look for a recurrence that computes these sums from right to left. ``` #### What This Part Should Cover - A correct forward pass and the chain-rule expression for each input's gradient - The special case of the total product, solved without division using products from both sides - A linear-time backward pass for general upstream gradients - A numerical gradient check, including inputs with one zero and with two zeros ### Part 2 — Cross-entropy from scratch Given logits of shape `N × C` (a batch of `N` examples and `C` classes) and integer labels, implement the mean softmax cross-entropy loss and its gradient with respect to the logits, in NumPy, without calling any library loss function. ```hint Stay in log space Consider what happens to exponentials of large logits, and how subtracting a per-row constant changes the softmax. ``` #### What This Part Should Cover - A numerically stable forward pass that does not overflow for large logits - The gradient with respect to the logits, and its derivation - Correct scaling by the batch size - A finite-difference gradient check ### What a Strong Answer Covers - Correct chain-rule reasoning, with clear notation for upstream and local gradients - Division-free, linear-time gradients that remain correct when inputs contain zeros - Numerical stability in the cross-entropy implementation - Verification of every gradient against finite differences ### Follow-up Questions - How would you implement the backward pass of the prefix sum? Of the running maximum? - How would you combine these pieces into a tiny autograd engine that builds a graph and runs backward automatically? - How does label smoothing change the cross-entropy gradient? - Why does the gradient of softmax combined with cross-entropy have such a simple form, and why do frameworks fuse the two?

Overview: Write gradients by hand: the forward and backward pass of the prefix-product operation, in linear time and without division so inputs may contain zeros, and a numerically stable softmax cross-entropy loss with its gradient in NumPy. It tests the chain rule, prefix and suffix products, and gradient checking.

|Home/Machine Learning/OpenAI
OpenAI logo
OpenAI
Sep 20, 2026
hardMachine Learning EngineerOnsiteMachine Learning
3
0

This machine learning coding round asks you to write gradients by hand, the way an automatic differentiation (autograd) engine would, without using any autograd library.

Clarifying Questions Guidance

  • Can the input arrays contain zeros, or negative values?
  • Should the functions work on plain Python lists, NumPy arrays or batches?
  • For cross-entropy, are the inputs raw scores (logits) or probabilities, and is the loss averaged over the batch or summed?
  • What precision is expected, and how will correctness be checked?

Part 1 — The prefix-product operation

The prefix-product operation maps an array a of length n to an array y with y[i] = a[0] × a[1] × … × a[i]. Implement its forward pass, and its backward pass: given the upstream gradient g[i] = ∂L/∂y[i] for some scalar loss L, return ∂L/∂a[k] for every k, applying the chain rule.

Start with the special case where the loss depends only on the total product y[n − 1]: then the gradient for a[k] is the product of all the other elements. Your backward pass must run in O(n) time and must not divide by elements of a, since the input can contain zeros.

What This Part Should Cover Guidance

  • A correct forward pass and the chain-rule expression for each input's gradient
  • The special case of the total product, solved without division using products from both sides
  • A linear-time backward pass for general upstream gradients
  • A numerical gradient check, including inputs with one zero and with two zeros

Part 2 — Cross-entropy from scratch

Given logits of shape N × C (a batch of N examples and C classes) and integer labels, implement the mean softmax cross-entropy loss and its gradient with respect to the logits, in NumPy, without calling any library loss function.

What This Part Should Cover Guidance

  • A numerically stable forward pass that does not overflow for large logits
  • The gradient with respect to the logits, and its derivation
  • Correct scaling by the batch size
  • A finite-difference gradient check

What a Strong Answer Covers Guidance

  • Correct chain-rule reasoning, with clear notation for upstream and local gradients
  • Division-free, linear-time gradients that remain correct when inputs contain zeros
  • Numerical stability in the cross-entropy implementation
  • Verification of every gradient against finite differences

Follow-up Questions Guidance

  • How would you implement the backward pass of the prefix sum? Of the running maximum?
  • How would you combine these pieces into a tiny autograd engine that builds a graph and runs backward automatically?
  • How does label smoothing change the cross-entropy gradient?
  • Why does the gradient of softmax combined with cross-entropy have such a simple form, and why do frameworks fuse the two?
Loading comments...