Every model you will ever train is a function. Before we can talk about learning one, we have to be ruthless about what the word means — and the part almost everyone skips is that a function is not just a formula.
Supervised machine learning has a one-sentence summary: we are searching for a function. We have inputs \(\vx\) (pixels, words, sensor readings), we want outputs \(y\) (a label, a price, the next token), and we believe some function \(f\) maps one to the other. Training is the search. Everything else — gradients, loss functions, backpropagation — is machinery for conducting that search efficiently.
So the object at the centre of the entire field is the function. It is worth two hours of your life to know exactly what one is. And I mean exactly, because the sloppy version — "a function is a formula like \(f(x)=x^2\)" — is the source of a specific class of error that will bite you in probability, in optimisation, and in numerical code.
A function is a machine with a contract. The contract says: give me any object from this set, and I promise to return exactly one object from that set. The contract is part of the machine. A machine that only accepts positive numbers is a different machine from one that accepts all numbers, even if they compute the same arithmetic where both are defined.
We build it from sets. A set is just a collection of distinct objects; \(x \in A\) means "\(x\) is a member of \(A\)". The sets we will use constantly:
Let \(A\) and \(B\) be sets. A function \(f\) from \(A\) to \(B\), written
\[ f : A \to B, \]is a rule that assigns to every element \(a \in A\) exactly one element \(f(a) \in B\).
Two words in that definition are doing all the work, and you should underline them:
The second condition is the one you may already know as the vertical line test: if a vertical line crosses the graph twice, the picture is not the graph of a function. But notice the test is a consequence of the definition, not the definition itself. The definition works for functions between any sets at all, including sets you cannot draw.
Here is the idea that most courses mention once and never return to, and that quietly causes real errors later. Changing the domain changes the function. The two objects
\[f:\RR\to\RR,\quad f(x)=x^2 \qquad\text{and}\qquad g:[0,\infty)\to\RR,\quad g(x)=x^2\]
are not the same function, even though the formula is identical. They have different graphs, different images, and — as we will see — one of them has an inverse and the other does not.
When you are handed a bare formula with no domain stated, the convention is to take the natural domain: the largest subset of \(\RR\) on which every operation in the formula is defined. In practice there are exactly three things to check, and you should check them mechanically, every time:
The third one is the one that catches people, and I am flagging it now because we will prove it properly in Lecture 2. It is tempting to think \(\log_2(-2)\) "should" be \(-1\), by some kind of sign symmetry with \(\log_2(2)=1\). It is not. There is no real number it equals. We will see exactly why by going back to the definition of a logarithm rather than to its rules — and the reason is the single most useful fact about exponentials in all of machine learning.
(a) \(\displaystyle h(x)=\frac{x+3}{x^2-4}\) (b) \(\displaystyle k(x)=\sqrt{5-x}\) (c) \(\displaystyle m(x)=\log(x^2-9)\)
(a) \(x^2-4=(x-2)(x+2)=0\) at \(x=\pm2\). Division check. \(\;\text{dom}(h)=\RR\setminus\{-2,2\}=(-\infty,-2)\cup(-2,2)\cup(2,\infty)\).
(b) Even root check: need \(5-x\ge 0\), i.e. \(x\le 5\). \(\;\text{dom}(k)=(-\infty,5]\). Note the bracket is closed at 5 — \(\sqrt{0}=0\) is perfectly legal.
(c) Logarithm check: need \(x^2-9>0\), i.e. \((x-3)(x+3)>0\). A product of two factors is positive when both are positive (\(x>3\)) or both are negative (\(x<-3\)). \(\;\text{dom}(m)=(-\infty,-3)\cup(3,\infty)\). The interval is open, because \(\log(0)\) is undefined too. If you wrote \(x^2-9\ge 0\) you made exactly the error I warned about above.
The definition demands one arrow out of every domain element. It says nothing about arrows into codomain elements. Classifying what happens on the receiving end gives us three properties that decide, among other things, whether a function can be undone.
Let \(f:A\to B\).
Notice that surjectivity depends on how you declared the codomain, which is a choice. \(f:\RR\to\RR,\;f(x)=x^2\) is not surjective (nothing maps to \(-1\)). But \(f:\RR\to[0,\infty),\;f(x)=x^2\) is surjective, with the same formula. This is the domain lesson again, wearing a different hat: the sets are part of the object.
To "undo" \(f\) means to find \(f^{-1}\) with \(f^{-1}(f(a))=a\) for every \(a\in A\), and \(f(f^{-1}(b))=b\) for every \(b\in B\). Ask when this is possible.
Geometrically, \(f^{-1}\) swaps the roles of input and output, so its graph is the graph of \(f\) reflected across the line \(y=x\). That gives a fast visual test: reflect the curve; if the reflection fails the vertical line test, \(f\) was not injective and has no inverse.
This is not a toy concern. Three examples you will meet within weeks:
Given \(f:A\to B\) and \(g:B\to C\), the composition \(g\circ f: A\to C\) is defined by
\[(g\circ f)(a) = g\big(f(a)\big).\]
Read it right to left: \(f\) fires first. The codomain of \(f\) must match (or sit inside) the domain of \(g\), or the pipeline does not type-check. This "shape matching" requirement is the abstract version of the matrix-dimension errors you will spend a lot of your life debugging.
A feedforward neural network is nothing but a composition: \[ f_{\vt} = f_L \circ f_{L-1} \circ \cdots \circ f_2 \circ f_1, \] where each \(f_\ell(\vz) = \sigma(\mA_\ell \vz + \vb_\ell)\). Depth is composition. And the chain rule, which we derive in Lecture 5, is the statement that you can differentiate a composition by multiplying the derivatives of the pieces. Backpropagation is that statement, applied efficiently.
Now let us make all of this concrete. The code below is live: edit it and press Run. The first run downloads Python and NumPy into your browser, which takes about ten seconds; after that it is instant.
Now the specific trap from the warning above. Run this and read the output carefully.
Read that last output once more. \(2^c > 0\) for every real \(c\) — as \(c\to-\infty\) the value shrinks towards zero but never reaches it, let alone crosses it. So the equation \(2^c=-2\) has no solution, so \(\log_2(-2)\) names nothing. Negative outputs of a log come from inputs strictly between 0 and 1. That is Lecture 2's opening result, and we will derive it properly rather than assert it.
Let me close the loop on why any of this matters. In supervised learning we posit a hypothesis class — a set \(\mathcal{H}\) of candidate functions — and search inside it:
\[\mathcal{H} = \{\, f_{\vt} : \RR^d \to \RR^k \;\mid\; \vt \in \Theta \,\}\]
Every symbol there is a thing you now have language for:
And the domain lesson has one more payoff here. When you normalise your inputs, or clip them, or one-hot encode them, you are changing the domain of your model. That is why a model trained on normalised inputs produces nonsense on raw ones: you handed it an element that was never in \(A\).
Do these with pen and paper before opening the solutions. If you can only do some of them, that is useful information — tell me which ones and I will target the next lecture accordingly.
State the natural domain of each function, in interval notation, and name which of the three checks produced each exclusion.
(a) \(\displaystyle f(x)=\frac{\sqrt{x+2}}{x^2-x-6}\) (b) \(\displaystyle g(x)=\log\!\left(\frac{1}{x-4}\right)\) (c) \(\displaystyle h(x)=\frac{1}{\sqrt{9-x^2}}\)
(a) Root: \(x+2\ge 0 \Rightarrow x\ge-2\). Denominator: \(x^2-x-6=(x-3)(x+2)=0\) at \(x=3,-2\). Combining, \(x\ge -2\) but \(x\neq -2\) and \(x\neq 3\): \(\text{dom}=(-2,3)\cup(3,\infty)\). Note \(x=-2\) is excluded by the denominator check even though the root allowed it — you must apply all checks and intersect.
(b) Log: need \(\frac{1}{x-4}>0\), which holds iff \(x-4>0\), i.e. \(x>4\). (The division check gives \(x\neq 4\), already implied.) \(\text{dom}=(4,\infty)\).
(c) Root and division: need \(9-x^2>0\) — strictly, because it sits in a denominator as well as under a root. \((3-x)(3+x)>0 \Rightarrow -3<x<3\). \(\text{dom}=(-3,3)\), open at both ends.
Let \(f:\RR\to\RR\) be \(f(x)=\dfrac{2x-1}{x+3}\)… except that as written this is not a function \(\RR\to\RR\). Fix the domain, then:
Domain fix: \(x\neq-3\), so \(f:\RR\setminus\{-3\}\to\RR\).
1. Injectivity. Suppose \(\frac{2a-1}{a+3}=\frac{2b-1}{b+3}\) with \(a,b\neq-3\). Cross-multiplying (legal, since both denominators are nonzero):
\[(2a-1)(b+3)=(2b-1)(a+3)\] \[2ab+6a-b-3 = 2ab+6b-a-3\]Cancel \(2ab\) and \(-3\) from both sides: \(6a-b = 6b-a\), so \(7a=7b\), so \(a=b\). Injective. ∎
2. Image. Solve \(y=\frac{2x-1}{x+3}\) for \(x\): \(y(x+3)=2x-1 \Rightarrow yx+3y=2x-1 \Rightarrow x(y-2)=-1-3y \Rightarrow x=\frac{-1-3y}{y-2}=\frac{3y+1}{2-y}\). This has a solution for every \(y\) except \(y=2\). So \(\text{image}(f)=\RR\setminus\{2\}\).
Sanity check on why \(2\) is missed: for large \(|x|\) the ratio \(\frac{2x-1}{x+3}\to 2\), approached but never attained — \(y=2\) is a horizontal asymptote.
3. Inverse. \(f^{-1}(y)=\dfrac{3y+1}{2-y}\), with domain \(\RR\setminus\{2\}\) — which is exactly the image of \(f\), as the theory demanded. Verify: \(f^{-1}(f(0)) = f^{-1}(-1/3) = \frac{-1+1}{2+1/3}=0\) ✓.
Let \(f(x)=x^2\) and \(g(x)=x+1\), both from \(\RR\) to \(\RR\).
1. \((g\circ f)(x)=g(x^2)=x^2+1\). \((f\circ g)(x)=f(x+1)=(x+1)^2=x^2+2x+1\).
2. Set them equal: \(x^2+1 = x^2+2x+1 \Rightarrow 0=2x \Rightarrow x=0\). They agree at exactly one point, \(x=0\), where both give \(1\).
3. The two composites are both upward parabolas of identical shape; they differ by the horizontal shift that \(g\) applies before versus after squaring. \(x^2+1\) is the parabola shifted up by one; \((x+1)^2\) is it shifted left by one. Two congruent parabolas offset from one another meet in at most one point, and here that point is \((0,1)\).
Prove the following, from the definitions, with no pictures:
If \(f:A\to B\) and \(g:B\to C\) are both injective, then \(g\circ f:A\to C\) is injective.
Then find a concrete counterexample showing the converse fails: \(g\circ f\) can be injective while \(g\) is not.
Proof. Take any \(a_1,a_2\in A\) with \((g\circ f)(a_1)=(g\circ f)(a_2)\). By definition of composition this says \(g(f(a_1))=g(f(a_2))\).
Now \(f(a_1)\) and \(f(a_2)\) are both elements of \(B\), and \(g\) is injective, so from \(g(u_1)=g(u_2)\) we may conclude \(u_1=u_2\). Hence \(f(a_1)=f(a_2)\).
But \(f\) is injective too, so \(f(a_1)=f(a_2)\) forces \(a_1=a_2\).
We assumed \((g\circ f)(a_1)=(g\circ f)(a_2)\) and derived \(a_1=a_2\); that is exactly the definition of \(g\circ f\) being injective. ∎
Notice the shape of the argument: peel the outer function off using its injectivity, then the inner one using its injectivity. This peeling pattern recurs constantly.
Counterexample to the converse. Let \(A=\{1\}\), \(B=\{1,2\}\), \(C=\{7\}\). Define \(f(1)=1\) and \(g(1)=g(2)=7\). Then \(g\) is not injective (both elements of \(B\) map to \(7\)), yet \(g\circ f:A\to C\) is injective vacuously — its domain has only one element, so no two distinct inputs can collide.
A real-valued version: \(f:\RR\to\RR,\;f(x)=e^x\) (image \((0,\infty)\)) and \(g:\RR\to\RR,\;g(u)=u^2\). \(g\) is not injective, but \((g\circ f)(x)=e^{2x}\) is. The reason: \(f\) never lands on the part of \(g\)'s domain where the collisions happen. Injectivity of \(g\) only matters on the image of \(f\).
Coding. Write a NumPy function safe_log2(x) that returns
\(\log_2 x\) where \(x>0\) and returns -np.inf where \(x=0\), and raises a
ValueError naming the offending values if any entry is negative.
Then explain in one sentence why returning -inf at zero is defensible while returning any
number at all for a negative input is not.
import numpy as np
def safe_log2(x):
x = np.asarray(x, dtype=float)
bad = x < 0
if bad.any():
raise ValueError(f"log2 undefined for negative inputs: {x[bad]}")
out = np.full_like(x, -np.inf)
pos = x > 0
out[pos] = np.log2(x[pos])
return out
Why the asymmetry. As \(x\to 0^{+}\), \(\log_2 x\) decreases without bound, so \(-\infty\) is the honest limit of the function — it is the value the function is heading towards, and floating point has a symbol for exactly that. A negative input is not a limiting case of anything: the function is not merely large there, it is absent, because no real power of 2 is negative. Returning \(-\infty\) there would be inventing a value.
This distinction matters in practice: cross-entropy loss \(-\sum y\log \hat p\) legitimately blows up to \(+\infty\) when a model assigns probability 0 to the true class, and libraries clip \(\hat p\) into \([\varepsilon, 1-\varepsilon]\) to keep it finite. But a negative \(\hat p\) means your model is broken, and silently taking its log would hide the bug.
Next: we take the logarithm apart from the definition up. You will see precisely why \(\log_2(-2)\) is undefined, why every log rule is a disguised exponent rule, and why machine learning is so addicted to logs that half its loss functions have one in the name.