Каковы подводные камни использования Dill для сериализации моделей scikit-learn/statsmodels?
Мне нужно сериализовать модели scikit-learn/statsmodels, чтобы все зависимости (код + данные) были упакованы в артефакт, и этот артефакт можно использовать для инициализации модели и создания прогнозов. Использование pickle module
не является вариантом, потому что это будет только заботиться о зависимости данных (код не будет упакован). Итак, я проводил эксперименты с Dill. Чтобы уточнить мой вопрос, следующий пример - пример создания модели и ее сохранения.
from sklearn import datasets
from sklearn import svm
from sklearn.preprocessing import Normalizer
import dill
digits = datasets.load_digits()
training_data_X = digits.data[:-5]
training_data_Y = digits.target[:-5]
test_data_X = digits.data[-5:]
test_data_Y = digits.target[-5:]
class Model:
def __init__(self):
self.normalizer = Normalizer()
self.clf = svm.SVC(gamma=0.001, C=100.)
def train(self, training_data_X, training_data_Y):
normalised_training_data_X = normalizer.fit_transform(training_data_X)
self.clf.fit(normalised_training_data_X, training_data_Y)
def predict(self, test_data_X):
return self.clf.predict(self.normalizer.fit_transform(test_data_X))
model = Model()
model.train(training_data_X, training_data_Y)
print model.predict(test_data_X)
dill.dump(model, open("my_model.dill", 'w'))
В соответствии с этим, вот как я инициализирую сохраненную модель (в новом сеансе) и сделаю прогноз. Обратите внимание, что этот код явно не инициализирует или не знает о значении class Model
.
import dill
from sklearn import datasets
digits = datasets.load_digits()
training_data_X = digits.data[:-5]
training_data_Y = digits.target[:-5]
test_data_X = digits.data[-5:]
test_data_Y = digits.target[-5:]
with open("my_model.dill") as model_file:
model = dill.load(model_file)
print model.predict(test_data_X)
Кто-нибудь использовал Уилла таким образом?. Идея состоит в том, чтобы ученый-данник расширил ModelWrapper class
для каждой модели, которую они реализуют, а затем построил инфраструктуру вокруг этого, которая сохраняет модели, развертывает модели как службы и управляет всем жизненным циклом модели.
class ModelWrapper(object):
__metaclass__ = abc.ABCMeta
def __init__(self, model):
self.model = model
@abc.abstractmethod
def predict(self, input):
return
def dumps(self):
return dill.dumps(self)
def loads(self, model_string):
self.model = dill.loads(model_string)
Помимо последствий для безопасности (выполнение произвольного кода) и требования о том, что модули, такие как scikit-learn
, должны быть установлены на машине, обслуживающей модель, есть ли и какие-либо другие подводные камни в этом подходе? Любые комментарии или слова совета были бы наиболее полезными.
Я думаю, что YHat и Dato приняли аналогичный подход, но выкатили собственный реализации Уилла для аналогичных целей.
Ответы
Ответ 1
Я автор dill
. dill
был создан для того, чтобы делать именно то, что вы делаете... (чтобы сохранить числовые значения в экземплярах класса для статистики), где эти объекты затем могут быть распределены по разным ресурсам и выполняться неловко параллельно. Итак, ответ да - у меня есть код, похожий на ваш, используя mystic
и/или sklearn
.
Обратите внимание, что многие из авторов sklearn
используют cloudpickle
для включения параллельных вычислений на объектах sklearn
, а не dill
. dill
может рассортировать больше типов объектов, чем cloudpickle
, однако cloudpickle
немного лучше (в это время написания) на травильных объектах, которые ссылаются на глобальный словарь как часть закрытия - по умолчанию dill
делает это по ссылке, а cloudpickle
физически сохраняет зависимости. Однако dill
имеет режим "recurse"
, который действует как cloudpickle
, поэтому разница при использовании этого режима незначительна. (Чтобы включить режим "recurse"
, сделайте dill.settings['recurse'] = True
или используйте recurse=True
как флаг в dill.dump
). Другое незначительное отличие состоит в том, что cloudpickle
содержит специальную поддержку таких вещей, как scikits.timeseries
и PIL.Image
, а dill
- нет.
С положительной стороны dill
не разбирает классы по ссылке, поэтому, выбирая экземпляр класса, он сериализует сам объект класса - что является большим преимуществом, поскольку оно сериализует экземпляры производных классов классификаторов, моделей, и т.д. из sklearn
в их точном состоянии во время травления... поэтому, если вы вносите изменения в объект класса, экземпляр все еще печатает правильно. Существуют и другие преимущества dill
более cloudpickle
, помимо более широкого диапазона объектов (и, как правило, меньшего рассола), однако я не буду перечислять их здесь. Вы просили ловушки, поэтому различия не являются ловушками.
Основные подводные камни:
-
У вас должно быть все, на что ссылаются ваши классы, на
удаленный компьютер, на всякий случай dill
(или cloudpickle
) разжигает его
ссылка.
-
Вы должны попытаться сделать ваши классы и методы класса такими, как
самодостаточны (например, не ссылаются на объекты, определенные в
глобальный охват из ваших классов).
-
sklearn
объекты могут быть большими, поэтому сохранение многих из них в один
pickle не всегда хорошая идея... вы можете использовать klepto
который имеет интерфейс dict
для кэширования и архивирования и позволяет настроить интерфейс архива для хранения каждой пары значений ключа отдельно (например, одной записи на файл).
Ответ 2
Хорошо, в вашем примере кода pickle
может работать нормально, я все время использую pickle для упаковки модели и использую ее позже, если вы не хотите отправить модель напрямую на другой сервер или сохранить interpreter state
, потому что это то, что Dill
хорошо, а pickle
не может сделать. Это также зависит от вашего кода, каких типов и т.д. Вы используете, pickle
может завершиться неудачей, Dill
более стабилен.
Dill
в основном базируется на pickle
, и поэтому они очень похожи, некоторые вещи, которые вы должны учитывать/изучать:
-
Ограничения Dill
frame
, generator
, traceback
стандартные типы не могут быть упакованы.
-
cloudpickle
может быть хорошей идеей и для вашей проблемы, у нее есть лучшая поддержка при травлении объектов (чем рассол, а не лучше, чем у Дила), и вы также можете легко расчесывать код.
После того, как на целевой машине будут загружены правильные библиотеки (будьте осторожны для разных версий python
, так как они могут привести к ошибке вашего кода), все должно работать нормально как с Dill
, так и с cloudpickle
, пока вы не используете нестандартные стандартные типы.
Надеюсь, это поможет.
Ответ 3
Я пакет gaussian process (GP) из scikit-learn
с помощью pickle
.
Основная причина в том, что GP занимает много времени, чтобы строить и загружать гораздо быстрее, используя pickle
. Поэтому в моей инициализации кода я проверяю, обновлялись ли файлы данных для модели и при необходимости обновляли модель, иначе просто де-сериализуйте ее из pickle
!
Я использовал бы pickle
, dill
, cloudpickle
в соответствующем порядке.
Обратите внимание, что pickle
содержит аргумент ключевого слова protocol
, а некоторые значения могут значительно ускорить и значительно сократить использование памяти!
Наконец, я завершаю код сокета с сжатием из CPython STL, если это необходимо.