Сохранение и использование модели

Сохранение обученной модели через joblib

Содержание курса

Сохранить и загрузить модель: пример без CSV

Установите scikit-learn и joblib в своё Python-окружение. Этот пример обучает модель на данных, записанных прямо в коде, сохраняет её во временную папку и проверяет загрузку:

from pathlib import Path
from tempfile import TemporaryDirectory
import joblib
from sklearn.linear_model import LinearRegression

model = LinearRegression().fit([[1], [2], [3]], [3, 5, 7])
with TemporaryDirectory() as directory:
    path = Path(directory) / "model.joblib"
    joblib.dump(model, path)
    restored = joblib.load(path)
    print(round(restored.predict([[4]])[0], 6))

Результат — 9.0. Временная папка удалится после выхода из with; для постоянного хранения задайте свой путь вместо неё. Загружайте только созданный вами или проверенным источником файл: joblib.load может исполнить код из подменённого артефакта. Окружение для загрузки должно соответствовать окружению обучения.

Ниже разберём более полный вариант: сохранение модели доставки, списка признаков и версии библиотеки.

В прошлом уроке вы обучили LogisticRegression на данных о задержках доставки и сравнили её метрики с DummyClassifier. Объект lr с подобранными весами существовал в памяти — но только до конца той сессии. В новом сеансе переменные не сохраняются, поэтому здесь мы сначала воспроизведём обучение, а затем зафиксируем результат на диске через joblib, чтобы не повторять этот шаг каждый раз заново.

joblib.dump: сериализация обученной модели в бинарный файл

joblib превращает любой Python-объект в последовательность байт и записывает её в файл. Для scikit-learn это означает, что все внутренние атрибуты модели — веса coef_, смещение intercept_, параметры вроде C и max_iter — упакованы в один бинарный файл. Файл можно открыть в следующей сессии или скопировать на другую машину, однако scikit-learn не поддерживает загрузку между разными версиями как официальный контракт: файл, сохранённый в одном окружении, может не загрузиться в другом. Даже при совпадающих версиях Python, scikit-learn и joblib корректный результат — это воспроизводимое окружение, а не безусловная гарантия.

Именно поэтому позже мы запишем метаданные с версией библиотеки — чтобы перед загрузкой сверить окружения и не получить молчаливо неверный результат.

Переменные не сохраняются между сессиями, поэтому начнём с полного воспроизводимого блока обучения — он запускается здесь целиком и ни на какие переменные из предыдущего урока не рассчитывает:

import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
import joblib

df = pd.read_csv('delivery_risk.csv')

features = ['distance_km', 'items_count', 'warehouse_load', 'forecast_rain_mm', 'is_weekend']
X = df[features]
y = df['delayed']

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42, stratify=y
)

lr = LogisticRegression(max_iter=1000, random_state=42)
lr.fit(X_train, y_train)

Теперь lr — обученный объект в памяти. Стоит перезапустить ядро, и все веса исчезнут.

Критический момент: порядок вызовов. joblib.dump не знает, обучена ли модель. Если вызвать его до lr.fit(X_train, y_train), файл всё равно создастся — без ошибки и без предупреждения. Внутри окажется объект с параметрами, заданными при создании (max_iter=1000, random_state=42), но без обученных коэффициентов — атрибут coef_ будет отсутствовать. При последующей попытке предсказания такой объект выбросит NotFittedError: scikit-learn проверяет состояние fit перед predict и не допускает работы необученной модели.

Чтобы зафиксировать результат на диске, сначала убеждаемся в fitted-состоянии, а затем делаем единственный вызов joblib.dump:

assert hasattr(lr, 'coef_'), 'модель не обучена — fit не вызван'

saved = joblib.dump(lr, 'model.joblib')
print(saved)  # ['model.joblib']

coef_ появляется у LogisticRegression только после успешного fit, поэтому его наличие — надёжный индикатор fitted-состояния. Это особенно важно в длинных ноутбуках, где ячейки легко запустить не в том порядке. joblib.dump возвращает список путей записанных файлов — обычно из одного элемента. Сам файл бинарный: открывать его текстовым редактором бессмысленно.

После вызова joblib.dump файл model.joblib содержит полный снимок обученной модели.

Начать заново?

Помощник начнёт новый диалог по этой задаче. Текущая переписка останется в истории до конца срока хранения.

О переписке

Можно приложить до трёх фото PNG, JPEG или WebP по 5 МБ.

Переписка и фото хранятся 90 дней после последнего принятого сообщения. Преподаватель получит доступ к диалогу, когда вы попросите его помочь.

Проверка решения остаётся отдельным действием.