BIツールTableauからPythonの機械学習モデルを呼び出す仕組みであるTabPy Serverへのデプロイを説明します。
TabPy Server
インストール
以下のサイトを参考にTabPy Serverをインストールします。
https://github.com/tableau/TabPy/blob/master/server.md
TabPy Serverは以下の2つの方法でインストールが可能です。
- セットアップスクリプトでAnaconda環境ごとインストールする
- 既存のPython環境にpipでインストールする
TabPy Serverをインストールすると、あわせてTabPy Clientもインストールされます。Clientは、Python環境からTabPy Serverへモデルをデプロイしたり、テスト用にTabPy Serverの処理を呼び出したりするのに使います。
サーバの起動
インストール後は、TabPy Serverのインストールフォルダにあるstartup.shまたはstartup.batによりTabPy Serverが起動できます。デフォルトではTabPy Serverはポート9004でListenします。
TabPy Serverの仕組み
TabPy Serverの使い方
Tabpy Serverは主に2つの使い方ができます。
- Evaluate : Pythonのコードをパラメータとして渡し、TabPy Server上で実行して結果を返却する
- Query : あらかじめ関数をエンドポイントとして登録しておき、呼び出し時にパラメータを渡して実行結果を返却する
教師あり機械学習による予測モデルを使う場合、あらかじめ予測モデルを構築するため、Queryの方を使います。
一方、クラスタリングなど教師なしモデルの場合、Evaluateでアドホックにコードを実行できます。
TabPy Serverの操作
TabPy Serverの操作はTableauからの接続の他、HTTPによるRESTでの呼び出し、またはPythonからTabPy-Clientライブラリ経由での操作が可能です。
教師ありモデルによる予測
関数のデプロイ
教師あり学習の予測モデルの場合、事前に学習済みの予測モデルを構築し、TabPy Serverにデプロイしておきます。以下に、その手順を説明します。
ライブラリをインポートします。
import tabpy_client
import pandas as pd
from sklearn.ensemble import RandomForestClassifier
データを読み込みます。
train = pd.read_csv("train_prep.csv")
学習のための説明変数と目的変数を準備します。
X_train = train[["Pclass","SexInt","AgeFillNa","IsAlone","FareFillNa","EmbarkedInt"]]
Y_train = train["Survived"]
アルゴリズムを選択して学習データを与え、予測モデルを生成します。ここではランダムフォレストを例として使います。
clf = RandomForestClassifier()
clf.fit(X_train, Y_train)
RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini',
max_depth=None, max_features='auto', max_leaf_nodes=None,
min_impurity_split=1e-07, 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)
生成した予測モデルを使って、説明変数をパラメータとして受け取って目的変数を予測する関数を定義します。
クラスを判別する関数を定義します。
def predict(Pclass,Sex,Age,SibSp,Parch,Fare,Embarked):
SexMap = {"male":0,"female":1}
EmbarkedMap = {"S":0,"C":1,"Q":2}
import pandas as pd
import numpy as np
SexInt = map(lambda val:SexMap[val],Sex)
IsAlone = map(lambda val:1 if val==0 else 0, np.array(SibSp) + np.array(Parch))
EmbarkedInt = map(lambda val:EmbarkedMap[val],Embarked)
data = pd.DataFrame({
"Pclass":Pclass,
"SexInt":SexInt,
"AgeFillNa":Age,
"IsAlone":IsAlone,
"FareFillNa":Fare,
"EmbarkedInt":EmbarkedInt
})
return clf.predict(data).tolist()
クラスに属する確率を返す関数を定義します。(任意)
def predict_proba(Pclass,Sex,Age,SibSp,Parch,Fare,Embarked):
SexMap = {"male":0,"female":1}
EmbarkedMap = {"S":0,"C":1,"Q":2}
import pandas as pd
import numpy as np
SexInt = map(lambda val:SexMap[val],Sex)
IsAlone = map(lambda val:1 if val==0 else 0, np.array(SibSp) + np.array(Parch))
EmbarkedInt = map(lambda val:EmbarkedMap[val],Embarked)
data = pd.DataFrame({
"Pclass":Pclass,
"SexInt":SexInt,
"AgeFillNa":Age,
"IsAlone":IsAlone,
"FareFillNa":Fare,
"EmbarkedInt":EmbarkedInt
})
return clf.predict_proba(data)[:,1].tolist()
TabPy ClientでTabPy Serverに接続します。
client = tabpy_client.Client("http://localhost:9004")
関数をデプロイします。
client.deploy("predict",predict,"predict titanic survival",override=True)
client.deploy("predict_proba",predict_proba,"predict probability of titanic survival",override=True)
デプロイした関数の確認・テスト
関数がエンドポイントとしてデプロイされていることを確認します。
client.get_endpoints()
{u'predict': {'name': 'predict', 'description': 'predict titanic survival', 'creation_time': datetime.datetime(2018, 2, 10, 16, 3, 43), 'version': 8, 'dependencies': [], 'last_modified_time': datetime.datetime(2018, 2, 11, 5, 50, 54), 'schema': None, 'type': 'model'},
u'predict_proba': {'name': 'predict_proba', 'description': 'predict probability of titanic survival', 'creation_time': datetime.datetime(2018, 2, 10, 16, 32, 34), 'version': 4, 'dependencies': [], 'last_modified_time': datetime.datetime(2018, 2, 11, 5, 50, 54), 'schema': None, 'type': 'model'}}
関数を実行してみます。
client.query("predict",[1,3],["male","female"],[0,30],[0,0],[0,1],[20,40],["Q","S"])
{u'model': u'predict',
u'response': [1, 0],
u'uuid': u'c9f6230a-c8c7-4b9e-b50c-cffe4d242e64',
u'version': 8}
REST APIでの関数実行も試してみます。
import requests
url = "http://localhost:9004/query/predict_proba"
payload = """
{
"data":{
"Pclass":[1,3],
"Sex":["male","male"],
"Age":[0,30],
"SibSp":[0,0],
"Parch":[0,1],
"Fare":[20,40],
"Embarked":["Q","S"]
}
}
"""
res = requests.post(url,data=payload)
print res.text
{"model": "predict_proba", "version": 4, "response": [0.9, 0.0], "uuid": "d4bcd22c-d055-4777-8424-afb841957ace"}
Tableauからの呼び出し
Tableauから呼び出す場合、以下の手順を実施します。
- ヘルプから外部サーバ接続の設定を表示し、TabPy ServerのURL、ポート番号をセット
- 関数を定義し、SCRIPT_XXX関数を計算式として定義
計算式の例(エンドポイント呼び出しの場合):
SCRIPT_INT('return tabpy.query("predict",_arg1,_arg2)["response"]',AVG([Pclass]),AVG(Age))