Problem 401265 · medium · Level 04 Non-Linear Data Structures

Catch the Wrong Derivative

gradient checking · numerical gradient · analytic gradient · relative error · testing

Your team derives gradients by hand, and every derivation goes through a check before any model is trained with it. You are given a loss f(p) and a function grad(p) that claims to return its gradient (a list with one partial derivative per parameter), both taking a list of floats.

Write find_wrong_slopes(f, grad, points, h) that checks grad at every point in points and returns the list of pairs (i, j) for which the claimed partial derivative grad(points[i])[j] disagrees with the estimate

num = (f(p with p[j] + h) - f(p with p[j] - h)) / (2 * h)        where p = points[i]

A claimed value a disagrees when |a - num| > 1e-4 · max(1, |a|, |num|). List the pairs in order of i, and for the same i in order of j. Return an empty list if every slope passes.

Examples

Input:  f = lambda p: p[0] ** 2 * p[1]
        grad = lambda p: [2 * p[0] * p[1], 2 * p[0]]
        points = [[1.0, 1.0], [2.0, 3.0]], h = 1e-5
Output: [(0, 1)]
Explanation: the slope in p[1] should be p[0]². At [1, 1] the claim is 2 but the estimate is 1.
At [2, 3] the wrong formula happens to give the right value, 4, so that point hides the bug.

Input:  f = lambda p: (p[0] - 3) ** 2 + (p[1] + 1) ** 2
        grad = lambda p: [p[0] - 3, p[1] + 1]
        points = [[3.0, -1.0], [3.0, 0.0], [5.0, -1.0]], h = 1e-5
Output: [(1, 1), (2, 0)]
Explanation: the factor 2 is missing. At the minimum both slopes are 0, so the mistake cannot show there.

Constraints

  • 0 <= len(points) <= 20, every point has the same length, between 1 and 20
  • 1e-6 <= h <= 1e-2; f and grad accept lists and must not be trusted to leave them unchanged, so pass copies
  • the tests avoid cases where a claimed value is close to the tolerance

Goals

  • Compare a hand-derived gradient with a numerical estimate at several points
  • Use a tolerance that is relative for large slopes and absolute for small ones
  • See that a single test point can hide a wrong derivative
Starting Python…