【機器學習從零到一】Day6: 利用Docker 建立機器學習容器

PJ Wang
9 min readAug 11, 2019

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

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

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

為什麼需要用 Docker ?

由於在每個專案、每個模型所要建立的環境都不同,有些是用 Tensorflow 框架寫的、有些是用 Pytorch 框架寫的,環境變數、 Library 的要求都不同、設備要求也都不同,這個時候如果使用 Docker 就可以很輕易的解決這些問題,因為 Docker 能夠建立獨立的的環境,能夠與外界隔絕,因此可以在 Docker 內部設立專屬的環境、不被外界影響,詳細的介紹可以參考這裡

Dockerfile

而要在 Docker 內部安裝什麼套件、建立什麼樣的環境,都是透過 Dockerfile 來進行溝通的,他最基本的指令有幾項:

FROM pytorch/pytorch:latestRUN pip install Pillow
RUN pip install flask
WORKDIR /models
COPY ./script ./
EXPOSE 5002
ENTRYPOINT ["bash","run.sh"]

From: 依據哪個 Docker image ,可以直接在 Docker hub 去搜尋已經被建立好的 Docker Image,好處是可以節省自己建立環境、安裝套件的時間。

RUN: 執行的指令,通常會在內部用 pip install 去安裝一些套件

WORKDIR: 去到該資料夾,跟 RUN cd 不同的地方在於,RUN cd 只會在剛行程式碼去到指定位置,但下一行執行時,就又會回到原本的地方。

COPY: 可以將本機端的資料,複製到 Docker 內部所指定的位置

EXPOSE: 開啟的 Port

ENTRYPOINT: Docker 建立好後,需要執行的指令

還有更多其他的指令,可以參考 Dockerfile reference

Docker 基本指令

建立好 Dockfile 後,就可以直接利用 Docker 的基本指令將它實際的建立起來,比較常用到的語法如:

  1. 查看目前有的 Docker image
$ docker images

2. 建置 Docker

$ docker build -t <Docker image 名稱> --build-arg <變數1=... 變數2=...>.

3. 刪除 Docker image

# 刪除 (-f 表強制)
$ docker rmi -f <id>
# 強制刪除正在執行的
$ docker ps -a|awk ‘{print $1}’ |xargs docker rm

4. 執行 Docker

# 進入 Docker 內部
$ docker run -it <id> /bin/bash
# 若希望 docker 執行後立刻執行服務
# -d: 背景執行
$ docker run -d --runtime=<容器名稱> -p 本機端port:docker內部port <Docker image 名稱>

5. 查看現有的 Container

$ docker ps

6. 停止 Container

$ docker stop <container id>

7. 刪除 Container

$ docker rm <container id>

專案挑戰:化妝品分類

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

進入正題

我們這次選擇的框架是 Pytorch ,這次會延續上一篇 【機器學習從零到一】Day5: 利用 Flask 部署機器學習模型 繼續往下做,因此我們這次會用到的文件為:

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

0. 事前準備

  1. 所需檔案:【機器學習從零到一】Day5: 利用 Flask 部署機器學習模型
  2. 準備 run.sh ,並且打上
python server.py --model ./model.pth --classes classes.txt --eval eval.txt --port 8891

3. 安裝 Docker ,安裝的步驟為:

Step 1 : 更新

$ sudo apt-get update

Step 2:加入 Docker 的 repository 金鑰

$ sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D

Step 3:加入 Docker 官方 repository

$ sudo apt-add-repository 'deb https://apt.dockerproject.org/repo ubuntu-xenial main'

Step 4:更新

$ sudo apt-get update

Step 5:安裝 Docker

$ sudo apt-get install -y docker-engine

Step 6:系統安裝好後會直接啟動 Docker 服務

$ sudo systemctl status docker

1. 編寫 Dockerfile

由於我們這次是用 pytorch 框架,因此我們可以先在 Docker hub 上搜尋 pytorch ,利用別人已經編制好的 Image ,可以節省大量自己建置的時間,我們這次使用的是 floydhub/pytorch:1.0.0-gpu.cuda9cudnn7-py3.40 ,會選擇這個 Image 而不是用官方的 pytorch/pytorch ,是因為上面的已經順便建立好 Python 3 的環境,而不用自己重新安裝,因此整份 Dockerfile 就會顯得很簡單,如下:

FROM floydhub/pytorch:1.0.0-gpu.cuda9cudnn7-py3.40

RUN pip install Pillow
RUN pip install flask
RUN pip install requests

WORKDIR /cosmetics-classification
COPY ./script/server.py ./
COPY ./dataset/classes.txt ./
COPY ./log/eval.txt ./
COPY ./model/model.pth ./
COPY ./run.sh ./


EXPOSE 8891
ENTRYPOINT ["bash","run.sh"]

總共只安裝了 Pillowflaskrequests ,其他則是在複製 Local 端的程式碼、模型到 Docker 當中去執行。

2. 建立 Docker

寫好 Dockerfile 後,只要在他所在的目錄執行,下面這段程式碼後,就會開始建立,而若建完後發現有些地方錯誤了,盡量不要去更改執行的順序,因為你會發現,第二、第三次在 Build 的時候,並不會重新安裝上次已經安裝的套件,但如果更換了順序,此時就會被認定為是不同的,因此就會等待更多的時間再重新安裝。

$ sudo docker build -t cosmetics-classification .

3. 執行 Docker

由於我們要用 GPU 來執行我們的模型,但 GPU 是在 local 端,要怎麼讓 Docker 也能用 GPU 來跑呢?這個時候只要照著這篇 NVIDIA Container Toolkit 去安裝 nvidia 的容器,再執行

$ sudo docker run --runtime=nvidia -p 8891:8891 cosmetics-classification

就可以順利的完成。

4. 測試 Docker

可以利用上一篇的方式,去發 request

錯誤訊息:

AttributeError: ‘ReLU’ object has no attribute ‘threshold’

我這邊的解決方式是照著這篇說的,存模型時的方法是存參數、而非存整個模型,實際可以參考這篇文章,我的解決方法是:

import torchvision.models as modelsdef VGG16_pretrained_model(numClasses, featureExtract=True, usePretrained=True):
model = models.vgg16(pretrained=True)
numFtrs = model.classifier[6].in_features
model.classifier[6] = nn.Linear(numFtrs,numClasses)
return model
model = VGG16_pretrained_model(numClasses=NUM_CLASSES, featureExtract=True, usePretrained=True).to(device)
model.load_state_dict(torch.load(path))

恭喜

恭喜各位完成了這一系列的實作練習,下一階段正在規劃中,但不外乎會講的主題有:

  1. 模型的加速 → 如何能同時服務更多的 request
  2. Data pipeline → 從資料進來到模型評估如何自動化建立
  3. 資料處理 → 從 Business Intelligence 到 Data Intelligence

若還有想知道的更多 Topic 歡迎在下面留言分享,也不忘按讚鼓勵!

--

--

PJ Wang

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