Save and load models with joblib

Learn to save and load machine learning models using joblib in Python. This Applied AI engineering lesson covers core concepts, step-by-step walkthroughs, comparisons, and troubleshooting for reliable model persistence.

Focus: save and load models with joblib

Sponsored

You’ve just spent hours training a model — tuning hyperparameters, wrestling with a tricky dataset, and finally getting metrics you’re proud of. Then your script ends, and the model vanishes into the void. Next time you need it, you’re back to square one: retraining from scratch, hoping the data hasn’t changed, and burning compute and time. If this sounds familiar, you’re facing the persistence problem — and solving it is non-negotiable for any real-world AI application. This lesson shows you how to save and load models with joblib, the tool Python’s scikit-learn ecosystem uses for exactly this job, so your models survive restarts, get deployed to production, and actually become reusable assets.

The problem this lesson solves

In a typical ML workflow, you train a model and then… what? In a Jupyter notebook or a training script, the model lives only in memory. Close the kernel, reboot the server, or finish the pipeline, and it’s gone. Every retraining run costs you:

  • Time — training a random forest on a large dataset can take minutes or hours.
  • Compute — every retrain consumes CPU/GPU cycles and energy.
  • Money — especially if you’re using cloud instances or managed services.
  • Consistency — you might retrain on slightly different data splits, leading to drift and irreproducible results.

The pain is immediate when you need to: - Serve a model behind an API (e.g., Flask, FastAPI, or a cloud function). - Share a trained model with a colleague or a deployment team. - A/B test or roll back to a previous model version. - Keep a model in a notebook or CI/CD artifact for later evaluation.

The solution is model persistence: serialize the trained object to disk, and later deserialize it back into memory. joblib is the de facto standard for this in the Python data science ecosystem, especially for models trained with scikit-learn. It handles large NumPy arrays efficiently, is fast, and is battle-tested in production.

Core concept / mental model

Think of training a model as cooking a complex dish. The recipe (your training code) and the ingredients (your dataset) are important, but the finished dish — the trained model — is what you actually serve. You wouldn’t throw away the dish and re-cook it every time a customer orders it. You’d store it properly (in a fridge or freezer) so you can reheat and serve instantly.

joblib is your fridge-freezer for trained models. It serializes the model’s internal state — the learned weights, coefficients, decision trees, etc. — into a binary file on disk. When you need the model, you can load it back into memory in seconds, ready to predict.

Technically, joblib uses its own serialization format, optimized for Python objects that contain large NumPy arrays. Under the hood, it picks the fastest serialization method (pickle or a more efficient alternative) and compresses the data to save space. The result is a single file (often with .joblib extension) that contains everything needed to reconstruct the model.

Key concepts: - Serialization — converting a Python object into a byte stream. - Deserialization — converting the byte stream back into a live Python object. - Pickle — Python’s standard serialization module; joblib is built on top of it but with better performance for NumPy arrays. - Model artifact — the saved file that encapsulates the trained model.

How it works step by step

Saving and loading with joblib is as simple as three lines of code, but there’s a mental model to follow for a robust workflow.

  1. Train your model — fit it on your training data as usual.
  2. Import joblibimport joblib gives you dump and load functions.
  3. Save the model — call joblib.dump(model, 'model.joblib'). The second argument can be a file path or a file object.
  4. Load the model later — call model = joblib.load('model.joblib'), and you get a fully functional model.
  5. Use the loaded model — call predict or transform as normal.

Why does joblib beat plain pickle? - Speed — joblib fast-tracks NumPy arrays, avoiding Python’s slow pickle protocol for these large binary objects. - Memory efficiency — it can dump to disk as a single file or a directory of files, and it supports compression out of the box. - Compatibility — it’s the recommended way in scikit-learn docs, so it’s safe for models like Pipeline, GridSearchCV, and custom transformers.

Important: The saved model is tied to the Python environment and library versions. If you update scikit-learn or joblib, the model may not load or may behave differently. For production, always pin versions or re-save the model after upgrading.

Hands-on walkthrough

Let’s put this into practice. We’ll train a simple classifier on the iris dataset, save it, reload it, and verify predictions match.

# file: save_model.py
from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
import joblib

# 1. Load data and train
iris = load_iris()
X_train, X_test, y_train, y_test = train_test_split(
    iris.data, iris.target, test_size=0.2, random_state=42
)

model = RandomForestClassifier(n_estimators=100, random_state=42)
model.fit(X_train, y_train)

# 2. Save the model
joblib.dump(model, 'iris_model.joblib')
print("Model saved to iris_model.joblib")

# Verify file size
import os
print(f"File size: {os.path.getsize('iris_model.joblib')} bytes")

Expected output:

Model saved to iris_model.joblib
File size: 114389 bytes

Now load the model in a separate script (or a new notebook cell) and use it:

# file: load_model.py
import joblib
import numpy as np

# 1. Load the saved model
loaded_model = joblib.load('iris_model.joblib')

# 2. Make a prediction on a new sample
sample = np.array([[5.1, 3.5, 1.4, 0.2]])  # looks like a setosa
prediction = loaded_model.predict(sample)
print(f"Predicted class: {prediction[0]}")  # 0 = setosa

# 3. Verify the model is exactly the same
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
iris = load_iris()
X_train, X_test, y_train, y_test = train_test_split(
    iris.data, iris.target, test_size=0.2, random_state=42
)
print(f"Test accuracy (loaded): {loaded_model.score(X_test, y_test):.4f}")

Expected output:

Predicted class: 0
Test accuracy (loaded): 1.0000

If you rerun the original model.score(X_test, y_test), you get exactly the same accuracy — proving the loaded model is identical.

Pro tip: If you want to compress the file for storage or transfer, use the compress parameter:

joblib.dump(model, 'iris_model_compressed.joblib', compress=3)

The compress level ranges from 0 (no compression) to 9 (maximum). A value of 3 is a good trade-off between speed and size.

Compare options / when to choose what

joblib vs pickle — the classic comparison:

Feature joblib pickle
Default for scikit-learn Yes No
Handles large NumPy arrays Optimized (fast) Slow (generic protocol)
File format Single file or directory Single file
Compression Built-in (compress param) Not built-in (wrap with gzip)
Memory mapping for large models Supported Not natively
Compatibility with Python objects Good, but limited Broader (pickles almost any object)

When to choose what: - For models built with scikit-learn (including pipelines and grid search results), always use joblib. It’s faster, smaller, and the official recommendation. - For deep learning models (PyTorch, TensorFlow), use their native save methods (torch.save, model.save). Joblib is not designed for those large graph structures. - For sending a model to a non-Python system (via JSON/ONNX), you need a different serialization format — joblib is Python-only.

Alternatives to joblib:

  1. pickle — standard library, more general, but slower for big arrays.
  2. cloudpickle — extended pickle that can serialize more object types, but still Python-only.
  3. ONNX (Open Neural Network Exchange) — formats the model as a portable graph, allowing cross-framework deployment (e.g., to mobile, C++). Great for interop but more complex setup.
  4. Model-specific save methods — e.g., torch.save, tf.keras.models.save_model — always prefer these for their respective frameworks.

Bottom line: For an Applied AI engineer working in Python with scikit-learn, joblib is your go-to. It’s simple, fast, and widely used in production pipelines.

Troubleshooting & edge cases

Error: ModuleNotFoundError: No module named 'joblib'

Install it: pip install joblib. It’s a dependency of scikit-learn, so it’s usually already present.

Error: AttributeError: 'RandomForestClassifier' object has no attribute 'predict' after loading

This almost always means the model was saved incorrectly (e.g., you saved the training data instead), or the class definition changed. Re-check your dump call — you must pass the model object, not the predictions.

Error: ValueError: Cannot load pickle file

This happens when you try to load a file that isn’t a valid joblib artifact (e.g., corrupt file, or you used pickle.load on a joblib dump). Always use joblib.load.

Edge case: Loading model in a different Python version

You may see ImportError or AttributeError if the model was pickled with a newer version of the library. Always test loading in a fresh environment, or use version pinning (e.g., scikit-learn==1.3.2) in your deployment requirements.

Edge case: Very large models (over 1 GB)

Joblib is memory-efficient, but loading a huge model can still take time and RAM. Consider using mmap_mode='r' when loading to memory-map the arrays from disk instead of loading them fully into RAM:

model = joblib.load('large_model.joblib', mmap_mode='r')

This is useful for prediction servers where you need to serve multiple workers from the same file.

Pro tip: Always save your model after training, but also save the preprocessing pipeline (e.g., StandardScaler + model) as a single Pipeline object. That way, you never forget to scale new data before predicting.

What you learned & what's next

You now know how to save and load models with joblib — the core concept, the step-by-step workflow, comparisons with alternatives, and common pitfalls. You can make your ML pipelines persistent, shareable, and production-ready.

Key achievements: - You understand why model persistence matters. - You can use joblib.dump and joblib.load confidently. - You know when to choose joblib over pickle or framework-native methods. - You can troubleshoot common serialization errors.

Next step: In the next lesson of the Applied AI engineering track, you’ll build on this skill to deploy a model as a REST API — you’ll pair your saved model with a web framework like FastAPI to serve predictions to real users. You’ll also learn how to version your models for robust CI/CD pipelines. Keep this lesson handy: every time you train a model, you’ll now save it first.

Practice recap

Try this: train a LogisticRegression on the breast cancer dataset, save it with compression (compress=3), then load it in a new script and check the prediction on a single sample. Experiment with compress levels to see the file size vs. load speed trade-off. Next, bundle your preprocessing and model into a Pipeline and save that — it’s a tiny change that will prevent hours of debugging later.

Common mistakes

  • Saving the model with pickle.dump and loading with joblib.load — these formats are not interchangeable; always use matching methods.
  • Forgetting to install joblib when using a minimal environment, leading to ModuleNotFoundError.
  • Saving a model and then upgrading scikit-learn — often causes load failures due to version mismatches.
  • Only saving the trained model but not the preprocessing pipeline, so predictions on new data are wrong because scaling is missed.

Variations

  1. Use cloudpickle if you need to serialize custom Python objects with functions, but stick with joblib for scikit-learn models.
  2. For deep learning, use framework-native methods like torch.save or model.save in Keras.
  3. Export to ONNX for cross-platform deployment if you need to serve models outside Python.

Real-world use cases

  • A Flask API that loads a pre-trained recommender model at startup and serves personalized recommendations instantly.
  • Batch inference in a nightly cron job that reloads a churn prediction model, eliminating costly retrains each run.
  • Sharing a trained model artifact with a data science team so they can reproduce results without retraining.

Key takeaways

  • Model persistence prevents repetitive retraining and ensures reproducibility across sessions.
  • joblib is the standard for saving scikit-learn models because it's fast and handles NumPy arrays efficiently.
  • A save-load workflow is just two functions: joblib.dump and joblib.load.
  • Always save the full pipeline, not just the estimator, to avoid silent preprocessing errors at inference.
  • Version your dependencies and model files to maintain compatibility in production.
  • Compress large models with compress to save disk space, or use memory mapping for massive models.

Sponsored

Sponsored

Discussion

Questions, corrections, and tips help everyone reading this page.

0 comments

Add a comment

Shown publicly with your comment.

Be constructive · max 4,000 characters

No comments yet — start the thread.

Related tutorials, quizzes, and articles for this topic.