開發環境建立
- 開發工具:Visual Studio Code
- 安裝 Python
- 安裝 Visual Studio Code Jupyter 套件
- 安裝會用到的 Python 函式庫
pip install numpy scikit-learn flask-restful requests pandas
建立 Machine Learning Model 並產出 pickle 檔 (參考 Github 上的 Jupyter Notebook)
- 這個 Jupyter Notebook 模擬一個機器學習演算法產出模型的 pickle 檔,使用機器學習資料集 — Iris dataset 進行資料載入、分群、訓練模型、預測模型,使用混淆矩陣 (confusion matrix) 判斷模型表現的好不好,最後產出模型的 pickle 檔。
- Iris dataset 是一個花朵資料集,由英國統計學家 Ronald Fisher 爵士在 1936 年對加斯帕半島上的鳶尾屬花朵所提取的花瓣花萼的長寬數據資料,有以下共150筆資料:
- 三種花卉:setosa(山鳶尾)、versicolor(變色鳶尾)、virginica(維吉尼亞鳶尾)
- 四種屬性:Sepal length: 花萼長度 (cm)、Sepal width: 花萼寬度 (cm)、Petal length: 花瓣長度 (cm)、Petal width: 花瓣寬度 (cm)
開發 RESTful API 介接 Machine Learning Model(參考 Github 上的 Python Code)
- 載入需要的開發模組:
from flask import Flask, jsonify
from flask_restful import Api, Resource, reqparse
import pickle
import numpy as np
import json
2. 建立 Flask app 物件:
app = Flask(__name__)
api = Api(app)
3. 建立 request parser 去解析 request 傳入的參數:
# Create parser for the payload data
parser = reqparse.RequestParser()
parser.add_argument('data')
4. 定義繼承自 flask_restful Resource 的類別,實作 Post Request:
class IrisClassifier(Resource):
def post(self):
args = parser.parse_args()
X = np.array(json.loads(args['data']))
prediction = model.predict(X)
return jsonify(prediction.tolist())
5. 定義 RESTful API URL:
api.add_resource(IrisClassifier, '/iris')
6. 載入模型並啟動 flask app:
if __name__ == '__main__':
# Load model
with open('model.pickle', 'rb') as f:
model = pickle.load(f)
app.run(port=5000 ,debug=True)
7. 執行 RESTful API:
python api.py
8. 啟動結果:
開發測試 RESTful API(參考 Github 上的 Python Code)
- 載入需要的開發模組:
import numpy as np
import requests
import json
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
2. 載入機器學習資料集 — Iris dataset:
# Load data
iris = load_iris()
3. 進行測試資料分群:
# Split into train and test sets using the same random state
X_train, X_test, y_train, y_test = train_test_split(iris['data'], iris['target'], random_state=12)
4. 設定 request payload:
# Serialize the data into json and send the request to the model
xTest = json.dumps(X_test.tolist());
payload = {'data': xTest}
# Fix the single quote bug
payload = json.dumps(payload)
5. 將資料送到 API:
# Set request's header.
headers = {'Content-type': 'application/json'}
y_predict = requests.post('http://127.0.0.1:5000/iris', headers=headers, data=payload)
6. 印出模型回覆的結果:
# Make array from the list
y_predict = np.array(y_predict.json())
print(y_predict)
7. 執行 Test API:
python test_api.py
8. 測試結果:
測試 API 依序遇到問題與解法
- 測試 API 依序遇到問題與解法
- 錯誤訊息:Did not attempt to load JSON data because the request Content-Type was not ‘application/json’ request header 'Content-type': 'application/json',
- 將資料送到 API 時,request 沒加 request header ‘Content-type’: ‘application/json’,加了就跳下一個錯誤訊息。
2. 錯誤訊息:Failed to decode JSON object: Expecting value: line 1 column 1 (char 0)
- 送到 Request 的 payload 的 JSON 格式錯誤,把 payload = {‘data’: xTest} 的值放到 JSON Editor Online 一看,怎麼是單引號?
- 根據《Python 技術者們:練功!老手帶路教你精通正宗 Python 程式 The Quick Python Book Third Edition》P.22–9 的定義,JSON 物件的鍵值必須是用「雙引號」括起來的字串呀!改成 payload = {“data”: xTest} 也還是單引號:
- 根據《Python 技術者們:練功!老手帶路教你精通正宗 Python 程式 The Quick Python Book Third Edition》P.22–12 的定義,json.dumps() 會以字串傳回轉換後的 JSON 資料呀!只好再 dumps 一次,就是正常的雙引號了: