---
title: AI Maker 案例教學 - 影像分類模型應用
description: OneAI 文件
tags: 案例教學
---
[OneAI 文件](/s/xKNcU3O5D)
# AI Maker 案例教學 - 影像分類模型應用
[TOC]
<br>
## 0. 部署圖像分類模型
在本範例中,我們將使用 AI Maker 針對圖像分類應用所提供的 **image-classification** 範本,逐步建立一個圖像分類應用,在此範本中定義了從訓練任務到推論任務中所需環境變數、映像檔、程式等設定,您只須上傳欲訓練或推論的資料集,並修改相關的設定,即可快速執行訓練與推論任務。
主要步驟如下:
- **資料集準備**
在此階段,我們要著手準備要讓電腦學習的影像集,並將資料集上傳至指定位置。
- **訓練模型**
在此階段,我們將配置訓練任務,以進行神經網路的訓練與擬合,並將訓練好的模型儲存。
- **建立推論服務**
在此階段,我們會將儲存下來的模型部署到服務中,以執行推論。
## 1. 準備資料集並上傳
在正式開始前,請先準備好訓練用資料集,如:貓、狗、花… 等。依照下列步驟,將資料集上傳至系統所提供的儲存服務服務中,並將資料集依照指定的目錄結構存放,以供後續開發使用。
### 1.1 準備資料集
開始進行系統操作前,我們先準備用來訓練的資料集。
在本範例中所使用的資料集,是由 Alexander Mamaev 提供於 kaggle 的 [Flowers Recognition(花卉資料集)](https://www.kaggle.com/alxmamaev/flowers-recognition),但為了加速範例的操作我們將類別數目由原先所提供的 5 類刪減至 3 類。
:::info
:bulb: **提示:資料集大小建議**
每個類別應至少準備 300 張訓練影像與 100 張測試影像,以訓練出具備最低可行精度的模型。
:::
1. **建立一個名為 `dataset` 的資料夾**
開始調整資料集結構前,請先建議一個名為 **`dataset`** 資料夾,稍後用來存放我們的圖像資料。
2. **準備圖像資料**
進入 **dataset** 資料夾,將圖像資料依比例分成 **train** 與 **valid** 兩個子資料夾。將子資料夾(**train** 與 **valid**)中的圖像依 **標籤** 建立 **資料夾** 進行分類。完成後,資料集結構如下:
![資料集結構](/uploads/2Av7fxB.png)
:::warning
:warning: **注意:資料夾名稱請勿更改**
請注意 **`dataset`** 、**`train`** 與 **`valid`** 等資料夾名稱,與程式的讀取有關,即上圖紅線所框出的資料夾,請勿任意更換資料夾名稱,以免程式無法順利讀取到資料集。
:::
### 1.2 建立儲存體
完成資料集的準備後,接著需將資料集上傳到「**儲存服務**」服務。
1. **建立儲存服務**
從 OneAI 服務列表選單選擇「**儲存服務**」,進入儲存服務管理頁面,接著點擊「**+建立**」,新增一個名為 **`flowers-dataset`** 的儲存體,此儲存體會用來存放我們的資料集。
![](/uploads/upload_8c722c9f33aaa601e7ddcb9a61cf7844.png)
2. **檢視儲存體**
完成儲存體的建立後,重新回到「**儲存服務管理**」頁面。此時,畫面上會出現剛剛建立的儲存體。
![](/uploads/upload_fdde1adbbc4b518f774634e46d27ae57.png)
點擊建立好的儲存體 **`flowers-dataset`**,即可以開始上傳資料集。
![開始上傳資料集](/uploads/NrAOMON.png)
### 1.3 上傳自有資料集
在完成資料集準備與儲存體建立後,我們就可以將資料集也就是名為 **`dataset`** 的資料夾,存放到儲存體中。若資料量不大,可以直接點選上傳,將整理好的資料集拖曳上傳。
![上傳頁面](/uploads/6kOwJ0C.png)
若資料量太大,可以一個個資料夾分批上傳;或是使用第三方軟體如 [**S3 Browser**](http://s3browser.com/) 或 [**Cyberduck**](https://cyberduck.io/) 上傳。
上傳完成後,在頁面上所呈現的結果如下:
![](/uploads/upload_abac8c514ac4a34edeb16ac60f1483b7.png)
![](/uploads/upload_1eb7ae7413d250636a668ab8b6c721f0.png)
![](/uploads/upload_ab52c6e9da4816b1976728102f07c132.png)
## 2. 訓練分類任務模型
完成 [**自有資料集的上傳**](#1-準備資料集並上傳) 後,就可以使用這些資料,來訓練與擬合我們的分類任務模型。
### 2.1 建立訓練任務
從 OneAI 服務列表選擇「**AI Maker**」,再點擊「**訓練任務**」,進入訓練任務管理頁面。AI Maker 提供 **Smart ML 訓練任務** 與 **一般訓練任務** 兩種訓練方法,訓練方法不同所須設定的參數也有所不同。
![訓練任務管理](/uploads/upload_23c6fdd3e1f534366eba29c61b4c55d9.png)
- **一般訓練任務**
根據您所給定的訓練參數,執行一次性的訓練。
- **Smart ML 訓練任務**
可自動調整超參數,能夠有效地將計算資源用於多個模型訓練,節省您在分析和調整模型訓練參數上的時間和成本。
在此範例中我們選用 **一般訓練任務** 來建立一個新的訓練任務。訓練任務的建立步驟如下,詳細說明可參考 [**AI Maker 使用手册**](/s/QFn7N5R-H)。
1. **基本資訊**
AI Maker 為圖像分類訓練提供 **`image-classification`** 範本,在輸入名稱與描述後,您可以選擇系統所提供的 **`image-classification`** 範本,自動帶出公用映像檔 **`imageclassifier:v1`** 及後續步驟的各項參數設定。
![](/uploads/upload_af1bb5596caefdef19aeb8f6ccba248e.png)
2. **硬體設定**
參考目前的可用配額與訓練程式的需求,從列表中選出合適的硬體資源。但需注意的是本範例的映像檔中所使用的機器學習框架為 **tensorflow-gpu**,因此在挑選硬體時,請選擇包含 **GPU** 的規格。
3. **儲存設定**
這個階段是將我們存放訓練資料的儲存體掛載到容器中。掛載路徑與環境變數的宣告在範本中已經設定完成,這邊只要選擇在 [**建立儲存體**](#12-建立儲存體) 所建立的儲存體名稱即可。
![](/uploads/upload_52e59e3bf63c3eed80c459951eadfdb5.png)
4. **變數設定**
在此說明映像檔 **`image-classification:v1`** 所提供的參數:
|變數|預設值|介紹|
|--|--|--|
|IS_BINARY_CLASS| false |若當資料類型為**二元分類 (Binary Class)** 時,也就是 Yes/No 問題,則將此值設定為 `1` / `true`;若否,則將此值設為 `0` / `false` ,即表示資料為**多元分類 (Multi Class)**。|
|PRETRAIN_MODEL|Auto|欲使用的欲訓練模型,目前支援 ```MobileNetV3Small、MobileNetV2、NasNetMobile、EfficientNetB0、MobileNetV3Large、DenseNet121、Xception、InceptionV3、ResNet50、InceptionResNetV2、EfficientNetB7、VGG16、VGG19```,共 13 種模型。若選擇 ```Auto```,則會依照資料集大小,從支援列表中挑選模型。|
|BATCH_SIZE|8|每批資料量的大小,不建議大於資料集中圖像的總個數。|
|NUM_EPOCHS|15|指定訓練集中全部樣本訓練的次數。|
|LEARNING_RATE |0.0001|學習率|
當在填寫基本資訊,選擇套用 **`image-classification`** 的範本,上述的變數與慣用的指令會自動帶入,惟有各個變數的設定值需依照開發需求進行調整。
#### 參數補充說明
1. **IS_BINARY_CLASS**
若依照資料的類型可區分:**二元分類(Binary Class)** 與 **多元分類(Multi Class)** 兩種:
* **二元分類 Binary Class**
主要用來解決只有兩種結果的問題,簡單來說就是一個 **Yes/No** 問題的回答。例如:這是一張貓的圖片嗎?
:::danger
:no_entry_sign: **資料限制**
若將 IS_BINARY_CLASS 為 `1`,即二元分類模式,但所提供資料類別卻有多於兩種時,會強制中斷任務!
:::
* **多元分類 Multi Class**
顧名思義,它可以用來解決有多種回答的問題。當所提供資料類別多於兩個時,請將 `IS_BINARY_CLASS` 此值設定為 `0`。例如:這是一張貓、狗還是花的圖片?
2. **環境變數與超參數**
根據在 [**建立訓練任務**](#21-建立訓練任務) 時所選擇的訓練方法不同,即 **Smart ML 訓練任務** 與 **一般訓練任務**,變數設定會稍有不同:
|欄位名稱|說明|
| --------|--------- |
| 環境變數 | 輸入環境變數的名稱及數值。這邊的環境變數除了包含訓練執行的相關設定外,也包括了訓練網路所需的參數設定。 |
| 超參數<sup style="color:red"><b>\*</b></sup> | **(Smart ML 訓練任務)** 這是告訴任務,有哪些參數需要進行嘗試。每個參數在設定時,須包含參數的名稱、類型及數值(或數值範圍),選擇類型後(整數、小數和陣列),請依提示輸入相對的數值格式。 |
| 目標參數 | 訓練結束,會回傳一值做為最終結果,這邊須為該值設定名稱及目標方向。例如:若回傳的數值為準確率,則可命名為 accuracy,並設定其目標方向為最大值;若回傳的值為錯誤率,則命名為 error ,其方向為最小值。 |
| 命令 | 輸入欲執行的命令或程式名稱。根據此映像檔所提供的指令為:`python /workspace/train.py`。|
| 任務次數<sup style="color:red"><b>\*</b></sup> | **(Smart ML 訓練任務)** 即訓練次數設定,讓訓練任務執行多次,以找到更好的參數組合。|
<sup style="color:red"><b>\*</b></sup> **超參數** 與 **任務次數** 為選擇 **Smart ML 訓練任務** 時才需設定的參數。
其中,**環境變數** 與 **超參數** 可以互相移動。若您想固定該參數,則可將該參數從超參數區域中移除,新增至環境變數區域,並給定固定值;反之,若想將該參數加入嘗試,則將它從環境變數中移除,加入至下方的超參數區域。
![](/uploads/upload_c7196bb5256676715a88e6834b5bd2d7.png)
5. **檢閱 + 建立**
最後,確認填寫的資訊無誤後,就可按下建立。
### 2.2 啟動訓練任務
完成訓練任務的設定後,回到「**訓練任務管理**」頁面,可以看到剛剛建立的任務。
![](/uploads/upload_20d4ac10ad9089a170bab6bc0f44dba0.png)
點擊該任務,可檢視訓練任務的詳細設定。在上方命令列有 **儲存(儲存為範本)**、**啟動**、**停止**、**編輯**、**删除** 及 **重新整理** 6 個圖示。
![](/uploads/upload_f4613a6bca9e40d763e1ab4cc1a05c06.png)
若此時任務的狀態顯示為 **`Ready`** ,即可點擊 **啟動** 圖示,執行訓練任務。啟動後,透過點擊上方的「**運行列表**」頁籤,可以在列表中查看該任務的執行狀況與排程。在訓練進行中,可以點擊任務右方清單中的「**查看日誌**」或「**查看詳細狀態**」,來得知目前執行的詳細資訊。
### 2.3 檢視訓練結果並儲存模型
等待訓練時,可以先至「**模型**」功能,建立存放此訓練結果的模型目錄。點選左側服務列表的「**模型**」,進入「**模型管理**」頁面,再點擊「**新增目錄**」,建立一個模型目錄,例如:`flower`。
![](/uploads/QK45hjw.png)
回到訓練列表頁面,當訓練完成後,會出現一或多個結果,從中挑選出符合預期結果,再點選右側「**儲存為模型**」,將之儲存至模型庫中;若無符合預期結果,則重新調整環境變數與超參數的數值或數值範圍。
![](/uploads/upload_98054b464182d5e85ce1b409e6a3c787.png)
其中模型名稱可從下拉式選單中選擇剛剛建立的模型目錄。
![](/uploads/WEv3VWL.png)
儲存後,回到 **「模型管理」** 頁面。在列表中找到該模型,點擊進入該模型的版本列表,可看到儲存模型的所有版本、描述、來源與結果等資訊。
![](/uploads/Hz9Hywm.png)
## 3. 建立推論服務
當您訓練好圖像分類任務網路,並儲存訓練好的模型後,即可藉由 **推論** 功能將其部署至應用程式或服務執行推論。
### 3.1 建立推論任務
首先點選左側服務列表「**推論**」,進入「**推論管理**」頁面,並按下「**+建立**」,建立一個推論服務。
推論任務的建立步驟說明如下:
1. **基本資訊**
與前面設定相似,我們也是使用 **`image-classification`** 的範本進行套用。不過,所要載入的模型名稱與版號仍須使用者手動設定。
- **名稱**
載入後模型的檔案名稱,與程式進行中的讀取有關。這值會由 `image-classification` 推論範本設定。
- **模型名稱**
所要載入模型的名稱,即我們在 [**2.3 檢視訓練結果並儲存模型**](#23-檢視訓練結果並儲存模型) 中所儲存的模型。
- **版本**
所要載入模型的版號,亦是 [**2.3 檢視訓練結果並儲存模型**](#23-檢視訓練結果並儲存模型) 中所設定的版號。
- **掛載位置**
載入後模型所在位置,與程式進行中的讀取有關。這值會由 `image-classification` 推論範本設定。
![](/uploads/upload_86e678ef15c1063994ef542ad845db16.png)
2. **硬體設定**
參考目前的可用配額與需求,從列表中選出合適的硬體資源。但因本範例的映像檔中所使用的機器學習框架為 **tensorflow-gpu**,因此在挑選硬體時,請選擇包含 **GPU** 的規格。
3. **儲存設定**
此步驟無須設定。
4. **變數設定**
在變數設定步驟,這些慣用的指令與參數,會在套用範本時自動帶入。
![](/uploads/upload_a0c9a7a66552aa0d0f39b23557481a0f.png)
5. **進階設定**
* **監控資料**
此監控用途在於觀測推論服務一段時間內,API 呼叫次數以及推論結果所呈現的統計資訊。
![](/uploads/upload_a3c3c1e548cbc73abd471d780879277a.png)
| 名稱 | 類別 |說明 |
|-----|-----|------------|
|Predict Result | 標籤 | 在指定的時間區間內,該物件被偵測出所累計的總次數。<br>換言之,可看出各類別在一段時間內的分佈狀況。<br> ![](/uploads/upload_2385459a5375e8a3bf06dd4339eda601.png)
|Score| 數量 | 在某個時間點呼叫推論 API 一次,該物件被偵測出的信心值。<br>![](/uploads/upload_5db7708ec6ec2ab7633de40e648e5d3c.png)|
<br>
6. **檢閱 + 建立**
最後,確認填寫的資訊,就可按下建立。
### 3.2 進行推論
設置任務完成後,請到該服務的推論詳細設定確認是否成功啟動。當服務的狀態顯示為 **Ready** 時,即可以開始連線到推論服務進行推論。
![](/uploads/upload_d4ee6afe287fede0e20b8dbbe15f3589.png)
也可點擊「**查看日誌**」,若在日誌中有看到以下訊息,表示推論服務已經在運作中。
> * Running on http://{ip}:{port}/ (Press CTRL+C to quit)
![](/uploads/E4Uyq3I.png)
<br>
此處須注意的是日誌中所顯示的網址 http://{ip}:{port} 只是容器的內部網址,並無法從外部存取。由於目前推論服務為了安全性考量沒有開放對外埠服務,但我們可以透過容器服務來跟已經建立好的推論服務溝通,溝通的方式就是靠「**推論詳細資料**」頁面下方 **網路** 資訊所顯示的網址。
![](/uploads/upload_26c6c23324d75c84925175738da3b2ee.png)
:::info
:bulb: **提示:推論服務網址**
- 為安全性考量,目前推論服務提供的 **網址** 僅能在系統的內部網路中使用,無法透過外部的網際網路存取。
- 若要對外提供此推論服務,需要透過 [**容器服務**](/s/yGbG4JJyi) 轉接到推論服務以提供對外服務。
:::
若要查看推論監控,可點擊「**監控**」頁籤,即可在監控頁面看到相關資料,下圖為經過一段時間後的推論結果。
![](/uploads/upload_64226043c0cfede95cb56928e593ed19.png)
點選期間選單可篩選特定期間呼叫推論 API 的統計資訊,例如:1 小時、3 小時、6 小時、12 小時、1 天、7 天、14 天、1 個月、3 個月、6 個月、1 年或自訂。
![](/uploads/upload_a074311c7aaea03c588d1c3e0146a178.png)
:::info
:bulb: **關於觀測期間的起始與結束時間**
例如當前時間為 15:10,則:
- **1 小時** 的範圍是指 15:00 ~ 16:00(並非指過去一小時 14:10 ~ 15:10)
- **3 小時** 的範圍是指 13:00 ~ 16:00
- **6 小時** 的範圍是指 10:00 ~ 16:00
- 以此類推
:::
#### 3.2.1 使用 curl 指令測試推論服務
如要快速測試推論服務,可使用 [**容器服務**](/s/yGbG4JJyi),並藉由 **終端機** 來進行連線。
```bash
curl -X GET {推論服務網址}/hello
```
依照取得的詳細資訊,替換各參數後執行,可以看到回傳的文字訊息:
```bash
$> curl -X GET image-inf-i:9999/hello
Hello World!
```
若服務啟動成功,可以呼叫 `predict` 的 API,來進行推論:
```bash
$> curl -X POST image-inf-i:9999/predict \
-H 'Content-Type: multipart/form-data' \
-F file=@{欲傳遞的圖片在本地端的位置}
```
當 server 端收到圖片後會進行 predict,最終返回該圖片在各類別的信心分數:
![](/uploads/2QOvLue.png)
若是二元分類,則返回 Label 0、1 的標籤意義與信心分數,一般來說信心分數大於 0.5 會視為 Label 1、小於 0.5 會視為 Label 0。但該門檻值並非絕對,可依照資料集特性進行調整。
![](/uploads/OBhT4ED.png)
#### 3.2.2 使用 Python 程式執行推論服務
除了使用終端機連線外,也可以透過 [**容器服務**](/s/yGbG4JJyi) 來啟動 Jupyter 與推論服務來進行連線,本教學的附件中提供一份 Jupyter 程式碼,您可以直接透過這份程式碼進行推論。附件程式碼的說明如下:
1. **發送請求**
在這邊使用 requests 模組產生 HTTP 的 POST 請求,並將圖片上傳。
```python=
INFE_IP = "image-inf-i"
INFE_NODE_PORT = "9999"
URL_BASE = "http://{ip}:{node_port}".format(
ip=INFE_IP,
node_port=INFE_NODE_PORT)
ROUTE = "/predict"
url = "{base}{route}".format(
base=URL_BASE,
route=ROUTE)
image_path = "/input/dataset/train/rose/7865295712_bcc94d120c.jpg"
image_file_descriptor = open(image_path, 'rb')
res = requests.post(url, files={'file': image_file_descriptor})
image_file_descriptor.close()
```
其中 `INFE_IP` 以及 `INFE_NODE_PORT` 需要填入推論服務的 DNS 名稱。`image_path` 則是所要推論的圖片位置。
2. **取回結果**
完成物件偵測後,結果將以 JSON 格式回傳,包含預測結果與分數。
```python=
img = mpimg.imread(image_path)
imgplot = plt.imshow(img)
plt.show()
status_code = res.status_code
content = json.loads(res.content)
content = json.dumps(content, indent=4)
print("[code]:\t\t{code}\n[content]:\n{text}".format(code=status_code,text=content))
```
![](/uploads/mMUerLm.png)
#### 3.2.3 附件程式碼
:::spoiler **程式碼**
```python=1
import requests
import json
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
INFE_IP = "image-inf-i"
INFE_NODE_PORT = "9999"
## Case1、Connect test
URL_BASE = "http://{ip}:{node_port}".format(
ip=INFE_IP,
node_port=INFE_NODE_PORT)
res = requests.get(URL_BASE)
print("[code]:\t\t{code}\n[content]:\t{text}".format(code=res.status_code,text=res.text))
# Case2、Hello World
ROUTE = "/hello"
url = "{base}{route}".format(
base=URL_BASE,
route=ROUTE)
res = requests.get(url)
print("[code]:\t\t{code}\n[content]:\t{text}".format(code=res.status_code,text=res.text))
# Case3、Predict
ROUTE = "/predict"
url = "{base}{route}".format(
base=URL_BASE,
route=ROUTE)
image_path = "/input/dataset/train/rose/7865295712_bcc94d120c.jpg"
image_file_descriptor = open(image_path, 'rb')
res = requests.post(url, files={'file': image_file_descriptor})
image_file_descriptor.close()
img = mpimg.imread(image_path)
imgplot = plt.imshow(img)
plt.show()
status_code = res.status_code
content = json.loads(res.content)
content = json.dumps(content, indent=4)
print("[code]:\t\t{code}\n[content]:\n{text}".format(code=status_code,text=content))
```
:::