Lesson 6.3ModelCode~55 min
Many features at once
Predict from many inputs with one dot product — and see why features must be put on the same scale before training.
Module 6 · 0/11 done
More than one clue
A house’s price doesn’t depend on its size alone. Bedrooms, age and distance to the city centre all matter. With several features, the line becomes a weighted sum: one weight per feature, plus a bias. You’ve seen this shape before. It’s a dot product.
Training works exactly as before. Each weight gets its own partial derivative, and they all step downhill together. Let’s try it on 200 houses. Note the scales: sizes in the hundreds, bedrooms between 1 and 6.
const { X, y, features } = data.housing(200)
console.log(features)
console.table(X.slice(0, 5).map((x, i) => ({ size: x[0], bedrooms: x[1], age: x[2], km: x[3], price: y[i] })))
// Gradient descent on the RAW numbers: sizes are ~100s, bedroom counts ~1–6.
// The size weight's gradient is huge, the others tiny — one learning rate can't suit them all.
for (const lr of [1e-6, 1e-5, 1e-4]) {
let w = [0, 0, 0, 0], b = 0
for (let epoch = 0; epoch < 200; epoch++) {
const gw = [0, 0, 0, 0]
let gb = 0
for (let i = 0; i < X.length; i++) {
const err = vec.dot(w, X[i]) + b - y[i]
for (let j = 0; j < 4; j++) gw[j] += (2 * err * X[i][j]) / X.length
gb += (2 * err) / X.length
}
w = w.map((v, j) => v - lr * gw[j])
b -= lr * gb
}
const mse = vec.mean(X.map((x, i) => (vec.dot(w, x) + b - y[i]) ** 2))
console.log("lr", lr, "→ error", round(mse, 1))
}
Press Run (⌘/Ctrl + Enter) to see output here.
A small learning rate barely moves. A slightly bigger one blows up. There’s no good setting. Why?