【機器學習從零到一】 Day3: Pytorch 介紹與範例 (cosmetics classification)

PJ Wang
9 min readJul 8, 2019

--

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

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

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

專案挑戰:化妝品分類

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

Pytorch 前言

Pytorch 為 Facebook 的深度學習框架,目前網路上也有相當多的資源以及介紹,因此在此不在多加贅述,那我這邊要介紹什麼呢?有許多的範例與教學都是以 MNIST 的資料集當作範例,但只要當我們想要開始客製化要修改資料集、Loss function等等,就會開始遇到貧頸,因此這邊會直接利用客製化搜集的資料集,來當作範例,供大家修改以及使用。

基本框架

基本上 Pytorch 可以把程式碼分成幾個段落,並且我們會依序來介紹如何建立

0. 事前準備

程式碼: github (本章節會用到的為 cosmetics-classification.ipynb 以及 split-train-test.ipynb )

資料集:下載連結

整體的資料夾結構為:

./ 
├── script
│ ├─ split_data.py
│ ├─ train.py
│ ├─ evaluate.py
│ ├─ config.py
│ └─ server.py
├── dataset
│ ├── cosmetics-category
│ │ ├── train
│ │ │ ├─ Compact_foundation
│ │ │ ├─ Eyebrow_pencil
│ │ │ ├─ Foundation
│ │ │ ├─ Lipstick
│ │ │ ├─ Makeup_brushes
│ │ │ ├─ Nail_Polish
│ │ │ └─ Palette
│ │ └── valid
│ │ ├─ Compact_foundation
│ │ ├─ Eyebrow_pencil
│ │ ├─ Foundation
│ │ ├─ Lipstick
│ │ ├─ Makeup_brushes
│ │ ├─ Nail_Polish
│ │ └─ Palette
│ ├── cosmetics-all
│ │ ├─ _PRrlNrSLhHGaUTfduYgQ6eOAdI.jpg
│ │ ├─ -b2cugUzKg6IIHIJjfXGcDnKsZA.jpg
│ │ ├─ -FK0wRGzJZ5NirgLw0CbvUf-7Og.jpg
│ │ └─ ...
│ ├── train.csv
│ ├── test.csv
│ └── annotation.csv
├── model
│ ├── model.pth
│ └── evaluate.txt
├── Dockerfile
├── cosmetics-classification.ipynb
└── split-train-test.ipynb

1. 資料搜集

利用 Google 的 Extension 叫做 Image downloader 來批次的下載 Pinterest 以及 Instagram 上的圖片,下面是我搜集的化妝品

總共為 7 個類別,分別為「口紅、眼線筆、指甲油、粉底液、粉餅、刷具以及眼影盤」,每個分類的圖片數量為:

粉餅: 161
眼線筆: 190
粉底液: 129
口紅: 431
刷具: 156
指甲油: 275
眼影盤: 61

資料集:下載連結

dataset
├── cosmetics-category
│ ├─ train
│ │ ├─ Compact_foundation
│ │ ├─ Eyebrow_pencil
│ │ ├─ Foundation
│ │ ├─ Lipstick
│ │ ├─ Makeup_brushes
│ │ ├─ Nail_Polish
│ │ └─ Palette
│ └─ valid
│ ├─ Compact_foundation
│ ├─ Eyebrow_pencil
│ ├─ Foundation
│ ├─ Lipstick
│ ├─ Makeup_brushes
│ ├─ Nail_Polish
│ └─ Palette
├── cosmetics-all
└── annotation.csv

解壓縮後資料節同上, cosmetics-category 為每個化妝品種類個別的圖片, cosmetics-all 為全部圖片都混在一起、而標記的 labelannotation.csv ,會區分兩個資料夾的原因,是因為在後續我們會分別利用這兩個資料夾來練習 pytorchdataloader

2. 資料切割

這邊用的資料夾為 cosmetics-all 以及 annotation.csv 來做資料集的切割,程式碼的使用方式為 python split_data.py (Testing data 的比例) (random seed) ,例如我是切 80% 以及 20%:python split_data.py 0.2 10

3. 訓練模型

訓練模型這邊分五個部分

A. 匯入套件

這邊我們使用到的所以套件為:requirement.txt

B. 製作 Dataset

Dataset 的製作在 Pytorch 中的格式為:

class customize_dataset(Dataset):
def __init__(self):
# 初始化變數,這邊會一次將 Data 做處理
def __getitem__(self, index):
# 後續 Dataloader 會自動傳遞 index 變數
# 當 index = N 時,可以想像是要回傳資料集中的第 N 筆資料
def __len__(self):
# 資料集的長度

我們後續會將 train.csv 以及 test.csv 丟進 csvFile 的欄位當中,而 rootPath 為圖片所在位置的根目錄,transform 為是否將圖片做擴增的函示。

由於目前的 label[Compact_foundation, Eyebrow_pencil, Foundation, Lipstick, Makeup_brushes, Nail_Polish, Palette] ,所以需先轉成數字類別 0 ~ 6 ,這邊使用的方法是 pd.factorize() 。(注意:pytorch 的 crossentropy 函數,不需將 label 轉乘 one-hot-encoding)

C. VGG16 模型

Pytorch 的模型製作格式為:

class customize_model(nn.Module):
def __init__(self):
super(customize_model, self).__init__() # 繼承
# 設定每一層的函數
def forward(self, x):
# 此時的 x 為輸入 (以此例則為 (batch_size, 224, 224, 3))

為了讓大家更好的去學習模型製作,這邊是將 VGG16 的模型展開,而更多的函數可以參考 torch.nn

倘若我們想要用 Pretrained 好的模型再做 Finetuning 則可參考:FINETUNING TORCHVISION MODELS,而我們這邊是利用 VGG16 在 imagenet pretrained 好的模型再進一步做 Finetuning。

C. 訓練函數

訓練函數的格式為:

def train(net, dataloader, optimizer, criterion, epochs):
net.train()
for i in range(epochs):
for x, label in dataloader:
x = x.to(device)
label = label.to(device, dtype=torch.long)
optimizer.zero_grad()
output = net(x)
# 預留位置01
loss = criterion(output, label)
loss.backward()
optimizer.step()
return net

上面的 script 為最基本的格式,如果需要近一步計算 accuracy 則可在預留位置01加上:

_, predicted = torch.max(output.data, 1)
accuracy = (predicted == label).sum().item()

D. 測試函數

測試函數的格式為:

def test(net, dataLoader, criterion):
net.eval()
for x, label in dataLoader:
x = x.to(device)
label = label.to(device, dtype=torch.long)
output = net(x)
loss = criterion(output, label)
# 預留位置01

這邊需注意的地方是,第一行需要先設定 net.eval() ,這樣的話會在模型遇到 Dropout 以及 Batch normalization 時有不一樣的調整。

E. 損失函數

損失函數的格式為:

class custom_loss(nn.Module):
def __init__(self):
super(custom_loss, self).__init__()
# 初始化變數設定
def forward(self, output, target):
# output 為模型預測出來的結果,target 為資料的 label

在實際上,蠻少的機會會需要客製化損失函數,通常會直接利用 pytorch 提供的,可以參考:TORCH.NN.MODULES.LOSS

F. 彙整 main()

這邊會將所有東西串在一起,並且會去呼叫上述的函數

4. Inference

在 inference 的時候,會需要將原本 Data transform 時的函數加入,如果只有一張圖的話,需要用 unsqueeze_(0) 將三維的陣列擴增為四維。

Next…

下一章,會開始進入 Data science 的版本控制,同時的去管控資料、參數、模型與程式碼。

Day 4: Data science 的版控 → DVC 的建立與使用

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

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

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

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

--

--

PJ Wang

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