Should This Change the Prediction?
The same measurement can arrive in different units or a different batch. State the behaviour that should survive those changes, then test the actual prediction pipeline.
14 sections · 3 questions · 1 coding challenge
The arrow keys move between sections too.
Testing Behaviour That a Single Score Misses
BOTIE
We use that testing perspective for a temperature service. Our unit and batching checks are examples of declared relationships between inputs and outputs, not a claim to implement the CheckList software or certify robustness.
Sources for this history
The Sensor Started Reporting Fahrenheit
A greenhouse sensor update changes its output from Celsius to Fahrenheit. The physical temperature has not changed. If our prediction service feeds the new number into the old feature position, Botie sees a much hotter greenhouse.
A random test split may never contain that deployment change. We need tests built around a relationship we understand: after the correct unit conversion, the same physical input should produce the same result from a deterministic frozen pipeline.
The workshop uses recorded expected and observed outputs. The worked Python program runs a real unit conversion; no trained model is called in the workshop.
Step 1 of 2
Equivalent measurements should remain equivalent
The adapter converts the incoming value into the feature unit expected by the frozen pipeline.
| Input representation | Raw value | After adapter in °C |
|---|---|---|
| Celsius | 20 | 20 |
| Fahrenheit | 68 | 20 |
Celsius
- Raw value
- 20
- After adapter in °C
- 20
Fahrenheit
- Raw value
- 68
- After adapter in °C
- 20
Step 2 of 2
Some changes should matter
Define the expected relationship before constructing a test.
| Change | Expected relation | Reason |
|---|---|---|
| Convert units correctly | Same prediction within tolerance | Same physical input |
| Reorder batch rows | Same outputs after restoring row order | Independent deterministic predictions |
| Increase heater setting | Task-dependent change | The physical condition changed |
Convert units correctly
- Expected relation
- Same prediction within tolerance
- Reason
- Same physical input
Reorder batch rows
- Expected relation
- Same outputs after restoring row order
- Reason
- Independent deterministic predictions
Increase heater setting
- Expected relation
- Task-dependent change
- Reason
- The physical condition changed
Choose the transformation before declaring invariance
The difference remains visible before a pass/fail flag replaces it. Trace a failed check back to its recorded output.
Test a declared relationship
Compare each transformed run with its expected output and a stated absolute tolerance.
BOTIE
Behaviour check counts
{
"passed": 2,
"failed": 0
}Follow the operations
1. Observed outputs from transformed runs
- 010
- 120.25
2. Expected outputs for declared checks
- 010
- 120
3. Observed minus predicted
- 00
- 10.25
4. Allowed absolute difference
0.25
5. Check the allowed change
- 01
- 11
6. Count behaviour checks
{ "passed": 2, "failed": 0 }7. Result
{ "passed": 2, "failed": 0 }
This report cannot decide which transformations should preserve meaning. The task definition must supply that relationship.
1. Name the behaviour we rely on
An invariance test changes representation while preserving something the task should treat as equivalent. A unit conversion is one example. A metamorphic test describes a relationship between multiple runs even when we do not know an exact answer for every input.
Begin with the intended use. Flipping a photo may preserve its object label but change the meaning of left and right. Removing “not” from a sentence should often change its meaning. A convenient transformation is not automatically a valid test.
Write the expected relation and the conditions under which it holds.
2. Keep the adapter inside the tested pipeline
A model receives numbers in a particular schema. Test from the input boundary through the adapter to prediction. A unit test of the coefficient multiplication alone will not catch a wrong conversion before it.
Keep named feature order as part of the contract. If temperature and heater setting swap columns, a matrix still has the right shape while representing the wrong input.
Fahrenheit to Celsius:
C = (F - 32) × 5 / 9
For F = 68:
C = (68 - 32) × 5 / 9 = 20Reject unknown units rather than silently assuming the training unit.
3. Use a tolerance that fits the output
Floating-point calculations can differ slightly across implementations. For a deterministic numerical pipeline, compare differences with a justified absolute or relative tolerance. State the units and avoid a tolerance so broad that a real bug passes.
Our workshop uses an absolute tolerance. Equality at the limit passes. NumPy isclose also supports relative tolerance; if you use it, understand how both terms combine.
pass = abs(observed - expected) <= absolute_tolerance
Expected: 20; observed: 20.25; tolerance: 0.25 → passA stochastic generator needs repeated-distribution or task-level checks, not an assumption that every run produces identical text or pixels.
Check yourself
Questions on Parts 1 to 3
Question 1 of 2
Where should the test begin?
Use the information below.
20 °C and 68 °F represent the same temperature. A frozen deterministic model expects Celsius.4. Test batches without mixing up identities
For a row-independent deterministic predictor, predicting records individually should agree with predicting them as a batch. Permuting rows should permute outputs in the same way. Restore IDs before comparing.
Some models legitimately use context across records, such as sequence models. Their batching and ordering contracts need different tests. State that dependency instead of treating every order effect as a bug.
A test can reveal a contract mismatch even when the model’s ordinary benchmark remains unchanged.
5. Try plausible corruptions and measure degradation
Sensor noise, missing fields and delayed measurements deserve separate tests. These can change the available information, so identical outputs may be the wrong requirement. Check degradation against a task-specific allowance or require a clear rejection of unsupported inputs.
Record corruption severity and why it is plausible. Test important slices too. Passing a few perturbations does not establish resistance to arbitrary adversarial inputs or unseen distribution shifts.
Use failures to create specific regression tests. Keep a fresh assessment to check whether the resulting repair generalises.
6. Run the conversion before comparing results
The program below uses an explicit unit argument. It tests an equivalent measurement through the adapter instead of comparing two unrelated raw numbers.
The challenge builds a reusable report for many recorded checks, including one failure.
Convert the same physical temperature
This is an executable adapter example, not a fitted model.
def celsius(value, unit):
if unit == "C":
return float(value)
if unit == "F":
return (value - 32) * 5 / 9
raise ValueError("Unknown temperature unit")
print(celsius(20, "C"))
print(celsius(68, "F"))20.0
20.0The conversion establishes equal physical inputs. A model test would now send both through the same frozen prediction pipeline.
7. Keep the failed input with the repair
When a test fails, preserve the smallest meaningful example, the expected relationship and the pipeline version. Repair the adapter or model behaviour that violated the contract. Rerun earlier tests as well as the new one.
A passing count is useful only alongside what was tested. Document excluded conditions and keep observing production inputs for new ones.
The final project combines behaviour checks with errors and operational requirements before a limited trial.
Check yourself
Questions on Parts 1 to 7
Question 1 of 1
Which claim is supported?
Use the information below.
Three unit-conversion tests pass.Try it with Botie
Build with Botie
Write and run Python