Math · Week 1 · Note 1 of 14
Functions, exponents and logarithms
Three ideas from school algebra carry most of the math in machine learning: a function is a machine, an exponent is repeated growth, and a logarithm asks “which power?”. This note rebuilds them with small numbers, then shows the one place they matter most: how a model’s score for being wrong is written.
Why this note matters
The previous note ended with a model, three houses, and a loss: one number that says how wrong the model’s guesses are. For guessing a price, the loss was the average of the squared errors. But a great deal of machine learning is not about guessing a number. It is about guessing how likely something is: that this email is spam, that the next word is “the”, that this picture shows a cat. How wrong is a guess of “70% likely” when the thing then happens? Or does not happen?
The answer that the whole field settled on is built from a logarithm, and the step that turns a model’s raw scores into percentages is built from the number . Those two facts are the reason this note exists. Small things that look like notation also live here: a step size written as , a chart with a “log scale” axis. If these are comfortable, the rest of the math track is a series of short steps.
Interviewers do not ask “what is a logarithm?”. They ask questions whose answers are built from it:
- “Why is the loss for a probability guess the negative log of the probability, not just one minus the probability?”
- “Why do we add up logs of probabilities instead of multiplying the probabilities?”
- “Your code that turns scores into percentages returns NaN on large inputs. Why, and how do you fix it?”
- “Why search step sizes on a log scale?”
By the end of this note you can answer all four, in plain words. Each is worked out below and then collected in the interview section. Words that belong to later notes are marked like this : hover for a one-line meaning, or ignore them on a first read.
In plain words
Each everyday idea has a precise name and symbol. You need both: the first to think, the second to speak.
| Everyday idea | Technical term | Symbol |
|---|---|---|
| A machine from input to output | function | |
| The knobs on the machine | parameters | or |
| Feeding one machine’s output into another | composition | |
| Repeated multiplication | exponent (power) | |
| Growth that compounds continuously | the exponential function | or |
| “Which power gives this number?” | logarithm | |
| The logarithm with base | natural log | (in code, np.log) |
| A function that undoes another | inverse |
Functions: machines with knobs
A function is a rule that gives exactly one output for each input. We write the rule with a name and the input in brackets: means “the function called takes a number , doubles it and adds one”. The brackets do not mean multiplication; they mean “apply the rule to”. So and .
Three things about functions matter later, so meet them now with small numbers.
1. Parameters are the knobs. has two numbers baked in, the 2 and the 1. Make them adjustable and write . Now and are parameters: they are part of the machine, not inputs. With you get the function above; with you get a different one. When the parameters matter, we write or , where (“theta”) is the standard name for “all the parameters together”. Training a model means: the inputs are fixed by the data, so search over for the knobs that give the best outputs.
2. Functions can be chained. Feed one machine’s output into another. If and , then means “first , then ”: , then . The other order gives something else: , then . This is composition. It matters because the big models of later weeks are nothing but long compositions: a neural network is “do this simple step, then this one, then this one”, dozens of times. Week 2 adds the tool for finding the slope of a composition, the chain rule.
3. A function has a picture. Plot inputs along the bottom and outputs up the side and you get its graph. is a straight line, which is why it is called linear. is a U-shaped curve. Most of what follows is about recognising a handful of these shapes on sight; the zoo at the end collects them.
Read Khan Academy: Functions (Algebra 1) If function notation feels rusty, do the first two exercise sets, “Evaluating functions” and “Inputs and outputs of a function”. Twenty minutes.Exponents and their rules
An exponent counts repeated multiplication: . The 2 is the base, the 3 is the exponent or power. The rumour after hours is people, and .
Six rules cover everything you will meet. Check each with the small numbers next to it rather than memorising it.
| Rule | Check with numbers | Why it is true |
|---|---|---|
| Three 2s multiplied, then two more: five 2s in all. | ||
| Two of the five 2s cancel. | ||
| Three 2s, taken twice over: six 2s. | ||
| and also | Follows from the division rule with . | |
| Keep dividing by 2 past : 4, 2, 1, ½, ¼, ⅛. | ||
| because | Two copies of must multiply to . |
The negative-exponent rule is the one that shows up most. The size of each training step (the
learning rate , week 2) is written like , which is
. In code that is 3e-4. Reading these without pausing
is a small skill worth having before you tune your first model.
Two facts about Fig. 2 are used constantly. First, is always positive: no matter how negative gets, only creeps towards zero and never touches or crosses it. Second, it is always increasing: a bigger always gives a bigger , so the order of inputs is preserved in the outputs. Keep both in mind. They are exactly the two properties you need to turn a list of scores, which can be negative, into a list of percentages, which cannot, without changing which score was biggest. That recipe is below.
Read Khan Academy: Exponential models (Algebra 2) Only the exercise sets on exponent properties and on graphing exponentials. Skip the word problems about bank interest.The number e
is a constant like . It is the natural base for exponents because of one property, which the calculus notes will use on every page: the curve has, at every point, a slope equal to its own height. At the point where , the curve is rising at a rate of 5. No other base does this; rises at about 0.69 times its height, at about 2.3 times. Using makes every derivative involving exponentials come out without stray constants, which is the only reason mathematicians, and therefore machine learning, prefer it.
You will see written as when the exponent is long, for example
, the formula for the bell curve you will meet in week 3. In code it is
np.exp(x). A few values to have in your head:
| −3 | −1 | 0 | 1 | 2 | 3 | 5 | 10 | |
|---|---|---|---|---|---|---|---|---|
| 0.05 | 0.37 | 1 | 2.72 | 7.39 | 20.1 | 148 | 22,026 |
Notice how quickly the right side explodes. is already twenty-two thousand, and is larger than the biggest number a 64-bit float can hold. A model’s raw scores can easily be in the hundreds, so “ overflows” is a real bug you will meet, and the scores section shows the standard fix.
Watch 3Blue1Brown: What’s so special about Euler’s number e? About 14 minutes. The clearest explanation of the slope-equals-height property. You do not need the calculus it mentions yet; watch it for the pictures and rewatch it in week 2. Read Better Explained: An intuitive guide to exponential functions and e The bank-interest story in full, with pictures. Ten minutes, no calculus.Logarithms: “which power?”
Formally, is the number such that . The two statements say the same thing in two directions:
So because , and because . The base is which number is being raised to the power. Three bases appear in practice:
| Base | Written | Where it appears | In NumPy / PyTorch |
|---|---|---|---|
| or just in ML papers | Every loss built from probabilities; the log-probabilities that model APIs return | np.log | |
| 2 | Measuring information in bits (entropy , week 4) | np.log2 | |
| 10 | Step-size scales, plots, “orders of magnitude” | np.log10 |
Read the shape of Fig. 3 carefully, because it is the shape of the loss for probability guesses, which the next section builds:
- , because . A probability of 1 will mean zero loss.
- For inputs between 0 and 1 the log is negative, because you need a negative power of to get below 1. Probabilities live here, so log-probabilities are always negative numbers.
- Near zero the curve plunges: , , . Each extra zero costs another 2.3. Nothing stops the fall; the log of a number heads to minus infinity as the number heads to zero.
- Above 1 it keeps rising but ever more slowly. Going from 1 to 7.39 raises the log by 2; going from 7.39 to 54.6 raises it by 2 again. Multiplying the input by the same factor adds the same amount to the output.
- and the log of any negative number do not exist. In code they come out as
-infandnan, and they are the most common source of a NaN loss.
The log rules you will use
| Rule | Check with numbers | Comes from |
|---|---|---|
| any base | ||
| and | log and power undo each other | |
| change of base; any base is a constant multiple of any other |
The change-of-base rule has a practical consequence: which base you use changes nothing except a constant factor. The place where a function is smallest is the same whether you measure in , or . This is why a paper can say “log” without caring, and why the same quantity can be reported “in bits” (base 2) or “in nats” (base ) and mean the same thing.
Two things that look like rules but are not:
Read Khan Academy: Logarithms (Algebra 2) Do the exercise sets “Evaluate logarithms”, “Use the properties of logarithms” and “Change of base”. Thirty minutes, and it covers every rule above with instant feedback.Why machine learning loves logs
Reason 1: products become sums. A model that assigns probabilities to several independent events gives the whole sequence a probability equal to the product of the individual ones. Say a model gives probabilities 0.9, 0.8 and 0.95 to three correct answers. The probability of getting all three right is
Take the log of each and add instead:
and , the same answer. For three numbers this is no easier. For a million training examples, or for a 500-token sentence where each token has its own probability, a sum is something you can compute, average and take slopes of, and a product is not. Almost every model is trained by making this sum of logs as large as possible, which is the same as making the product as large as possible. In week 3 the product gets a name, the likelihood , and the maximum likelihood note builds on exactly this calculation.
Reason 2: tiny numbers stay representable. Multiply a thousand probabilities of 0.001 and the true answer is . A 64-bit float cannot hold anything smaller than about , so the computer returns exactly 0, and every model you compare returns 0 too. This is called underflow. In log space the same quantity is , an ordinary number. The code section shows this happening.
Reason 3: confident mistakes are punished without limit. A loss should be a number that is small when the model is right and large when it is wrong, and logs of probabilities are negative, so flip the sign. The loss used for probability guesses is , where is the probability the model gave to the answer that turned out to be correct. It is called the log loss. Fig. 4 is its graph.
Compare this with the obvious alternative, a loss of . Being 90% sure and right costs 0.1 either way. But being 99% sure of the wrong answer, so for the right one, costs 0.99 under and 4.6 under . Push to and the first loss barely moves to 0.9999 while the second jumps to 9.2. The log loss keeps caring, more and more, exactly when the model is most confidently wrong, and that pressure is what teaches a model to say “70%” only when it really is right about 70% of the time. It is the forecaster’s score from the plain-words box: cheap when right, unbounded when arrogantly wrong.
A fourth, smaller reason: log scales. When you try step sizes for training, the useful candidates are 0.1, 0.01, 0.001, 0.0001: each a factor of 10 apart, not a fixed amount apart. On a log scale those are evenly spaced. The same holds for plotting a loss that falls from 1000 to 0.1: on a plain axis the interesting part is squashed into an invisible sliver at the bottom; on a log axis every factor of 10 gets equal room. Whenever a quantity varies by multiplication, think in logs.
Turning scores into percentages
For the scores :
Check the properties: every entry is positive, they add to 1, and the biggest score got the biggest share. Written as a formula, the share for score number is
where just means “add up over all the scores”. This recipe is so common that it has a name, softmax, and the raw scores are called logits. You do not need either word yet; the softmax note in week 5 tells the full story. Here the point is only to see doing the job Fig. 2 promised.
Now the bug. Scores of are perfectly ordinary for a model that has not been
trained yet, but is far beyond the largest float, so every term becomes inf
and the division gives nan. The fix: subtract the largest score from all of them first. The exponent
rule says , and the appears in both the top and the
bottom of the fraction, so it cancels. The percentages are identical; only the intermediate numbers
change:
After the shift the biggest exponent is 0, so the biggest term is and nothing can overflow. Every machine-learning library does this inside its scores-to-percentages function, which is why hand-written versions that forget it are a classic interview question. The code section shows both versions.
The functions to recognise on sight
Most formulas in the rest of these notes are made from six shapes. If you can sketch each from memory and name where it appears, you can read the formulas as pictures instead of symbols.
| Function | Shape | Key facts | Where you will meet it |
|---|---|---|---|
| straight line | slope , crosses the y-axis at | the house-price model; linear regression; every layer of a neural network before its bend | |
| U shape | never negative, minimum at 0, grows fast | mean squared error; penalties on big parameters (regularization , week 3) | |
| hockey stick up | always positive, increasing, slope equals value | scores to percentages (above); the bell curve (week 3) | |
| steep then flat | only for , zero at 1, negative below 1 | the log loss; measuring information (week 4) | |
| S curve | squashes any number into (0, 1), | turning one score into a yes/no probability (sigmoid , week 3) | |
| flat then diagonal | zero for negatives, identity for positives | the bend between layers of a network (ReLU , week 5) |
Look at the sigmoid formula with fresh eyes now. It is built from Fig. 2: is huge for very negative , so is nearly 0; it is nearly 0 for very positive , so is nearly 1; and at it is . The whole S-shape falls out of the exponential’s behaviour at the two ends.
Try it Desmos graphing calculator Type each of the six functions, then change the numbers in them and watch the shape move. Ten minutes of this beats an hour of staring at formulas. Try y = 1/(1+e^(−k x)) and slide k.Code
Type these out; do not paste. Each one demonstrates a sentence from above.
The rules, checked numerically
import numpy as np
a = 6.0
b = 1.5
n = 3.0
# np.log is the NATURAL log (base e). np.log2 is base 2, np.log10 is base
# 10.
# product -> sum: 2.1972... 2.1972...
print(np.log(a * b), np.log(a) + np.log(b))
# quotient -> difference: 1.3863... 1.3863...
print(np.log(a / b), np.log(a) - np.log(b))
# power -> multiple: 5.3753... 5.3753...
print(np.log(a ** n), n * np.log(a))
print(np.log(np.exp(2.5))) # log undoes exp: 2.5
print(np.exp(np.log(a))) # exp undoes log: 6.0
# change of base: 2.5850... 2.5850...
print(np.log2(a), np.log(a) / np.log(2))
# The same check written as a test: assert stops the program if the
# condition is false.
# np.isclose allows for the tiny rounding differences that decimals always
# have.
assert np.isclose(np.log(a * b), np.log(a) + np.log(b))
print(np.log(1), np.log(np.e), np.log(0.5), np.log(0.01))
# 0.0 1.0 -0.6931471805599453 -4.605170185988091
Underflow, and why log space fixes it
import numpy as np
# One thousand examples, each given probability 0.001 by a model.
p = np.full(1000, 0.001) # an array of 1000 copies of 0.001
product = np.prod(p) # multiply all 1000 together
log_sum = np.sum(np.log(p)) # add up their logs instead
# underflow: the true value 10^-3000 is below the smallest float
print(product)
# prints 0.0
# the same quantity in log space: an ordinary number
print(log_sum)
# prints -6907.755278982137
# A second model gives each example 0.002, so it is clearly better. Can we
# tell?
q = np.full(1000, 0.002)
# both products are 0.0
print(np.prod(q) > np.prod(p))
# prints False
# log space sees the difference
print(np.sum(np.log(q)) > np.sum(np.log(p)))
# prints True
Scores to percentages, naive and safe
import numpy as np
def scores_to_percentages_naive(z):
# step 1: make every score positive, keep the order
e = np.exp(z)
# step 2: divide by the total so they add up to 1
return e / np.sum(e)
def scores_to_percentages(z):
# shift every score down by the biggest one (same answer, safe numbers)
z = z - np.max(z)
e = np.exp(z)
return e / np.sum(e)
small = np.array([2.0, 1.0, 0.0])
big = np.array([1000.0, 1001.0, 1002.0])
print(scores_to_percentages(small).round(3)) # [0.665 0.245 0.09 ]
# exp(1000) overflows to inf; inf / inf is nan
print(scores_to_percentages_naive(big))
# prints [nan nan nan]
print(scores_to_percentages(big).round(3)) # [0.09 0.245 0.665]
# NumPy also prints "RuntimeWarning: overflow encountered in exp" for the
# naive line.
# This recipe is called softmax; the week 5 note builds on it.
The log loss
import numpy as np
def log_loss(p_correct):
# minus the log of the probability the model gave to the right answer
return -np.log(p_correct)
for p in [0.99, 0.9, 0.5, 0.1, 0.01]:
loss = log_loss(p)
# :<5 pads p to 5 characters, :.3f prints 3 decimals
print(f"p = {p:<5} loss = {loss:.3f}")
# p = 0.99 loss = 0.010
# p = 0.9 loss = 0.105
# p = 0.5 loss = 0.693
# p = 0.1 loss = 2.303
# p = 0.01 loss = 4.605
# Compare with the obvious alternative, 1 - p. At p = 0.01 it is only 0.99:
# it has stopped caring.
print(1 - 0.01) # 0.99
print(log_loss(0.01)) # 4.605170185988091
Where you will meet this again
- Week 2, loss functions. The log loss, , becomes the standard loss for yes/no questions, with the probability coming out of the S-curve from the zoo.
- Week 3, maximum likelihood. “Add up the logs of the probabilities the model gave to what really happened, and make that as big as possible” turns out to be where almost every loss comes from. The product gets the name likelihood .
- Week 4, entropy. averaged over outcomes measures how unpredictable something is; that is entropy , and the log loss gets its grown-up name, cross-entropy .
- Week 5, softmax. The scores-to-percentages recipe becomes softmax , the last step of every model that chooses between categories, and the subtract-the-max trick returns with a second trick for taking its log safely.
- Weeks 7 to 9, language models. A language model gives a score to every token it could write next, about 50,000 of them, and softmax turns those into percentages. Its training loss is of the token that actually came next, averaged over every position: Fig. 4 applied billions of times. People report raised to that average loss and call it perplexity ; a value of 10 means “as unsure as choosing among 10 equally likely tokens”. And model APIs return log-probabilities rather than probabilities, for Reasons 1 and 2 above.
The full interview answer
The most common form this note takes in an interview is “why is the loss for a probability a log?” or “why not just use one minus the probability?”. (After week 4 you will hear it asked as “why is cross-entropy a log?”; same question.) Here is a complete answer in about sixty seconds, built from the short lines above.
If they push further, be ready for:
- “Why base rather than 2?” Any base differs by a constant factor, so the minimizer is the same; makes derivatives clean. Base 2 gives the loss in bits.
- “What loss means the model was perfect?” 0: it gave probability 1 to every correct answer. In practice that is a sign of a bug or of test answers leaking into training.
- “Why does my loss show NaN?” Usually somewhere: a probability that reached exactly zero, or an overflow in a hand-written scores-to-percentages step. Clip probabilities away from zero, or use the library’s loss function, which does the safe version.
- “Why are logprobs negative?” Probabilities are at most 1 and , so every log-probability is zero or below.
Practice questions
Answer each one out loud before opening it.
What does f(x; θ) mean, and what changes during training?
It is a function of the input whose behaviour depends on parameters . The input comes from the data and is fixed; training changes to minimize a loss. In a big network is every weight and bias.
Why is e^x used to turn scores into percentages instead of, say, x² or |x|, which also make numbers positive?
is strictly increasing, so a larger score always gets a larger percentage; and would give the scores −5 and 5 the same percentage. It is also smooth, with a slope equal to its own value, which makes the calculus in week 5 come out clean.
Without a calculator: log₂ 32, log₁₀ 0.001, ln(e⁵), e^(ln 7), ln 1.
5 (since ); −3 (since ); 5; 7; 0.
A model gives the correct answer probability 0.01. What is its loss? And at 0.99?
and . The confidently wrong case costs about 460 times more than the confidently right case.
Why do we add log-probabilities instead of multiplying probabilities?
Because : the sum of logs is the log of the product, so nothing is lost. The sum is numerically safe (no underflow), it is easy to average and differentiate, and maximizing it finds the same parameters as maximizing the product.
Each extra zero in a probability (0.1, 0.01, 0.001) adds how much to the log loss? Why?
About 2.3 each time, because and . Dividing the input by a fixed factor subtracts a fixed amount from the log: that is the power rule in action.
Your hand-written scores-to-percentages function returns NaN for scores around 1000. What happened and what is the fix?
overflows to infinity, and infinity divided by infinity is NaN. Subtract the maximum score from all scores before exponentiating. The factor cancels between numerator and denominator, so the output is unchanged and all intermediate values are at most 1.
Is log(a + b) = log a + log b?
No. There is no simple rule for the log of a sum. is the product rule. Check: but .
Why does changing the base of the log not change which parameters minimize a loss?
Change of base: , so every base is a positive constant times the natural log. Multiplying a loss by a positive constant leaves its minimizer where it was; only the reported value changes.
Why are training step sizes (learning rates) searched on a log scale?
Because the effect of a step size is multiplicative: 0.01 and 0.001 behave very differently, while 0.011 and 0.010 behave the same. Spacing candidates by a constant factor (evenly spaced in log space) spends the budget where behaviour actually changes.
If g(x) = x² and f(x) = 2x + 1, what are f(g(2)) and g(f(2))?
and . Order matters in composition; a big model is a specific order of compositions.
What is 3e-4 as a decimal, and what does the notation mean?
0.0003. It means . Negative exponents are reciprocals.
Practice problems
Do these on paper first, then check. The hand problems are the kind interviewers ask you to do on a whiteboard; the coding ones build the habits you will use every day. Solutions are hidden under each problem.
By hand
1. Simplify: 2³ · 2⁴, (3²)³, 5⁰, 2⁻³, 16^(1/2), (e²)(e⁻²).
; ; ; ; ; .
2. Expand ln(a b² / c) into separate logs.
. Product rule for the numerator, power rule for , quotient rule for the division.
3. Write log₂ x using only natural logs, then evaluate log₂ 1000 given ln 1000 = 6.908 and ln 2 = 0.693.
. So , consistent with .
4. Three probabilities: 0.5, 0.25, 0.125. Compute their product directly, then as a sum of base-2 logs.
Product: . Logs: , and . Same answer, and the sum is easier.
5. How many times larger is the loss −ln 0.1 than −ln 0.9?
and . Ratio about 22. Being 10% confident in the right answer costs 22 times more than being 90% confident.
6. Solve 2ˣ = 1000 approximately, and 10ˣ = 0.0003.
. For the second, , so .
7. A loss falls from 1000 to 0.1 over training. On a plain axis, what fraction of the chart’s height does the part from 1 to 0.1 occupy? On a log₁₀ axis?
Plain axis: the range is 1000, and the interesting final stretch spans 0.9 of it, so 0.09% of the height, invisible. Log axis: the range is units, and the stretch from 1 to 0.1 is 1 unit, a quarter of the chart. That is why loss curves are plotted on log axes.
8. Turn the scores (2, 1, 0) into percentages by hand. (e² = 7.389, e = 2.718.)
Sum . Percentages . They add to 1 and keep the order of the scores.
9. Turn the scores (1000, 1001, 1002) into percentages by hand without overflowing anything.
Subtract 1002: . Exponentials , sum 1.503, so . Note it is the mirror image of problem 8, because the gaps between the scores are the same but reversed.
10. Scores (3, 3, 3). What percentages come out, and why? What about scores (3, 3, 3) + 100?
Equal scores give equal shares: . Adding the same number to every score multiplies every by the same , which cancels in the division, so the shares are unchanged. Only the gaps between scores matter. That is also why subtracting the maximum is harmless.
11. Sketch, from memory, y = ln x, y = −ln x for 0 < x ≤ 1, and y = eˣ on the same axes. Mark where each crosses an axis.
crosses the x-axis at 1 and plunges towards the y-axis. is its mirror: 0 at , rising steeply towards the y-axis. crosses the y-axis at 1 and never touches the x-axis. Compare with Figs. 2, 3 and 4.
12. The sigmoid is σ(x) = 1 / (1 + e⁻ˣ). Evaluate σ(0), σ(4), σ(−4), and show σ(−x) = 1 − σ(x).
; ; . For the identity: ; multiply top and bottom by to get .
Code
13. Write a function that checks all seven log rules from the table numerically for random positive a, b and random n, using np.isclose. Run it a thousand times.
import numpy as np
def check_log_rules(trials):
# random-number generator; seed 0 repeats the same numbers
rng = np.random.default_rng(0)
for trial in range(trials):
# a random number between 0.01 and 100
a = rng.uniform(0.01, 100)
b = rng.uniform(0.01, 100)
n = rng.uniform(-3, 3)
assert np.isclose(np.log(a * b), np.log(a) + np.log(b))
assert np.isclose(np.log(a / b), np.log(a) - np.log(b))
assert np.isclose(np.log(a ** n), n * np.log(a))
assert np.isclose(np.log(1.0), 0.0)
assert np.isclose(np.log(np.e), 1.0)
assert np.isclose(np.log(np.exp(n)), n)
assert np.isclose(np.exp(np.log(a)), a)
assert np.isclose(np.log2(a), np.log(a) / np.log(2))
print("all rules hold in", trials, "random trials")
check_log_rules(1000) # all rules hold in 1000 random trials
14. Show underflow: multiply 400 probabilities of 0.1 directly and in log space. At what count does the direct product first become exactly 0.0?
import numpy as np
p = 0.1
direct = 1.0 # the running product
logspace = 0.0 # the running sum of logs
# the first count at which the product hits exactly 0.0
first_zero = None
for k in range(1, 401):
direct = direct * p
logspace = logspace + np.log(p)
if first_zero is None and direct == 0.0:
first_zero = k
print(first_zero)
# prints 324: 0.1 to the 324 is below the smallest positive float (about 4.9e-324)
print(logspace)
# prints -921.0340371976151: still a perfectly ordinary number
print(np.exp(logspace)) # 0.0 again, as soon as you leave log space
15. Write scores_to_percentages(z) with the subtract-the-max trick. On 1,000 random lists of 50 scores between −1000 and 1000, check that no share is negative and that the shares add to 1. On small scores (between −5 and 5), check it agrees with the naive version.
import numpy as np
def scores_to_percentages(z):
z = z - np.max(z) # subtract the biggest score
e = np.exp(z)
return e / np.sum(e)
def naive(z):
e = np.exp(z)
return e / np.sum(e)
rng = np.random.default_rng(0)
for trial in range(1000):
# 50 random scores, some huge
big = rng.uniform(-1000, 1000, size=50)
p = scores_to_percentages(big)
# nothing negative (very small shares round to 0.0)
assert np.min(p) >= 0
assert np.isclose(np.sum(p), 1.0) # shares add up to 1
# scores the naive version can handle
small = rng.uniform(-5, 5, size=50)
# every share agrees
assert np.allclose(scores_to_percentages(small), naive(small))
print("safe version: never breaks, always adds to 1, matches the naive one on small scores")
print(np.sum(naive(big)))
# prints nan: the naive version on the last big scores does not even add to 1
16. Plot −log p for p from 0.001 to 1, and on the same axes plot 1 − p. Mark p = 0.01 on both. Write one sentence about what you see.
import numpy as np
import matplotlib.pyplot as plt
# 500 evenly spaced values from 0.001 to 1
p = np.linspace(0.001, 1, 500)
plt.plot(p, -np.log(p), label="-log p")
plt.plot(p, 1 - p, label="1 - p")
# the two losses at p = 0.01
plt.scatter([0.01, 0.01], [-np.log(0.01), 0.99], color="red")
plt.xlabel("probability given to the correct answer")
plt.ylabel("loss")
plt.legend()
plt.show()
# At p = 0.01 the two losses are 4.6 and 0.99: the log loss keeps punishing
# confident mistakes
# while 1 - p has already stopped caring.
Common mistakes
All sources
Everything linked above, in the order to use it, plus one reference for later. Every np. function used on this page is explained on the NumPy reference.
- Khan Academy, Functions (exercises). Only if function notation is rusty.
- Khan Academy, Exponential models and Logarithms (exercises). The graded problem sets for this note.
- 3Blue1Brown, “What’s so special about Euler’s number e?” (video, 14 min).
- Better Explained, “An intuitive guide to exponential functions and e” and “Demystifying the natural logarithm” (articles).
- Desmos (interactive), for the function zoo.
- Deisenroth, Faisal and Ong, Mathematics for Machine Learning (free PDF). Not needed for this note; it is the reference for the rest of the math track, and its chapter 2 starts where the next note starts.