為你的機器學習模型建立一個API服務
1. 什麼是API
當調包俠們訓練好一個模型後,下一步要做的就是與業務開發組同學們進行程式碼對接,以便這些‘AI大腦’們可以順利的被使用。然而往往要面臨不同程式語言的挑戰,例如很常見的是調包俠們用Python訓練模型,開發同學用Java寫業務程式碼,這時候,Api就作為一種解決方案被使用。
簡單地說,API可以看作是顧客與商家之間的聯絡方式。如果顧客以預先定義的格式提供輸入資訊,則商家將獲得顧客的輸入資訊並向其提供結果。
從本質上講,API非常類似於web應用程式,但它沒有提供一個樣式良好的HTML頁面,而是傾向於以標準資料交換格式返回資料,比如JSON、XML等。
接下來讓我們看看如何將機器學習模型(在Python中開發的)封裝為一個API。
首先需要明白什麼是Web服務?Web服務是API的一種形式,只是它假定API駐留在伺服器上,並且可以使用。Web API、Web服務——這些術語通常可以互換使用。
Flask——Python中的Web服務框架。它不是Python中唯一的一個Web框架,其它的例如Django、Falcon、Hug等。Flask框架帶有一個內建的輕量級Web伺服器,它需要最少的配置,因此在本文中將使用Flask框架來開發我們的模型API。
2. 建立一個簡單模型
以一個kaggle經典的比賽專案:泰坦尼克號生還者預測為例,訓練一個簡單的模型。
以下是整個機器學習模型的API程式碼目錄樹:
首先,我們需要匯入訓練集並選擇特徵。因為本文主要是介紹機器學習模型API的編寫,所以模型訓練過程並不做為重點內容,因此我們只選擇其中的'Age', 'Sex', 'Embarked', 'Survived' 這四個特徵來構造訓練集。
import pandas as pd # 匯入訓練集並選擇特徵 url = "http://s3.amazonaws.com/assets.datacamp.com/course/Kaggle/train.csv" df = pd.read_csv(url) include = ['Age', 'Sex', 'Embarked', 'Survived'] df_ = df[include]
然後,是一個簡單的資料處理過程。
這裡主要是對類別型特徵進行One-hot編碼,對連續型特徵進行空缺值填充。
categoricals = [] for col, col_type indf_.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)
最後,是模型的訓練以及持久化儲存。
模型採用的是邏輯迴歸,使用sklearn.externals.joblib將模型儲存為序列化檔案.pkl。需要注意的是,如果傳入的請求不包含所有可能的category變數值,那麼在預測時,get_dummies()生成的dataframe的列數比訓練得到分類器的列數少,這會導致執行報錯發生。所以在模型訓練期間還需要持久化訓練集One-hot後的列名列表。
from sklearn.linear_model import LogisticRegression from sklearn.externals import joblib dependent_variable = 'Survived' x = df_ohe[df_ohe.columns.difference([dependent_variable])] y = df_ohe[dependent_variable] lr = LogisticRegression() lr.fit(x, y) # 儲存模型 joblib.dump(lr, 'model.pkl') print("Model dumped!") # 把訓練集中的列名儲存為pkl model_columns = list(x.columns) joblib.dump(model_columns, 'model_columns.pkl') print("Models columns dumped!")
到此,我們的model.py的程式碼部分構造完畢。
3. 基於Flask框架建立API服務
使用Flask部署模型服務,需要寫一個函式predict(),並完成以下兩件事:
- 當應用程式啟動時,將已持久化的模型載入到記憶體中;
- 建立一個API站點,該站點接受輸入變數的請求後,將輸入轉換為適當的格式,並返回預測。
更具體地說,需要API的輸入如下(一個由JSON組成的列表):
[
{"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]}
import traceback import sys import pandas as pd from flask import request from flask import Flask from flask import jsonify app = Flask(__name__) @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': str(prediction)}) except: return jsonify({'trace': traceback.format_exc()}) else: print('Train the model first') return 'No model here to use'
我們已經在“/predict”API中包含了所有必需的元素,現在只需編寫主類即可。
from sklearn.externals import joblib if __name__ == '__main__': try: port = int(sys.argv[1]) except: port = 8000 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(host='192.168.100.162', port=port, debug=True)
到此,我們的機器學習模型API已經建立完畢,flask_api.py的程式碼部分也已構造完畢。但在進一步深入之前,讓我們回顧一下之前的所有操作:
- 載入了泰坦尼克資料集並選擇了四個特徵。
- 進行了必要的資料預處理。
- 訓練了一個邏輯迴歸分類器模型並將其序列化。
- 持久化訓練集中的列名的列表。
- 使用Flask編寫了一個簡單的API,該API通過接收一個由JSON組成的列表,預測一個人是否在沉船中倖存。
4. API的有效性測試
首先執行我們的模型API服務,我們通過Pycharm來啟動上一小節編寫完成的flask_api.py:
可以看到,在啟動API服務後,模型以及列名被順利的載入到了記憶體中。
之後可以通過Postman軟體模擬網頁請求,通過傳遞測試資料來觀察模型API是否能正常返回預測資訊。具體操作如下:
可以看到,模型API順利的接收到了POST請求併發送預測結果。
當然,除了Postman以外,我們也可以編寫Python指令碼request_api.py完成API測試:
import requests years_exp = [{"Age": 22, "Sex": "male", "Embarked": "S"}, {"Age": 22, "Sex": "female", "Embarked": "C"}, {"Age": 80, "Sex": "female", "Embarked": "C"}, {"Age": 22, "Sex": "male", "Embarked": "S"}, {"Age": 22, "Sex": "female", "Embarked": "C"}, {"Age": 80, "Sex": "female", "Embarked": "C"}, {"Age": 22, "Sex": "male", "Embarked": "S"}, {"Age": 22, "Sex": "female", "Embarked": "C"}, {"Age": 80, "Sex": "female", "Embarked": "C"}, {"Age": 22, "Sex": "male", "Embarked": "S"}, {"Age": 22, "Sex": "female", "Embarked": "C"}, {"Age": 80, "Sex": "female", "Embarked": "C"}, {"Age": 22, "Sex": "male", "Embarked": "S"}, {"Age": 22, "Sex": "female", "Embarked": "C"}, {"Age": 80, "Sex": "female", "Embarked": "C"}, ] response = requests.post(url='http://192.168.100.162:8000/predict', json=years_exp) result = response.json() print('model API返回結果:', result)
同樣我們順利地接收到了模型的返回結果:
這證明我們的機器學習API已經順利開發完畢,接下來要做的就是交給業務開發組的同學來使用了。
5. 總結
本文介紹瞭如何從機器學習模型構建一個API。儘管這個API很簡單,但描述的還算相對清晰。
此外,除了可以對模型預測部分構建API以外,也可以對訓練過程構建一個API,包括通過傳送超引數、傳送模型型別等讓客戶來構建屬於自己的機器學習模型。當然,這也將是我下一步要做的事情。