從學生時期開始接觸機器學習,當初以為的 Machine learning 就只要會處理資料、訓練模型、得到比較高的準確率,然後就此結束,但出社會後發現,從 Jupyter 到 Production 的路還有段距離,那該怎麼辦呢?
因此,接下來將會分幾個章節來介紹:
- 建立 GCP 機器 — VM 執行個體
- 建立機器學習環境 (GPU environment)
- Pytorch 介紹與範例 (image classification)
- Data science 的版控 → DVC 的建立與使用
- 利用 Flask 部署機器學習套件
- 利用Docker 建立機器學習容器
Data Version Control ( DVC ) 介紹
當 Data scientist 利用機器學習、深度學習來建立模型時,會需要去讀取資料、處理資料、建立模型、調整參數、訓練模型、評估模型 … 等,因此這邊會有大量的參數會需要去嘗試,如果沒有一個好的版本控制,常常會全部混淆在一起,更常看到的命名法是 model_1 model_2 …
,這樣完全沒辦法分辨哪個模型是幹嘛的。
因此 DVC 的概念如下圖所示,每一個模型的產生應該要同時考慮資料集、參數、程式碼。在用 DVC 控制之後,之後就可以針對比較好的參數、資料集輕易地做 reproduce 。
那為什麼 git
不行取代呢?是因為 git
推到 github
時,就會有檔案上傳的限制,而且更多的是,你可能想要看每個版本的 precision, recall, f1 分數的比較,這邊 DVC 都有辦法幫你一次搞定,更好的是,它會建立起一個 Pipeline ,具體來說就是,調整完參數後,輸入 dvc repro
,就能幫你從「資料的切割 → 資料的處理 → 訓練模型 → 模型評估」做一條龍式的處理,最後用 dvc metrics show
就能看到最後評估完的結果。
DVC 真正的運作邏輯是在原本受 git 版控的環境,多加了一個 資料版控進去,因此 DVC 會和 git 兩個相輔相成的配合。而 DVC 是利用輸入與輸出來將資料做 pipeline 的串接,所以在執行一段程式時,都要告訴他輸入是哪些檔案,輸出是哪些檔案。更多詳細的步驟我們會一步一步的走過,若還想更多,可以參考官網: https://dvc.org/ 。
專案挑戰:化妝品分類
題目:是否能分辨一張圖片有出現哪一個種類的化妝品,而化妝品的種類粗類為 → 口紅、眼線筆、指甲油、粉底液、粉餅、刷具、眼影盤。
進入正題
我們這次選擇的框架是 Pytorch ,如果想知道更多 Pytorch 如何實作,可以參考我上一篇文章 【機器學習從零到一】 Day3: Pytorch 介紹與範例 (cosmetics classification) ,而我們這一篇會從上一篇 cosmetics-classification.ipynb ,拆成四段程式 config.py evaluate.py split_train_test.py train.py。
程式碼:Github
資料集:下載連結 → 下載好後執行 tar zxvf dataset.tar.gz
這次會使用到的資料如下,以及資料夾的編制如下(粗體字表示由程式產生,並非一開始就在資料夾內):
./
├── script
│ ├─ split_data.py
│ ├─ train.py
│ ├─ evaluate.py
│ └─ config.py
├── dataset
│ ├── cosmetics-category
│ ├── cosmetics-all
│ │ ├─ _PRrlNrSLhHGaUTfduYgQ6eOAdI.jpg
│ │ ├─ -b2cugUzKg6IIHIJjfXGcDnKsZA.jpg
│ │ ├─ -FK0wRGzJZ5NirgLw0CbvUf-7Og.jpg
│ │ └─ ...
│ ├── train.csv
│ ├── test.csv
│ └── annotation.csv
├── model
│ └── model.pth
└── log
├── train-output.txt
├── test-output.txt
└── eval.txt
0. 事前準備
將 repository clone 下來、和資料集解壓縮後,資料夾編制如下:
./
├── script
│ ├─ split_data.py
│ ├─ train.py
│ ├─ evaluate.py
│ └─ config.py
├── dataset
│ ├── cosmetics-category
│ ├── cosmetics-all
│ │ ├─ _PRrlNrSLhHGaUTfduYgQ6eOAdI.jpg
│ │ ├─ -b2cugUzKg6IIHIJjfXGcDnKsZA.jpg
│ │ ├─ -FK0wRGzJZ5NirgLw0CbvUf-7Og.jpg
│ │ └─ ...
│ ├── train.csv
│ ├── test.csv
│ └── annotation.csv
├── dataset.tar.gz
├── cosmetics-classification.ipynb
└── split-train-test.ipynb
可以先將這次不會用到的檔案刪除
$ rm dataset.tar.gz
$ rm cosmetics-classification.ipynb
$ rm split-train-test.ipynb
刪除好後,加入到 git 當中
$ git add .
$ git commit -m "remove useless file"
DVC 套件安裝
$ pip install dvc
1. DVC 託管專案
利用指令 dvc init
,並且會產生 .dvc/.gitignore
、 .dvc/cache/
、 . dvc/config
三份檔案,其中最重要的是 .dvc/cache/
, DVC 會在這邊建立檔案的連結,也是最後會 push 到雲端的檔案。
$ dvc init
2. 切割檔案
DVC 在建立 pipeline 的方式是透過輸入、輸出,所以在執行程式碼的過程,會需要告訴 DVC 會需要吃進哪些檔案、輸出哪些檔案。DVC 執行程式的方法如下:
$ dvc run -d 要執行的程式或要輸入的檔案 -o 要輸出的檔案 python 要執行的程式
以我們要切割資料集來看的話,會執行下面這段程式
$ dvc run -d script/split_train_test.py -d script/config.py -d dataset/annotation.csv -o dataset/train.csv -o dataset/test.csv python script/split_train_test.py
執行完後,可以看一下 DVC 當中做了什麼事情
$ git status -s
?? train.csv.dvc$ cat train.csv.dvc
md5: c53dd6d76f7cdc25aaf2146db6223bf0
cmd: python script/split_train_test.py
wdir: .
deps:
- md5: 826ea439f28fb04923de739af8c26b5d
path: script/split_train_test.py
- md5: a933ce5d996b1687817a60b3453e18ed
path: script/config.py
- md5: 87c46f0402b54b960b294ef7791f7cf8
path: dataset/annotation.csv
outs:
- md5: 896389741ff20a2055acfe5c65893bf1
path: dataset/train.csv
cache: true
metric: false
persist: false
- md5: 1ea600a9a7b720cd28fe99ff6d1c3e70
path: dataset/test.csv
cache: true
metric: false
persist: false
會發現幾件事:
- 他會以第一個
-o
的檔案去加上.dvc
成為新的檔案 - 承1,但我有輸出兩個檔案
test.csv
和train.csv
,因此他會都記錄在train.csv.dvc
這分檔案當中。 - md5 會紀錄的是檔案在
.dvc/config
當中的位置,而每一份檔案對於 DVC 來說是建立一個 link 去連到原本的檔案位置,而並非去創建新的檔案。
$ du -sh .dvc/cache/89/* .dvc/cache/1e/*
60K .dvc/cache/89/6389741ff20a2055acfe5c65893bf1
16K .dvc/cache/1e/a600a9a7b720cd28fe99ff6d1c3e70$ du -sh dataset/train.csv dataset/test.csv
60K dataset/train.csv
16K dataset/test.csv
最後我們可以在用 git 來管理我們的檔案
$ git add .
$ git commit -m "split data"
2. 訓練模型
由於我們會建立 log 檔以及 model,因此我們先建立起兩個資料夾:
$ mkdir log
$ mkdir model
而利用 DVC 執行 train.py
的方法和步驟一雷同,將會用到的檔案用 -d
去加入,而輸出的檔案用 -o
去建立。
$ dvc run -d script/train.py -d script/config.py -d dataset/train.csv -d dataset/test.csv -o model/model.pth -o log/train-output.txt -o log/test-output.txt python script/train.py
執行完後,會建立兩份 log 檔 log/train-output.txt
、 log/test-output.txt
以及模型 model/model.pth
,我們可以把建立出來的檔案用 git 來管理。
$ git add .
$ git commit -m "train model"
3. 評估模型
這裡比較特別要注意的是,輸出的 eval.txt
檔案,是要用 -M
而並非 -o
,原因是 DVC 後續會去追蹤這份檔案,讓我們能夠快速地去做成效的比較。而因為這樣的話沒有輸出檔案,所以可以用 -f
的方式來指定輸出的檔案。
$ dvc run -d script/evaluate.py -d script/config.py -d dataset/test.csv -d model/model.pth -M log/eval.txt -f Dvcfile python script/evaluate.py
執行完後,會建立 log/eval.txt
以及 Dvcfile
,會取名為Dvcfile
是因為 稍後在做 dvc repro
時若未指定檔案,則會用讀取 default 的檔案,而default 檔案的檔名就為 Dvcfile
,而Dvcfile
和上面步驟的 .dvc
檔案是一樣的,記載 md5
、 cache
等資訊。
而由於我們剛剛是利用 -M
的方式去指定檔案路徑,因此利用 DVC 內建的語法 dvc metrics show
就能夠快速地看到模型表現。
$ dvc metrics show
log/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.8626760563380281$ cat log/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.8626760563380281
最後,我們可以把建立出來的檔案用 git 來管理。
$ git add .
$ git commit -m "evaluation"
4. Reproduce
如果我今天想要修改 epochs 從,21 修改為 51時,那我會這樣做
$ git checkout -b epochs51
$ vi script/config.py # 將epochs = 21 改為 epochs = 51
$ dvc repro
由於我們一開始從資料分割到最後評估模型的過程,都有手動的將輸入輸出去做設定,因此 DVC 就可以很快地做到自動化,一條龍式的完成。
$ dvc metrics show -a
epochs51:
log/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.8661971830985915
master:
log/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.8661971830985915
5. Merge the model to master
為了能夠練習 merge ,因此我多訓練了一組沒有使用 pretrained model 的模型(先切回 master 再 checkout -b)
$ git checkout master
$ git checkout -b wo_pretrained
$ vi script/config.py # 將usePretrained = True 改為 False
$ dvc repro
$ git add .
$ git commit -m "without pretrained"
$ dvc metrics show -a
epochs51:
log/eval.txt:
input size: 224
classes number: 7
use pretrained: True
epochs: 51
batch size: 32
learning rate: 0.001
momentum: 0.9
accuracy: 0.8626760563380281
master:
log/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.8661971830985915
wo_pretrained:
log/eval.txt:
input size: 224
classes number: 7
use pretrained: False
epochs: 21
batch size: 32
learning rate: 0.001
momentum: 0.9
accuracy: 0.30633802816901406
若我們要將 wo_pretrained
以及 epochs51
merge 在一起的話,需要做一些處理
$ git merge epochs51
Auto-merging train.csv.dvc
CONFLICT (content): Merge conflict in train.csv.dvc
Auto-merging script/config.py
CONFLICT (content): Merge conflict in script/config.py
Auto-merging model.pth.dvc
CONFLICT (content): Merge conflict in model.pth.dvc
Auto-merging log/eval.txt
CONFLICT (content): Merge conflict in log/eval.txt
Auto-merging Dvcfile
CONFLICT (content): Merge conflict in Dvcfile
Automatic merge failed; fix conflicts and then commit the result.
會發現有許多地方都有 conflict ,因此會需要手動的調整,並且保留你認為比較好的模型的參數,以 train.csv.dvc 為例:
<<<<<<< HEAD
md5: 8d2bb1a5dd7542df80a684cf04179444
=======
md5: 0a72f2ce076e4d8f5ba6cd476d1cb464
>>>>>>> epochs51
cmd: python script/split_train_test.py
wdir: .
deps:
- md5: 826ea439f28fb04923de739af8c26b5d
path: script/split_train_test.py
<<<<<<< HEAD
- md5: acb7b60a7d2f31a2ea5c527731f3b5f7
=======
- md5: 1ffb468a9e98a8248f5071286cfd0111
>>>>>>> epochs51
path: script/config.py
- md5: 87c46f0402b54b960b294ef7791f7cf8
path: dataset/annotation.csv
outs:
- md5: 896389741ff20a2055acfe5c65893bf1
path: dataset/train.csv
cache: true
metric: false
persist: false
- md5: 1ea600a9a7b720cd28fe99ff6d1c3e70
path: dataset/test.csv
cache: true
metric: false
persist: false
會把 HEAD 的部分刪除,保留 epochs51 ,如下所示:
md5: 0a72f2ce076e4d8f5ba6cd476d1cb464
cmd: python script/split_train_test.py
wdir: .
deps:
- md5: 826ea439f28fb04923de739af8c26b5d
path: script/split_train_test.py
- md5: 1ffb468a9e98a8248f5071286cfd0111
path: script/config.py
- md5: 87c46f0402b54b960b294ef7791f7cf8
path: dataset/annotation.csv
outs:
- md5: 896389741ff20a2055acfe5c65893bf1
path: dataset/train.csv
cache: true
metric: false
persist: false
- md5: 1ea600a9a7b720cd28fe99ff6d1c3e70
path: dataset/test.csv
cache: true
metric: false
persist: false
照著這樣的步驟,就可以一一的把 conflict 都解完,修改完後可以輸入 dvc checkout
,目的是為了將 DVC 控制的 pipeline 切回剛剛設定的,並且重新 reproduce 。
$ dvc checkout
$ dvc repro
$ git add .
$ git commit -m "merge wo_pretrained and epochs51"
最後,可以再切回 master
去 merge 原本的 branch ,就完成了。
$ git checkout master
$ git merge wo_pretrained
Fast-forward
Dvcfile | 8 ++++----
log/eval.txt | 4 ++--
model.pth.dvc | 12 ++++++------
script/config.py | 2 +-
script/train.py | 1 -
train.csv.dvc | 4 ++--
6 files changed, 15 insertions(+), 16 deletions(-)
6. Pushing data to the cloud
我們這邊以上傳到 gcp 為範例,因此會需要先安裝套件
$ pip install dvc[gs]
並且需要先在 GCP 上去做設定,取得憑證資料,才能夠順利上傳,方法如下:
- 進入 API和服務 → 資訊主頁
2. 點擊 啟用 API 和服務
3. 在搜尋欄位輸入 Cloud Storage
4. 點擊連結
5. 點擊 管理
6. 選擇左方的憑證 → Create Credentials → 服務帳戶金鑰
7–1. 點選 請選取 … 就會跳出 新增服務帳戶
7–2. 輸入帳戶名稱,以及選擇角色「Storage物件建立者」以及「Storage管理員」
8. 建立後,會輸出一份 .json 檔案,可以將這份檔案加入專案當中。
9. 在搜尋欄位輸入 Storage 並且選擇 Storage
10. 點選 建立 Bucket
11–1. 輸入資料,最重要的為上面兩項 ,下面兩項則可以用 Default值即可。
12. 建立好後,就可以去查看詳細的資料
完成設定 GCP 設定後,即可回到我們的專案來,並且把剛剛的憑證 gcp-test.json 加入 .dvc/config
當中。
$ dvc remote add -d upstream gs://cosmetics-bucket/
$ dvc remote modify upstream credentialpath gcp-test.json
$ dvc push
Preparing to upload data to 'gs://cosmetics-bucket/'
Preparing to collect status from gs://cosmetics-bucket/
Collecting information from local cache...
[##############################] 100%
Collecting information from remote cache...
[##############################] 100
[##############################] 100% Analysing status
[##############################] 100% log/test-output.txt
[##############################] 100% dataset/test.csv
[##############################] 100% log/train-output.txt
[##############################] 100% dataset/train.csv
[##############################] 100% model/model.pth
DVC 會將 cache 裡面的資料全部都上傳上去,如下圖所示:
7. Pulling data from the cloud
pull 的話很簡單,直接利用 git 的指令即可
$ git clone https://github.com/dmpetrov/new_tag_classifier.git
$ dvc pull
Next…
下一章,會開始進入 Flask 套件的使用,讓你在最後完成的模型,能夠架成一個服務,提供大家使用。
Day 5: Flask 套件 → 架成 Server
如果喜歡我的文章內容,請幫我多多鼓掌
1 個鼓掌:喜歡這篇的內容
10個鼓掌:期待這一系列的課程
30個鼓掌:希望未來能有更多相關文章