在這系列的第五部分中,我將概述創建 Docker 容器的步驟,以訓練你的圖像分類模型、評估性能並準備部署。
人工智慧/機器學習工程師通常希望專注於模型訓練和數據工程,但實際上,我們也需要了解幕後的基礎設施和運作機制。
我希望分享一些小技巧,不僅是讓你的訓練運行起來,還有如何在 Kubernetes 等雲資源上以成本效益高的方式簡化這個過程。
我將參考我之前文章中的一些元素,以獲得最佳模型性能,因此務必查看第一部分和第二部分有關數據集的內容,以及第三部分和第四部分有關模型評估的內容。
以下是我將與你分享的學習內容,一旦我們為基礎設施奠定基礎:
建立你的 Docker 容器
執行你的訓練運行
部署你的模型
基礎設施概述
首先,讓我簡要描述我創建的設置,特別是關於 Kubernetes 的部分。你的設置可能完全不同,這沒關係。我只是想為基礎設施設置舞台,以便後面的討論更有意義。
圖像管理系統
這是一個你部署的伺服器,提供用戶界面讓你的專家標記和評估圖像,以用於圖像分類應用。該伺服器可以作為 pod 在你的 Kubernetes 集群上運行,但你可能會發現運行一個專用伺服器,並使用更快的硬碟會更好。
圖像文件存儲在如下的目錄結構中,這樣的結構自我說明且易於修改。
Image_Library/
– cats/
– image1001.png
– dogs/
– image2001.png
理想情況下,這些文件應該存放在本地伺服器存儲中(而不是雲端或集群存儲),以獲得更好的性能。這個原因在我們看到圖像庫增長時會變得明顯。
雲端存儲
雲端存儲提供了一種幾乎無限制且方便的方式來在系統之間共享文件。在這種情況下,你的管理系統中的圖像庫可以訪問與你的 Kubernetes 集群或 Docker 引擎相同的文件。
然而,雲端存儲的缺點是打開文件的延遲。你的圖像庫將擁有成千上萬的圖像,讀取每個文件的延遲將對你的訓練運行時間產生重大影響。較長的訓練運行意味著使用昂貴的 GPU 處理器的成本更高!
我發現加快速度的方法是創建一個圖像庫的 tar 文件,並將其複製到雲端存儲。更好的方法是並行創建多個 tar 文件,每個文件包含 10,000 到 20,000 張圖像。
這樣,你只需在少數幾個文件上面對網絡延遲(這些文件一旦解壓縮就包含成千上萬的圖像),並且你可以更快地開始訓練運行。
Kubernetes 或 Docker 引擎
適當配置的 Kubernetes 集群將允許你動態擴展或縮減節點,因此你可以根據需要在 GPU 硬體上進行模型訓練。Kubernetes 是一個相當繁重的設置,還有其他容器引擎可以使用。
技術選項不斷變化!
主要思想是,你希望啟動所需的資源——只在需要的時候使用它們——然後縮減以減少運行昂貴 GPU 資源的時間(因此也減少成本)。
一旦你的 GPU 節點啟動並且 Docker 容器運行,你可以將上述的 tar 文件提取到節點上的本地存儲,例如 emptyDir。該節點通常具有高速 SSD 硬碟,非常適合這類工作負載。有一個注意事項——你的節點存儲容量必須能夠處理你的圖像庫。
假設一切正常,讓我們談談如何構建你的 Docker 容器,以便你可以在圖像庫上訓練模型。
建立你的 Docker 容器
能夠以一致的方式執行訓練運行非常適合構建 Docker 容器。你可以“固定”庫的版本,因此你知道每次你的腳本將如何運行。你也可以對你的容器進行版本控制,並在需要時恢復到已知的良好映像。Docker 的一個好處是你幾乎可以在任何地方運行容器。
在容器中運行時的權衡,特別是對於圖像分類模型,是文件存儲的速度。你可以將任意數量的卷附加到你的容器,但它們通常是網絡附加的,因此每次讀取文件時都有延遲。如果你有少量文件,這可能不是問題。但當處理成百上千的圖像數據時,這種延遲就會累積!
這就是為什麼使用上述的 tar 文件方法是有益的。
此外,請記住,Docker 容器可能會意外終止,因此你應確保將重要信息存儲在容器外部,例如雲端存儲或數據庫。我將在下面告訴你如何做。
Dockerfile
知道你需要在 GPU 硬體上運行(這裡我假設使用 Nvidia),請確保為你的 Dockerfile 選擇正確的基礎映像,例如 nvidia/cuda,並使用“devel”版本,這將包含正確的驅動程序。
接下來,你將把腳本文件添加到你的容器中,還有一個“批處理”腳本來協調執行。這是一個示例 Dockerfile,然後我將描述每個腳本的功能。
##### Dockerfile #####
FROM nvidia/cuda:12.8.0-devel-ubuntu24.04
# 安裝系統軟體
RUN apt-get -y update && apt-get -y upgrade
RUN apt-get install -y python3-pip python3-dev
# 設置 python
WORKDIR /app
COPY requirements.txt
RUN python3 -m pip install –upgrade pip
RUN python3 -m pip install -r requirements.txt
# Python 和批處理腳本
COPY ExtractImageLibrary.py .
COPY Training.py .
COPY Evaluation.py .
COPY ScorePerformance.py .
COPY ExportModel.py .
COPY BulkIdentification.py .
COPY BatchControl.sh .
# 允許互動式 shell
CMD tail -f /dev/null
Dockerfile 是聲明式的,幾乎像是構建小伺服器的食譜——你知道每次會得到什麼。Python 庫也受益於這種聲明式的方法。這是一個示例 requirements.txt 文件,載入支持 GPU 加速的 TensorFlow 庫。
##### requirements.txt #####
numpy==1.26.3
pandas==2.1.4
scipy==1.11.4
keras==2.15.0
tensorflow[and-cuda]
提取圖像庫腳本
在 Kubernetes 中,Docker 容器可以訪問物理節點上的本地高速存儲。這可以通過 emptyDir 卷類型來實現。如前所述,這僅在你的節點上的本地存儲能夠處理你的庫的大小時有效。
##### Kubernetes 中的 25GB emptyDir 卷示例 #####
containers:
– name: training-container
volumeMounts:
– name: image-library
mountPath: /mnt/image-library
volumes:
– name: image-library
emptyDir:
sizeLimit: 25Gi
你還需要有另一個 volumeMount 指向你的雲端存儲,在那裡你有 tar 文件。這將取決於你的提供商,或者如果你使用持久卷聲明,因此我不會在這裡詳細說明。
現在你可以提取 tar 文件——理想情況下並行提取以獲得性能提升——到本地掛載點。
訓練腳本
作為 AI/ML 工程師,模型訓練是我們希望花費大部分時間的地方。
這就是魔法發生的地方!
隨著你的圖像庫現在被提取,我們可以創建訓練-驗證-測試集,載入預訓練模型或構建新模型,擬合模型並保存結果。
一個對我來說非常有效的技巧是加載最近訓練的模型作為我的基礎。我在第四部分的“微調”中詳細討論了這一點,這會導致更快的訓練時間和顯著改善的模型性能。
務必利用本地存儲在訓練過程中檢查點你的模型,因為模型相當大,而你在閒置時仍需為 GPU 付費。
這當然引發了一個擔憂,即如果 Docker 容器在訓練過程中意外終止會發生什麼。雲提供商的風險(希望)很低,你可能不想要不完整的訓練。但如果真的發生,你至少要了解原因,這就是將主要日誌文件保存到雲端存儲(如下所述)或使用像 MLflow 的包的好處。
評估腳本
在你的訓練運行完成並且你已妥善保存工作後,是時候看看它的表現如何了。
通常這個評估腳本會檢查剛剛完成的模型。但你也可以選擇通過互動會話指向先前的模型版本。這就是為什麼將腳本設置為獨立的。
由於它是獨立的腳本,這意味著它需要從磁碟讀取已完成的模型——理想情況下是本地磁碟以提高速度。我喜歡有兩個獨立的腳本(訓練和評估),但你可能會發現將它們合併更好,以避免重新加載模型。
現在模型已加載,評估腳本應該對訓練、驗證、測試和基準集中的每個圖像生成預測。我將結果保存為一個巨大的矩陣,包含每個類別標籤的 softmax 信心分數。因此,如果有 1,000 個類別和 100,000 張圖像,那就是一個擁有 1 億個分數的表格!
我將這些結果保存為 pickle 文件,然後在下一步的分數生成中使用。
分數生成腳本
根據評估腳本生成的分數矩陣,我們現在可以創建各種模型性能指標。同樣,這個過程可以與上面的評估腳本合併,但我更喜歡獨立的腳本。例如,我可能想要重新生成先前訓練運行的分數。看看哪種方法對你最有效。
以下是一些 sklearn 函數,可以產生有用的見解,如 F1、log loss、AUC-ROC 和 Matthews 相關係數。
from sklearn.metrics import average_precision_score, classification_report
from sklearn.metrics import log_loss, matthews_corrcoef, roc_auc_score
除了這些基本的統計分析外,對於每個數據集(訓練、驗證、測試和基準),還有一些有用的識別:
- 哪個真實標籤出現最多錯誤?
- 哪個預測標籤出現最多錯誤猜測?
- 有多少真實標籤與預測標籤的配對?換句話說,哪些類別容易混淆?
- 在應用最低 softmax 信心分數閾值時的準確率是多少?
- 在該 softmax 閾值以上的錯誤率是多少?
- 對於“困難”的基準集,你是否獲得了足夠高的分數?
- 對於“超出範圍”的基準集,你是否獲得了足夠低的分數?
如你所見,有多種計算,並且很難得出一個單一的評估來決定訓練的模型是否足夠好以進入生產。
事實上,對於圖像分類模型,手動審查模型錯誤的圖像以及那些低 softmax 信心分數的圖像是非常有幫助的。利用這個腳本的分數創建一個圖像列表以進行手動審查,然後對模型的表現進行直觀評估。
請查看第三部分以獲得更深入的評估和打分討論。
導出腳本
到目前為止,所有的重任都已完成。由於你的 Docker 容器將很快關閉,現在是將模型工件複製到雲端存儲並準備使用的時候。
以下的 Python 代碼片段更適合 Keras 和 TensorFlow。這將把訓練好的模型導出為 saved_model。稍後,我將展示這是如何在下面的部署部分中由 TensorFlow Serving 使用的。
# 增加當前模型的版本並創建新目錄
next_version_dir, version_number = create_new_version_folder()
# 將模型工件複製到新目錄
copy_model_artifacts(next_version_dir)
# 創建保存模型導出的目錄
saved_model_dir = os.path.join(next_version_dir, str(version_number))
# 保存模型導出以供 TensorFlow Serving 使用
tf.keras.backend.set_learning_phase(0)
model = tf.keras.models.load_model(keras_model_file)
tf.saved_model.save(model, export_dir=saved_model_dir)
這個腳本還複製了其他訓練運行的工件,如模型評估結果、分數摘要和從模型訓練生成的日誌文件。別忘了你的標籤映射,以便你可以為你的類別提供易於理解的名稱!
批量識別腳本
你的訓練運行已完成,模型已被評分,並且新版本已導出並準備好提供服務。現在是時候使用這個最新模型來幫助你識別未標記的圖像。
正如我在第四部分中所描述的,你可能有一組“未知”——非常好的圖片,但不知道它們是什麼。讓你的新模型對這些進行最佳猜測,並將結果記錄到文件或數據庫中。現在你可以根據最接近的匹配和高/低分數創建過濾器。這使得你的專家可以利用這些過濾器找到新的圖像類別,添加到現有類別中,或刪除那些得分非常低且不好的圖像。
順便說一下,我將這一步放在 GPU 容器內,因為你可能有成千上萬的“未知”圖像需要處理,加速硬體將使其輕鬆完成。然而,如果你不著急,你可以在單獨的 CPU 節點上執行這一步,並更早關閉你的 GPU 節點以節省成本。如果你的“未知”文件夾在較慢的雲存儲上,這樣做尤其有意義。
批處理腳本
上述所有腳本執行特定任務——從提取圖像庫、執行模型訓練、進行評估和打分、導出模型工件以供部署,甚至可能進行批量識別。
一個腳本來統治它們所有
為了協調整個過程,這個批處理腳本為你的容器提供了入口點和觸發所有操作的簡單方法。務必生成日誌文件,以便在需要分析任何故障時使用。此外,務必將日誌寫入你的雲端存儲,以防容器意外終止。
#!/bin/bash
# 主批處理控制腳本
# 將標準輸出和標準錯誤重定向到日誌文件
exec > /cloud_storage/batch-logfile.txt 2>&1
/app/ExtractImageLibrary.py
/app/Training.py
/app/Evaluation.py
/app/ScorePerformance.py
/app/ExportModel.py
/app/BulkIdentification.py
執行你的訓練運行
現在是時候讓一切運行起來了……
啟動你的引擎!
讓我們通過步驟來準備你的圖像庫,啟動你的 Docker 容器以訓練你的模型,然後檢查結果。
圖像庫的 ‘tar’ 文件
你的圖像管理系統現在應該創建一個 tar 文件備份你的數據。由於 tar 是單線程功能,通過並行創建多個 tar 文件,每個文件包含部分數據,你將獲得顯著的速度提升。
現在這些文件可以複製到你的共享雲存儲中,進行下一步。
啟動 Docker 容器
你為創建容器所做的所有努力(如上所述)將接受考驗。如果你正在運行 Kubernetes,你可以創建一個作業來執行 BatchControl.sh 腳本。
在 Kubernetes 作業定義中,你可以傳遞環境變量來調整腳本的執行。例如,這裡設置批量大小和訓練的輪數,然後在你的 Python 腳本中提取,因此你可以在不更改代碼的情況下改變行為。
##### Kubernetes 中的作業示例 #####
containers:
– name: training-job
env:
– name: BATCH_SIZE
value: 50
– name: NUM_EPOCHS
value: 30
command: [“/app/BatchControl.sh”]
一旦作業完成,務必確認 GPU 節點根據 Kubernetes 中的擴展配置正確縮減到零——你不想因為簡單的配置錯誤而背負高額賬單。
手動審查結果
隨著訓練運行的完成,你現在應該有保存的模型工件,可以檢查性能。查看指標,如 F1 和 log loss,以及高 softmax 信心分數的基準準確率。
如前所述,報告僅告訴你部分故事。花時間和精力手動審查模型錯誤的圖像或產生低信心分數的圖像是值得的。
別忘了批量識別。務必利用這些來定位新圖像,以填補你的數據集,或找到新類別。
部署你的模型
一旦你檢查了模型性能並對結果感到滿意,就該修改你的 TensorFlow Serving 容器,將新模型投入生產。
TensorFlow Serving 可作為 Docker 容器使用,提供了一種非常快速和方便的方式來提供你的模型。這個容器可以監聽並響應對你的模型的 API 調用。
假設你的新模型是版本 7,而你的導出腳本(見上文)已將模型保存在你的雲共享中,路徑為 /image_application/models/007。你可以啟動 TensorFlow Serving 容器並掛載該卷。在這個例子中,shareName 指向版本 007 的文件夾。
##### Kubernetes 中的 TensorFlow pod 示例 #####
containers:
– name: tensorflow-serving
image: bitnami/tensorflow-serving:2.18.0
ports:
– containerPort: 8501
env:
– name: TENSORFLOW_SERVING_MODEL_NAME
value: “image_application”
volumeMounts:
– name: models-subfolder
mountPath: “/bitnami/model-data”
volumes:
– name: models-subfolder
azureFile:
shareName: “image_application/models/007”
這裡有一個微妙的注意事項——導出腳本應該創建一個名為 007 的子文件夾(與基礎文件夾相同),其中包含保存的模型導出。這可能看起來有點混淆,但 TensorFlow Serving 將掛載這個共享文件夾為 /bitnami/model-data,並檢測到其中的編號子文件夾以提供服務。這將允許你查詢模型版本的 API 以及識別。
結論
正如我在本文開頭提到的,這個設置對我來說是有效的。這當然不是處理這個挑戰的唯一方法,我邀請你自定義自己的解決方案。
我想分享我在使用 Kubernetes 的雲服務時所獲得的艱辛經驗,並希望能控制成本。當然,在保持高水平模型性能的同時做到這一點是一個額外的挑戰,但這是你可以實現的。
我希望我提供的信息能幫助你在自己的努力中取得成功。祝你學習愉快!
本文由 AI 台灣 運用 AI 技術編撰,內容僅供參考,請自行核實相關資訊。
歡迎加入我們的 AI TAIWAN 台灣人工智慧中心 FB 社團,
隨時掌握最新 AI 動態與實用資訊!