Designing a clean experiment
A good experiment answers one clear question fairly. Learn hypotheses, baselines, ablations, random seeds and clean data splits, with a small Python example.
In 60 seconds
Designing a clean experiment
A good experiment answers one clear question fairly. Learn hypotheses, baselines, ablations, random seeds and clean data splits, with a small Python example.
1. Start with a hypothesis
- "My method is better."
- "Let's see what happens with data augmentation."
- "Adding back-translation data improves Hindi-to-English accuracy on our test set, compared with the same model without it."
- "If the mean gain across 5 seeds is smaller than the seed spread, I will conclude it does not clearly help."
2. Choose fair baselines
3. Plan ablations
| Run | Part A | Part B | Part C | What it tells you |
|---|---|---|---|---|
| Full method | on | on | on | The headline result |
| No A | off | on | on | How much A contributes |
| No B | on | off | on | How much B contributes |
| No C | on | on | off | How much C contributes |
| Baseline | off | off | off | Where you started |
4. Keep train, validation and test separate
- Training set: the model learns from this.
- Validation set: you use this to choose settings, such as learning rate or when to stop.
- Test set: you look at this once, at the end, to report the final number.
5. Run several random seeds
import math
import random
import statistics
def make_data(rng, n=400, noise=1.2):
"""Two overlapping clouds of points. Label 1 sits up and right."""
rows = []
for _ in range(n):
y = rng.randint(0, 1)
c = 1.0 if y == 1 else -1.0
rows.append(((rng.gauss(c, noise), rng.gauss(0.5 * c, noise)), y))
return rows
def split(rows, rng):
rows = rows[:]
rng.shuffle(rows)
a, b = int(0.6 * len(rows)), int(0.8 * len(rows))
return rows[:a], rows[a:b], rows[b:] # train, validation, test
def train_logreg(train, rng, lr, epochs=20):
w1, w2, bias = rng.gauss(0, 0.1), rng.gauss(0, 0.1), 0.0
train = train[:]
for _ in range(epochs):
rng.shuffle(train)
for (x1, x2), y in train:
z = max(-30.0, min(30.0, w1 * x1 + w2 * x2 + bias))
g = 1 / (1 + math.exp(-z)) - y
w1, w2, bias = w1 - lr * g * x1, w2 - lr * g * x2, bias - lr * g
return lambda x: 1 if w1 * x[0] + w2 * x[1] + bias > 0 else 0
def majority_baseline(train):
guess = 1 if 2 * sum(y for _, y in train) >= len(train) else 0
return lambda x: guess
def accuracy(model, rows):
return sum(model(x) == y for x, y in rows) / len(rows)
def run_once(seed):
rng = random.Random(seed)
train, val, test = split(make_data(rng), rng)
# Choose the learning rate on VALIDATION data only.
best_lr = max([0.001, 0.01, 0.1],
key=lambda lr: accuracy(train_logreg(train, random.Random(seed), lr), val))
model = train_logreg(train, random.Random(seed), best_lr)
# Touch the TEST set once, at the very end.
return accuracy(majority_baseline(train), test), accuracy(model, test), best_lr
SEEDS = [0, 1, 2, 3, 4]
base, ours = [], []
for s in SEEDS:
b, m, lr = run_once(s)
base.append(b)
ours.append(m)
print(f"seed {s}: baseline {b:.3f} model {m:.3f} (lr chosen on val: {lr})")
print(f"baseline: {statistics.mean(base):.3f} +/- {statistics.stdev(base):.3f}")
print(f"model: {statistics.mean(ours):.3f} +/- {statistics.stdev(ours):.3f}")
print(f"runs per setting: {len(SEEDS)} (std is the sample standard deviation)")6. Write down the plan before you look at results
- 1
Hypothesis
One sentence, plus what would prove it wrong. - 2
Setup
Data, split, metric, baselines, ablations. - 3
Budget
How many seeds and settings, and how the settings are chosen (validation only). - 4
Run and log
Every run in the lab notebook, including failed ones. - 5
Report all of it
Mean, spread, number of seeds, and results that did not go your way.
noise=1.2 to noise=2.0 in the example and run it again. What happens to the mean and to the spread? Write one sentence about why.Watch and read more
Lab
A small seed-variance study showing whether a change really helps, with mean, spread and per-seed differences.
The problem
Save the starter as seed_lab.py and run it with Python 3 (standard library only). It compares two settings, A and B, of the same small model.
- Run it with
NUM_SEEDS = 1. Write down what you would conclude from this single run. - Change to
NUM_SEEDS = 5, then20. Write down the means, standard deviations, and the wins, losses and ties. - Now change
SETTING_Bto{"epochs": 30}andSETTING_Ato{"epochs": 1}, and repeat with 20 seeds. - For each comparison write one honest sentence: is B clearly better, clearly worse, or unclear? Use the spread to justify it.
Practice lab
Edit the code and run it. Python runs in your browser — nothing is sent to our server.
No output. Add print(...) to see values.
You are done when
Hard questions
Try to answer before you reveal. If you can answer these, you understood the lesson.
Q1Why does the starter use the same seeds for A and B and look at per-seed differences?Reveal
With the same seed, A and B see the same data and the same starting point, so much of the random noise is shared. The per-seed difference then shows the effect of the setting more clearly than comparing two separate averages. This is called a paired comparison.
Q2With 20 seeds, B wins on 11, loses on 9. A friend says "B wins more often, so it's better". What do you say?Reveal
11 against 9 is close to what you would expect from chance if A and B were equally good. Look at the size of the differences and the spread too. The honest summary is "no clear difference" unless more seeds or a proper statistical test shows otherwise.
Questions people ask
How many seeds are enough?
There is no single number. More seeds give a clearer picture, but each costs compute. Many small studies use a handful. What matters most is to use more than one, report how many, and show the spread. If the gap between methods is small compared with the spread, run more seeds before claiming anything.
Isn't the standard deviation enough to prove my method is better?
It is a good first check, not a proof. If the two ranges overlap a lot, you cannot claim a clear win. For stronger claims, use the same seeds for both methods and look at the per-seed differences, or use a proper statistical test. A statistics textbook or a teacher can help you choose one.
What if I have no validation set?
Make one. Split off part of your training data before you start, and never train on it. If data is very small, cross-validation (rotating which part is held out) is a common option.
Should I report failed experiments?
In your lab notebook, always. In a paper, report the ones that matter for the reader, such as ablations that did not help or settings where your method loses. This makes your work more believable, not less.
Lesson test
5 questions. Get 3 right (60%) to pass and complete this lesson.