10. Сохранение модели#

Обзор методов сохранения моделей#

Метод сохранения

Преимущества

Риски / Недостатки

ONNX

  • Обслуживайте модели без среды Python

  • Обслуживание и обучение в независимых средах

  • Самый безопасный вариант

  • Не все модели scikit-learn поддерживаются

  • Пользовательские оценщики требуют больше работы для поддержки

  • Исходный объект Python утерян и не может быть восстановлен

skops.io

  • Более безопасно, чем pickle форматы на основе

  • Содержимое можно частично проверить без загрузки

  • Не так быстро, как pickle форматы на основе

  • Поддерживает меньше типов, чем pickle форматы на основе

  • Требует ту же среду, что и среда обучения

pickle

  • Встроено в Python

  • Может сериализовать большинство объектов Python

  • Эффективное использование памяти с protocol=5

  • Загрузка может выполнять произвольный код

  • Требует ту же среду, что и среда обучения

joblib

  • Эффективное использование памяти

  • Поддерживает отображение в память

  • Простые сокращения для сжатия и распаковки

  • Формат на основе Pickle

  • Загрузка может выполнять произвольный код

  • Требует ту же среду, что и среда обучения

cloudpickle

  • Может сериализовать непакетированный, пользовательский код Python

  • Сопоставимая эффективность загрузки с pickle с protocol=5

  • Формат на основе Pickle

  • Загрузка может выполнять произвольный код

  • Гарантии прямой совместимости отсутствуют

  • Требует ту же среду, что и среда обучения

После обучения модели scikit-learn желательно иметь способ сохранить модель для будущего использования без необходимости переобучения. В зависимости от вашего случая использования есть несколько различных способов сохранения модели scikit-learn, и здесь мы поможем вам решить, какой из них подходит вам лучше всего. Чтобы принять решение, вам нужно ответить на следующие вопросы:

  1. Нужен ли вам Python-объект после сохранения, или вам нужно только сохранить модель для её обслуживания и получения предсказаний?

Если вам нужно только обслуживать модель и дальнейшее исследование самого объекта Python не требуется, то ONNX может быть лучшим выбором для вас. Обратите внимание, что не все модели поддерживаются ONNX.

Если ONNX не подходит для вашего случая использования, следующий вопрос:

  1. Вы абсолютно доверяете источнику модели, или есть какие-либо проблемы безопасности относительно происхождения сохраненной модели?

Если у вас есть опасения по безопасности, то вам следует рассмотреть использование skops.io которая возвращает объект Python, но в отличие от pickle основанные на сохранении решения, загрузка сохраненной модели не позволяет автоматически выполнять произвольный код. Обратите внимание, что это требует ручного исследования сохраненного файла, который skops.io позволяет вам делать.

Другие решения предполагают, что вы абсолютно доверяете источнику загружаемого файла, поскольку все они подвержены выполнению произвольного кода при загрузке сохранённого файла, так как все они используют протокол pickle под капотом.

  1. Вас беспокоит производительность загрузки модели и её совместное использование между процессами, где полезен объект, отображенный в память на диске?

Если да, то вы можете рассмотреть использование joblib. Если это не является серьезной проблемой для вас, то вы можете использовать встроенный pickle модуль.

  1. Монреаль, PQ / Честервилл, ON pickle или joblib и обнаружили, что модель не может быть сохранена? Это может произойти, например, когда в вашей модели есть пользовательские функции.

Если да, то вы можете использовать cloudpickle который может сериализовать определенные объекты, которые не могут быть сериализованы с помощью pickle или joblib.

10.1. Обзор рабочего процесса#

В типичном рабочем процессе первый шаг — обучение модели с использованием scikit-learn и совместимых с scikit-learn библиотек. Обратите внимание, что поддержка scikit-learn и сторонних оценщиков варьируется в зависимости от различных методов сохранения.

10.1.1. Обучите и сохраните модель#

Создание подходящей модели зависит от вашего случая использования. В качестве примера здесь мы обучаем sklearn.ensemble.HistGradientBoostingClassifier на наборе данных iris:

>>> from sklearn import ensemble
>>> from sklearn import datasets
>>> clf = ensemble.HistGradientBoostingClassifier()
>>> X, y = datasets.load_iris(return_X_y=True)
>>> clf.fit(X, y)
HistGradientBoostingClassifier()

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

  • ONNX: Вам нужен ONNX время выполнения и среда с установленными соответствующими зависимостями для загрузки модели и использования времени выполнения для получения предсказаний. Эта среда может быть минимальной и не обязательно требует установки Python для загрузки модели и вычисления предсказаний. Также обратите внимание, что onnxruntime обычно требует значительно меньше оперативной памяти, чем Python, для вычисления предсказаний на небольших моделях.

  • skops.io, pickle, joblib, cloudpickle: Вам нужна среда Python с соответствующими зависимостями, установленными для загрузки модели и получения предсказаний из нее. Эта среда должна иметь те же пакеты и тот же версии как окружение, в котором модель была обучена. Обратите внимание, что ни один из этих методов не поддерживает загрузку модели, обученной с другой версией scikit-learn, и, возможно, другими версиями других зависимостей, таких как numpy и scipy. Другой проблемой может быть запуск сохраненной модели на другом оборудовании, и в большинстве случаев вы должны иметь возможность загрузить сохраненную модель на другое оборудование.

10.2. ONNX#

ONNX, или Open Neural Network Exchange формат наиболее подходит в случаях использования, когда необходимо сохранить модель, а затем использовать сохраненный артефакт для получения прогнозов без необходимости загружать сам объект Python. Это также полезно в случаях, когда среда обслуживания должна быть легкой и минимальной, поскольку ONNX время выполнения не требует python.

ONNX является бинарной сериализацией модели. Она была разработана для улучшения удобства использования совместимого представления моделей данных. Цель состоит в том, чтобы облегчить преобразование моделей данных между различными фреймворками машинного обучения и улучшить их переносимость на различных вычислительных архитектурах. Более подробная информация доступна в ONNX учебникДля преобразования модели scikit-learn в ONNX sklearn-onnx был разработан. Однако, не все модели scikit-learn поддерживаются, и он ограничен ядром scikit-learn и не поддерживает большинство сторонних оценщиков. Можно написать пользовательский конвертер для сторонних или пользовательских оценщиков, но документация для этого скудна, и это может быть сложно сделать.

Использование ONNX#

Чтобы преобразовать модель в ONNX формате, вам необходимо предоставить конвертеру некоторую информацию о входных данных, о которой можно прочитать подробнее здесь:

from skl2onnx import to_onnx
onx = to_onnx(clf, X[:1].astype(numpy.float32), target_opset=12)
with open("filename.onnx", "wb") as f:
    f.write(onx.SerializeToString())

Вы можете загрузить модель в Python и использовать ONNX время выполнения для получения предсказаний:

from onnxruntime import InferenceSession
with open("filename.onnx", "rb") as f:
    onx = f.read()
sess = InferenceSession(onx, providers=["CPUExecutionProvider"])
pred_ort = sess.run(None, {"X": X_test.astype(numpy.float32)})[0]

10.3. skops.io#

skops.io избегает использования pickle и загружает только файлы, которые содержат типы и ссылки на функции, доверенные по умолчанию или пользователем. Поэтому он предоставляет более безопасный формат, чем pickle, joblib, и cloudpickle.

Используя skops#

API очень похож на pickle, и вы можете сохранять свои модели, как объяснено в документация используя skops.io.dump и skops.io.dumps:

import skops.io as sio
obj = sio.dump(clf, "filename.skops")

И вы можете загрузить их обратно с помощью skops.io.load и skops.io.loads. Однако вам нужно указать типы, которым вы доверяете. Вы можете получить существующие неизвестные типы в дампе объекта / файла используя skops.io.get_untrusted_types, и после проверки его содержимого передать его функции загрузки:

unknown_types = sio.get_untrusted_types(file="filename.skops")
# investigate the contents of unknown_types, and only load if you trust
# everything you see.
clf = sio.load("filename.skops", trusted=unknown_types)

Пожалуйста, сообщайте о проблемах и запросах функций, связанных с этим форматом, на skops трекер проблем.

10.4. pickle, joblib, и cloudpickle#

Эти три модуля / пакета используют pickle протокол под капотом, но с небольшими вариациями:

  • pickle является модулем из стандартной библиотеки Python. Он может сериализовать и десериализовать любой объект Python, включая пользовательские классы и объекты Python.

  • joblib более эффективен, чем pickle при работе с большими моделями машинного обучения или большими массивами numpy.

  • cloudpickle может сериализовать определённые объекты, которые не могут быть сериализованы pickle или joblib, таких как пользовательские функции и лямбда- функции. Это может произойти, например, при использовании FunctionTransformer и использование пользовательской функции для преобразования данных.

Используя pickle, joblib, или cloudpickle#

В зависимости от вашего случая использования, вы можете выбрать один из этих трех методов для сохранения и загрузки вашей модели scikit-learn, и все они следуют одинаковому API:

# Here you can replace pickle with joblib or cloudpickle
from pickle import dump
with open("filename.pkl", "wb") as f:
    dump(clf, f, protocol=5)

Используя protocol=5 рекомендуется для уменьшения использования памяти и ускорения хранения и загрузки любого большого массива NumPy, хранящегося как подобранный атрибут в модели. В качестве альтернативы можно передать protocol=pickle.HIGHEST_PROTOCOL что эквивалентно protocol=5 в Python 3.8 и более поздних версиях (на момент написания).

И позже, когда потребуется, вы можете загрузить тот же объект из сохраненного файла:

# Here you can replace pickle with joblib or cloudpickle
from pickle import load
with open("filename.pkl", "rb") as f:
    clf = load(f)

10.5. Ограничения безопасности и поддерживаемости#

picklejoblib и cloudpickle по расширению), имеет множество задокументированных уязвимостей безопасности по своей природе и должен использоваться только если артефакт, т.е. pickle-файл, поступает из доверенного и проверенного источника. Никогда не загружайте pickle-файл из ненадежного источника, аналогично тому, как никогда не следует выполнять код из ненадежного источника.

Также обратите внимание, что произвольные вычисления могут быть представлены с использованием ONNX формат, и поэтому рекомендуется обслуживать модели с использованием ONNX в изолированной среде для защиты от вычислительных и эксплуатационных уязвимостей памяти.

Также обратите внимание, что нет поддерживаемых способов загрузить модель, обученную с другой версией scikit-learn. При использовании skops.io, joblib, pickle, или cloudpickle, модели, сохранённые с использованием одной версии scikit-learn, могут загружаться в других версиях, однако это полностью неподдерживаемо и не рекомендуется. Также следует иметь в виду, что операции, выполняемые с такими данными, могут давать разные и неожиданные результаты или даже привести к сбою вашего процесса Python.

Чтобы пересобрать аналогичную модель с будущими версиями scikit-learn, вместе с сохранённой моделью следует сохранить дополнительные метаданные:

  • Обучающие данные, например, ссылка на неизменяемый снимок

  • Исходный код Python, использованный для генерации модели

  • Версии scikit-learn и его зависимостей

  • Оценка перекрестной проверки, полученная на обучающих данных

Это должно позволить проверить, что оценка перекрестной проверки находится в том же диапазоне, что и ранее.

За исключением нескольких случаев, сохранённые модели должны быть переносимы между операционными системами и аппаратными архитектурами при условии использования тех же версий зависимостей и Python. Если вы столкнётесь с оценщиком, который не является переносимым, пожалуйста, создайте issue на GitHub. Сохранённые модели часто развёртываются в продакшене с использованием контейнеров, таких как Docker, чтобы заморозить окружение и зависимости.

Если вы хотите узнать больше об этих проблемах, обратитесь к следующим выступлениям:

10.5.1. Воспроизведение среды обучения в производстве#

Если версии используемых зависимостей могут отличаться от обучения к производству, это может привести к неожиданному поведению и ошибкам при использовании обученной модели. Чтобы предотвратить такие ситуации, рекомендуется использовать одинаковые зависимости и версии как в обучающей, так и в производственной среде. Эти транзитивные зависимости могут быть зафиксированы с помощью инструментов управления пакетами, таких как pip, mamba, conda, poetry, conda-lock, pixi, и т.д.

Не всегда возможно загрузить модель, обученную с более старыми версиями библиотеки scikit-learn и её зависимостей, в обновлённой программной среде. Вместо этого может потребоваться переобучить модель с новыми версиями всех библиотек. Поэтому при обучении модели важно записать рецепт обучения (например, скрипт на Python) и информацию о наборе обучения, а также метаданные о всех зависимостях, чтобы иметь возможность автоматически воссоздать ту же среду обучения для обновлённого программного обеспечения.

InconsistentVersionWarning#

Когда оценщик загружается с версией scikit-learn, которая несовместима с версией, в которой оценщик был сохранен, возникает InconsistentVersionWarning вызывается. Это предупреждение может быть перехвачено для получения исходной версии, с которой был сериализован оценщик:

from sklearn.exceptions import InconsistentVersionWarning
warnings.simplefilter("error", InconsistentVersionWarning)

try:
    with open("model_from_previous_version.pickle", "rb") as f:
        est = pickle.load(f)
except InconsistentVersionWarning as w:
    print(w.original_sklearn_version)

10.5.2. Обслуживание артефакта модели#

Последним шагом после обучения модели scikit-learn является обслуживание модели. После успешной загрузки обученной модели её можно обслуживать для обработки различных запросов на предсказание. Это может включать развёртывание модели как веб-сервиса с использованием контейнеризации или других стратегий развёртывания моделей, в соответствии со спецификациями.

10.6. Обобщение ключевых моментов#

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

  • ONNX: Он предоставляет единый формат для сохранения любой модели машинного обучения или глубокого обучения (кроме scikit-learn) и полезен для вывода модели (предсказаний). Однако это может привести к проблемам совместимости с различными фреймворками.

  • skops.io: Обученные модели scikit-learn могут быть легко распространены и внедрены в производство с использованием skops.io. Это более безопасно по сравнению с альтернативными подходами, основанными на pickle ослабляет это предположение и исследует все возможные масштабы плотности, создавая альтернативное представление задачи кластеризации.

  • joblib: Эффективные техники отображения памяти делают его быстрее при использовании той же сохраненной модели в нескольких процессах Python при использовании mmap_mode="r". Он также предоставляет простые ярлыки для сжатия и распаковки сохранённого объекта без необходимости в дополнительном коде. Однако он может запустить выполнение вредоносного кода при загрузке модели из ненадёжного источника, как и любой другой механизм сохранения на основе pickle.

  • pickle: Он является родным для Python, и большинство объектов Python могут быть сериализованы и десериализованы с использованием pickle, включая пользовательские Python классы и функции, если они определены в пакете, который можно импортировать в целевом окружении. Хотя pickle можно использовать для простого сохранения и загрузки моделей scikit-learn, это может вызвать выполнение вредоносного кода при загрузке модели из ненадёжного источника. pickle также может быть очень эффективным с точки зрения памяти, если модель была сохранена с protocol=5 но он не поддерживает отображение в память.

  • cloudpickle: Имеет сопоставимую эффективность загрузки с pickle и joblib (без отображения в память), но предоставляет дополнительную гибкость для сериализации пользовательского кода Python, такого как лямбда-выражения и интерактивно определенные функции и классы. Это может быть последним средством для сохранения конвейеров с пользовательскими компонентами Python, такими как sklearn.preprocessing.FunctionTransformer который оборачивает функцию, определённую в самом обучающем скрипте или, в более общем случае, вне любого импортируемого пакета Python. Обратите внимание, что cloudpickle не предоставляет гарантий прямой совместимости, и вам может потребоваться та же версия cloudpickle для загрузки сохранённой модели вместе с той же версией всех библиотек, используемых для определения модели. Как и другие механизмы сохранения на основе pickle, это может вызвать выполнение вредоносного кода при загрузке модели из ненадёжного источника.