Друзья, в конце марта мы запускаем новый поток по курсу «Data Scientist». И прямо сейчас начинаем делиться с вами полезным материалом по курсу.
Введение
Вспоминая ранний опыт своего увлечения машинным обучением (ML) могу сказать, что много усилий уходило на построение действительно хорошей модели. Я советовался с экспертами в этой области, чтобы понять, как улучшить свою модель, думал о необходимых функциях, пытался убедиться, что все предлагаемые ими советы учтены. Но все же я столкнулся с проблемой.
Как же внедрить модель в реальный проект? Идей на этот счет у меня не было. Вся литература, которую я изучал до этого момента, фокусировалась только на улучшении моделей. Я не видел следующего шага в их развитии.
![](https://habrastorage.org/webt/dp/dc/yl/dpdcylcyxdnyzfasmeoxefjf-e8.png)
Именно поэтому я сейчас пишу это руководство. Мне хочется, чтобы вы столкнулись с той проблемой, с которой столкнулся я в свое время, но смогли достаточно быстро ее решить. К концу этой статьи я покажу вам как реализовать модель машинного обучения используя фреймворк Flask на Python.
Содержание
Варианты реализации моделей машинного обучения.
В большинстве случаев реальное использование моделей машинного обучения – это центральная часть разработки, даже если это всего лишь маленький компонент системы автоматизации рассылки электронной почты или чатбот. Порой случаются моменты, когда барьеры в реализации кажутся непреодолимыми.
К примеру, большинство ML специалистов используют R или Python для своих научных исканий. Однако потребителями этих моделей будут инженеры-программисты, которые используют совсем другой стек технологий. Есть два варианта, которыми можно решить эту проблему:
Вариант 1: Переписать весь код на том языке, с которым работают инженеры-разработчики. Звучит в какой-то степени логично, однако необходимо большое количество сил и времени, чтобы тиражировать разработанные модели. По итогу это получается просто тратой времени. Большинство языков, как например JavaScript, не имеют удобных библиотек для работы с ML. Поэтому будет достаточно рациональным решением этот вариант не использовать.
Вариант 2: Использовать API. Сетевые API решили проблему работы с приложениями на разных языках. Если фронт-энд разработчику необходимо использовать вашу модель машинного обучения, чтобы на ее основе создать веб-приложение, им нужно всего лишь получить URL конечного сервера, обсуживающего API.
Что такое API?
Если говорить простыми словами, то API (Application Programming Interface) – это своеобразный договор между двумя программами, говорящий, что если пользовательская программа предоставляет входные данные в определенном формате, то программа разработчика (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 предлагает для этого хорошие решения. Дальше будет вестись работа с командной строкой.
2) Мы попробуем создать свое простое «Hello world» приложение на Flask с использованием gunicorn.
![](https://habrastorage.org/webt/fl/69/hj/fl69hjmljbp5ezgivwxa6o-b73k.jpeg)
![](https://habrastorage.org/webt/8i/gj/v-/8igjv-h34rhxj6rie9rbffgdda8.jpeg)
Ура! Вы написали свою первую программу на Flask! Поскольку у вас уже есть некоторый опыт в выполнении этих простых шагов, мы сможем создать сетевые конечные точки, которые могут быть доступны локально.
Используя Flask мы можем оборачивать наши модели и использовать их в качестве Web API. Если мы хотим создавать более сложные сетевые приложения (например, на JavaScript), то нам нужно добавить некоторые изменения.
Создание модели машинного обучения.
Чтобы понять как мы его создали, прочитайте следующее.
Создадим пайплайн чтобы убедиться, что все шаги предобработки, которые мы делаем это работа оценщика scikit-learn.
Для поиска подходящих гипер-параметров (степень для полиномиальных объектов и альфа для ребра) сделаем поиск по сетке (Grid Search):
Наш пайплайн выглядит достаточно хорошо, чтобы перейти к следующему важному шагу: Сериализации модели машинного обучения.
Сохранение модели машинного обучения: Сериализация и Десериализация.
В Python консервация (pickling) – это стандартный способ хранение объектов и позже получения их в исходном состоянии. Чтобы звучало понятнее, приведу простой пример:
Затем мы снова выгрузим законсервированный объект:
Мы можем сохранять законсервированные объекты в файл и использовать их. Этот метод похож на создание
Заметка: Некоторым может не понравиться такой способ консервации для сериализации. Альтернативой может стать
У нас имеется пользовательский класс (Class), который нам нужно импортировать пока идет обучение (training), поэтому мы будем использовать модуль
Желательно создать отдельный файл
Модель сохранится в выбранной выше директории. Как только модель законсервирована, ее можно обернуть в Flask wrapper. Однако перед этим нужно убедиться, что законсервированный файл работает. Давайте загрузим его обратно и сделаем прогноз:
Поскольку мы выполнили шаги предобрабоки для того, чтобы вновь поступившие данные были частью пайплайна, нам просто нужно запустить predict(). Используя библиотеку scikit-learn достаточно просто работать с пайплайнами. Оценщики и пайплайны берегут ваше время и нервы, даже если первоначальная реализация кажется дикой.
Создание API с использованием Flask.
Давайте сохраним структуру папок максимально простой:
![](https://habrastorage.org/webt/y4/hm/2p/y4hm2pao2sje1zdmwe4t8cnjvhg.png)
В создании wrapper функции
HTTP сообщения создаются из заголовка и тела. В общем случае основное содержимое тела передается в формате JSON. Мы будем отправлять (
Заметка: Вы можете отправлять обычный текст, XML, cvs или картинку напрямую для взаимозаменяемости формата, однако предпочтительнее в нашем случае использовать именно JSON.
После выполнения, введите:
Давайте сгенерируем данные для прогнозирования и очередь для локального запуска API по адресу
Заключение
В этой статье мы прошли лишь половину пути, создав рабочее API, которое выдает прогнозы, и стали на шаг ближе к интеграции ML решений непосредственно в разрабатываемые приложения. Мы создали достаточно простое API, которое поможет в прототипировании продукта и сделает его действительно функциональным, но чтобы отправить его в продакшн, необходимо внести несколько корректив, которые уже не входят в сферу изучения машинного обучения.
Есть несколько вещей, о которых нельзя забывать, в процессе создания API:
Следующим логическим шагом будет создание механики для развертывания такого API на маленькой виртуальной машине. Есть разные способы сделать это, однако их мы рассмотрим в следующей статье.
Код и пояснения для этой статьи
Полезные источники:
[1] Don’t Pickle your data.
[2] Building Scikit Learn compatible transformers.
[3] Using jsonify in Flask.
[4] Flask-QuickStart.
Вот такой получился материал. Подписывайтесь на нас, если понравилась публикация, а также записывайтесь на бесплатный открытый вебинар по теме: «Метрические алгоритмы классификации», который уже 12 марта проведет разработчик и data scientist с 5-летним опытом — Александр Никитин.
Введение
Вспоминая ранний опыт своего увлечения машинным обучением (ML) могу сказать, что много усилий уходило на построение действительно хорошей модели. Я советовался с экспертами в этой области, чтобы понять, как улучшить свою модель, думал о необходимых функциях, пытался убедиться, что все предлагаемые ими советы учтены. Но все же я столкнулся с проблемой.
Как же внедрить модель в реальный проект? Идей на этот счет у меня не было. Вся литература, которую я изучал до этого момента, фокусировалась только на улучшении моделей. Я не видел следующего шага в их развитии.
![](https://habrastorage.org/webt/dp/dc/yl/dpdcylcyxdnyzfasmeoxefjf-e8.png)
Именно поэтому я сейчас пишу это руководство. Мне хочется, чтобы вы столкнулись с той проблемой, с которой столкнулся я в свое время, но смогли достаточно быстро ее решить. К концу этой статьи я покажу вам как реализовать модель машинного обучения используя фреймворк 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 является 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 <environment-name> python=3.6
- Следуйте шагам, которые вам будет предложено сделать и в конце введите:
source activate <environment-name>
- Установите необходимые пакеты Python. Самые важные это flask и gunicorn.
2) Мы попробуем создать свое простое «Hello world» приложение на Flask с использованием gunicorn.
- Откройте свой любимый текстовый редактор и создайте в папке файл
hello-world.py
- Напишите следующий код:
"""Filename: hello-world.py
"""
from flask import Flask
app = Flask(__name__)
@app.route('/users/<string:username>')
def hello_world(username=None):
return("Hello {}!".format(username))
- Сохраните файл и вернитесь к терминалу.
- Для запуска API выполните в терминале:
gunicorn --bind 0.0.0.0:8000 hello-world:app
- Если получите следующее, то вы на правильном пути:
![](https://habrastorage.org/webt/fl/69/hj/fl69hjmljbp5ezgivwxa6o-b73k.jpeg)
- В браузере введите следующее:
https://localhost:8000/users/any-name
![](https://habrastorage.org/webt/8i/gj/v-/8igjv-h34rhxj6rie9rbffgdda8.jpeg)
Ура! Вы написали свою первую программу на 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)
- Находим 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 = {'Female' : 0, 'Male' : 1}
married_values = {'No' : 0, 'Yes' : 1}
education_values = {'Graduate' : 0, 'Not Graduate' : 1}
employed_values = {'No' : 0, 'Yes' : 1}
property_values = {'Rural' : 0, 'Urban' : 1, 'Semiurban' : 2}
dependent_values = {'3+': 3, '0': 0, '2': 2, '1': 1}
df.replace({'Gender': gender_values, 'Married': married_values, 'Education': education_values, 'Self_Employed': employed_values, 'Property_Area': property_values, 'Dependents': dependent_values}, 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({'Y':1, 'N':0}).as_matrix()
y_test = y_test.replace({'Y':1, 'N':0}).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 = {"randomforestclassifier__n_estimators" : [10, 20, 30],
"randomforestclassifier__max_depth" : [None, 6, 8, 10],
"randomforestclassifier__max_leaf_nodes": [None, 5, 10, 20],
"randomforestclassifier__min_impurity_split": [0.1, 0.2, 0.3]}
- Запускам поиск по сетке:
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={'randomforestclassifier__n_estimators': [10, 20, 30], 'randomforestclassifier__max_leaf_nodes': [None, 5, 10, 20], 'randomforestclassifier__min_impurity_split': [0.1, 0.2, 0.3], 'randomforestclassifier__max_depth': [None, 6, 8, 10]},
pre_dispatch='2*n_jobs', refit=True, return_train_score=True,
scoring=None, verbose=0)
- Посмотрим, какой параметр выбрал поиск по сетке:
print("Best parameters: {}".format(grid.best_params_))
Best parameters: {'randomforestclassifier__n_estimators': 30, 'randomforestclassifier__max_leaf_nodes': 20, 'randomforestclassifier__min_impurity_split': 0.2, 'randomforestclassifier__max_depth': 8}
- Подсчитаем:
print("Validation set score: {:.2f}".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{X\x06\x00\x00\x00walkerq\x02e.'
Затем мы снова выгрузим законсервированный объект:
loaded_pickle = pickle.loads(list_pickle)
loaded_pickle
[1, 'here', 123, 'walker']
Мы можем сохранять законсервированные объекты в файл и использовать их. Этот метод похож на создание
.rda
файлов, как в программировании на R, например.Заметка: Некоторым может не понравиться такой способ консервации для сериализации. Альтернативой может стать
h5py
. У нас имеется пользовательский класс (Class), который нам нужно импортировать пока идет обучение (training), поэтому мы будем использовать модуль
dill
для упаковки оценщика класса (Class) с объектом сетки.Желательно создать отдельный файл
training.py
, содержащий весь код для обучния модели. (Пример можно посмотреть здесь)- Устанавливаем
dill
!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.
Давайте сохраним структуру папок максимально простой:
![](https://habrastorage.org/webt/y4/hm/2p/y4hm2pao2sje1zdmwe4t8cnjvhg.png)
В создании wrapper функции
apicall()
есть три важные части:- Получение
request
данных (для которых будет делаться прогноз); - Загрузка законсервированного оценщика;
- Перевод наших прогнозов в формат JSON и получение ответа
status code: 200
;
HTTP сообщения создаются из заголовка и тела. В общем случае основное содержимое тела передается в формате JSON. Мы будем отправлять (
POST url-endpoint/
) поступающие данные как пакет для получения прогнозов. Заметка: Вы можете отправлять обычный текст, 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 = {'Content-Type': 'application/json', 'Accept': 'application/json'}
"""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
'[{"Loan_ID":"LP001015","Gender":"Male","Married":"Yes","Dependents":"0","Education":"Graduate","Self_Employed":"No","ApplicantIncome":5720,"CoapplicantIncome":0,"LoanAmount":110.0,"Loan_Amount_Term":360.0,"Credit_History":1.0,"Property_Area":"Urban"},{"Loan_ID":"LP001022","Gender":"Male","Married":"Yes","Dependents":"1","Education":"Graduate","Self_Employed":"No","ApplicantIncome":3076,"CoapplicantIncome":1500,"LoanAmount":126.0,"Loan_Amount_Term":360.0,"Credit_History":1.0,"Property_Area":"Urban"},{"Loan_ID":"LP001031","Gender":"Male","Married":"Yes","Dependents":"2","Education":"Graduate","Self_Employed":"No","ApplicantIncome":5000,"CoapplicantIncome":1800,"LoanAmount":208.0,"Loan_Amount_Term":360.0,"Credit_History":1.0,"Property_Area":"Urban"},{"Loan_ID":"LP001035","Gender":"Male","Married":"Yes","Dependents":"2","Education":"Graduate","Self_Employed":"No","ApplicantIncome":2340,"CoapplicantIncome":2546,"LoanAmount":100.0,"Loan_Amount_Term":360.0,"Credit_History":null,"Property_Area":"Urban"},{"Loan_ID":"LP001051","Gender":"Male","Married":"No","Dependents":"0","Education":"Not Graduate","Self_Employed":"No","ApplicantIncome":3276,"CoapplicantIncome":0,"LoanAmount":78.0,"Loan_Amount_Term":360.0,"Credit_History":1.0,"Property_Area":"Urban"}]'
"""POST <url>/predict
"""
resp = requests.post("http://0.0.0.0:8000/predict", data = json.dumps(data), headers= header)
resp.status_code
200
"""The final response we get is as follows:
"""
resp.json()
{'predictions': '[{"0":"LP001015","1":1},{...
Заключение
В этой статье мы прошли лишь половину пути, создав рабочее API, которое выдает прогнозы, и стали на шаг ближе к интеграции ML решений непосредственно в разрабатываемые приложения. Мы создали достаточно простое API, которое поможет в прототипировании продукта и сделает его действительно функциональным, но чтобы отправить его в продакшн, необходимо внести несколько корректив, которые уже не входят в сферу изучения машинного обучения.
Есть несколько вещей, о которых нельзя забывать, в процессе создания API:
- Создание качественного API из спагетти-кода вещь почти невозможная, поэтому применяйте свои знания в области машинного обучения, чтобы создать полезное и удобное API.
- Попробуйте использовать контроль версий для моделей и кода API. Помните о том, что Flask не обеспечивает поддержку средств контроля версий. Сохранение и отслеживание ML моделей – это сложная задача, найдите удобный для себя способ. Здесь есть статья, которая рассказывает о том, как это делать.
- В связи со спецификой scikit-learn моделей, необходимо удостовериться что оценщик и код для обучения лежат рядом (в случае использования пользовательского оценщика для предобработки или иной подобной задачи). Таким образом законсервированная модель будет иметь рядом с собой оценщик класса.
Следующим логическим шагом будет создание механики для развертывания такого API на маленькой виртуальной машине. Есть разные способы сделать это, однако их мы рассмотрим в следующей статье.
Код и пояснения для этой статьи
Полезные источники:
[1] Don’t Pickle your data.
[2] Building Scikit Learn compatible transformers.
[3] Using jsonify in Flask.
[4] Flask-QuickStart.
Вот такой получился материал. Подписывайтесь на нас, если понравилась публикация, а также записывайтесь на бесплатный открытый вебинар по теме: «Метрические алгоритмы классификации», который уже 12 марта проведет разработчик и data scientist с 5-летним опытом — Александр Никитин.