scikit-learn: Сохранение и восстановление моделей

Во многих случаях при работе с библиотекой scikit-learn [http://scikit-learn.org/stable/] вам необходимо сохранить свои модели прогнозов в файл, а затем восстановить их, чтобы повторно использовать вашу предыдущую работу. чтобы: протестировать вашу модель на новых данных, сравнить несколько моделей или что-то еще. Эта процедура сохранения также известна как сериализация объекта - представляет объект с потоком байтов, чтобы сохранить его на диске, отправить по сети или сохранить в базе данных, в то время как процедура восстановления

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

Инструменты для сохранения и восстановления моделей

Первый инструмент, который мы описываем, - это Pickle , стандартный инструмент Python для (де) сериализации объектов. После этого мы рассмотрим библиотеку Joblib, которая предлагает простую (де) сериализацию объектов, содержащих большие массивы данных, и, наконец, мы представляем ручной подход для сохранения и восстановления объектов в / из JSON (нотация объектов JavaScript). Ни один из этих подходов не представляет собой оптимального решения, но правильный выбор следует выбирать в соответствии с потребностями вашего проекта.

Инициализация модели

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

 from sklearn.linear_model import LogisticRegression 
 from sklearn.datasets import load_iris 
 from sklearn.model_selection import train_test_split 
 
 # Load and split data 
 data = load_iris() 
 Xtrain, Xtest, Ytrain, Ytest = train_test_split(data.data, data.target, test_size=0.3, random_state=4) 

Теперь давайте создадим модель с некоторыми параметрами, не заданными по умолчанию, и подгоним ее под данные обучения. Мы предполагаем, что вы ранее нашли оптимальные параметры модели, т. Е. Те, которые обеспечивают максимальную расчетную точность.

 # Create a model 
 model = LogisticRegression(C=0.1, 
 max_iter=20, 
 fit_intercept=True, 
 n_jobs=3, 
 solver='liblinear') 
 model.fit(Xtrain, Ytrain) 

И наша получившаяся модель:

 LogisticRegression(C=0.1, class_weight=None, dual=False, fit_intercept=True, 
 intercept_scaling=1, max_iter=20, multi_class='ovr', n_jobs=3, 
 penalty='l2', random_state=None, solver='liblinear', tol=0.0001, 
 verbose=0, warm_start=False) 

Используя fit метод, модель узнала ее коэффициенты , которые хранятся в model.coef_ . Цель состоит в том, чтобы сохранить параметры и коэффициенты модели в файл, чтобы вам не нужно было снова повторять шаги обучения модели и оптимизации параметров для новых данных.

Модуль рассола

В следующих нескольких строках кода модель, которую мы создали на предыдущем шаге, сохраняется в файл, а затем загружается как новый объект с именем pickled_model . Затем загруженная модель используется для расчета показателя точности и прогнозирования результатов на новых невидимых (тестовых) данных.

 import pickle 
 
 # 
 # Create your model here (same as above) 
 # 
 
 # Save to file in the current working directory 
 pkl_filename = "pickle_model.pkl" 
 with open(pkl_filename, 'wb') as file: 
 pickle.dump(model, file) 
 
 # Load from file 
 with open(pkl_filename, 'rb') as file: 
 pickle_model = pickle.load(file) 
 
 # Calculate the accuracy score and predict target values 
 score = pickle_model.score(Xtest, Ytest) 
 print("Test score: {0:.2f} %".format(100 * score)) 
 Ypredict = pickle_model.predict(Xtest) 

Выполнение этого кода должно дать ваш результат и сохранить модель через Pickle:

 $ python save_model_pickle.py 
 Test score: 91.11 % 

Самое замечательное в использовании Pickle для сохранения и восстановления наших моделей обучения заключается в том, что это быстро

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

    tuple_objects = (model, Xtrain, Ytrain, score)

    Save tuple

    pickle.dump(tuple_objects, open(“tuple_model.pkl”, ‘wb’))

    Restore tuple

    pickled_model, pickled_Xtrain, pickled_Ytrain, pickled_score = pickle.load(open(“tuple_model.pkl”, ‘rb’))

Модуль Joblib

Библиотека Joblib предназначена для замены Pickle для объектов, содержащих большие данные. Повторим процедуру сохранения и восстановления, как и в случае с Pickle.

 from sklearn.externals import joblib 
 
 # Save to file in the current working directory 
 joblib_file = "joblib_model.pkl" 
 joblib.dump(model, joblib_file) 
 
 # Load from file 
 joblib_model = joblib.load(joblib_file) 
 
 # Calculate the accuracy and predictions 
 score = joblib_model.score(Xtest, Ytest) 
 print("Test score: {0:.2f} %".format(100 * score)) 
 Ypredict = pickle_model.predict(Xtest) 

 $ python save_model_joblib.py 
 Test score: 91.11 % 

Как видно из примера, библиотека Joblib предлагает немного более простой рабочий процесс по сравнению с Pickle. В то время как Pickle требует передачи файлового объекта в качестве аргумента, Joblib работает как с файловыми объектами, так и с строковыми именами файлов. Если ваша модель содержит большие массивы данных, каждый массив будет храниться в отдельном файле, но процедура сохранения и восстановления останется прежней. Joblib также поддерживает различные методы сжатия, такие как zlib, gzip, bz2, а также различные уровни сжатия.

Ручное сохранение и восстановление в JSON

В зависимости от вашего проекта, Pickle и Joblib часто оказываются неподходящими решениями. Некоторые из этих причин обсуждаются позже в разделе « Проблемы совместимости ». В любом случае, когда вы хотите иметь полный контроль над процессом сохранения и восстановления, лучший способ - создать свои собственные функции вручную.

Ниже показан пример сохранения и восстановления объектов вручную с помощью JSON. Этот подход позволяет нам выбрать данные, которые необходимо сохранить, такие как параметры модели, коэффициенты, данные обучения и все, что нам нужно.

Поскольку мы хотим сохранить все эти данные в одном объекте, один из возможных способов сделать это - создать новый класс, наследующий от класса модели, которым в нашем примере является LogisticRegression . Новый класс, названный MyLogReg , затем реализует методы save_json и load_json для сохранения и восстановления в / из файла JSON соответственно.

Для простоты мы сохраним только три параметра модели и данные обучения. Некоторые дополнительные данные, которые мы могли бы сохранить с помощью этого подхода, - это, например, оценка перекрестной проверки обучающего набора, тестовые данные, оценка точности тестовых данных и т. Д.

 import json 
 import numpy as np 
 
 class MyLogReg(LogisticRegression): 
 
 # Override the class constructor 
 def __init__(self, C=1.0, solver='liblinear', max_iter=100, X_train=None, Y_train=None): 
 LogisticRegression.__init__(self, C=C, solver=solver, max_iter=max_iter) 
 self.X_train = X_train 
 self.Y_train = Y_train 
 
 # A method for saving object data to JSON file 
 def save_json(self, filepath): 
 dict_ = {} 
 dict_['C'] = self.C 
 dict_['max_iter'] = self.max_iter 
 dict_['solver'] = self.solver 
 dict_['X_train'] = self.X_train.tolist() if self.X_train is not None else 'None' 
 dict_['Y_train'] = self.Y_train.tolist() if self.Y_train is not None else 'None' 
 
 # Creat json and save to file 
 json_txt = json.dumps(dict_, indent=4) 
 with open(filepath, 'w') as file: 
 file.write(json_txt) 
 
 # A method for loading data from JSON file 
 def load_json(self, filepath): 
 with open(filepath, 'r') as file: 
 dict_ = json.load(file) 
 
 self.C = dict_['C'] 
 self.max_iter = dict_['max_iter'] 
 self.solver = dict_['solver'] 
 self.X_train = np.asarray(dict_['X_train']) if dict_['X_train'] != 'None' else None 
 self.Y_train = np.asarray(dict_['Y_train']) if dict_['Y_train'] != 'None' else None 

Теперь попробуем класс MyLogReg Сначала мы создаем объект mylogreg , передаем ему обучающие данные и сохраняем в файл. Затем мы создаем новый объект json_mylogreg и вызываем load_json для загрузки данных из файла.

 filepath = "mylogreg.json" 
 
 # Create a model and train it 
 mylogreg = MyLogReg(X_train=Xtrain, Y_train=Ytrain) 
 mylogreg.save_json(filepath) 
 
 # Create a new object and load its data from JSON file 
 json_mylogreg = MyLogReg() 
 json_mylogreg.load_json(filepath) 
 json_mylogreg 

Распечатав новый объект, мы сможем увидеть наши параметры и данные обучения по мере необходимости.

 MyLogReg(C=1.0, 
 X_train=array([[ 4.3, 3. , 1.1, 0.1], 
 [ 5.7, 4.4, 1.5, 0.4], 
 ..., 
 [ 7.2, 3. , 5.8, 1.6], 
 [ 7.7, 2.8, 6.7, 2. ]]), 
 Y_train=array([0, 0, ..., 2, 2]), class_weight=None, dual=False, 
 fit_intercept=True, intercept_scaling=1, max_iter=100, 
 multi_class='ovr', n_jobs=1, penalty='l2', random_state=None, 
 solver='liblinear', tol=0.0001, verbose=0, warm_start=False) 

Поскольку сериализация данных с использованием JSON фактически сохраняет объект в строковом формате, а не в потоке байтов, файл mylogreg.json можно открывать и изменять с помощью текстового редактора. Хотя этот подход был бы удобен для разработчика, он менее безопасен, поскольку злоумышленник может просматривать и изменять содержимое файла JSON. Более того, этот подход больше подходит для объектов с небольшим количеством переменных экземпляра, таких как модели scikit-learn, потому что любое добавление новых переменных требует изменений в методах сохранения и восстановления.

Проблемы совместимости

Хотя некоторые из плюсов и минусов каждого инструмента до сих пор были рассмотрены в тексте, вероятно, самым большим недостатком инструментов Pickle и Joblib является их совместимость с различными моделями и версиями Python.

Совместимость версий Python. В документации к обоим инструментам указано, что не рекомендуется (де) сериализовать объекты в разных версиях Python, хотя это может работать при изменении второстепенных версий.

Совместимость модели. Одна из наиболее частых ошибок - сохранение модели с помощью Pickle или Joblib, а затем изменение модели перед попыткой восстановления из файла. Внутренняя структура модели должна оставаться неизменной между сохранением и перезагрузкой.

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

Выводы

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

comments powered by Disqus

Содержание