Try first
Three pairs of vectors. For each, multiply position by position and add up the results.
[1, 0] and [1, 0]
[1, 0] and [0, 1]
[1, 0] and [-1, 0]
You get 1, 0 and -1. Now look at the three pairs on paper and say what those numbers are measuring.
The dot product
Multiply matching slots, add up, get a single number.
function dotProduct(a, b) {
if (a.length !== b.length) throw new Error("length mismatch");
let sum = 0;
for (let i = 0; i < a.length; i++) sum += a[i] * b[i];
return sum;
}
The name is dotProduct rather than dot because the playground already has a dot that draws a circle. Two very different things share a name in this subject and the clash is not your fault, but the interpreter will not care about that.
You have written this three times already without naming it. It is the inside of predict in lesson 3063. It is the sum over j inside matmul. Matrix multiplication is nothing but a table of dot products between rows and columns.
What the number means
Your three answers say it. Same direction gives a positive number. Perpendicular gives zero. Opposite gives a negative number.
The full statement is
dotProduct(a, b) = norm(a) * norm(b) * cos(angle between them)
So the dot product is about direction and size together. Two long vectors pointing the same way give a large positive result. Turn one of them ninety degrees and the result is zero regardless of how long they are.
Divide out the lengths and you are left with the angle alone, which is the standard way to compare two things while ignoring their magnitude.
function cosineSimilarity(a, b) {
return dotProduct(a, b) / (norm(a) * norm(b) || 1);
}
That runs from 1 for identical directions, through 0 for unrelated, to -1 for opposite. If you have ever read that two word embeddings are close, this is the number being quoted.
Draw it
const a = [3, 1];
view.xMin = -4; view.xMax = 4;
view.yMin = -4; view.yMax = 4;
clear(); grid(1);
line(0, 0, a[0], a[1], "#333");
for (let t = 0; t < Math.PI * 2; t += 0.05) {
const b = [3 * Math.cos(t), 3 * Math.sin(t)];
const d = dotProduct(a, b);
const color = d > 0 ? "#38a" : "#c33";
dot(b[0], b[1], color, 2 + Math.abs(d) / 3);
}
b sweeps around a circle of fixed length, so only its direction changes. Blue where the dot product is positive, red where it is negative, and the dot grows with the size of the result.
You get two large blobs on opposite sides and two pinch points where the circle crosses the line perpendicular to a. Those pinch points are where the dot product passes through zero. The picture is the cosine, drawn.
Why this is the operation everything is built from
Read the prediction again with this in mind.
prediction = dotProduct(w, x) + b
The weights are a direction. The features are a direction. The prediction is large when the input points the same way as the weights, and negative when it points against them. So training is searching for a direction in feature space that lines up with the thing you want to predict.
That reading survives all the way through. A neuron in section 7 does the same thing: it holds a pattern as a vector and reports how strongly the input matches it. Everything after that is stacking those matches.
What to watch
The dot product does not care about units, so it will happily combine grams with centimeters and give you a confident number. This is the lesson 3042 problem in a new place. Scale first, then compare.
Exercises
- Find a vector perpendicular to
[3, 1]by solvingdotProduct(a, b) = 0by hand. Then check it. - Confirm that
dotProduct(a, a)equalsnorm(a)squared, and say why from the formula. - Compute the cosine similarity between each pair of the six fruit rows from lesson 3036, scaled first. Do the apples group?
- Rewrite
predictfrom lesson 3063 as a single call todotProductplus the bias.