Kumita← Open visual lesson

Softmax

The code behind the picture

Follow the lesson’s implementation, one code cell at a time.

Choose a language and read the cells in order. These are small teaching examples; the visual lesson runs separately.

Follow the calculation

Same inputs · Same operation · Same result

3 teaching cells · Python standard library · Read-only

  1. 1. Start from the four logits

    These are the Logits lesson's raw class scores for the marked watermelon pixel, recomputed at full precision from its inputs and the hand-set weights (a teaching example, not learned).

    import json
    import math
    
    pixel = [27, 166, 85]  # the marked watermelon pixel: red, green, blue bytes
    x = [value / 255 for value in pixel]
    classes = ["flesh", "rind", "seed", "background"]
    W = [[2, -2, 0], [-1, 3, -1], [-1, -1, -1], [1, 1, 2]]
    b = [-0.5, -0.3, 1, -2.5]
    z = [sum(w * xi for w, xi in zip(row, x)) + bias for row, bias in zip(W, b)]
  2. 2. Turn the scores into shares, safely

    Subtract the largest score, exponentiate, add the amounts into one total, and divide each amount by that total. Adding the same amount to every score (here 2) gives the same shares; raising seed's score by one changes every share. Only the reported result is rounded.

    def softmax(scores):
        m = max(scores)
        amounts = [math.exp(s - m) for s in scores]
        total = sum(amounts)
        return [a / total for a in amounts]
    
    round6 = lambda v: round(v, 6)
    p = softmax(z)
    result = {
        "shares": {name: round6(v) for name, v in zip(classes, p)},
        "sum": round6(sum(p)),
        "offset_unchanged": all(abs(a - c) < 1e-12 for a, c in zip(p, softmax([s + 2 for s in z]))),
        "seed_plus_one": {name: round6(v) for name, v in zip(classes, softmax([s + (k == 2) for k, s in enumerate(z)]))},
    }
  3. 3. Inspect the result

    Print the result as JSON so both languages use the same output format.

    print(json.dumps(result))

Expected result

Running the three cells in order produces this result in either language.

{
  "shares": {
    "flesh": 0.042261,
    "rind": 0.697699,
    "seed": 0.189401,
    "background": 0.07064
  },
  "sum": 1,
  "offset_unchanged": true,
  "seed_plus_one": {
    "flesh": 0.031884,
    "rind": 0.526389,
    "seed": 0.388432,
    "background": 0.053295
  }
}