Validation Strategies and Data Leakage › Example
Machine Learning from ScratchAll chapters Validation Strategies and Data Leakage › ExampleSection view · Sections in this chapter6 / 10 ↓
Reading tools Definitions, links & notes
Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.
Article links
Make inline references clickable
We now replay the same logic on a dataset small enough that every number can be verified by hand, which separates the mechanism from any library behavior. We use four customers, each contributing two rows. This is a smaller version of the setup in the first figure.
The four customers and their observations are:
- Customer A: and
- Customer B: and
- Customer C: and
- Customer D: and
For each customer, the second observation is one unit higher in feature space and one unit higher in the target, so a model that assigns each row its same-customer sibling will predict it almost exactly.
| customer | row | x | y | |
|---|---|---|---|---|
| 0 | A | 1 | 10 | 5 |
| 1 | A | 2 | 11 | 6 |
| 2 | B | 1 | 50 | 25 |
| 3 | B | 2 | 51 | 26 |
| 4 | C | 1 | 20 | 10 |
| 5 | C | 2 | 21 | 11 |
| 6 | D | 1 | 80 | 40 |
| 7 | D | 2 | 81 | 41 |
| customer | row | x | y |
|---|---|---|---|
| A | 1 | 10 | 5 |
| A | 2 | 11 | 6 |
| B | 1 | 50 | 25 |
| B | 2 | 51 | 26 |
| C | 1 | 20 | 10 |
| C | 2 | 21 | 11 |
| D | 1 | 80 | 40 |
| D | 2 | 81 | 41 |
We use a 1-NN regressor on a single scalar feature . The prediction for a test observation is the target of the training observation with the smallest . For our data this rule is easy to evaluate by hand.
The row split
The leaking row split assigns the first row of every customer to training and the second to test. This deterministic allocation guarantees a training sibling for every test row. It illustrates overlap that a random row split can produce; a random split can also place both of a customer's rows in the test set.
Training set: , one row per customer.
Test set: , the sibling row of each customer.
For each test row, we find the nearest training and read off its .
- Test row : distances , , , . Nearest is with . Prediction . Squared error .
- Test row : distances , , , . Nearest is with . Prediction . Squared error .
- Test row : distances , , , . Nearest is with . Prediction . Squared error .
- Test row : distances , , , . Nearest is with . Prediction . Squared error .
Mean squared error . RMSE .
The group split
Now we assign whole customers to one side, enforcing the group-disjointness condition. We place customers A and B in training and customers C and D in test. All four test rows belong to unseen customers.
Training set: .
Test set: .
- Test row : distances , , , . Nearest is with . Prediction . Squared error .
- Test row : distances , , , . Nearest is with . Prediction . Squared error .
- Test row : distances , , , . Nearest is with . Prediction . Squared error .
- Test row : distances , , , . Nearest is with . Prediction . Squared error .
Mean squared error . RMSE .
Reading the two estimates side by side
The row-split RMSE is 1.000 and the group-split RMSE is about 10.747, a difference of about 9.747 target units. The row split tests siblings of training observations; the group split tests different customers on a different set of target values. This illustrates how task and partition choices can change the error. It does not estimate a universal leakage penalty or imply that every learner would make the same errors.
PythonIn [9]Expand
def one_nn_predict(train_df, x_test_values):
preds = []
for x_val in x_test_values:
distances = np.abs(train_df["x"].values - x_val)
nearest = train_df.iloc[np.argmin(distances)]
preds.append(nearest["y"])
return np.array(preds)
row_train = example_df[example_df["row"] == 1].reset_index(drop=True)
row_test = example_df[example_df["row"] == 2].reset_index(drop=True)
row_preds = one_nn_predict(row_train, row_test["x"].values)
row_rmse = np.sqrt(np.mean((row_preds - row_test["y"].values) ** 2))
group_train = example_df[example_df["customer"].isin(["A", "B"])].reset_index(
drop=True
)
group_test = example_df[example_df["customer"].isin(["C", "D"])].reset_index(
drop=True
)
group_preds = one_nn_predict(group_train, group_test["x"].values)
group_rmse = np.sqrt(np.mean((group_preds - group_test["y"].values) ** 2))ConsoleOut [10]Expand
Row split RMSE: 1.000 Group split RMSE: 10.747 Score difference: 9.747
In this tiny row allocation, every nearest training observation is the held-out row's same-customer sibling. In the grouped allocation, C and D have no training siblings, and the stated 1-NN rule selects rows from A or B. These are properties of this constructed dataset, not guarantees for all repeated-customer data.
Comments
Add to the conversation
A question, a correction, or an example from your own work.
No comments yet. Be the first to share your thoughts!