Kahibaro
Discord Login Register

13.6 Reproducible Packaging of Code and Weights

What “reproducible packaging” means in practice

Reproducible packaging is the discipline of bundling everything needed to run your model later, on a different machine, or by a different person, and getting the same behavior. In deep learning this is not only the model weights. It also includes the exact model code that defines the architecture, the preprocessing steps that turn raw inputs into tensors, the label mapping, the dependency versions, and enough metadata to understand how the checkpoint was produced.

The goal is that inference becomes a scripted, predictable procedure: create the same environment, load the same artifacts, apply the same preprocessing, run the model, and interpret outputs in the same way.

If you package only a .pth file with weights, you have not packaged a runnable model. You also need the model definition, preprocessing, and the metadata that explains how to interpret inputs and outputs.

The minimum artifact set you should ship

For a beginner friendly but robust packaging approach, aim to ship a small folder that contains a checkpoint, a configuration file, and small pieces of metadata used at inference time. The key idea is that every value required to reconstruct inference is stored, not assumed.

A typical minimal set looks like this: a model checkpoint file, a config.json that records architecture choices and preprocessing parameters, a label mapping file such as labels.json for classification, and a requirements.txt or environment.yml that captures dependencies. If you used a tokenizer, vocabulary, or feature normalizer, include those files too, for example tokenizer.json, vocab.txt, mean_std.json, or a serialized sklearn preprocessor if that is part of your pipeline.

Keep the folder structure stable so your loading code can be simple and reliable.

Always store the label to index mapping used during training. If the order changes at inference time, your predictions will be assigned to the wrong classes even if the model is correct.

Putting model configuration in a file, not in your head

Weights alone do not tell you which hidden sizes, number of layers, dropout, vocabulary size, or input dimension were used. You should write these into a configuration file and treat it as a required input to model construction.

A practical pattern is to define a plain Python dictionary during training, save it as JSON, and use it during inference to rebuild the model object before loading weights. The configuration should also include information about input expectations such as image size, normalization constants, feature order for tabular data, tokenization settings, and maximum sequence length.

If preprocessing differs between training and inference, your model can fail silently. Always package preprocessing parameters and enforce them in the inference code.

Saving enough training metadata to be able to trust the artifact

Even if you only need inference, you still want provenance. Store the git commit hash or a version string of your code, the dataset version identifier if you have one, and the key training settings that affect outputs such as random seed, number of epochs, and validation metric at the time of saving.

A lightweight approach is to include a metadata.json alongside the checkpoint. It can contain the timestamp, PyTorch version, CUDA version if applicable, and the best validation score. This is not about recreating training perfectly, it is about being able to answer what produced this model.

Determinism versus reproducibility for packaged inference

For packaged inference, you usually want determinism. That means you run the same model on the same input and get the same output. Many models include dropout or other stochastic components, so you must set the model to evaluation mode.

The basic rule is to call model.eval() before inference. If you have randomness in preprocessing, remove it or fix it. If you rely on GPU kernels, note that bitwise identical results across different hardware are not always guaranteed, but for most deployment settings, consistent predictions within a tiny tolerance is acceptable.

Always run inference with model.eval(). Forgetting this keeps dropout and certain normalization behavior in training mode and can change outputs.

A reproducible loading contract

A good packaging strategy creates a clear contract between artifacts and code. The contract is that a single function can take the artifact directory and return a ready to run predictor with no hidden dependencies.

At a conceptual level, the load procedure is: read configuration, construct model, load weights, load any preprocessing assets, set device, set evaluation mode, and return a callable that applies preprocessing, forward pass, and postprocessing.

Your predictor should also validate inputs. For example, check that a tabular feature list matches the expected order from the config, or that an image has the expected number of channels. Failing fast is part of reproducibility because it prevents silent misuse.

Add explicit checks that inputs match the expected schema. Silent schema drift is one of the most common causes of “it worked yesterday” failures.

Environment pinning and dependency capture

Your model code depends on a Python environment. If you want others to reproduce behavior, you need to provide a way to recreate that environment. The simplest approach for beginners is a requirements.txt with pinned versions such as torch==2.3.1 rather than torch>=2.0.

If you use Conda, include an environment.yml. If you need higher assurance, consider containerization later, but for this chapter the key is to record exact versions of core libraries like PyTorch, torchvision, numpy, and tokenizers.

Also record the Python version. Small differences can matter, especially around serialization, tokenization, and numerical libraries.

Relative paths, portability, and “no local secrets”

Packaged artifacts should not rely on absolute paths like /home/yourname/data. Use paths relative to the package root. Make sure the loading code can locate files by joining the artifact directory with known filenames.

Avoid packaging anything that is machine specific or secret, such as API keys, local database credentials, or internal filesystem assumptions. If you need configurable values, put them in a separate runtime configuration that is not committed with the model artifacts.

Versioning model packages like products

Treat each packaged model as a versioned release. A simple convention is semantic versioning for the model interface. Increment versions when you change inputs, preprocessing, output meanings, or label sets. Store the version in metadata.json and include it in logs at inference.

Keeping an explicit version helps you compare runs fairly, roll back safely, and avoid confusing two similar looking checkpoints.

If you change the preprocessing, label set, or input schema, you must bump the model package version. Otherwise old clients may feed incompatible data to a new model.

A final checklist before you call it “reproducible”

A package is in good shape when a fresh environment can run a small smoke test. The smoke test should load the artifacts, run one or two fixed inputs that you include as test fixtures, and verify outputs are consistent with expected results within tolerance. This turns reproducibility from a hope into an automated check.

If you can clone the repo, create the environment, download the artifact folder, run a single command, and get the expected prediction, you have achieved reproducible packaging of code and weights.

Views: 59

Comments

Please login to add a comment.

Don't have an account? Register now!