Pythonで機械学習モデルをAPIに変換する
- APIの紹介
- フラスコの基本
- 機械学習モデルを構築する
- 機械学習モデルの保存-シリアル化と逆シリアル化
- Flaskを使用してモデルのAPIを作成します
- PostmanでAPIをテストする
APIの紹介
簡単に言えば、APIは実際には2つのソフトウェア間のインターフェースです。エンドユーザー指向のソフトウェアが事前定義された形式で入力を提供できる場合、別のソフトウェアがその機能を拡張し、エンドユーザー指向のソフトウェアに出力結果を提供できます。本質的に、Analytics Vidhya APIはWebアプリケーションに非常に似ていますが、前者は多くの場合、標準のデータ交換形式(JSON、XMLなど)でデータを返します。開発者は、必要な出力を取得したら、さまざまなニーズに応じてそれを設計できます。
フラスコの基本
Flaskは、Werkzeug WSGIツールボックスとJinja2テンプレートエンジンに基づいて、Pythonで開発された小さなWebフレームワークです。FlaskはBSD認証を使用します。Flaskは、単純なコアを使用し、拡張機能を使用して他の機能を追加するため、「マイクロフレームワーク」とも呼ばれます。Flaskには、デフォルトのデータベースまたはフォーム検証ツールがありません。ただし、Flaskは拡張の柔軟性を保持しています。Flask拡張を使用して、ORM、フォーム検証ツール、ファイルアップロード、さまざまなオープン認証テクノロジーなどの機能を追加できます。同様の競合製品には、Django、Falcon、Hugなどがあります。
Anacondaバージョンをダウンロードした場合は、Flaskが含まれています。pipを使用してダウンロードすることもできます。
pip install flask
あなたはそれが非常に小さいことに気付くでしょう、それはそれがPython開発者の間でとても人気がある理由の1つです。もう1つの理由は、Flaskフレームワークに軽量のWebサーバーが組み込まれているため、構成が少なくて済み、Pythonコードで直接制御できることです。
次のコードは、Flaskの単純さを非常によく示しています。特定のURLを受信したときに特定の出力を生成する単純なWeb-APIを作成します。
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Welcome to machine learning model APIs!"
if __name__ == '__main__':
app.run(debug=True)
実行後、ターミナルブラウザにこのURLを入力して、結果を確認できます。
-
Jupyter Notebookは、Python、R、およびマークダウンに関連するものを処理するのに非常に適しています。しかし、Webサーバーの構築が含まれると、多くの奇妙なバグが発生します。したがって、SublimeなどのテキストエディタでFlaskコードを記述し、ターミナル/コマンドプロンプトからコードを実行することをお勧めします。
-
ファイルにflask.pyという名前を付けないでください。
-
デフォルトでは、Flaskを実行するためのポート番号は5000です。サーバーはこのポートで正常に起動できる場合もありますが、WebブラウザまたはAPIクライアント(Postmanなど)でURLを使用して起動すると、次の図に示すようにエラーが報告される場合があります。
-
Flaskのプロンプトによると、この時点でサーバーはポート5000で正常に起動されていますが、ブラウザでURLを使用して起動すると、コンテンツは出力されませんでした。したがって、これはポート番号の競合である可能性があります。この場合、デフォルトのポート番号5000を必要なポート番号に変更できます。次のように入力するだけです。
app.run(debug=True,port=12345)
-
上記のコードを入力すると、Flaskサーバーは次のよう
になります。入力したコードを見てみましょう。 -
Flaskインスタンスを作成した後、Pythonは自動的に名前変数を生成します。このファイルがPythonをスクリプトとして直接実行される場合、この変数は「main」になります。インポートされたファイルの場合、「name」の値はインポートされたファイルの名前になります。あなたが持っている場合たとえば、test.pyとrun.py、およびインポートtest.pyをにrun.py、test.pyの「名前」の値はなりますテスト(アプリ=フラスコ(テスト)) 。
-
上記のhello()の定義に関しては、 @ app.route( "/")を使用できます。同時に、デコレータ** route()**は、定義されたhello()をトリガーできるURLをFlaskに通知できます。
-
hello()の役割は、APIを使用するときに出力を生成することです。この場合、Webブラウザーでlocalhost:5000 /に移動すると、期待される出力が生成されます(デフォルトのポートを想定)。
機械学習モデル用のAPIを作成する場合は、次の点に注意してください。
機械学習モデルを構築する
ここでは、Flaskを使用してScikit-learnモデルを学習する方法を紹介する例として、最も一般的なScikit-learnモデルを取り上げます。まず、Scikit-learnの一般的なモジュールを確認しましょう。
- クラスタリング
- 戻る
- 分類
- 次元削減
- モデルの選択
- 前処理
一般的なデータの場合、送受信する際に、オブジェクトを送信に便利な形式に変換する操作が必要になります。これらは、オブジェクトのシリアル化(シリアル化)および逆シリアル化(逆シリアル化)とも呼ばれます。モデルはデータとは大きく異なりますが、Scikit-learnはトレーニングモデルのシリアル化と逆シリアル化をサポートしているだけなので、モデルを再トレーニングする時間を節約できます。scikit-learnでモデルのシリアル化されたコピーを使用することで、FlaskAPIを記述できます。
同時に、Scikit-learnモデルの要件は、データがデジタル形式である必要があることです。そのため、データセットの分類特徴をデジタル特徴0および1に変換する必要があります。実際、分類に加えて、Scikit-learnのsklearn.preprocessingモジュールは、LabelEncoderやOneHotEncoderなどのエンコードメソッドも提供します。
さらに、Scikit-learnはデータセットに欠落している値を自動的に入力することはできませんが、モデルに入る前に手動で処理する必要があります。欠測値と上記の機能エンコーディングは、実際にはデータ前処理の重要なステップであり、高性能の機械学習モデルを構築するために非常に重要です。
デモンストレーションを容易にするために、ここでは、Kaggle- Titanicで最も人気のあるデータセットを例として説明します。このデータセットは主に分類の問題です。私たちのタスクは、表形式のデータに基づいて乗客の生存確率を予測することです。さらに単純化するために、年齢(年齢)、性別(性別)、乗船(乗船港:C =シェルブール、Q =クイーンズタウン、S =サウサンプトン)の4つの変数のみを使用します。Survivedはカテゴリラベルです。
# Import dependencies
import pandas as pd
import numpy as np
# Load the dataset in a dataframe object and include only four features as mentioned
url = "http://s3.amazonaws.com/assets.datacamp.com/course/Kaggle/train.csv"
df = pd.read_csv(url)
include = ['Age', 'Sex', 'Embarked', 'Survived'] # Only four features
df_ = df[include]
「Sex」と「Embarked」は数値以外のカテゴリ機能であるため、エンコードする必要があります。「age」機能には多くの欠落値があり、要約統計量の後に中央値または平均で埋めることができます。Scikit-learnCan NaNを認識しないため、このためのヘルパー関数を作成する必要があります。
categoricals = []
for col, col_type in df_.dtypes.iteritems():
if col_type == 'O':
categoricals.append(col)
else:
df_[col].fillna(0, inplace=True)
上記のコードは、データセットの不足している値を入力するためのものです。ここで注意すべきことの1つは、欠落している値は実際にはモデルのパフォーマンスにとって非常に重要であるということです。特に空の値が多すぎる場合は、単一の値を入力するときに非常に注意する必要があります。そうしないと、大きな偏差が発生する可能性があります。このデータセットでは、値が欠落している列は年齢であるため、NaNに0を入力しないでください。
非デジタル機能をデジタル運転に変換する場合は、One HotEncodingまたはPandasが提供するものを使用できます。
get_dummies():
df_ohe = pd.get_dummies(df_, columns=categoricals, dummy_na=True)
前処理が完了したので、機械学習モデルをトレーニングする準備が整いました。ロジスティック回帰分類器を選択します。
from sklearn.linear_model import LogisticRegression
dependent_variable = 'Survived'
x = df_ohe[df_ohe.columns.difference([dependent_variable])]
y = df_ohe[dependent_variable]
lr = LogisticRegression()
lr.fit(x, y)
LogisticRegression(C=1.0, 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)
モデルを入手したら、モデルを保存します。技術的には、モデルをシリアル化する必要があります。Pythonでは、この操作はPicklingと呼ばれます。
機械学習モデルの保存:シリアル化と逆シリアル化
sklearnのjoblibを呼び出します:
from sklearn.externals import joblib
joblib.dump(lr, 'model.pkl')
['model.pkl']
ロジスティック回帰モデルは変更されていません。1行のコードでメモリにロードでき、モデルをワークスペースにロードする操作は逆シリアル化です。
lr = joblib.load('model.pkl')
Flaskを使用してモデルのAPIを作成します
Flaskを使用してモデルのサーバーを作成するには、次の2つのことを行う必要があります。
- APPが起動すると、既存のモデルがメモリに読み込まれます。
- 入力変数を受け入れ、それらを適切な形式に変換し、予測を返すAPIパワーオフを作成します。
具体的には、次のように入力すると、次のようになります。
[
{
"Age": 85, "Sex": "male", "Embarked": "S"},
{
"Age": 24, "Sex": '"female"', "Embarked": "C"},
{
"Age": 3, "Sex": "male", "Embarked": "C"},
{
"Age": 21, "Sex": "male", "Embarked": "S"}
]
APIの出力は次のようになります。
{
"prediction": [0, 1, 1, 0]}
その中で、0は死んだことを意味し、1は生き残ったことを意味します。ここでの入力形式はJSONであり、これは最も広く使用されているデータ交換形式の1つです。
上記の効果を実現するには、最初に関数predict()を作成する必要があります。その目標は、前述のとおりです。
- APPが起動すると、既存のモデルがメモリに読み込まれます。
- 入力変数を受け入れ、それらを適切な形式に変換し、予測を返す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)})
簡単そうに見えますが、このステップで小さな問題が発生する可能性があります。
作成した関数を正常に実行するには、これら4つのカテゴリ変数のすべての可能な値を受信リクエストに含める必要があります。これらの値は、リアルタイムである場合とそうでない場合があります。着信リクエストに必要な値がない場合、現在のメソッドで定義されたpredict()によって生成されたデータ列は分類子のデータ列よりも少なくなり、モデルはエラーを報告します。
この問題を解決するには、モデルのトレーニング中に列を保持し、Pythonオブジェクトを.pklファイルにシリアル化する必要があります。
model_columns = list(x.columns)
joblib.dump(model_columns, 'model_columns.pkl')
['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')
「/ predict」APIに必要なすべての要素を含めたので、メインクラスを作成するだけで済みます。
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 "model.pkl"
print ('Model loaded')
model_columns = joblib.load(model_columns_file_name) # Load "model_columns.pkl"
print ('Model columns loaded')
app.run(port=port, debug=True)
これで、このAPIはすべて完成し、ホストする準備が整いました。
もちろん、ロジスティック回帰モデルコードとFlask APIコードを別々の.pyファイルに分離したい場合、これは実際には良いプログラミング習慣です。次に、model.pyコードは次のようになります。
# Import dependencies
import pandas as pd
import numpy as np
# Load the dataset in a dataframe object and include only four features as mentioned
url = "http://s3.amazonaws.com/assets.datacamp.com/course/Kaggle/train.csv"
df = pd.read_csv(url)
include = ['Age', 'Sex', 'Embarked', 'Survived'] # Only four features
df_ = df[include]
# Data Preprocessing
categoricals = []
for col, col_type in df_.dtypes.iteritems():
if col_type == 'O':
categoricals.append(col)
else:
df_[col].fillna(0, inplace=True)
df_ohe = pd.get_dummies(df_, columns=categoricals, dummy_na=True)
# Logistic Regression classifier
from sklearn.linear_model import LogisticRegression
dependent_variable = 'Survived'
x = df_ohe[df_ohe.columns.difference([dependent_variable])]
y = df_ohe[dependent_variable]
lr = LogisticRegression()
lr.fit(x, y)
# Save your model
from sklearn.externals import joblib
joblib.dump(lr, 'model.pkl')
print("Model dumped!")
# Load the model that you just saved
lr = joblib.load('model.pkl')
# Saving the data columns from training
model_columns = list(x.columns)
joblib.dump(model_columns, 'model_columns.pkl')
print("Models columns dumped!")
而api.py则是:
# Dependencies
from fla
sk import Flask、request、jsonify
from sklearn.externals import joblib
import traceback import pandas
as pd
import numpy as np
API定義
app = Flask(name)
@ app.route( '/ predict'、methods = ['POST'])
def Forecast ():
if lr:
try:
json_ = request.json
print(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': str(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])#これはコマンドライン入力用です。
ただし、
port = 12345#ポートを指定しない場合、ポートは次のように設定されます。 12345
lr = joblib.load("model.pkl") # Load "model.pkl"
print ('Model loaded')
model_columns = joblib.load("model_columns.pkl") # Load "model_columns.pkl"
print ('Model columns loaded')
app.run(port=port, debug=True)现在,你可以在名为Postman的API客户端中测试此API 。只要确保model.py与api.py在同一个目录下,并确保两者都已在测试前编译好了,如下图所示: