Majid Al-RaimiActivation functions and the transformer

COE 592Lecture 02Part 06

Activation functions and the transformer

The non-linearities used in embedded networks, then the transformer block: scaled dot-product attention worked by hand and the position-wise feed-forward network.

Concepts
6
Slides
39-45
Reading
36 min
Understood
0/6 concepts

Why this part matters

Every model you will compress, quantize or deploy on a microcontroller in this course has an activation function between its layers, and that one choice decides whether int8 inference is exact and cheap or approximate and slow. MobileNetV2 and V3 picked ReLU6 and hard swish for exactly this reason.

The second half of the part opens the transformer, now the model family you are most likely to be asked to shrink for the edge. Its attention block has a cost that grows with the square of the sequence length, which is the first number an embedded designer must reason about. The exam asks for the attention formula with shapes, the sqrt(d_k) argument, and a small attention computed by hand, so the part ends with two worked slides checked line by line, including two arithmetic slips on the slides themselves.

By the end you can

  1. Explain why activations must be non-linear, and write sigmoid, ReLU, ReLU6, leaky ReLU, swish and hard swish with their ranges and derivatives.
  2. Argue which activations suit quantized embedded models and why: a bounded range and no exponential.
  3. Draw the transformer block and place multi-head attention, Add and Norm, the feed-forward network and positional encoding.
  4. State Attention(Q, K, V) with every matrix shape, explain the query, key and value roles, and justify the sqrt(d_k) scaling.
  5. Compute a small self-attention and a position-wise FFN by hand, and explain why the N x N attention matrix is the embedded bottleneck.

Start with the Neuron from the beginning of the lecture: three inputs, three weights, a Bias, and then y = f(w0 x0 + w1 x1 + w2 x2 + b). Now ask what happens if f is the identity and you stack two such layers. The second layer computes W2 (W1 x + b1) + b2, which multiplies out to (W2 W1) x + (W2 b1 + b2): one matrix, one bias, one linear layer. Ten layers would collapse the same way. Depth buys nothing.

Worked example

Two linear layers collapse into one

  1. Write the second layer in terms of the first

    y = W2 h + b2 with h = W1 x + b1, so y = W2 W1 x + W2 b1 + b2.
  2. Name the products

    Set W = W2 W1 and b = W2 b1 + b2. Both are constants once training stops.
  3. A single fully connected layer

    y = W x + b. The stacked network can only draw straight decision boundaries, exactly like the one layer it collapsed into. Goodfellow, Bengio and Courville open their chapter on feedforward networks with XOR for this reason: no single line separates its four points, but a hidden layer of rectified units does.

That is why the slide says activation functions are typically non-linear. The Activation function is the bend that stops the collapse, and it sits after every weighted sum in every Hidden layer. Slide 39 places it on the CNN component map beside convolution, pooling, fully connected layers and the normalization block of the previous part: the only component with no parameters and no shape arithmetic, just a pointwise bend applied to every value. Which bend to use is a real design decision, and on embedded hardware it is decided by two questions the slide leaves implicit: does the function need an exponential, and is its output bounded?

The six on the slide, and what each costs a microcontroller

Sigmoid, 1 / (1 + e^-x), squashes any input into (0, 1). Its derivative is s(1 - s), at most 0.25 at zero and already 0.0066 at x = 5. A sigmoid unit therefore saturates across most of its domain, which is why Goodfellow and colleagues say its use as a hidden unit is now discouraged. It also needs an exponential per element. ReLU, max(0, x), has derivative 0 or 1, needs a single comparison, and is the same textbook's recommended default; AlexNet is the network that made it standard.

ReLU6, min(max(0, x), 6), is ReLU with a ceiling. Krizhevsky introduced the cap in 2010 to encourage sparse features; MobileNetV2 kept it for a different reason, robustness when used with low-precision computation. Leaky ReLU, max(alpha x, x), replaces the flat negative side with a small slope so a unit that is off still passes a gradient; Maas, Hannun and Ng used alpha = 0.01, which is still PyTorch's default. Swish, x / (1 + e^-x), is x times its own sigmoid (the general form is x sigmoid(beta x), PyTorch calls it SiLU). It dips to -0.278 at x = -1.28 before rising, and Ramachandran, Zoph and Le found it beats ReLU on deeper models. It costs an exponential, which is the whole problem on a Cortex-M.

Hard swish is MobileNetV3's answer: x ReLU6(x + 3) / 6. Expand it and you get the slide's three cases, 0 for x <= -3, x for x >= 3, and x(x + 3) / 6 between. Howard and colleagues give three reasons: optimized ReLU6 kernels exist on virtually every framework and chip, the piecewise form removes the precision loss that different approximate sigmoids introduce in quantized mode, and it can run with fewer memory accesses. They saw no discernible accuracy difference against real swish, and an optimized h-swish added about 1 ms over ReLU on a Pixel 1.

h-swish(x)=xReLU6(x+3)6={0x3xx3x(x+3)6otherwise\text{h-swish}(x) = x \cdot \frac{\operatorname{ReLU6}(x + 3)}{6} = \begin{cases} 0 & x \le -3 \\ x & x \ge 3 \\ \dfrac{x(x + 3)}{6} & \text{otherwise} \end{cases}
One clamp, one add, one multiply, no exponential
Swish (faint) and hard swish drawn over it: flat until -3, a parabola to 3, then the identity line
xswishhard swishgap
-3-0.14200.142
-1-0.269-0.3330.064
0000
10.7310.6670.064
32.85830.142
43.92840.072
Swish against hard swish at a few inputs

The largest gap on [-6, 6] is 0.142, at the two knots x = -3 and x = 3. The derivative also lines up: swish has slope 0.5 at zero and hard swish has (2x + 3) / 6 = 0.5 there too.

FunctionFormulaRangeDerivativeNeedsEmbedded note
Sigmoid1 / (1 + e^-x)(0, 1)s(1 - s), at most 0.25expSaturates both sides; output gates and probabilities only
ReLUmax(0, x)[0, inf)0 or 1max onlyDefault hidden unit; unbounded, so the int8 range must be calibrated
ReLU6min(max(0, x), 6)[0, 6]1 on (0, 6), else 0max and minBounded: fixed quantization grid; MobileNetV2's choice
Leaky ReLUmax(alpha x, x)(-inf, inf)alpha or 1max onlyKeeps a small gradient for negative inputs, alpha = 0.01 by default
Swishx / (1 + e^-x)[-0.278, inf)s + x s(1 - s)expSmooth and non-monotonic; better than ReLU on deep models, costly on MCUs
Hard swishx ReLU6(x + 3) / 6[-0.375, inf)0, (2x + 3)/6, or 1multiply and clampSwish traced with a ruler; MobileNetV3's replacement for swish
tanh2 s(2x) - 1(-1, 1)1 - tanh^2expZero-centred sigmoid; still saturates
GELUx Phi(x)[-0.17, inf)Phi(x) + x phi(x)erf or tanhDefault in BERT and GPT style transformers
ELUx if x > 0 else e^x - 1(-1, inf)1 or e^xexpNegative saturation at -1 pushes the mean toward zero
Mishx tanh(softplus(x))[-0.31, inf)numeric in practiceexp, log, tanhSmooth like swish, even more expensive
Activation functions: formula, range, derivative and MCU cost

Why bounded is what int8 wants

Quantizing an Activation tensor to 8 bits means choosing a range and dividing it into 255 steps. With ReLU6 the range is known before you see a single input: [0, 6], so each step is 6 / 255 = 0.0235 and every layer shares the same grid. With plain ReLU the range depends on the data. If one activation channel happens to reach 60, the step becomes 60 / 255 = 0.235, ten times coarser, for every value in that tensor, including the many small ones. MobileNetV2's sentence about low-precision robustness is the citation; the arithmetic is the reason.

ReLU rises out of the picture; a ceiling at 6 bends it flat and hands the quantizer a fixed grid of 255 steps

Worked example

uint8 grid for ReLU6 versus an unbounded ReLU

  1. Bounded

    Range [0, 6], 255 steps, step size 6 / 255 = 0.0235. Fixed at design time.
  2. Unbounded, calibrated on data

    Observed range [0, 60], step size 60 / 255 = 0.235. One outlier channel sets the grid for everyone.
  3. Result

    Same 8 bits, ten times the rounding error per value. A bounded activation removes the calibration risk entirely.
SimulatorActivation plotter with derivative and MCU cost
-20246-6-4-20246
Swish: y = x / (1 + e^-x)Hard swish: y = x ReLU6(x + 3) / 6
1.0
f(1.0)0.731
derivative0.928slope the gradient sees
gap to Hard swish0.064largest gap on the plot 0.142
cost on an MCU
needs an exponential
bounded
no, range [-0.278, inf)

Quick check

Which activation removes the exponential so a quantized microcontroller can run it cheaply?

Recall

Which activation would you pick for an int8 MCU model and why, and what would you use instead of swish?

ReLU6, because its output is bounded in [0, 6] so the quantizer has a fixed grid (MobileNetV2 chose it for low-precision robustness). Instead of swish, hard swish, because it replaces the sigmoid with ReLU6(x + 3) / 6: no exponential, piecewise polynomial.

Feed a four-word sentence into the model of Vaswani and colleagues. Each word becomes an embedding of d_model = 512 numbers, a positional encoding of the same size is added to it, and the four vectors enter the first encoder layer. That layer does exactly two things: a Multi-head attention step that lets each word read the others, then a position-wise feed-forward network that processes each word on its own. Around each of the two sits an Add and Norm. Repeat the layer N = 6 times and you have the encoder of the original Transformer.

Embedding + PE
d_model = 512

Token vector plus a sinusoidal position vector.

Multi-head attention
h = 8 heads

Every token reads every other token.

Add and Norm
residual + LayerNorm

LayerNorm(x + Sublayer(x)).

Feed-forward
512 to 2048 to 512

Same two-layer MLP on each position.

Add and Norm, then x N
N = 6

Output feeds the next identical layer.

One encoder layer: two sub-layers, each wrapped in a residual add and a LayerNorm, stacked N times

The Add and Norm box is the link back to the normalization part. Each sub-layer's output is added to its own input (a residual connection, the ResNet idea) and the sum goes through Layer normalization: LayerNorm(x + Sublayer(x)). LayerNorm rather than BatchNorm because it normalizes each token's 512 features on their own, so it behaves identically at training and inference and needs no batch statistics, which is what you want for variable-length sequences. Jurafsky and Martin describe the whole stack as a residual stream that each component reads from and adds back to.

A residual stream runs left to right; attention and the FFN branch off, compute, and add their result back before a LayerNorm

Base model hyperparameters (Vaswani et al. 2017)

Layers per stack N
6
Model width d_model
512
Heads h
8
Head size d_k = d_v
512 / 8 = 64
FFN hidden width d_ff
2048

Inside the two sub-layers

Multi-head attention runs h = 8 copies of Scaled dot-product attention in parallel. Each head first projects the queries, keys and values with its own small linear layers down to d_k = 64, computes attention, and the eight 64-wide results are concatenated back to 512 and passed through one more linear layer. Because each head works at one eighth of the width, the total cost is about that of a single head at full width, and each head is free to attend to a different relationship.

MultiHead(Q,K,V)=Concat(head1,,headh)WO,headi=Attention(QWiQ,KWiK,VWiV)\text{MultiHead}(Q, K, V) = \text{Concat}(\text{head}_1, \dots, \text{head}_h)\, W^O, \qquad \text{head}_i = \text{Attention}(Q W_i^Q, K W_i^K, V W_i^V)
h parallel attentions on projected inputs, concatenated, then one linear layer

The feed-forward sub-layer is a two-layer MLP with a ReLU between, widening from 512 to 2048 and back. It is applied to each position separately and identically, which the paper notes is the same as two convolutions with kernel size 1. This is where the block's non-linearity lives: once the attention weights are fixed, attention itself is a linear mix of the values.

FFN(x)=max(0,  xW1+b1)W2+b2\text{FFN}(x) = \max(0,\; x W_1 + b_1)\, W_2 + b_2
Linear, ReLU, linear, the same weights at every position

Position, the decoder, and the output

Attention has no notion of order: swap two tokens and the same weights come out swapped. So before the first layer, a positional encoding is added to each embedding. The paper uses sinusoids of different wavelengths, PE(pos, 2i) = sin(pos / 10000^(2i / d_model)) and cosine for odd indices, so that every position gets a distinct 512-vector and relative offsets are easy to express.

The decoder stack (right side of the slide) repeats the same layer with two changes. Its self-attention is masked so that position i cannot see positions after i, which together with the outputs being shifted right by one keeps training honest about what is known when. And a third sub-layer, encoder-decoder attention, takes queries from the decoder and keys and values from the encoder output. A final linear layer and softmax turn the last vector into next-token probabilities.

Recall

What does the position-wise FFN do that attention does not, and vice versa?

The FFN applies the same two-layer MLP with ReLU to each token independently and never mixes positions. Attention is the only sub-layer that moves information between positions, and once its weights are fixed it is a linear mix of the values.

Type a phrase into YouTube's search bar. The phrase is your query. Every video carries a title and description, which is its key. The video itself is the value. Search scores your query against every key, ranks the matches, and hands you back videos. Attention does the same three things with vectors, with one twist: instead of returning the single best video it returns a blend of all the values, weighted by how well each key matched. That is the Query, key, value design of the slide.

In self-attention all three come from the same input matrix X of N tokens, each through its own learned projection: Q = X W_Q, K = X W_K, V = X W_V. Jurafsky and Martin give the roles precisely: the query is the current element being compared, a key is a preceding input being compared to it to produce a similarity weight, and a value is what gets weighted and summed. The comparison is a dot product, so the whole score table is one matrix multiply, and the paper names the result Scaled dot-product attention.

Attention(Q,K,V)=softmax ⁣(QKTdk)V\text{Attention}(Q, K, V) = \text{softmax}\!\left(\frac{Q K^{T}}{\sqrt{d_k}}\right) V
Scaled dot-product attention

Shapes for N tokens with head size d_k and value size d_v

Q, K
N x d_k
V
N x d_v
Q K^T (scores) and softmax output
N x N
Output
N x d_v

Read the slide's diagram bottom to top with those shapes: Q and K of shape N x d multiply into an N x N table of scores, the optional mask sets forbidden entries to minus infinity, softmax turns each row into a probability distribution, and multiplying by V (N x d) gives one output row per token. The masked version is what the decoder uses so that a token cannot attend to what comes after it.

Why divide by the square root of d_k

If the components of a query and a key have mean zero and variance one, their dot product has variance d_k: it is a sum of d_k products. So raw scores grow with the head size, and Vaswani and colleagues observe that large dot products push the softmax into regions where it has extremely small gradients. Goodfellow and colleagues say the same about softmax in general: its outputs saturate when the differences between inputs become extreme. Dividing by sqrt(d_k) brings the variance back to one, whatever the head size.

d_kUnscaled scoressoftmax unscaledsoftmax after / sqrt(d_k)
4(2, 0, -2)(0.867, 0.117, 0.016)(0.665, 0.245, 0.090)
64(8, 0, -8)(1.000, 0.000, 0.000)(0.665, 0.245, 0.090)
512(22.6, 0, -22.6)(1.000, 0.000, 0.000)(0.665, 0.245, 0.090)
Three scores that are one standard deviation apart, before and after scaling

Without the division, a head of size 64 already turns a modest spread into a one-hot answer of (1.000, 0.000, 0.000), and the gradient through those zeros is essentially zero. With it, every head size sees the same healthy distribution. Softmax is also shift-invariant, softmax(z) = softmax(z + c), so only differences between scores matter, which is what implementations exploit for numerical stability.

The N x N matrix is the embedded constraint

Every query is scored against every key, so one head at one layer produces N^2 scores, whatever the feature size. Jurafsky and Martin state it plainly: attention is quadratic in the length of the input. Vaswani's complexity table writes the per-layer cost as O(n^2 d). Double the sequence and you quadruple the score matrix; multiply by heads and layers and it is the dominant activation memory of the model.

A 4 x 4 score grid grows to 8 x 8 and 16 x 16: four times the tokens, sixteen times the scores
Tokens NEntries N^2Memory
162561 KB
644,09616 KB
25665,536256 KB
1,0241,048,5764 MB
4,09616,777,21664 MB
Scores per head per layer in fp32

Quick check

What is the shape of the softmax output in single-head attention over N tokens with head size d_k?

Quick check

Why does Vaswani et al. divide Q K^T by the square root of d_k?

Recall

Write the attention formula and give the shape of every matrix for N tokens and head size d_k.

Attention(Q, K, V) = softmax(Q K^T / sqrt(d_k)) V. Q and K are N x d_k, V is N x d_v, Q K^T and its softmax are N x N, and the output is N x d_v.

Recall

Where does the N^2 come from and why does it matter on embedded hardware?

Every query is scored against every key, so N tokens give N x N scores per head per layer. Memory and compute grow quadratically with sequence length (Vaswani Table 1: O(n^2 d)), so on a small SRAM the score matrix caps the usable sequence length.

Tom Yeh's drawing makes Scaled dot-product attention concrete with numbers you can check at a desk. Four tokens, each with six features, are the columns x1 = (2, 0, 0, 0, 2, 1), x2 = (0, 1, 2, 0, 0, 0), x3 = (0, 0, 1, 1, 0, 1) and x4 = (2, 0, 0, 1, 0, 1). Three 3 x 6 matrices W_Q, W_K and W_V project them to the queries, keys and values of head size d_k = 3. Those 54 numbers are the only learned parameters; everything else is computed from the input.

Two shortcuts keep the arithmetic in your head. Dividing by sqrt(3) = 1.732 is replaced by dividing by 2 and dropping the fraction (rounding toward zero, so -1 / 2 becomes 0), and e^x is replaced by 3^x, which has the same shape and integer values for non-negative exponents. The shortcuts preserve the ranking for three of the four queries; the comparison table below shows the one query where halving squashes the gap away.

Worked example

Column 1: what token 1 attends to

  1. Project

    q1 = W_Q x1 = (2, 0, 3). The keys are k1 = (0, 0, 1), k2 = (2, 1, 0), k3 = (1, 0, -1), k4 = (0, 0, 1), and the values v1 = (20, 0, 0), v2 = (0, 0, 10), v3 = (0, 10, 0), v4 = (20, 10, 0).
  2. Score

    Dot products of q1 with each key: 3, 4, -1, 3. Key 2 is the best match.
  3. Scale

    Divide by 2 and truncate: 1, 2, 0, 1. (Exact: divide by 1.732, giving 1.73, 2.31, -0.58, 1.73.)
  4. Softmax with powers of three

    3^1, 3^2, 3^0, 3^1 = 3, 9, 1, 3, sum 16, weights 0.19, 0.56, 0.06, 0.19, drawn as .2, .6, 0, .2.
  5. Weighted sum of values

    z1 = 0.2 v1 + 0.6 v2 + 0 v3 + 0.2 v4 = (4, 0, 0) + (0, 0, 6) + (4, 2, 0).
  6. z1 = (8, 2, 6)

    Token 1's new representation is mostly value 2 (weight 0.6), with a fifth each of values 1 and 4. The exact computation gives (10.3, 2.8, 4.6) with the same ranking.
wij=3trunc(sij/2)j3trunc(sij/2)    esij/3jesij/3w_{ij} = \frac{3^{\operatorname{trunc}(s_{ij} / 2)}}{\sum_{j'} 3^{\operatorname{trunc}(s_{ij'} / 2)}} \;\approx\; \frac{e^{s_{ij} / \sqrt{3}}}{\sum_{j'} e^{s_{ij'} / \sqrt{3}}}
The by-hand softmax: base 3 instead of e, halving and rounding toward zero instead of dividing by sqrt(3)
SimulatorScaled dot-product attention on four tokens, one step at a time
Q (rows q1 to q4)
d1d2d3
q1
q2
q3
q4
K (rows k1 to k4)
d1d2d3
k1
k2
k3
k4
V (rows v1 to v4)
d1d2d3
v1
v2
v3
v4
paper view: Q K^T, rows are queries
k1k2k3k4
q10.200.600.100.20
q20.300.300.100.30
q30.400.100.000.40
q40.100.800.100.10
query q1 against every key
q . ktrunc(s / 2)3^sweight
k1313.000.200
k2429.000.600
k3-101.000.100
k4313.000.200

Sum of the exponentials 16.00. Each weight is its power divided by this sum, rounded to one decimal as on the slide.

z1 = sum of weight times value

0.20 (20, 0, 0) + 0.60 (0, 0, 10) + 0.10 (0, 10, 0) + 0.20 (20, 10, 0)

(8.0, 3.0, 6.0)

Head size d_k = 3, N = 4 tokens, so the score matrix has 16 entries whatever the feature size. The highlighted row is the selected query; the teal entries are the keys that win (all of them when tied). Switch between the two arithmetics: the weights move and the winner rarely does. Try q2 to see the one default row where halving erases the gap.

4
scores per head16= N^2
fp32 memory64 Bone head, one layer, scores only
QueryScores q . kSlide weightsExact weightsSlide zExact z
q1 = (2, 0, 3)3, 4, -1, 3.2, .6, 0, .2.258, .459, .026, .258(8, 2, 6)(10.3, 2.8, 4.6)
q2 = (1, 1, 2)2, 3, -1, 2.3, .3, .1, .3.253, .450, .045, .253(12, 4, 3)(10.1, 3.0, 4.5)
q3 = (0, 1, 2)2, 1, -2, 2.4, .2, 0, .4.376, .211, .037, .376(16, 4, 2)(15.0, 4.1, 2.1)
q4 = (2, 1, 1)1, 5, 1, 1.1, .7, .1, .1.077, .770, .077, .077(4, 2, 7)(3.1, 1.5, 7.7)
Slide arithmetic versus exact, all four queries (keys in order k1 to k4, q2 corrected)

For q1, q3 and q4 the same key wins under both arithmetics, and the second-best key is the same too: softmax cares about differences between scores, and a monotone squashing of those differences preserves the order as long as the differences survive. The numbers drift (a weight of 0.6 becomes 0.46), the decision does not. The corrected q2 is the exception that shows how coarse the shortcut is: its scores 2, 3, -1, 2 halve to 1, 1, 0, 1, so the by-hand softmax hands keys 1, 2 and 4 the same 0.3 while the exact softmax still separates key 2 at 0.45. Integer halving erased a difference of one, which is exactly the kind of information a real quantizer must be careful not to lose.

Quick check

On slide 43 with query q1 = (2, 0, 3), which key receives the largest attention weight?

Recall

With q = (2, 0, 3) and keys (0, 0, 1), (2, 1, 0), (1, 0, -1), (0, 0, 1), which key wins and what are the raw scores?

Scores 3, 4, -1, 3. Key 2 wins; keys 1 and 4 tie for second, key 3 is the only negative match.

The second by-hand slide picks up where the first stopped. Five tokens with three features arrive from the previous block, x1 = (5, 0, 1), x2 = (6, 2, 0), x3 = (0, 4, 1), x4 = (7, 0, 1), x5 = (0, 3, 0). The attention weight matrix A is given rather than computed, and it is deliberately simple: each column has exactly two ones, so each token attends equally to itself and to one other token (the next neighbour for tokens 1 to 4, and token 1 for token 5). The attention-weighted feature of token 1 is then a plain sum, z1 = x1 + x2 = (11, 2, 1), and likewise z2 = x2 + x3, z3 = x3 + x4, z4 = x4 + x5 and z5 = x1 + x5.

Now the position-wise feed-forward network takes each z_j on its own. The first Fully connected layer has weights W1 of shape 4 x 3 with rows (1, -1, 0), (1, 1, 0), (0, 1, 1), (-1, 1, 1) and biases (1, 0, 1, 0). A ReLU follows. The second layer has W2 of shape 3 x 4 with rows (1, 0, 0, -1), (0, 1, 1, 0), (0, 0, 1, -1) and biases (0, 0, 1). Widen from three to four, bend, narrow back to three: the same expand-and-contract shape as the paper's 512 to 2048 to 512.

Worked example

Token 1 through the FFN

  1. First layer

    W1 z1 + b1 with z1 = (11, 2, 1): row 1 gives 11 - 2 + 0 + 1 = 10, row 2 11 + 2 + 0 = 13, row 3 0 + 2 + 1 + 1 = 4, row 4 -11 + 2 + 1 + 0 = -8. Hidden vector (10, 13, 4, -8).
  2. ReLU

    max(0, h) = (10, 13, 4, 0). The fourth unit is off for this token.
  3. Second layer

    W2 h + b2: row 1 10 - 0 + 0 = 10, row 2 13 + 4 + 0 = 17, row 3 4 - 0 + 1 = 5.
  4. out1 = (10, 17, 5)

    This vector goes to the next block. Run the same three steps with the same W1, b1, W2, b2 on the other four tokens and you get the slide's five outputs.
TokenzW1 z + b1after ReLUW2 h + b2
z1 = x1 + x2(11, 2, 1)(10, 13, 4, -8)(10, 13, 4, 0)(10, 17, 5)
z2 = x2 + x3(6, 6, 1)(1, 12, 8, 1)(1, 12, 8, 1)(0, 20, 8)
z3 = x3 + x4(7, 4, 2)(4, 11, 7, -1)(4, 11, 7, 0)(4, 18, 8)
z4 = x4 + x5(7, 3, 1)(5, 10, 5, -3)(5, 10, 5, 0)(5, 15, 6)
z5 = x1 + x5(5, 3, 1)(3, 8, 5, -1)(3, 8, 5, 0)(3, 13, 6)
All five tokens through the same FFN
FFN(zj)=W2max(0,  W1zj+b1)+b2for each j independently\text{FFN}(z_j) = W_2\, \max(0,\; W_1 z_j + b_1) + b_2 \quad \text{for each } j \text{ independently}
Same weights, every position (column convention of the slide)

Where the parameters sit

The FFN is small on the slide and large in the real model. Derive it from the base hyperparameters: the four attention projections W_Q, W_K, W_V, W_O are each 512 x 512, about 1.05 M weights per layer. The FFN's two matrices are 512 x 2048 and 2048 x 512, about 2.1 M. Two thirds of a layer's weights live in the FFN, so pruning and quantization work in later lectures will spend most of its effort there, while the N^2 activation cost of the previous concept lives in attention.

Weights in one base encoder layer, derived from d_model = 512 and d_ff = 2048 (biases omitted)

Attention projections (4 x 512 x 512)
1,048,576
FFN (2 x 512 x 2048)
2,097,152
FFN share of the layer
about 67%

Quick check

In the position-wise feed-forward network, which statement is true?

Recall

Slide 44: compute the FFN output for token 4, whose attention-weighted feature is z4 = (7, 3, 1).

First layer: (7 - 3 + 1, 7 + 3, 3 + 1 + 1, -7 + 3 + 1) = (5, 10, 5, -3). ReLU: (5, 10, 5, 0). Second layer: (5 - 0, 10 + 5, 5 - 0 + 1) = (5, 15, 6). The slide writes the hidden entry as -4, but ReLU makes that difference vanish.

Where this lecture's ideas come from

The closing slide lists the ten works the lecturer built this lecture from. Read them as a map of where each part came from: the convolution arithmetic animations and CS231n behind parts 2 to 4, the two normalization papers behind part 5, the four architecture papers whose design choices (ReLU in AlexNet, depth in VGG, residuals in ResNet, ReLU6 in MobileNetV2) run through this part, and the survey and course that frame the whole thing for embedded deployment.

Sources

Recap

If you remember nothing else

  • Without a non-linearity, stacked linear layers collapse into one linear layer, so depth buys nothing.
  • Sigmoid saturates on both sides with a derivative of at most 0.25. ReLU, max(0, x), is the default hidden unit.
  • ReLU6 = min(max(0, x), 6) is bounded, which hands int8 a fixed grid of 6/255 per step. MobileNetV2 chose it for low-precision robustness.
  • Hard swish = x ReLU6(x + 3)/6 stays within 0.15 of swish everywhere and needs no exponential.
  • A transformer block is multi-head attention, Add and Norm (residual plus LayerNorm), position-wise FFN, Add and Norm. Base model: N = 6, d_model = 512, h = 8, d_k = 64, d_ff = 2048.
  • Attention(Q, K, V) = softmax(Q K^T / sqrt(d_k)) V. Q and K are N x d_k, the weights are N x N, the output is N x d_v.
  • Dividing by sqrt(d_k) keeps the dot-product variance at 1 so softmax does not saturate and its gradients stay alive.
  • Attention memory and compute grow with N^2. At N = 1024 one fp32 head already holds 4 MB of scores.
  • Slide 43 by hand: q1 = (2, 0, 3) scores 3, 4, -1, 3 against the keys, weights about 0.2, 0.6, 0, 0.2, z1 = (8, 2, 6). The slide's q2 should be (1, 1, 2).
  • The FFN applies the same linear, ReLU, linear to every token independently. Slide 44's two wrong hidden entries (-9 and -4 instead of -8 and -3) are zeroed by ReLU anyway.

Sources