Rukovodstvo
статьи и идеи для разработчиков программного обеспечения и веб-разработчиков.
scikit-learn: Сохранение и восстановление моделей
Во многих случаях при работе с библиотекой scikit-learn [http://scikit-learn.org/stable/] вам необходимо сохранить свои модели прогнозов в файл, а затем восстановить их, чтобы повторно использовать вашу предыдущую работу. чтобы: протестировать вашу модель на новых данных, сравнить несколько моделей или что-то еще. Эта процедура сохранения также известна как сериализация объекта — представляет объект с потоком байтов, чтобы сохранить его на диске, отправить по сети или сохранить в базе данных, в то время как процедура восстановления
Время чтения: 7 мин.
Во многих случаях при работе с библиотекой 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: %".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
Модуль 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: %".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 и изменениями в модели обучения. С другой стороны, ручной подход сложнее реализовать, и его необходимо модифицировать при любых изменениях в структуре модели, но с другой стороны, он может быть легко адаптирован к различным потребностям и не имеет проблем с совместимостью.
Licensed under CC BY-NC-SA 4.0
Руководство по развертыванию моделей машинного обучения в рабочей среде в качестве API с помощью Flask
Друзья, в конце марта мы запускаем новый поток по курсу «Data Scientist». И прямо сейчас начинаем делиться с вами полезным материалом по курсу.
Вспоминая ранний опыт своего увлечения машинным обучением (ML) могу сказать, что много усилий уходило на построение действительно хорошей модели. Я советовался с экспертами в этой области, чтобы понять, как улучшить свою модель, думал о необходимых функциях, пытался убедиться, что все предлагаемые ими советы учтены. Но все же я столкнулся с проблемой.
Как же внедрить модель в реальный проект? Идей на этот счет у меня не было. Вся литература, которую я изучал до этого момента, фокусировалась только на улучшении моделей. Я не видел следующего шага в их развитии.

Именно поэтому я сейчас пишу это руководство. Мне хочется, чтобы вы столкнулись с той проблемой, с которой столкнулся я в свое время, но смогли достаточно быстро ее решить. К концу этой статьи я покажу вам как реализовать модель машинного обучения используя фреймворк Flask на Python.
- Варианты реализации моделей машинного обучения.
- Что такое API?
- Установка среды для Python и базовые сведения о Flask.
- Создание модели машинного обучения.
- Сохранения модели машинного обучения: Сериализация и Десериализация.
- Создание API с использованием Flask.
К примеру, большинство ML специалистов используют R или Python для своих научных исканий. Однако потребителями этих моделей будут инженеры-программисты, которые используют совсем другой стек технологий. Есть два варианта, которыми можно решить эту проблему:
Вариант 1: Переписать весь код на том языке, с которым работают инженеры-разработчики. Звучит в какой-то степени логично, однако необходимо большое количество сил и времени, чтобы тиражировать разработанные модели. По итогу это получается просто тратой времени. Большинство языков, как например JavaScript, не имеют удобных библиотек для работы с ML. Поэтому будет достаточно рациональным решением этот вариант не использовать.
Вариант 2: Использовать API. Сетевые API решили проблему работы с приложениями на разных языках. Если фронт-энд разработчику необходимо использовать вашу модель машинного обучения, чтобы на ее основе создать веб-приложение, им нужно всего лишь получить URL конечного сервера, обсуживающего API.
Что такое API?
Если говорить простыми словами, то API (Application Programming Interface) – это своеобразный договор между двумя программами, говорящий, что если пользовательская программа предоставляет входные данные в определенном формате, то программа разработчика (API) пропускает их через себя и выдает необходимые пользователю выходные данные.
Вы сможете самостоятельно прочитать пару статей, в которых хорошо описано, почему API – это достаточно популярный выбор среди разработчиков.
- История API
- Введение в API
Например, одним из таких поставщиков API является Google со своим Google Vision API.
Все, что необходимо сделать разработчику, это просто вызвать REST (Representational State Transfer) API с помощью SDK, предоставляемой Google. Посмотрите, что можно сделать используя Google Vision API.
Звучит замечательно, не так ли? В этой статье мы разберемся, как создать свое собственное API с использованием Flask, фреймворка на Python.
Внимание: Flask — это не единственный сетевой фреймворк для этих целей. Есть еще Django, Falcon, Hug и множество других, о которых в данной статье не говорится. Например, для R есть пакет, который называется plumber
Установка среды для Python и базовые сведения о Flask.
1) Создание виртуальной среды с использованием Anaconda. Если вам необходимо создать свою виртуальную среду для Python и сохранить необходимое состояние зависимостей, то Anaconda предлагает для этого хорошие решения. Дальше будет вестись работа с командной строкой.
- Здесь вы найдете установщик miniconda для Python;
- wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh
- bash Miniconda3-latest-Linux-x86_64.sh
- Следуйте последовательности вопросов.
- source .bashrc
- Если вы введете: conda , то сможете увидеть список доступных команд и помощь.
- Чтобы создать новую среду, введите: conda create —name python=3.6
- Следуйте шагам, которые вам будет предложено сделать и в конце введите: source activate
- Установите необходимые пакеты Python. Самые важные это flask и gunicorn.
- Откройте свой любимый текстовый редактор и создайте в папке файл hello-world.py
- Напишите следующий код:
"""Filename: hello-world.py """ from flask import Flask app = Flask(__name__) @app.route('/users/') def hello_world(username=None): return("Hello <>!".format(username))

- Сохраните файл и вернитесь к терминалу.
- Для запуска API выполните в терминале: gunicorn —bind 0.0.0.0:8000 hello-world:app
- Если получите следующее, то вы на правильном пути:

- В браузере введите следующее: https://localhost:8000/users/any-name
Ура! Вы написали свою первую программу на Flask! Поскольку у вас уже есть некоторый опыт в выполнении этих простых шагов, мы сможем создать сетевые конечные точки, которые могут быть доступны локально.
Используя Flask мы можем оборачивать наши модели и использовать их в качестве Web API. Если мы хотим создавать более сложные сетевые приложения (например, на JavaScript), то нам нужно добавить некоторые изменения.
Создание модели машинного обучения.
- Для начала займемся соревнованием по машинному обучению Loan Prediction Competition. Основная цель состоит в том, чтобы настроить предобработку пайплайна (pre-processing pipeline) и создать ML модели для облегчения задачи прогнозирования во время развертывания.
import os import json import numpy as np import pandas as pd from sklearn.externals import joblib from sklearn.model_selection import train_test_split, GridSearchCV from sklearn.base import BaseEstimator, TransformerMixin from sklearn.ensemble import RandomForestClassifier from sklearn.pipeline import make_pipeline import warnings warnings.filterwarnings("ignore")
- Сохраняем датасет в папке:
!ls /home/pratos/Side-Project/av_articles/flask_api/data/
test.csv training.csv
data = pd.read_csv('../data/training.csv')
list(data.columns)
['Loan_ID', 'Gender', 'Married', 'Dependents', 'Education', 'Self_Employed', 'ApplicantIncome', 'CoapplicantIncome', 'LoanAmount', 'Loan_Amount_Term', 'Credit_History', 'Property_Area', 'Loan_Status']
data.shape
(614, 13)
ul>
Находим null/Nan значения в столбцах:
for _ in data.columns: print("The number of null values in:<> == <>".format(_, data[_].isnull().sum()))
The number of null values in:Loan_ID == 0 The number of null values in:Gender == 13 The number of null values in:Married == 3 The number of null values in:Dependents == 15 The number of null values in:Education == 0 The number of null values in:Self_Employed == 32 The number of null values in:ApplicantIncome == 0 The number of null values in:CoapplicantIncome == 0 The number of null values in:LoanAmount == 22 The number of null values in:Loan_Amount_Term == 14 The number of null values in:Credit_History == 50 The number of null values in:Property_Area == 0 The number of null values in:Loan_Status == 0
- Следующим шагом создаем датасеты для обучения и тестирования:
red_var = ['Gender','Married','Dependents','Education','Self_Employed','ApplicantIncome','CoapplicantIncome',\ 'LoanAmount','Loan_Amount_Term','Credit_History','Property_Area'] X_train, X_test, y_train, y_test = train_test_split(data[pred_var], data['Loan_Status'], \ test_size=0.25, random_state=42)
- Чтобы убедиться, что все шаги предобработки (pre-processing) выполнены верно даже после того как мы провели эксперименты, и мы не упустили ничего во время прогнозирования, мы создадим собственный оценщик на Scikit-learn для предобработки (pre-processing Scikit-learn estimator).
from sklearn.base import BaseEstimator, TransformerMixin class PreProcessing(BaseEstimator, TransformerMixin): """Custom Pre-Processing estimator for our use-case """ def __init__(self): pass def transform(self, df): """Regular transform() that is a help for training, validation & testing datasets (NOTE: The operations performed here are the ones that we did prior to this cell) """ pred_var = ['Gender','Married','Dependents','Education','Self_Employed','ApplicantIncome',\ 'CoapplicantIncome','LoanAmount','Loan_Amount_Term','Credit_History','Property_Area'] df = df[pred_var] df['Dependents'] = df['Dependents'].fillna(0) df['Self_Employed'] = df['Self_Employed'].fillna('No') df['Loan_Amount_Term'] = df['Loan_Amount_Term'].fillna(self.term_mean_) df['Credit_History'] = df['Credit_History'].fillna(1) df['Married'] = df['Married'].fillna('No') df['Gender'] = df['Gender'].fillna('Male') df['LoanAmount'] = df['LoanAmount'].fillna(self.amt_mean_) gender_values = married_values = education_values = employed_values = property_values = dependent_values = df.replace(, inplace=True) return df.as_matrix() def fit(self, df, y=None, **fit_params): """Fitting the Training dataset & calculating the required values from train e.g: We will need the mean of X_train['Loan_Amount_Term'] that will be used in transformation of X_test """ self.term_mean_ = df['Loan_Amount_Term'].mean() self.amt_mean_ = df['LoanAmount'].mean() return self
- Конвертируем y_train и y_test в np.array :
y_train = y_train.replace().as_matrix() y_test = y_test.replace().as_matrix()
Создадим пайплайн чтобы убедиться, что все шаги предобработки, которые мы делаем это работа оценщика scikit-learn.
pipe = make_pipeline(PreProcessing(), RandomForestClassifier())
pipe
Pipeline(memory=None, steps=[('preprocessing', PreProcessing()), ('randomforestclassifier', RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini', max_depth=None, max_features='auto', max_leaf_nodes=None, min_impurity_decrease=0.0, min_impurity_split=None, min_samples_leaf=1, min_samples_split=2, min_weight_fraction_leaf=0.0, n_estimators=10, n_jobs=1, oob_score=False, random_state=None, verbose=0, warm_start=False))])
Для поиска подходящих гипер-параметров (степень для полиномиальных объектов и альфа для ребра) сделаем поиск по сетке (Grid Search):
- Определяем param_grid:
param_grid =
- Запускам поиск по сетке:
grid = GridSearchCV(pipe, param_grid=param_grid, cv=3)
- Подгоняем обучающие данные для оценщика пайплайна (pipeline estimator):
grid.fit(X_train, y_train)
GridSearchCV(cv=3, error_score='raise', estimator=Pipeline(memory=None, steps=[('preprocessing', PreProcessing()), ('randomforestclassifier', RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini', max_depth=None, max_features='auto', max_leaf_nodes=None, min_impurity_decrease=0.0, min_impu. _jobs=1, oob_score=False, random_state=None, verbose=0, warm_start=False))]), fit_params=None, iid=True, n_jobs=1, param_grid=, pre_dispatch='2*n_jobs', refit=True, return_train_score=True, scoring=None, verbose=0)
- Посмотрим, какой параметр выбрал поиск по сетке:
print("Best parameters: <>".format(grid.best_params_))
Best parameters:
print("Validation set score: ".format(grid.score(X_test, y_test)))
Validation set score: 0.79
- Загрузим тестовый набор:
test_df = pd.read_csv('../data/test.csv', encoding="utf-8-sig") test_df = test_df.head()
grid.predict(test_df)
array([1, 1, 1, 1, 1])
Наш пайплайн выглядит достаточно хорошо, чтобы перейти к следующему важному шагу: Сериализации модели машинного обучения.
Сохранение модели машинного обучения: Сериализация и Десериализация.
«В computer science, в контексте хранение данных, сериализация – это процесс перевода структур данных или состояний объекта в хранимый формат (например, файл или буфер памяти) и воссоздания позже в этой же или другой среде компьютера.»
В Python консервация (pickling) – это стандартный способ хранение объектов и позже получения их в исходном состоянии. Чтобы звучало понятнее, приведу простой пример:
list_to_pickle = [1, 'here', 123, 'walker'] #Pickling the list import pickle list_pickle = pickle.dumps(list_to_pickle)
list_pickle
b'\x80\x03]q\x00(K\x01X\x04\x00\x00\x00hereq\x01K
Затем мы снова выгрузим законсервированный объект:
loaded_pickle = pickle.loads(list_pickle)
loaded_pickle
[1, 'here', 123, 'walker']
Мы можем сохранять законсервированные объекты в файл и использовать их. Этот метод похож на создание .rda файлов, как в программировании на R, например.
Заметка: Некоторым может не понравиться такой способ консервации для сериализации. Альтернативой может стать h5py .
У нас имеется пользовательский класс (Class), который нам нужно импортировать пока идет обучение (training), поэтому мы будем использовать модуль dill для упаковки оценщика класса (Class) с объектом сетки.
Желательно создать отдельный файл training.py , содержащий весь код для обучния модели. (Пример можно посмотреть здесь).
!pip install dill
Requirement already satisfied: dill in /home/pratos/miniconda3/envs/ordermanagement/lib/python3.5/site-packages
import dill as pickle filename = 'model_v1.pk'
with open('../flask_api/models/'+filename, 'wb') as file: pickle.dump(grid, file)
Модель сохранится в выбранной выше директории. Как только модель законсервирована, ее можно обернуть в Flask wrapper. Однако перед этим нужно убедиться, что законсервированный файл работает. Давайте загрузим его обратно и сделаем прогноз:
with open('../flask_api/models/'+filename ,'rb') as f: loaded_model = pickle.load(f)
loaded_model.predict(test_df)
array([1, 1, 1, 1, 1])
Поскольку мы выполнили шаги предобрабоки для того, чтобы вновь поступившие данные были частью пайплайна, нам просто нужно запустить predict(). Используя библиотеку scikit-learn достаточно просто работать с пайплайнами. Оценщики и пайплайны берегут ваше время и нервы, даже если первоначальная реализация кажется дикой.
Создание API с использованием Flask
Давайте сохраним структуру папок максимально простой:

В создании wrapper функции apicall() есть три важные части:
- Получение request данных (для которых будет делаться прогноз);
- Загрузка законсервированного оценщика;
- Перевод наших прогнозов в формат JSON и получение ответа status code: 200 ;
Заметка: Вы можете отправлять обычный текст, XML, cvs или картинку напрямую для взаимозаменяемости формата, однако предпочтительнее в нашем случае использовать именно JSON.
"""Filename: server.py """ import os import pandas as pd from sklearn.externals import joblib from flask import Flask, jsonify, request app = Flask(__name__) @app.route('/predict', methods=['POST']) def apicall(): """API Call Pandas dataframe (sent as a payload) from API Call """ try: test_json = request.get_json() test = pd.read_json(test_json, orient='records') #To resolve the issue of TypeError: Cannot compare types 'ndarray(dtype=int64)' and 'str' test['Dependents'] = [str(x) for x in list(test['Dependents'])] #Getting the Loan_IDs separated out loan_ids = test['Loan_ID'] except Exception as e: raise e clf = 'model_v1.pk' if test.empty: return(bad_request()) else: #Load the saved model print("Loading the model. ") loaded_model = None with open('./models/'+clf,'rb') as f: loaded_model = pickle.load(f) print("The model has been loaded. doing predictions now. ") predictions = loaded_model.predict(test) """Add the predictions as Series to a new pandas dataframe OR Depending on the use-case, the entire test data appended with the new files """ prediction_series = list(pd.Series(predictions)) final_predictions = pd.DataFrame(list(zip(loan_ids, prediction_series))) """We can be as creative in sending the responses. But we need to send the response codes as well. """ responses = jsonify(predictions=final_predictions.to_json(orient="records")) responses.status_code = 200 return (responses)
После выполнения, введите: gunicorn --bind 0.0.0.0:8000 server:app
Давайте сгенерируем данные для прогнозирования и очередь для локального запуска API по адресу https:0.0.0.0:8000/predict
import json import requests
"""Setting the headers to send and accept json responses """ header = """Reading test batch """ df = pd.read_csv('../data/test.csv', encoding="utf-8-sig") df = df.head() """Converting Pandas Dataframe to json """ data = df.to_json(orient='records')
data
"""POST /predict """ resp = requests.post("http://0.0.0.0:8000/predict", \ data = json.dumps(data),\ headers= header)
resp.status_code
"""The final response we get is as follows: """ resp.json()
В этой статье мы прошли лишь половину пути, создав рабочее API, которое выдает прогнозы, и стали на шаг ближе к интеграции ML решений непосредственно в разрабатываемые приложения. Мы создали достаточно простое API, которое поможет в прототипировании продукта и сделает его действительно функциональным, но чтобы отправить его в продакшн, необходимо внести несколько корректив, которые уже не входят в сферу изучения машинного обучения.
Есть несколько вещей, о которых нельзя забывать, в процессе создания API:
- Создание качественного API из спагетти-кода вещь почти невозможная, поэтому применяйте свои знания в области машинного обучения, чтобы создать полезное и удобное API.
- Попробуйте использовать контроль версий для моделей и кода API. Помните о том, что Flask не обеспечивает поддержку средств контроля версий. Сохранение и отслеживание ML моделей – это сложная задача, найдите удобный для себя способ. Здесь есть статья, которая рассказывает о том, как это делать.
- В связи со спецификой scikit-learn моделей, необходимо удостовериться что оценщик и код для обучения лежат рядом (в случае использования пользовательского оценщика для предобработки или иной подобной задачи). Таким образом законсервированная модель будет иметь рядом с собой оценщик класса.
Полезные источники:
Вот такой получился материал. Подписывайтесь на нас, если понравилась публикация, а также записывайтесь на бесплатный открытый вебинар по теме: «Метрические алгоритмы классификации», который уже 12 марта проведет разработчик и data scientist с 5-летним опытом — Александр Никитин.
- machine learning
- python
- data science
- Блог компании OTUS
- Машинное обучение
Как сохранить модель машинного обучения в файл?
Возможно ли сохранить обученную модель машинного обучения в файл и открыть этот файл в другом .py файле? Пробовал joblib и pickle. Эти способы сработали только в рамках одного файла (обучаю модель, сохраняю в файл, загружаю в нем же). Но при обучении и сохранении в одном .py файле и загрузке модели для предсказания в другом .py файле возникает ошибка. в первом файле:
clf = LogisticRegression(max_iter=1000) clf.fit(df_train,df_train_target) joblib.dump(clf, "model.pkl")
clf2 = joblib.load("model.pkl") y_pred = clf2.predict(df_val)

ошибка:
Отслеживать
51.6k 199 199 золотых знаков 59 59 серебряных знаков 242 242 бронзовых знака
Сохранение обученной ML модели (объекта python) в базе данных (BLOB)
Стандартная архитектура при создании сервисов с использованием машинного обучения подразумеват обучение модели на одном сервере и её использование на другом сервере, который непосредственно работает на предикт. Однако, на предикт может работать много серверов, и возникает вопрос: как доставить обученную модель на все сервера? Обычно модель сохраняют в виде файла. Поэтому можно сделать mount какого-то общего сетевого ресурса, и на нём хранить модель. Но более гибко будет сохранить модель в базе данных, тем более что все инстансы уже скорее всего имеют соединение с общей базой.
Ниже приведу пример того, как сохранить объект python в базе данных в колонке типа блоб.
import _pickle import pymysql.cursors ## Для простоты возьмём лёгкий объект # однако обученная модель может достигать сотни мегабайт, а то и ещё больше listToPickle = [(10, 10), ("example", 10.0), (1.0, 2.0), "object"] ## Преобразование нашего объекта в строку pickledList = _pickle.dumps(listToPickle) ## Соединение с базой данных mysql connection = pymysql.connect(host='localhost', port=3306, user='dbuser', password='pass', db='somedb', cursorclass=pymysql.cursors.DictCursor) ## создание курсора with connection.cursor() as cursor: ## Вставка в БД cursor.execute("""INSERT INTO pickleTest VALUES (NULL, 'testCard', %s)""", (pickledList, )) connection.commit() ## Выборка по сохраненной модели cursor.execute("""SELECT features FROM pickleTest WHERE card = 'testCard'""") ## Получим и распечатаем результат res = cursor.fetchone() ## Обратное преобразование unpickledList = _pickle.loads(res['pickledStoredList']) print(unpickledList)
Как видим всё просто, достаточно перед сохранием в блоб преобразовать объект с помощью _pickle.dumps() а при загрузке объекта обратно преобразовать в строку в объект с помощью _pickle.loads().
Также отмечу, что в третьем питоне не надо устанавливать дополнительных библиотек, используется встроенная библиотека _pickle. Таким образом Pickling в Python — это способ хранения весьма сложных структур данных в двоичном представлении, которые требуется восстановить через некоторое время, для получения той же структуры данных.
Запись опубликована 10.07.2019 автором zab88 в рубрике Без рубрики с метками blob, mysql, python.