【機器學習從零到一】Day5: 利用 Flask 部署機器學習模型

PJ Wang
9 min readAug 9, 2019

從學生時期開始接觸機器學習,當初以為的 Machine learning 就只要會處理資料、訓練模型、得到比較高的準確率,然後就此結束,但出社會後發現,從 Jupyter 到 Production 的路還有段距離,那該怎麼辦呢?

因此,接下來將會分幾個章節來介紹:

  1. 建立 GCP 機器 — VM 執行個體
  2. 建立機器學習環境 (GPU environment)
  3. Pytorch 介紹與範例 (image classification)
  4. Data science 的版控 → DVC 的建立與使用
  5. 利用 Flask 部署機器學習套件
  6. 利用Docker 建立機器學習容器

為什麼需要讀這篇?

從學生時期開始學習機器學習後,我們花了大半的時間在處理資料、在研究模型、追求更高的準確率,但當我們都做完這一切的事情後,論文或許可以很順利的放上實驗結果,但如果真的要拿來應用時,卻沒有辦法很順利地使用,為什麼呢?

  1. 實際上的應用和實驗室的情境不同

實驗室只需要對固定的一組資料做預測、去計算他的準確率,而並不需要去對新的資料做預測,甚至實驗室只需要對 .jpg.txt 檔做預測,但當我們手上沒有圖片檔案,只有一個圖片網址該怎麼辦?當我們不是一份.txt 的文件,而是要去資料庫撈資料的話,又該怎麼辦呢?

2. 環境上的限制

在做機器學習時,有沒有 GPU 的環境是一個重要的因素,但如果別台電腦也想要有同樣的服務,該怎麼有效地去做串接呢?難道再開一台機器、再買一張顯卡嗎?

為了突破這些困難和限制,我們會希望的是,能夠把機器學習的模型,架設成一個服務,能夠讓各個情境、環境都能得到一樣的結果和成效,因此 Flask 就是一個能夠快速架設服務的框架。

Flask 介紹

當我們利用 Python 語言想要開發 web APP時,Flask 就是一個很適合使用的框架,而除了 Flask 以外,還有其他框架像是 Tornado, Pyramid, 和 Django 也是可以嘗試的選擇,當中的優勢、劣勢比較可以參考這篇。我們這次選擇 Flask 的原因則是因為 Flask 簡單、輕易上手,如下面這段程式 hello_world.py

from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello World!"
if __name__ == "__main__":
app.run()

只要利用終端機執行 python hello_world.py 後,就會出現

* Serving Flask app "hello_world" (lazy loading)
* Environment: production
WARNING: Do not use the development server in a production environment.
Use a production WSGI server instead.
* Debug mode: off
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

這個時候,就能透過 requests 去打這個網址,就會回傳 Hello World!

專案挑戰:化妝品分類

題目:是否能分辨一張圖片有出現哪一個種類的化妝品,而化妝品的種類粗類為 → 口紅、眼線筆、指甲油、粉底液、粉餅、刷具、眼影盤。

進入正題

我們這次選擇的框架是 Pytorch ,如果想知道更多 Pytorch 如何實作,可以參考我上一篇文章 【機器學習從零到一】 Day3: Pytorch 介紹與範例 (cosmetics classification) ,而我們這一篇會從上一篇【機器學習從零到一】 Day4: Data science 的版控 → DVC 的建立與使用 的四份程式 config.py evaluate.py split_train_test.py train.py,繼續延伸下去。

程式碼:Github → 當中包含上兩篇文章的程式碼

這次會使用到的資料如下,以及資料夾的編制如下:

./ 
├── dataset
│ └── classes.txt
├── script
│ └── server.py
├── model
│ └── model.pth
└── log
└── eval.txt

0. 事前準備

先透過 pip 來安裝 Flask 套件

pip install Flask

下載預先訓練好的模型:下載地址

其他檔案 classes.txt

Compact_foundation
Eyebrow_pencil
Foundation
Lipstick
Makeup_brushes
Nail_Polish
Palette

以及 eval.txt

input size: 224 
classes number: 7
use pretrained: True
epochs: 21
batch size: 32
learning rate: 0.001
momentum: 0.9
accuracy: 0.8591549295774648

1. 建立 server.py

透過前面 Flask 的介紹知道,我們可以先透過最基本的框架,再慢慢把模型加入,結構如下:

# import packageclass VGG16_model(nn.Module):
# 如果用自己客製化的模型,需要將模型加入
# 不然會 load 不到
def load_data(cls_file, log_file):
# 會將 classes.txt 讀進來,將 predict 完的結果做 mapping
def load_transform():
# 圖片會做一些預處理
def load_model():
# 會在一開始就讀取進來,這樣可以節省模型讀取時間
@app.route("/predict", methods=["POST"])
def predict():
output_dict = {"success": False}
if flask.request.method == "POST":
data = flask.request.json # 讀取 json
...
return flask.jsonify(output_dict), 200 # 回傳json, http status code
@app.route("/performance", methods=["GET"])
def performance():
output_dict = {"success": False}
if flask.request.method == "GET":
...
return flask.jsonify(output_dict), 200
if __name__ == "__main__":
...
app.run(host="0.0.0.0", debug=True, port=5000)

Flask 的使用為,在函式上方加入 @app.route… 這段程式碼,這樣子 API 在打進來的時候,就會執行下方的函式,而接收的方式可以用 GET 或者 POST ,通常如果 request 沒有帶資料進來的話會用 GET ,而反之則用 POST

為了加快整個讀取的時間,也會將模型、檔案事先都先讀取出來,而不是打 API 過來之後才去做,同時也要注意,當初在訓練時,如果我們有設定圖片大小、圖片如何預處理的過程,也要在 predict 的過程去做處理。

完整的程式碼如下:

建立好這份文件後,在專案根目錄執行:

$ python script/server.py --model model/model.pth --port 8891 --classes dataset/classes.txt --eval log/eval.txt

成功後會顯示

* Loading pytorch model and Flask starting server... please wait until server has fully started
* Serving Flask app "server" (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: on
* Running on http://0.0.0.0:8891/ (Press CTRL+C to quit)
* Restarting with stat

2. API 測試

這時候我們可以開一個 jupyter notebook 去發 request (需要在同一台機器做)。

3. 從外部使用 API

剛剛是在本機端測試,但如果我們要從外部環境如何使用呢?首先要先去 GCP 中加入

A. 先到 VPC防火牆規則

B. 點選:建立防火牆規則

C. 建立防火牆規則:輸入想要打開的 Port

D. 回到 VM 執行個體編輯 加入網路標記

完成上述步驟後,即可在其他地方,去使用這隻 API ,不過這樣的話有可能會讓全世界知道網址的人都可以使用,因此可以嘗試加入 Token ,去限制權限。使用方法如下(記得修改 IP 位址):

req = requests.get('http://35.202.231.43:8891/performance')

Next…

下一章也是我們系列文最後一章,會開始建立 docker ,使得能夠將這個服務能夠被執行在各個地方,並且能夠完整的被包好,而不會東少一塊、西少一塊的。

Day 6: 建立Docker

如果喜歡我的文章內容,請幫我多多鼓掌

1 個鼓掌:喜歡這篇的內容

10個鼓掌:期待這一系列的課程

30個鼓掌:希望未來能有更多相關文章

--

--

PJ Wang

台大資工所碩畢 / 設計思考教練 / 系統思考顧問 / 資料科學家 / 新創 / 科技 + 商業 + 使用者