Winning Kaggle the Reproducible Way

Interactive chapter

This page shows the chapter notebook with its saved results. Constructed examples do not measure competition gains.

Chapter 35: Memory Optimization for Large Datasets

Try the decision first

At what ID size does a blanket float32 downcast start merging customers, and how much memory does it save compared with a checked downcast?

Write your expectation before opening the worked results. Use the chapter activity to compare supported inputs. To change the calculation itself, run the notebook on GitHub.

Your answers stay on this page until you download them. They are not sent to a server.

Open the chapter activity

Worked notebook and saved results

At what ID size does a blanket float32 downcast start merging customers, and how much memory does it save compared with a checked downcast?

Memory work that silently changes the data is worse than a crash: the run finishes, the score drops, and nothing points at the dtype. The chapter's check compares values and distinct counts before accepting a smaller type.

Constructed data on this machine's pandas; the savings are properties of this table, not a general rule.

The experiment

A constructed table of 200,000 purchases from 40,000 customers: an integer customer ID that starts at the control value, an amount in dollars and cents, and a long region string. Three versions are compared: unchanged, a blanket float32 for the ID and amount columns, and a checked downcast (float32 only when the chapter's value and distinct-count check passes, int32 only when the range fits, repeated strings as categories). Each is scored by its memory, by how many distinct IDs remain, and by R-squared of a held-out-rows prediction from the mean amount per customer ID.

Calculation code
import tracemalloc

import numpy as np
import pandas as pd

from kaggle_companion.activities._common import clean

SEED = 35
ROWS, CUSTOMERS = 200_000, 40_000
REGIONS = [f"region_{i:02d}_north_america_east" for i in range(12)]   # a long, repeated string column
INT32_MAX = np.iinfo(np.int32).max


def generate(first_id, rng):
    """Purchases: a customer ID that starts at `first_id`, an amount whose mean depends on the customer, and a region string."""
    customer = rng.integers(0, CUSTOMERS, ROWS)
    taste = rng.normal(0, 1, CUSTOMERS)
    amount = np.round(60 * np.exp(0.6 * taste[customer] + rng.normal(0, 0.3, ROWS)), 2)
    return pd.DataFrame({"customer_id": (first_id + customer).astype(np.int64), "amount": amount,
                         "region": np.array(REGIONS)[rng.integers(0, 12, ROWS)]})


def blanket_float32(df):
    """The shortcut: every numeric column becomes float32 because the range fits."""
    out = df.copy()
    for name in ("customer_id", "amount"):
        out[name] = df[name].astype(np.float32)
    return out


def checked_downcast(df):
    """The chapter's information check: float32 only if values and distinct counts survive; integers shrink only if the range fits."""
    out = df.copy()
    for name in ("customer_id", "amount"):
        values = df[name].to_numpy()
        if values.dtype.kind == "i":
            if values.min() >= np.iinfo(np.int32).min and values.max() <= INT32_MAX:
                out[name] = values.astype(np.int32)
        else:
            candidate = values.astype(np.float32)
            same_values = np.allclose(values, candidate.astype(np.float64), atol=1e-7, rtol=1e-5)
            if same_values and np.unique(values).size == np.unique(candidate).size:
                out[name] = candidate
    out["region"] = df["region"].astype("category")        # repeated strings stored once
    return out


def customer_feature(df, train, fallback):
    """The feature a competitor would build: mean amount per customer ID on training rows, mapped onto held-out rows."""
    per_id = df[train].groupby("customer_id")["amount"].mean()
    return df.loc[~train, "customer_id"].map(per_id).fillna(fallback).to_numpy(dtype=float)


def run(first_id_power):
    rng = np.random.default_rng(SEED)
    df = generate(2 ** first_id_power, rng)
    train = np.arange(ROWS) % 2 == 0                        # even rows build the feature, odd rows are scored against it
    truth = df.loc[~train, "amount"].to_numpy()
    original_mb = df.memory_usage(deep=True).sum() / 1e6
    schemes = {}
    for name, convert in (("none", lambda d: d.copy()), ("blanket", blanket_float32), ("checked", checked_downcast)):
        tracemalloc.start()                                 # measure what the conversion itself allocates at its peak
        frame = convert(df)
        conversion_peak_mb = tracemalloc.get_traced_memory()[1] / 1e6
        tracemalloc.stop()
        feature = customer_feature(frame, train, df.loc[train, "amount"].mean())
        columns = frame.memory_usage(deep=True)
        schemes[name] = {
            "megabytes": columns.sum() / 1e6,
            "by_column": {"Customer ID": columns["customer_id"] / 1e6, "Amount": columns["amount"] / 1e6,
                          "Region": columns["region"] / 1e6},
            # measured peak while converting: the original table stays alive while the conversion allocates
            "conversion_peak": round(0.0 if name == "none" else conversion_peak_mb, 1),
            "while_converting": round(original_mb + (0.0 if name == "none" else conversion_peak_mb), 1),
            "distinct_ids": int(frame["customer_id"].nunique()),
            "feature_r2": float(np.corrcoef(feature, truth)[0, 1] ** 2),
            "max_amount_error_cents": float(100 * np.abs(frame["amount"].astype(np.float64) - df["amount"]).max()),
            "id_dtype": str(frame["customer_id"].dtype),
        }
    return clean({"first_id_power": first_id_power, "first_id": 2 ** first_id_power, "true_ids": int(df["customer_id"].nunique()),
                  "schemes": schemes, "rows": ROWS})

Measure it across the control

The control is smallest customer id, as a power of two. Each value below runs the whole experiment from the generator up.

Calculation code
import pandas as pd
from kaggle_companion.activities.ch35 import explain

VALUES = [22, 24, 26, 33]
results = {value: run(value) for value in VALUES}
table = pd.DataFrame({str(v): explain(results[v], v)['metrics'] for v in VALUES})
print(table.to_string())
                                          22                24               26            33
Unchanged table                      18.6 MB           18.6 MB          18.6 MB       18.6 MB
Blanket float32                      17.0 MB           17.0 MB          17.0 MB       17.0 MB
Checked downcast                      1.8 MB            1.8 MB           1.8 MB        2.6 MB
Distinct IDs after blanket  39,756 of 39,756  19,933 of 39,756  5,001 of 39,756  40 of 39,756
R-squared, blanket                     0.600             0.271            0.060         0.001
R-squared, checked                     0.600             0.600            0.600         0.600

Look at it

The figure shows the default setting, smallest customer id, as a power of two = 24.

Calculation code
import matplotlib.pyplot as plt
from kaggle_companion.activities.ch35 import draw, NCOLS, HEIGHT

DEFAULT = 24
fig, axes = plt.subplots(1, NCOLS, figsize=(6.6 if NCOLS == 1 else 10.4, HEIGHT), layout='constrained')
draw(axes, results[DEFAULT], DEFAULT)
plt.show()
Saved figure from this chapter calculation

What the result says

With IDs starting at 2^24: the table is 18.6 MB unchanged, 17.0 MB with blanket float32 (18.6 - 17.0 = 1.6 MB saved) and 1.8 MB checked (18.6 - 1.8 = 16.8 MB saved, ID stored as int32). 19,823 of 39,756 IDs vanish into a neighbor, and the held-out R-squared falls from 0.600 to 0.271. The checked table keeps all 39,756 IDs and R-squared 0.600. Amounts change by at most 0.003 cents.

Apply this to your competition

Book location: Chapter 35, Downcast With an Information Check.

Technical corrections

Apply a lesson from this chapter

Select a lesson whose conditions match your project. State how you would test it and what would make you reject it.

Separate feature and model ownership with an end-to-end compute budget

Conditions

  • large transactional processing dominates iteration time

Action

  • Proposed adaptation of source observation: Assign complementary feature/model responsibilities and budget preprocessing plus training and prediction.

Check

  • Proposed verification: Measure handoff completeness and complete pipeline wall time before late experiments.

Limits

  • Resource reports are estimates and not guarantees of speedup from extra cores.

Find source links and value grades in the lesson atlas

Estimate full ensemble runtime from concurrent model timings

Conditions

  • an ensemble contains many independently trained models

Action

  • Proposed adaptation of source observation: Benchmark the intended concurrent schedule and estimate total training time before expanding the model count.

Check

  • Proposed verification: Include memory contention and final inference time in a trial schedule.

Limits

  • The 24-hour total is an author estimate, not a measured reproducible hardware benchmark.

Find source links and value grades in the lesson atlas

Disable expensive feature families consistently in a fast pipeline

Conditions

  • extra radar features dominate runtime

Action

  • Proposed conditional application: Disable extra-feature generation and the corresponding training and prediction flags together for a reduced-cost run.

Check

  • Proposed check: Assert train/test feature schemas match after all flags change; benchmark total time and CRPS versus full pipeline.

Limits

  • Saving is README expectation; quality baseline and hardware unspecified.

Find source links and value grades in the lesson atlas

Extract reusable embeddings in inference mode

Conditions

  • saved classifiers provide intermediate features for downstream pair scoring

Action

  • Proposed: use inference-phase settings when extracting classifier features, and explicitly test stochasticity and batch sensitivity.

Check

  • Proposed: repeat extraction on identical inputs and compare batch sizes; verify chosen layer output semantics against model structure.

Limits

  • The code supplies learning_phase zero, but legacy BatchNormalization(mode=2) means batch independence must be checked separately; hardcoded layer indices are brittle, and no embedding comparison is reported.

Find source links and value grades in the lesson atlas

Unscale gradients before clipping accumulated mixed-precision steps

Conditions

  • mixed-precision training with gradient accumulation

Action

  • Proposed: divide loss by accumulation count and unscale optimizer gradients before clipping at update boundaries.

Check

  • Proposed: verify optimizer and scheduler step counts, including remainder microbatches; compare effective-batch updates with a non-accumulated reference.

Limits

  • The source advances the scheduler every microbatch; a proposed adaptation must define schedule units and handle remainder accumulation explicitly.

Find source links and value grades in the lesson atlas

Group decoder inputs by valid sequence length

Conditions

  • autoregressive decoding of variable-length encoder outputs

Action

  • Proposed: group equal valid lengths and slice away padding before decoder generation.

Check

  • Proposed: compare generated token sequences to masked padded decoding and benchmark time across the observed length distribution.

Limits

  • This is a configured validation mode rather than a guaranteed default. Many distinct lengths can create small batches and reduce efficiency.

Find source links and value grades in the lesson atlas

Separate reproduction time from artifact inference time

Conditions

  • a trained artifact can avoid rebuilding a pipeline for each prediction

Action

  • Proposed conditional application: Keep preprocessing, training and prediction separately callable; the author reports about two hours from scratch versus about three seconds using the existing model.

Check

  • Proposed check: Check frozen-artifact predictions match rebuilt ones; time preprocessing, training, loading and inference separately.

Limits

  • Saved artifacts need matching preprocessing and versioning; archived runtime is workload-specific.

Find source links and value grades in the lesson atlas

Budget feature construction separately from model training

Conditions

  • raw transactional health data require substantial aggregation

Action

  • Proposed adaptation of source observation: Measure feature-generation time separately and prioritize reusable feature artifacts before ensemble expansion.

Check

  • Proposed verification: Benchmark an end-to-end slice including aggregation, training and prediction before scheduling the full run.

Limits

  • The times are author estimates; node specifications and a comparable benchmark are absent.

Find source links and value grades in the lesson atlas

Checkpoint long training instead of rerunning preparation

Conditions

  • pipeline preparation steps are costly and runs may be interrupted

Action

  • Proposed conditional application: Persist model parameters and regular prediction outputs so training can resume and intermediate valid models can be evaluated.

Check

  • Proposed check: Interrupt a local test run, restore all required training state, and compare continuation with uninterrupted training; cache preparation stages by input version.

Limits

  • Published save/reload instructions are not successful recovery proof; parameter-only checkpoints may omit optimizer/random state.

Find source links and value grades in the lesson atlas

Budget scalable processing as part of feature experiments

Conditions

  • large relational datasets make each feature experiment costly

Action

  • Proposed adaptation of author advice: Profile processing, training and prediction separately and plan feature experiments within end-to-end runtime.

Check

  • Proposed verification: Measure scaling on increasing data sizes before full runs.

Limits

  • Twenty hours is local to the source implementation and hardware is not specified.

Find source links and value grades in the lesson atlas

Profile sparse text vectorization memory before custom implementation

Conditions

  • large field-specific sparse vocabularies exceed memory budget

Action

  • Proposed adaptation of source observation: Profile vectorization memory and, as proposed, compare efficient sparse/batched implementations before custom replacement.

Check

  • Proposed verification: Check exact vocabulary/weight parity and peak memory with a standard reference.

Limits

  • The source provides no implementation or numeric saving; bespoke vectorizers increase maintenance risk.

Find source links and value grades in the lesson atlas

Lower preprocessing parallelism when memory is exhausted

Conditions

  • volumetric preprocessing runs out of memory

Action

  • Proposed conditional application: Reduce preprocessing worker count below the CPU core count.

Check

  • Proposed check: Sweep worker count; record peak RSS, completion time and output parity.

Limits

  • Worker memory may include large volume copies; source gives no optimal count.

Find source links and value grades in the lesson atlas

Probe training memory with representative batches

Conditions

  • large image models or larger inputs risk memory exhaustion

Action

  • Proposed: run short representative training probes and reduce microbatch size with gradient accumulation before full experiments.

Check

  • Proposed: measure peak allocation including optimizer state at target resolution and compare accumulated versus larger-batch optimization, especially BatchNorm behavior.

Limits

  • Winner-owned exploratory Experiment 4 is not the final winning ensemble. Source memory prose compares small ConvNeXt at128 and accum1/2/4, but GPU model, peak-measurement method and full-dataset maxima are unknown; microbatch-sensitive layers can invalidate identical-gradient claims.

Find source links and value grades in the lesson atlas

Checkpoint and batch repeated training jobs when the host freezes

Conditions

  • long sequences of training jobs exhaust or destabilize a host
  • a VM freezes after several sequential model fits

Action

  • Proposed adaptation of source observation: Save each completed model and run bounded job batches while diagnosing memory/process accumulation.
  • Proposed conditional application: Train in batches of five as a workaround while preserving distinct seeds and saved predictions.

Check

  • Proposed verification: Verify saved artifacts and restart behavior and monitor resource usage per job.
  • Proposed check: Save weights and predictions after each fit, monitor memory and process state, and compare restarted batch runs with uninterrupted runs.

Limits

  • The cause was unresolved; batching is a workaround rather than a confirmed fix.
  • Five-model batches are a specific workaround; the root cause was unresolved and modern environments may differ.

Find source links and value grades in the lesson atlas

Stream high-resolution images with order-safe batch generators

Conditions

  • High-resolution image datasets exceed memory.

Action

  • Load images in bounded batches while preserving sample IDs and deterministic validation/test ordering.

Check

  • Check sample/label parity, prediction row alignment and peak memory against eager loading.

Limits

  • Batch loading establishes memory feasibility, not an isolated predictive gain.
  • Resolution/augmentation diversity and nineteen-run averaging are separate experiments.

Find source links and value grades in the lesson atlas