ML Foundations Lecture 1 of 14
Part I — The Language of Functions · Lecture 1

What a Function Really Is

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.

By the end you can

  • State the formal definition of a function and say why \(\text{domain}\) is part of the object, not an afterthought
  • Compute the domain of an expression and justify each exclusion
  • Decide whether a function is injective, surjective, bijective — and therefore invertible
  • Read \(g \circ f\) as a pipeline, which is exactly what a neural network is
  • Represent a function in NumPy and see what happens when you feed it inputs outside its domain

Assumed

  • Basic algebra: expanding, factoring, solving linear and quadratic equations
  • Comfort reading set notation, or willingness to learn it in the next five minutes

1Why we begin here

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.

Intuition

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.

2The definition

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:

Definition — function

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:

  1. every — the rule must be defined at every single point of \(A\). No exceptions, no gaps. This is called being total.
  2. exactly one — no input is allowed to produce two different outputs. This is called being well-defined or single-valued.

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.

Which of these relations is a function?Interactive
0
Arrows go from the domain set A on the left to the codomain set B on the right. Slide to inspect each relation. Ask two questions every time: does every left dot have an arrow leaving it? Does any left dot have two arrows leaving it?
Check yourself
Consider the rule "\(f(x) = \) a number whose square is \(x\)", proposed as a function \(f:\RR\to\RR\). What is wrong with it? (There is more than one defect — pick the most fundamental one.)
Correct. This is a violation of exactly one — the rule is not well-defined. It is also true that it fails for negative \(x\), but that is a totality problem you could fix by shrinking the domain. The two-valuedness is the deeper defect: it means the rule is not a function at all, it is a relation. The standard repair is to choose a branch: we define \(\sqrt{x}\) to mean the non-negative root, which is a genuine function \(\sqrt{\;\cdot\;}:[0,\infty)\to[0,\infty)\).
Not quite. True, and a real problem — the rule is not total on \(\RR\). But this defect is repairable by declaring the domain to be \([0,\infty)\). The two-valuedness at \(x=4\) is not repairable by shrinking the domain, so it is the more fundamental failure.
Not quite. The notation \(\pm\sqrt{x}\) is shorthand for a set of two numbers, not a single number. A function must return exactly one element of the codomain. \(\pm\sqrt{x}\) is a function into the set of subsets of \(\RR\) — a different and more exotic object.

3The domain is part of the function

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:

  1. Division. A denominator may not be zero. Exclude every root of the denominator.
  2. Even roots. \(\sqrt{u}\), \(\sqrt[4]{u}\), … require \(u \ge 0\) over the reals.
  3. Logarithms. \(\log_b(u)\) requires \(u > 0\), strictly. Not \(\ge 0\) — strictly greater.
Common trap

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.

Natural domain of f(x) = √(x − a) / (x − b)Interactive
f(x)excluded from the domain
-1.0
2.0
The red region is where the formula produces no real number. Notice that the excluded set is not a cosmetic detail — it is genuinely where the function does not exist. Slide b to the left of a and watch the puncture disappear, because it was never in the domain to begin with.
Try it before you read on
Write down the natural domain of each, in interval notation, and state which of the three checks you used for each exclusion:

(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.

4Injective, surjective, bijective

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.

Definition — injective, surjective, bijective

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.

Check yourself
Let \(f:\RR\to\RR\) be \(f(x)=x^3-3x\). Is it injective?
Correct. Correct, and the counterexample is the right kind of proof: to disprove injectivity you only need one collision. Check it: \(f(0)=0-0=0\), and \(f(\sqrt3)=3\sqrt3-3\sqrt3=0\). Also \(f(-\sqrt3)=0\). Three inputs, one output. The cubic wiggles, and any horizontal line through the wiggle is crossed three times.
Not quite. Every odd-degree polynomial is surjective onto \(\RR\) (it runs from \(-\infty\) to \(+\infty\) and is continuous), but it need not be increasing. \(f'(x)=3x^2-3\) is negative on \((-1,1)\), so \(f\) decreases there. \(x^3\) alone is injective; \(x^3-3x\) is not.
Not quite. The vertical line test checks whether the graph is a function at all. Injectivity is tested by the horizontal line test: if any horizontal line meets the graph more than once, two inputs share an output and the function is not injective.

5Inverses, and why they need injectivity

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.

Why an inverse exists exactly when f is bijective0 / 4
\[f^{-1}(b) = \text{the } a \in A \text{ such that } f(a)=b\]
The only possible definition. If \(f^{-1}\) is to undo \(f\), then given an output \(b\), it must return the input that produced it. There is nothing else it could return. So the whole question is whether this phrase describes a legal function.
\[\text{Total: } \forall b \in B,\ \exists a \in A \text{ with } f(a)=b\]
First requirement of a function. \(f^{-1}\) must be defined at every point of its domain, which is \(B\). So every \(b\) needs at least one preimage. That is precisely the statement that \(f\) is surjective.
\[\text{Well-defined: } f(a_1)=f(a_2)=b \Rightarrow a_1 = a_2\]
Second requirement of a function. \(f^{-1}(b)\) must be a single value. If two different inputs both produced \(b\), the phrase \"the \(a\) such that \(f(a)=b\)\" is ambiguous — exactly the defect we saw in the first quiz. Ruling this out is precisely the statement that \(f\) is injective.
\[f \text{ invertible} \iff f \text{ injective and surjective} \iff f \text{ bijective}\]
Conclusion. The two requirements of being a function, applied to \(f^{-1}\), translate one-for-one into injectivity and surjectivity of \(f\). Nothing else was needed. This is a small proof but it is a genuine one: we did not appeal to graphs or intuition at any step.

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.

Reflect f(x) = x² across y = x — restrict the domain until an inverse existsInteractive
f on its restricted domainreflection across y = xy = x
-3.00
4.00
Blue is f restricted to x ≥ d. Green is that curve reflected across the dashed diagonal. While d < 0 the green reflection is a sideways parabola: a vertical line crosses it twice, so the reflection is not a function and f has no inverse. Push d up to 0 and the reflection becomes y = √x.
Where this shows up in ML

This is not a toy concern. Three examples you will meet within weeks:

6Composition: the shape of a neural network

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.

Intuition

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.

Composition as a two-stage pipeline: x → f(x) = ax + b → g(u) = u²Interactive
f(x) = ax + bg(u) = u²(g∘f)(x) = (ax+b)²
1.5
-1.0
1.20
Follow the orange dot up to f, across to the diagonal, and up to g. The blue curve is the composite you get by doing that for every x at once. Try a = 0: the composite collapses to a constant, because f destroys all information about x before g ever sees it. No amount of cleverness in g can recover it — which is the first hint of why information lost in an early layer is lost for good.
Check yourself
With \(f(x)=ax+b\) and \(g(u)=u^2\) as above, is \(g\circ f\) the same function as \(f\circ g\)?
Correct. Right — composition is not commutative. Order matters, always. Check with \(a=1,b=1,x=1\): \((g\circ f)(1)=(1+1)^2=4\), while \((f\circ g)(1)=1\cdot 1+1=2\). This is why the layer order in a network is a real architectural decision, and why \(\mathbf{AB}\neq\mathbf{BA}\) for matrices — matrix multiplication is composition of linear maps, as we will prove in Lecture 9.
Not quite. Test it numerically before believing it. Take \(a=1,b=1,x=1\): \((g\circ f)(1)=4\) but \((f\circ g)(1)=2\). One counterexample is enough. Getting into the habit of testing a claim on a small concrete number is one of the most valuable things you can build in this course.
Not quite. Try \(a=1, b=1\): we just saw \(4\neq 2\). With \(a=1\) the two are \((x+b)^2 = x^2+2bx+b^2\) versus \(x^2+b\), which agree only if \(2bx + b^2 = b\) for all \(x\) — forcing \(b=0\), i.e. both maps are just squaring.

7Functions in NumPy

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.

A function, its domain, and what NumPy does when you violate itPython · NumPy

Now the specific trap from the warning above. Run this and read the output carefully.

The logarithm of a negative numberPython · NumPy
Common trap

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.

8The machine-learning restatement

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\).

9Exercises

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:

  1. Prove \(f\) is injective on that domain, using the \(f(a_1)=f(a_2)\Rightarrow a_1=a_2\) form.
  2. Find the image of \(f\) — that is, which \(y\) are actually achieved.
  3. Write down \(f^{-1}\) and state its domain.

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. Write \(g\circ f\) and \(f\circ g\) explicitly and confirm they differ.
  2. Find every \(x\) at which they happen to agree.
  3. Interpret the answer geometrically.

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.