Math for AI course · Lesson 37 of 180

How Affine Spaces Explain Bias and Decision Boundaries

See how a translation moves a line without changing its direction, and why a neural layer bias can move a classifier boundary away from the origin.

Share this article

A classifier’s weights decide which direction separates two classes. Its bias lets the separating line sit somewhere other than the origin. That small addition is a geometric translation: it changes a linear boundary into an affine one while keeping the boundary’s direction fixed.

By the end, you will be able to distinguish a translated subspace from a subspace through the origin, test whether a mapping is linear or affine, and calculate how a bias changes a simple classifier’s boundary.

Learning outcome: Distinguish linear and affine sets and transformations.

This builds on Lesson 034, which showed that linear mappings preserve vector sums and scaling. Section 2.8 of Mathematics for Machine Learning extends that structure with offsets. The examples, classifier calculation, visual, and assessments here are original.

A line can keep its direction and move away from zero

A vector subspace must contain the zero vector. For example, all multiples of d=(1,2)d=(1,2) form a line through the origin:

U={t(1,2):t∈R}.U = \{t(1,2):t\in\mathbb{R}\}.

Choose a point p=(0,−1)p=(0,-1) and add it to every vector in UU. The resulting set is a parallel line that no longer passes through the origin:

L=p+U={p+u:u∈U}={(0,−1)+t(1,2):t∈R}.L = p+U = \{p+u:u\in U\} = \{(0,-1)+t(1,2):t\in\mathbb{R}\}.

For this line, the parametric coordinates are (x1,x2)=(t,2t−1)(x_1,x_2)=(t,2t-1). Eliminating tt gives 2x1−x2=12x_1-x_2=1. The direction vector is unchanged from UU, but every point on LL has moved by pp. In particular, 0∉L0\notin L: making the first coordinate zero forces t=0t=0, which leaves the second coordinate at −1-1. When the chosen point pp is in UU, the translate p+Up+U equals UU; that is the special case where the affine subspace is also a vector subspace.

The same direction, with and without an offsetThe bias changes the boundary from 2x₁ − x₂ = 0 to 2x₁ − x₂ = 1. The lines stay parallel, and the translated boundary no longer contains the origin.
x₁x₂originp = (0, −1)offsetnormal w = (2, −1)
  • No bias: 2x₁ − x₂ = 0
  • Bias −1: 2x₁ − x₂ = 1

The two lines in the figure have the same direction vector (1,2)(1,2), since it is perpendicular to their shared normal vector (2,−1)(2,-1). The dashed line is a translation of the solid one. Its position changes, but its orientation does not. The labels and equations give the same information without relying on the colors.

Affine combinations preserve points in an offset set

A useful way to recognize affine geometry is to combine points with coefficients that sum to one. If x=p+ux=p+u and y=p+vy=p+v are in LL, then for any real number λ\lambda,

λx+(1−λ)y=λ(p+u)+(1−λ)(p+v)=p+(λu+(1−λ)v)∈p+U.\lambda x+(1-\lambda)y = \lambda(p+u)+(1-\lambda)(p+v) = p+\bigl(\lambda u+(1-\lambda)v\bigr) \in p+U.

The offset pp remains because λ+(1−λ)=1\lambda+(1-\lambda)=1, and the part in parentheses remains in UU because subspaces are closed under linear combinations. This is why points on a line stay on that line under interpolation, including extrapolation when λ\lambda is outside [0,1][0,1].

Ordinary sums have a different coefficient total. For x,y∈Lx,y\in L, x+y=2p+(u+v)x+y=2p+(u+v), which need not be in p+Up+U. With the example line, x=px=p and y=py=p give x+y=(0,−2)x+y=(0,-2), which fails the line equation 2x1−x2=12x_1-x_2=1. So an affine subspace keeps affine combinations, while a vector subspace keeps all linear combinations and includes zero.

A chosen anchor is not unique. If q∈Uq\in U, then p+U=(p+q)+Up+U=(p+q)+U because adding an element of UU only changes which point is used as the anchor. The direction space is the invariant part; the support point is one convenient representative.

A bias moves a classifier boundary

An affine mapping combines a linear map with a fixed translation:

For any two inputs, the translation cancels from their difference:

F(x)−F(y)=(Ax+b)−(Ay+b)=A(x−y).F(x)-F(y)=(Ax+b)-(Ay+b)=A(x-y).

And for coefficients that add to one, the output respects their affine combination:

F(λx+(1−λ)y)=λF(x)+(1−λ)F(y).F\bigl(\lambda x+(1-\lambda)y\bigr) = \lambda F(x)+(1-\lambda)F(y).

This differs from the linear-map rule for arbitrary sums. In particular, F(0)=bF(0)=b, so a nonzero bias moves the zero input away from the zero output.

Let x=(x1,x2)∈R2x=(x_1,x_2)\in\mathbb{R}^2 be a two-feature input. Its score is

s(x)=2x1−x2−1.s(x)=2x_1-x_2-1.

It assigns the positive class when s(x)≥0s(x)\ge 0 and the negative class when s(x)<0s(x)<0. The decision boundary is where the score is zero:

2x1−x2−1=0⟺2x1−x2=1.2x_1-x_2-1=0 \quad\Longleftrightarrow\quad 2x_1-x_2=1.

The weight vector w=(2,−1)w=(2,-1) sets the boundary’s normal, while the bias b=−1b=-1 sets the score level at wTx=1w^\mathsf{T}x=1. In two dimensions the boundary is a line; in higher-dimensional feature spaces, the corresponding flat set is called a hyperplane. The point p=(0,−1)p=(0,-1) is on it because 2(0)−(−1)−1=02(0)-(-1)-1=0. Adding the direction $d=(1,2) does not change the score:

Without the bias, the boundary would be 2x1−x2=02x_1-x_2=0 and would contain the origin. Adding bb changes its intercept, not its normal vector. More broadly, a classifier score wTx+bw^\mathsf{T}x+b uses ww to orient a hyperplane and bb to set its offset. PyTorch’s torch.nn.Linear documents this affine form as y=xAT+by=xA^\mathsf{T}+b and enables its learnable bias by default (torch.nn.Linear documentation). With one output, the layer can produce a score of this form; a following rule such as thresholding that score assigns the class.

The bias moves a boundary parallel to itself only while the weights stay fixed. If training changes ww, it changes the orientation too. A bias also cannot make one affine boundary represent every possible classification pattern; it only adjusts the offset of this particular linear score.

Verify the geometry with NumPy

The example can be checked directly. The code computes the scores at the support point and two points reached by moving along the direction:

import numpy as np

w = np.array([2.0, -1.0])
b = -1.0
p = np.array([0.0, -1.0])
d = np.array([1.0, 2.0])
score = lambda x: w @ x + b

print(score(p))          # 0.0: p is on the boundary
print(score(p + d))      # 0.0: moving along d stays on it
print(score(p + 3 * d))  # 0.0: extrapolating along d also stays on it
print(score(np.zeros(2)))  # -1.0: the origin is off the boundary

The calculations use column-vector geometry, while common tensor batches store examples in rows. PyTorch writes its batched linear layer as xAT+bxA^\mathsf{T}+b; the transpose accounts for that row-oriented convention. With its default bias, the operation is affine. Setting bias=False makes the layer’s mapping linear, though a later nonlinear activation is a separate operation.

Check your understanding

Question 1

For U = span((1, 2)) and p = (0, −1), write p + U parametrically and state whether it contains the origin.

Show the step-by-step solution

Every point has the form p+tup+tu for some t∈Rt\in\mathbb{R} and direction vector u=(1,2)u=(1,2). Therefore (x1,x2)=(0,−1)+t(1,2)=(t,2t−1)(x_1,x_2)=(0,-1)+t(1,2)=(t,2t-1). To reach the origin, the first coordinate requires t=0t=0, but then the second coordinate is −1-1. Thus the set does not contain the origin and is not a vector subspace.

Question 2

A line is given by (x₁, x₂) = (3, 1) + t(2, −1). Give one anchor and its direction space. Is the anchor unique?

Show the step-by-step solution

The displayed anchor is p=(3,1)p=(3,1) and the direction space is U=span⁡{(2,−1)}U=\operatorname{span}\{(2,-1)\}. If qq is any vector in UU, then (p+q)+U=p+U(p+q)+U=p+U, so another point on the line can serve as an anchor. The direction space does not change.

Question 3

Show that an affine subspace is closed under combinations λx + (1 − λ)y, even when λ is outside 0 to 1.

Show the step-by-step solution

Write x=p+ux=p+u and y=p+vy=p+v with u,v∈Uu,v\in U. Then λx+(1−λ)y=(λ+1−λ)p+λu+(1−λ)v\lambda x+(1-\lambda)y=(\lambda+1-\lambda)p+\lambda u+(1-\lambda)v. The coefficients of pp sum to one, and the remaining vector is in UU by closure under linear combinations. This holds for every real λ\lambda; values outside [0,1][0,1] extrapolate beyond the segment but stay on the same affine subspace.

Question 4

For the affine line L = (0, −1) + span((1, 2)), test whether (0, −2), the sum of its anchor with itself, lies on L.

Show the step-by-step solution

A point (x1,x2)(x_1,x_2) belongs to this line exactly when 2x1−x2=12x_1-x_2=1. Substituting (0,−2)(0,-2) gives 2(0)−(−2)=22(0)-(-2)=2, not 11. Therefore the ordinary sum lies outside LL. The two copies have coefficient total 22, so their affine anchor offset is doubled rather than retained once.

Question 5

For F(x) = Ax + b, calculate F(0) and explain the condition for F to be a linear mapping.

Show the step-by-step solution

Substitute the zero vector: F(0)=A0+b=bF(0)=A0+b=b. Every linear mapping must map zero to zero, so if b≠0b\ne0, FF is not linear. When b=0b=0, the rule becomes F(x)=AxF(x)=Ax, which preserves sums and scalar multiples and is linear.

Question 6

For s(x) = 2x₁ − x₂ − 1, verify that p = (0, −1) is on the boundary and that d = (1, 2) follows the boundary direction.

Show the step-by-step solution

At pp, s(p)=2(0)−(−1)−1=0s(p)=2(0)-(-1)-1=0, so it lies on the zero-score boundary. The change in score when adding dd is 2(1)−1(2)=02(1)-1(2)=0, because the bias is constant and the weight dot direction is zero. Thus p+d=(1,1)p+d=(1,1) remains on the boundary.

Question 7

If a classifier score is wᵀx + b, what does changing b do when w is held fixed? What changes if w also changes during training?

Show the step-by-step solution

The boundary satisfies wTx=−bw^\mathsf{T}x=-b. Holding ww fixed keeps the normal vector and orientation fixed; changing bb changes the level set, translating the boundary. If ww changes, its normal changes too, so the boundary can rotate as well as move. The bias alone does not control orientation.

Question 8

PyTorch constructs nn.Linear(4, 2) with its default bias option. What is the resulting mapping, and what change would make that layer linear?

Show the step-by-step solution

For a row-oriented input xx with four features, the layer returns y=xAT+by=xA^\mathsf{T}+b with two output features. Its default bias=True adds the learned vector bb, so it is affine. Constructing it with bias=False removes that translation and leaves y=xATy=xA^\mathsf{T}, a linear mapping.

Sources

  1. Mathematics for Machine Learning book PDF, Section 2.8
  2. PyTorch documentation: torch.nn.Linear