Модель joblib не загружается: где хранить собственные функции
Pipeline сохранился и загрузился в той же сессии ноутбука. В отдельном скрипте загрузка падает: собственная функция осталась в __main__ или модуль недоступен. Проверка внутри обучающего процесса не проверила переносимость кода.
Артефакт может ссылаться на код по имени
При обычной pickle-сериализации пользовательские функции и классы часто восстанавливаются через имя модуля и объекта. Поэтому принимающему процессу нужен импортируемый модуль с совместимой реализацией. Сохранённый объект не является самостоятельной копией всего проекта.
У FunctionTransformer особенно легко оставить lambda или функцию из ячейки. Для передачи через стандартный joblib путь с именованной функцией в установленном пакете понятнее. Если выбираете формат, который встраивает больше Python-кода, отдельно учитывайте его требования к совместимости. Формат не отменяет фиксации зависимостей.
Проверить восстановление без обучающей сессии
Пример создаёт маленький модуль feature_rules.py в временном каталоге. Pipeline использует его функцию, обучается, сохраняется и загружается уже другим процессом Python. Дочерний процесс получает путь к модулю через PYTHONPATH. В обычном проекте вместо этого устанавливают версию вашего пакета.
Функция действительно входит в Pipeline и вызывается при прогнозе. Новый процесс проверяет численный результат, а не только успешный load. Так тест обнаружил бы и отсутствие модуля, и несовместимый новый вариант преобразования.
import importlib
import os
from pathlib import Path
import subprocess
import sys
from tempfile import TemporaryDirectory
import joblib
from sklearn.linear_model import LinearRegression
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import FunctionTransformer
with TemporaryDirectory() as directory:
root = Path(directory)
(root / "feature_rules.py").write_text(
"def double_column(X):\n"
" return [[row[0] * 2] for row in X]\n", encoding="utf-8")
sys.path.insert(0, str(root))
try:
rules = importlib.import_module("feature_rules")
model = make_pipeline(FunctionTransformer(rules.double_column),
LinearRegression()).fit([[0], [1], [2]], [0, 2, 4])
path = root / "model.joblib"
joblib.dump(model, path)
env = os.environ.copy()
env["PYTHONPATH"] = str(root) + os.pathsep + env.get("PYTHONPATH", "")
check = ("import joblib,sys; m=joblib.load(sys.argv[1]); "
"p=float(m.predict([[4]])[0]); assert abs(p-8)<1e-9; print(p)")
result = subprocess.run([sys.executable, "-c", check, str(path)],
env=env, capture_output=True, text=True, check=True)
assert abs(float(result.stdout.strip()) - 8.0) < 1e-9
print("fresh process prediction:", result.stdout.strip())
finally:
sys.path.remove(str(root))
sys.modules.pop("feature_rules", None)Версионировать функцию вместе с моделью
Если сохранить старую модель и затем изменить feature_rules.double_column под тем же именем, load может выполниться, но прогноз будет относиться уже к новому преобразованию. Поэтому артефакт должен ссылаться на конкретную версию пакета, а не просто на название файла. Контрольные сырые входы помогают проверить весь путь после установки.
При приёмке запускайте загрузку в чистом процессе с теми зависимостями, которые получит исполнитель. Наличие нужного объекта в globals обучающего ноутбука больше не должно помогать тесту. Для ошибки ModuleNotFoundError сначала восстановите правильную версию пакета, затем повторите контрольный прогноз.
Закрепить базовую операцию
В открытом уроке joblib вы сохраните и восстановите обученную модель. Следующим шагом выполните загрузку из отдельного процесса, как в статье, если в модели есть ваш Python-код. Откройте урок «Сохранение обученной модели через joblib». Затем вернитесь к сценарию статьи и проверьте его на своём небольшом наборе. Посмотрите программу курса первой ML-модели, чтобы пройти путь от обучения к использованию результата.
Продолжить на практике
- Машинное обучение с нуля на Python: первая модель ↗
Бесплатный курс машинного обучения на Python: обучите первую модель в scikit-learn, проверьте её качество и решите 106 задач в браузере.
