Winning Kaggle the Reproducible Way

Chapter 9

Target Encoding Without Leakage

Encode categories from permitted labels, and assess the whole encoding pipeline.

A target encoding must be fitted without the row's own label and rebuilt inside every outer fold. One demonstration measures what each shortcut does to the cross-validation estimate and to the score on new rows.

Every example is constructed for teaching. Its numbers come from running the chapter activity's code, not from a competition result.

Demonstration 1 of 1

Three ways to target-encode, scored against new rows

How far does each target-encoding pipeline's cross-validation estimate drift from its score on new rows?

A naive encoding uses every training label, including the row's own, so the model learns a column that partly contains the answer. Cross-fitting removes the row's own label; nesting the encoder inside each outer fold also keeps the validation labels out. Change the category effect to see when the encoding is worth having at all.

Equation: e c equals s c plus m times mu, divided by n c plus m

Scroll sideways for the whole equation

e_c is the encoding of category c, s_c the sum and n_c the count of permitted training targets in c, mu their mean and m the smoothing strength (10 here).

Predict first. When the categories carry no signal at all, what cross-validation AUC does the naive pipeline report?

Your prediction

Choose an example

Scroll sideways for the whole figure

Figure: Three ways to target-encode, scored against new rows. Paired bars of cross-validation estimate and new-row AUC for three encoding pipelines at category effect 1.0; the naive pipeline's estimate is 0.113 above its new-row score.
True category effect (standard deviation, log odds): 1.0
Constructed example: seeded synthetic categories and a logistic regression, measured by the chapter activity.

Calculated values

Naive CV estimate
0.846
Naive on new rows
0.733
Nested CV estimate
0.727
Cross-fitted on new rows
0.735
No encoding on new rows
0.692

At category effect 1.0: the naive pipeline reports 0.846 but scores 0.733 on new rows, 0.846 - 0.733 = 0.113 of optimism. The nested estimate 0.727 is 0.008 from its new-row score 0.735; the global OOF table reports 0.733. Compared with no encoding, the encoded column adds 0.043 AUC on new rows.

Worked steps

  1. Naive: 0.846 - 0.733 = 0.113 (estimate minus new rows).
  2. Global OOF table: 0.733 - 0.735 = -0.003.
  3. Nested: 0.727 - 0.735 = -0.008.
  4. Value of the encoding on new rows: 0.735 - 0.692 = +0.043.

Use the idea

Build the encoder inside each outer training fold, cross-fit the training rows, map validation rows from the outer-training labels only, and compare against the same model without the encoded column.

Where the conclusion applies

Constructed data and a logistic model. In this generator the global OOF table's estimate is close to the nested one; with other models or data the second boundary can matter more, which is why the chapter nests by default.

Check your understanding: With no category signal, why does the naive pipeline also score worse on new rows, while with a strong signal it does not?
With no signal it learned to trust a column that carried each training row's own label, and new rows' encodings carry no such label (0.690 against 0.717). With a strong signal the column is genuinely useful, so the deployed models end up alike (0.773 against 0.772); the damage is then to the estimate you use to choose, not to the model.

Chapter 9 source: section "Two Boundaries, Not One Global OOF Table".