Узнайте, как создать простой API на основе модели машинного обучения на Python с помощью Flask.

Подходы к реализации моделей машинного обучения

Часто истинная ценность вашей модели машинного обучения проявляется в ядре интеллектуального продукта. Будь то жизненно важный компонент системы рекомендаций или интеллект чат-бота, эти моменты могут представлять собой огромные проблемы.

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

1 — Один из подходов — переписать всю кодовую базу машинного обучения на языке, который подходит команде разработчиков программного обеспечения. Хотя это может показаться логичным решением, оно может занять невероятно много времени и ресурсов, особенно при работе со сложными моделями. Кроме того, в некоторых языках, например в JavaScript, отсутствуют надежные библиотеки машинного обучения, что делает этот вариант менее привлекательным.

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

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

Что такое API?

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

По сути, API функционируют так же, как веб-приложения, хотя с особым упором на обмен данными, а не на представление информации в удобном для пользователя формате HTML. Вместо этого они обычно возвращают данные в стандартизированных форматах, таких как JSON или XML. Как только разработчик получает желаемый результат от API, он получает творческую свободу стилизовать и представить его в соответствии со своими конкретными предпочтениями и требованиями. Такая гибкость позволяет разработчикам беспрепятственно интегрировать данные в свои приложения, сохраняя при этом полный контроль над уровнем представления, что обеспечивает широкие возможности настройки и адаптации пользовательского интерфейса.

Теперь, когда вы имеете четкое представление о том, что такое API, давайте посмотрим, как можно объединить модель машинного обучения (разработанную на Python) в API на Python.

Flask — фреймворк веб-сервисов на Python

Flask — это веб-сервис (Веб-API, Веб-сервис — эти термины взаимозаменяемы). Платформа разработки на Python. Он не единственный в Python, есть еще несколько, например Django, Falcon, Hug и т. д. Но для этого урока вы будете использовать Flask.

Если вы загрузили дистрибутив Anaconda, у вас уже установлен Flask. В противном случае вам придется установить его самостоятельно с помощью:

pip install flask

Flask, любимый среди разработчиков Python по нескольким веским причинам, может похвастаться минималистичным дизайном. Среди его привлекательных функций — интегрированный легкий веб-сервер, который требует минимальной настройки и которым можно легко управлять с помощью кода Python. Эта присущая Flask простота является важным фактором, способствующим широкой популярности Flask.

from flask import Flask
from flask import Flask

app = Flask(__name__)


@app.route("'/landing-page', methods=['POST']")
def test():
    return "Welcome to machine learning model APIs!"


if __name__ == '__main__':
    app.run(debug=True)

После выполнения вы можете перейти по веб-адресу (ввести адрес в веб-браузере), который отображается на терминале, и наблюдать за результатом.

Модели обучения Scikit с Flask

Scikit-learn — это библиотека Python, предоставляющая простые и эффективные инструменты для интеллектуального анализа данных.

  • Кластеризация
  • Регрессия
  • Классификация
  • Уменьшение размерности
  • Выбор модели
  • Предварительная обработка

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

Однако модели scikit-learn требуют числовых данных, а это означает, что вам необходимо обрабатывать категориальные функции, которые не являются числовыми. К счастью, scikit-learn предоставляет полезные утилиты, такие как LabelEncoder и OneHotEncoder в модуле sklearn.preprocessing, которые делают это преобразование безболезненным.

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

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

Для простоты в нашем анализе мы будем работать всего с четырьмя ключевыми переменными: возраст, пол, начало пути и выживание. Переменная «survived» служит меткой нашего класса.

# Import necessary libraries
import pandas as pd
import numpy as np
from sklearn.linear_model import LogisticRegression

# Load the dataset from a URL and select only the specified features
url = "http://s3.amazonaws.com/assets.datacamp.com/course/Kaggle/train.csv"
df = pd.read_csv(url)
selected_features = ['Age', 'Sex', 'Embarked', 'Survived']  # We're interested in these four features
df = df[selected_features]

# Data Preprocessing
categorical_columns = []  # To store categorical columns
for column, data_type in df.dtypes.iteritems():
    if data_type == 'O':  # Check if the column has object data type (categorical)
        categorical_columns.append(column)
    else:
        df[column].fillna(0, inplace=True)  # Fill missing values in non-categorical columns with 0

# Perform one-hot encoding on categorical columns
df_encoded = pd.get_dummies(df, columns=categorical_columns, dummy_na=True)

# Define the dependent variable and split data into features (x) and target (y)
dependent_variable = 'Survived'
x = df_encoded[df_encoded.columns.difference([dependent_variable])]
y = df_encoded[dependent_variable]

# Create and train a Logistic Regression classifier
lr = LogisticRegression()
lr.fit(x, y)

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

from sklearn.externals import joblib
joblib.dump(lr, 'rl_model.pkl')
['lr_model.pkl']
lr = joblib.load('r_lmodel.pkl')

Теперь вы хорошо подготовлены к развертыванию сохраняемой модели с помощью Flask. Как вы уже видели, простота Flask делает его отличным выбором для начала вашего пути к веб-сервису.

Для обслуживания вашей модели с помощью Flask вам нужно сделать следующие две вещи:

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

Наши входные данные в модель будут выглядеть следующим образом:

[
{"Age": 80, "Sex": "male", "Embarked": "S"},
{"Age": 24, "Sex": "female", "Embarked": "C"},
{"Age": 5, "Sex": "male", "Embarked": "C"},
{"Age": 21, "Sex": "male", "Embarked": "S"}
]

И вывод нашей модели будет выглядеть так:

{"prediction": [0, 1, 1, 0]}

Давайте создадим удобную функцию predict(), которая служит двум ключевым целям:

  1. Загрузка модели. Сохраненная модель машинного обучения будет загружена в память сразу после запуска приложения.
  2. Создание конечной точки API. Эта функция также позволяет настроить конечную точку API. Эта конечная точка будет отвечать за прием входных переменных, интеллектуальное преобразование их в необходимый формат для обработки модели, а затем предоставление ценных прогнозов, которые мы ищем.
from flask import Flask, jsonify
app = Flask(__name__)
@app.route('/predict', methods=['POST'])
def predict():
     json_ = request.json
     query_df = pd.DataFrame(json_)
     query = pd.get_dummies(query_df)
     prediction = lr.predict(query)
     return jsonify({'prediction': list(prediction)})

Большой! Однако в текущей реализации есть небольшая проблема. Написанная вами функция предполагает, что входящие запросы содержат все возможные значения категориальных переменных, что может быть не так в сценариях реального времени. Если в запросе отсутствуют некоторые категориальные значения, текущее определение метода predict(), использующее get_dummies(), создаст DataFrame с меньшим количеством столбцов, чем ожидает классификатор, что приведет к ошибке времени выполнения.

Чтобы решить эту проблему, рассмотрите возможность сохранения списка столбцов во время обучения модели. Вы можете сериализовать эту информацию в файл .pkl с помощью joblib, как и раньше.

model_columns = list(x.columns)
joblib.dump(model_columns, 'lr_model_columns.pkl')

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

@app.route('/predict', methods=['POST']) # Your API endpoint URL would consist /predict
def predict():
    if lr:
        try:
            json_ = request.json
            query = pd.get_dummies(pd.DataFrame(json_))
            query = query.reindex(columns=model_columns, fill_value=0)

            prediction = list(lr.predict(query))

            return jsonify({'prediction': prediction})

        except:

            return jsonify({'trace': traceback.format_exc()})
    else:
        print ('Train the model first')
        return ('No model here to use')

if __name__ == '__main__':
    try:
        port = int(sys.argv[1]) # This is for a command-line argument
    except:
        port = 12345 # If you don't provide any port then the port will be set to 12345
    lr = joblib.load(model_file_name) # Load "lr_model.pkl"
    print ('Model loaded')
    model_columns = joblib.load(model_columns_file_name) # Load "r_lmodel_columns.pkl"
    print ('Model columns loaded')
    app.run(port=port, debug=True)

Теперь ваш API готов к размещению. Но прежде чем идти дальше, давайте подведем итоги всего, что вы сделали до этого момента:

Собираем все вместе:

  • Вы загрузили набор данных Титаника и выбрали четыре объекта.
  • Вы выполнили необходимую предварительную обработку данных.
  • Вы создали классификатор логистической регрессии и сериализовали его.
  • Вы также сериализовали все столбцы из обучения, поскольку решение проблемы меньшего, чем ожидалось, количества столбцов — сохранить список столбцов из обучения.
  • Затем вы написали простой API с использованием Flask, который мог бы предсказать, выжил ли человек после кораблекрушения, учитывая его возраст, пол и информацию о том, как он отправился на борт.