據(jù)到接口:ModelArts上跑通圖像分類訓(xùn)練與部署全流程)
上周接了一個圖像分類的小活兒目標(biāo)是把生產(chǎn)線上相機采集的零件照片按缺陷分成“OK”“劃痕”“凹陷”三類。數(shù)據(jù)量不大大概四千來張圖但我本地那臺機器的顯卡剛好被其他任務(wù)占滿排期直接排到了下個月。當(dāng)時正好在調(diào)研云上的AI開發(fā)平臺索性就把“基于 ModelArts 完成模型訓(xùn)練與部署”這條路完整走了一遍。整個過程從數(shù)據(jù)整理到訓(xùn)練作業(yè)跑通再到把模型發(fā)布成線上接口中間踩了不少坑也理順了不少思路。這篇文章不打算復(fù)讀官方文檔就把我實際操作過的流程、參數(shù)、報錯和解決辦法整理出來給想用這類云上平臺做訓(xùn)練和部署的朋友一個參考。1. 整體流程與設(shè)計思路從本地到云端的遷移邏輯1.1 一次模型上云的完整鏈路很多人剛開始接觸這類平臺時會有點懵我本地明明有 Python 環(huán)境、有 PyTorch、有自己的訓(xùn)練腳本為什么還要跑到云端折騰一遍其實完整鏈路并不復(fù)雜拆開看就是五步數(shù)據(jù)準(zhǔn)備 → 數(shù)據(jù)上傳 → 訓(xùn)練作業(yè) → 模型管理 → 部署上線第一步把訓(xùn)練集、驗證集按格式整理好同時把標(biāo)注信息梳理成平臺認(rèn)識的格式。第二步把數(shù)據(jù)集上傳到對象存儲服務(wù)也就是 OBS 桶里。第三步在訓(xùn)練作業(yè)里指定算法、數(shù)據(jù)路徑、輸出路徑和計算規(guī)格提交后等待訓(xùn)練。第四步訓(xùn)練完成后生成的模型文件會保存到 OBS 指定目錄平臺會自動將其注冊為模型版本。第五步部署成在線服務(wù)平臺會給一個可調(diào)用的推理接口之后業(yè)務(wù)系統(tǒng)直接發(fā)請求就行。鏈路本身不復(fù)雜復(fù)雜的是每一步里隱藏的細(xì)節(jié)。比如 OBS 路徑怎么填、數(shù)據(jù)集目錄結(jié)構(gòu)怎么設(shè)計、訓(xùn)練腳本要不要改造、推理代碼要按什么規(guī)范寫、端口該暴露哪個這些都會直接影響能否順利跑通。1.2 為什么我選擇 ModelArts 來跑訓(xùn)練任務(wù)選擇這個平臺的原因核心就兩個字省事。本地訓(xùn)練一個模型先得有個像樣的顯卡環(huán)境裝驅(qū)動、裝 CUDA、裝框架版本這些就能折騰半天。更麻煩的是多人共用機器時的資源分配排期不可控環(huán)境隔離也不好做。而 ModelArts 這類平臺把算力包裝成“提交作業(yè)即可使用”的形式我只需要選好規(guī)格尺寸點提交平臺自動分配資源訓(xùn)練完自動回收按用量計費。不需要維護(hù)任何物理機器也不擔(dān)心別人把我環(huán)境搞壞。還有一點是它的幾步流程是打通的。數(shù)據(jù)存在 OBS訓(xùn)練作業(yè)直接引用 OBS 路徑輸出模型再傳回 OBS部署時也通過 OBS 路徑讀取模型。不需要自己寫復(fù)雜的下載、上傳腳本。這一點對我來說特別順手因為團(tuán)隊里有人之前把時間花在折騰各個環(huán)境銜接上用這種平臺后這部分基本為零。當(dāng)然事務(wù)都有另一面。平臺雖然封裝了流程但也要求按它的規(guī)范來組織代碼和數(shù)據(jù)。如果你完全不管規(guī)范用本地習(xí)慣隨便扔個目錄上去訓(xùn)練作業(yè)很容易報“數(shù)據(jù)集路徑不存在”或者“模型文件找不到”這類問題。所以這套思路本質(zhì)上是用一部分自定義自由度換來了環(huán)境管理和算力編排的便利。1.3 前期規(guī)劃算力需求評估與成本控制別上來就選最大規(guī)格的機器跑。我自己吃過這個虧第一次跑一個很小的模型選了高性能 GPU 規(guī)格結(jié)果不到一個小時就訓(xùn)練完了費用卻花了不少。建議先根據(jù)數(shù)據(jù)量、模型復(fù)雜度、訓(xùn)練輪數(shù)估算一下再選規(guī)格。簡單的經(jīng)驗圖片數(shù)據(jù)量在萬張以內(nèi)、模型是 ResNet 這種常規(guī)分類網(wǎng)絡(luò)、訓(xùn)練幾十輪的話選入門級 GPU 規(guī)格基本夠用數(shù)據(jù)量超過幾萬張或者模型比較大再考慮更高檔的規(guī)格。另外訓(xùn)練作業(yè)一般支持設(shè)置最大運行時長建議填一個合理上限比如 2 小時防止因為異??ㄗ《掷m(xù)計費。費用方面也可以提前開好預(yù)算提醒超出閾值自動告警。這些配置看起來不起眼但對成本控制幫助很大。2. 數(shù)據(jù)準(zhǔn)備把數(shù)據(jù)集搬進(jìn)云端要先過這幾關(guān)2.1 數(shù)據(jù)集組織與 OBS 目錄設(shè)計很多人在本地訓(xùn)練時習(xí)慣把所有圖片放在一個大文件夾里然后在代碼里寫死路徑隨機切分訓(xùn)練驗證。這套邏輯在云上并不合適。平臺層面更希望看到清晰的數(shù)據(jù)集目錄因為后續(xù)的模型版本、數(shù)據(jù)集版本管理都依賴結(jié)構(gòu)化的路徑。我這次采用的結(jié)構(gòu)是obs://my-demo-bucket/dataset/ ├── train/ │ ├── ok/ │ ├── scratch/ │ └── dent/ ├── val/ │ ├── ok/ │ ├── scratch/ │ └── dent/ └── labels.txttrain 和 val 分別放訓(xùn)練集、驗證集子目錄名稱就是類別名。labels.txt 里按順序?qū)戭悇e名稱一行一個。這么組織的邏輯在于不管是用平臺自帶的圖像分類算法還是自己寫 PyTorch 腳本用 ImageFolder 讀取都能直接被識別。驗證集單獨搞出來可以避免訓(xùn)練腳本里做隨機切分帶來的不確定性也讓每次實驗的可比性更強。OBS 桶名是我的踩坑點之一。有些字符平臺不支持筒名必須全小寫而且不能有下劃線。我一開始建了一個帶下劃線的桶名創(chuàng)建時提示不合法換了好幾個名字才了解規(guī)則。建議桶名盡量用“短橫線數(shù)字”的格式比如 my-demo-bucket 就很穩(wěn)。2.2 數(shù)據(jù)上傳與校驗數(shù)據(jù)上傳方式有兩種控制臺點擊上傳和命令行工具。數(shù)據(jù)量小、幾百張圖的話控制臺直接傳問題不大到了幾千張以上我還是推薦 obsutil 工具支持批量、斷點續(xù)傳速度穩(wěn)定很多。我用的命令是obsutil cp ./dataset obs://my-demo-bucket/dataset -r -f-r 表示遞歸上傳目錄-f 表示強制覆蓋同名文件。上傳完成后盡量做一次校驗我通常是再把目錄列表拉出來看看obsutil ls obs://my-demo-bucket/dataset/ -r這一步別看簡單真能發(fā)現(xiàn)不少問題。比如本地某個子目錄是空的同步過程中被忽略或者某些文件傳一半失敗。做過一次校驗之后后面訓(xùn)練作業(yè)報“圖片解碼失敗”的概率會低很多。還有一個容易被忽視的細(xì)節(jié)上傳前先把文件命名統(tǒng)一好不要有中文名、空格和特殊符號否則后期加載圖片時很容易出現(xiàn)奇奇怪怪的編碼報錯。2.3 標(biāo)注策略自動標(biāo)注與人工修正如果數(shù)據(jù)集沒有現(xiàn)成標(biāo)簽平臺自帶的數(shù)據(jù)標(biāo)注功能可以把標(biāo)注環(huán)節(jié)放在云端來做。先按類別創(chuàng)建標(biāo)簽然后借助預(yù)置模型做自動標(biāo)注它會先跑一輪預(yù)測把“疑似”標(biāo)簽打上再人工過一遍修正。我實測下來對缺陷檢測這類場景自動標(biāo)注的準(zhǔn)確率大約七成左右剩下的主要靠人工檢查邊緣樣本。不要完全相信自動標(biāo)注的結(jié)果尤其是類別之間邊界模糊的樣本比如“輕微劃痕”和“OK”之間必須人工把關(guān)。我第一次圖省事直接用了自動標(biāo)注結(jié)果訓(xùn)練驗證集精度慘不忍睹回頭檢查是大量標(biāo)簽標(biāo)錯了。后來重新標(biāo)注了一次同樣的訓(xùn)練參數(shù)精度直接提升了好幾個百分點。3. 訓(xùn)練作業(yè)配置從創(chuàng)建到跑通的關(guān)鍵參數(shù)3.1 三種訓(xùn)練方式怎么選ModelArts 里訓(xùn)練方式大概分三類自動學(xué)習(xí)、預(yù)置算法、自定義訓(xùn)練。它們適合不同背景的人。自動學(xué)習(xí)適合對算法不熟、只想快速驗證效果的用戶平臺會自動調(diào)參、自動訓(xùn)練幾乎不用寫代碼。缺點是靈活性低想改網(wǎng)絡(luò)結(jié)構(gòu)或損失函數(shù)基本沒門。預(yù)置算法適合熟悉常規(guī)流程但懶得寫完整代碼的人平臺已經(jīng)準(zhǔn)備好訓(xùn)練腳本填好數(shù)據(jù)路徑和超參數(shù)就能跑。自定義訓(xùn)練自由度最高可以帶上自己的訓(xùn)練腳本、指定鏡像、完全控制訓(xùn)練邏輯適合想把已有本地訓(xùn)練項目遷移上云的人。我自己這次選的是自定義訓(xùn)練原因很簡單預(yù)置算法不一定完全匹配我的數(shù)據(jù)處理邏輯我本地已經(jīng)有一套成熟的 PyTorch 訓(xùn)練腳本遷移成本更低。如果你沒有特殊需求只是想快速出一個模型先看看效果建議直接從自動學(xué)習(xí)或預(yù)置算法開始沒必要上來就啃自定義流程。3.2 創(chuàng)建自定義訓(xùn)練作業(yè)的關(guān)鍵參數(shù)創(chuàng)建訓(xùn)練作業(yè)時核心要配置這么幾塊內(nèi)容數(shù)據(jù)來源、訓(xùn)練輸出路徑、算法來源、計算規(guī)格、超參數(shù)。數(shù)據(jù)來源填 OBS 路徑注意要定位到數(shù)據(jù)集根目錄。訓(xùn)練輸出路徑是模型文件的保存目錄平臺會把訓(xùn)練任務(wù)產(chǎn)生的文件自動上傳到這里。算法來源如果選自定義需要指定代碼包所在的 OBS 路徑或者使用平臺提供的公共鏡像加自己的訓(xùn)練腳本。我這次使用的是 PyTorch 環(huán)境訓(xùn)練腳本入口文件是 train.py。代碼里有一點必須處理到位數(shù)據(jù)加載路徑。本地訓(xùn)練時我寫的是相對路徑 ./data到云端后數(shù)據(jù)其實在 OBS平臺不會自動把它下載到訓(xùn)練容器的本地目錄需要在腳本里通過工具把數(shù)據(jù)從 OBS 拷貝到容器的 /cache 目錄。常規(guī)寫法是import os import moxing as mox mox.file.copy_parallel(obs://my-demo-bucket/dataset, /cache/dataset)然后在訓(xùn)練腳本里把數(shù)據(jù)集路徑指向 /cache/dataset。這個步驟很多人都漏了結(jié)果一提交訓(xùn)練作業(yè)就報找不到文件。超參數(shù)的設(shè)置同樣有講究。我初始用學(xué)習(xí)率 0.001、batch size 64、訓(xùn)練 50 輪。訓(xùn)練幾輪后發(fā)現(xiàn)驗證集 loss 下降很慢就把學(xué)習(xí)率調(diào)成 0.0001配合一個簡單的余弦退火策略效果立刻好轉(zhuǎn)。這里建議先把 batch size 設(shè)小一點跑通流程確認(rèn)數(shù)據(jù)加載、標(biāo)簽讀取都沒問題再調(diào)大 batch size 和訓(xùn)練輪數(shù)。3.3 日志監(jiān)控與訓(xùn)練調(diào)優(yōu)訓(xùn)練作業(yè)提交后控制臺可以看到實時日志。這個日志是排查問題的第一現(xiàn)場建議先確認(rèn)幾件事日志是否正常打印了數(shù)據(jù)集加載信息、每個 epoch 是否按預(yù)期輸出 loss、驗證集指標(biāo)是否在合理區(qū)間。我在訓(xùn)練過程中遇到過一個問題前兩個 epoch loss 在降到第三個 epoch 突然變成 nan。排查下來是數(shù)據(jù)集里有個別圖片的像素值異常導(dǎo)致計算出現(xiàn)數(shù)值溢出。解決方式是在數(shù)據(jù)加載時做歸一化并把異常像素值截斷到合理范圍。這個坑如果沒有看日志根本不會想到。訓(xùn)練調(diào)優(yōu)層面我最常用的是“先小后大”策略先用小數(shù)據(jù)集、小模型、少輪數(shù)快速驗證流程是否通暢再逐步擴大數(shù)據(jù)規(guī)模和訓(xùn)練輪數(shù)。用平臺訓(xùn)練有一個好處是每次實驗都會生成一條記錄日志和指標(biāo)可以翻回去對比這點比本地訓(xùn)練零散記錄方便很多。但要注意平臺日志有保留期限關(guān)鍵實驗最好自己下載日志到本地存檔免得過了一段時間想對比就找不到了。4. 模型部署上線把訓(xùn)練成果變成一個可調(diào)用的接口4.1 模型注冊與版本管理訓(xùn)練完成后模型文件會輸出到指定的 OBS 路徑。此時需要在模型管理模塊注冊一個“模型”把 OBS 路徑和推理腳本綁定起來。平臺支持一個模型下創(chuàng)建多個版本每次重新訓(xùn)練生成的新模型都可以存為新的版本號這對線上迭代非常友好。我建議從一開始就養(yǎng)成給每個版本打標(biāo)簽的習(xí)慣。版本號寫清楚是第幾版、訓(xùn)練數(shù)據(jù)范圍、精度指標(biāo)。平臺雖然支持模型描述字段但很多人不填等到線上服務(wù)出問題時才發(fā)現(xiàn)不知道當(dāng)前跑的是哪個版本排查起來非常痛苦。模型注冊時要上傳推理代碼。這里特別注意推理代碼不是獨立的 Python 腳本隨便放就行平臺對推理代碼的組織結(jié)構(gòu)有約定。常規(guī)做法是把模型文件和推理代碼打包成一個目錄上傳到 OBS 后注冊時指定到目錄路徑。推理代碼里需要實現(xiàn)模型加載和預(yù)測兩個核心函數(shù)平臺在調(diào)用時會加載你指定的模型文件把請求數(shù)據(jù)傳進(jìn)來拿到結(jié)果后再返回。4.2 創(chuàng)建在線推理服務(wù)模型注冊完成后進(jìn)入部署環(huán)節(jié)。這里要創(chuàng)建“在線服務(wù)”選擇一個運行規(guī)格CPU 或 GPU、實例數(shù)然后關(guān)聯(lián)已注冊的模型。平臺會自動拉起一個 HTTP 服務(wù)提供一個推理請求地址。我記得第一次部署時創(chuàng)建服務(wù)倒是很快但服務(wù)狀態(tài)一直顯示“異常”。點擊查看日志發(fā)現(xiàn)是推理代碼里缺少一個依賴庫。平臺不是默認(rèn)帶上所有 Python 第三方庫的需要在模型包中附帶 requirements.txt 文件說明依賴。加上文件后重新部署服務(wù)才穩(wěn)定運行起來。另一個關(guān)鍵點是端口配置。平臺在線服務(wù)默認(rèn)對外暴露的端口是 8080如果你的推理框架用的是其他端口比如 8000要么改代碼綁定到 8080要么在部署配置里把端口映射調(diào)整過來。我一開始選的模型服務(wù)框架默認(rèn)監(jiān)聽端口不是 8080導(dǎo)致健康檢查不過花了不少時間才定位到原因。4.3 服務(wù)測試與安全加固部署完成后先用控制臺自帶的測試功能發(fā)一條請求驗證基礎(chǔ)流程。我一般的做法是準(zhǔn)備一張典型的測試圖片轉(zhuǎn)成 base64 字符串或直接上傳文件看返回的預(yù)測結(jié)果是否準(zhǔn)確。請求格式一般是 JSON{ images: base64編碼的圖片數(shù)據(jù) }返回結(jié)果是類別和置信度{ result: [ { category: scratch, confidence: 0.97 } ] }測試通過后建議再看一下服務(wù)日志確認(rèn)沒有異常。同時對正式環(huán)境做以下幾件事開啟訪問鑒權(quán)給推理接口加上認(rèn)證信息防止未授權(quán)調(diào)用配置最小實例數(shù)如果業(yè)務(wù)量波動大可以結(jié)合自動伸縮策略調(diào)整實例數(shù)但要清楚自動伸縮有延遲核心場景還是建議保底一個實例。我這次部署時開了鑒權(quán)但初期測試沒帶上認(rèn)證信息結(jié)果一直收到 401 錯誤。我當(dāng)時還以為是服務(wù)地址填錯了排查半天才發(fā)現(xiàn)是認(rèn)證沒通過。因此測試階段就要把認(rèn)證信息準(zhǔn)備好放到請求頭里避免反復(fù)返工。5. 高頻報錯排查我踩過的坑和解決方法5.1 訓(xùn)練階段高頻報錯報錯現(xiàn)象可能原因解決思路數(shù)據(jù)集路徑不存在OBS 路徑填錯或者數(shù)據(jù)沒有傳到指定目錄先通過 OBS 工具確認(rèn)路徑再檢查桶名和目錄層級圖片解碼失敗數(shù)據(jù)集中混入損壞圖片預(yù)處理腳本掃描全部圖片剔除損壞文件CUDA 內(nèi)存不足batch size 偏大或規(guī)格類型資源不足調(diào)小 batch size或增大計算資源規(guī)格loss 為 nan輸入數(shù)據(jù)含異常值或?qū)W習(xí)率過大檢查數(shù)據(jù)歸一化調(diào)低學(xué)習(xí)率打印輸入統(tǒng)計信息模型無法保存輸出目錄不存在或沒有寫權(quán)限確認(rèn)訓(xùn)練輸出路徑已創(chuàng)建檢查權(quán)限設(shè)置最讓我頭疼的是“圖片解碼失敗”因為報錯不會直接指出是哪一張圖片。后來我在數(shù)據(jù)準(zhǔn)備腳本里加了一個校驗邏輯用圖像處理庫逐張檢查把所有無法正常打開的圖片輸出到單獨列表并移除之后訓(xùn)練就暢通了。這個過程建議放到上線前的數(shù)據(jù)校驗環(huán)節(jié)別等訓(xùn)練起來再處理。5.2 部署階段高頻報錯部署階段的問題和訓(xùn)練階段完全不同大多數(shù)集中在模型加載、端口、依賴這三個層面。模型加載失敗是最常見的。原因往往是推理代碼里模型路徑寫錯或者模型文件的文件名和代碼里寫的不一致。平臺會把模型文件掛載到容器內(nèi)的指定目錄并會通過環(huán)境變量注入模型路徑我在代碼里優(yōu)先讀取該環(huán)境變量而不是硬編碼路徑。這樣既不會漏文件也便于后續(xù)更新模型版本。依賴庫缺失是第二個高發(fā)問題。尤其用到特殊的圖像處理庫或版本比較新的框架時基礎(chǔ)鏡像不會內(nèi)置對應(yīng)依賴。解決辦法是把依賴清單寫到 requirements.txt 里部署時會自動安裝。有一點值得提醒依賴清單里盡量鎖定版本號避免“裝了個新版本接口行為變了模型推理結(jié)果不對”這種問題。服務(wù)端口配置不對是第三個坑。平臺健康檢查會去探測你指定的端口如果服務(wù)實際監(jiān)聽的端口不一致服務(wù)會被判定為異常并反復(fù)重啟。部署前先確認(rèn)推理服務(wù)的監(jiān)聽端口部署配置里保持兩者一致。5.3 避坑清單匯總說了這么多最后把經(jīng)驗濃縮成一條可直接對照的清單。第一數(shù)據(jù)目錄在 OBS 里保持“訓(xùn)練集/驗證集/類別子目錄”的層級標(biāo)簽順序提前固定下來。第二上傳完數(shù)據(jù)后做一次目錄列表校驗并在訓(xùn)練腳本里增加數(shù)據(jù)加載后的形狀打印。第三自定義訓(xùn)練腳本必須處理 OBS 數(shù)據(jù)到本地緩存的拷貝別指望平臺自動完成。第四首次訓(xùn)練用小數(shù)據(jù)集、少輪數(shù)跑通流程再逐步增加數(shù)據(jù)規(guī)模。第五模型注冊時確定好推理代碼的目錄結(jié)構(gòu)和文件命名模型路徑建議通過環(huán)境變量讀取。第六部署前檢查端口、依賴清單、認(rèn)證信息三個最容易出問題的點。第七每次訓(xùn)練記錄的日志和指標(biāo)文件及時下載備份方便后續(xù)對比和回溯。這套清單是我反復(fù)踩坑之后的總結(jié)現(xiàn)在每次做遷移訓(xùn)練或部署新模型我都會照著過一遍確實能省下不少排查時間。我個人在實際操作中的體會是這類云上平臺真正降低的是運維和資源管理的門檻但它并沒有降低“理解模型訓(xùn)練流程”的要求。數(shù)據(jù)組織、超參數(shù)調(diào)整、模型與推理代碼的約定這些底層邏輯依然和本地訓(xùn)練一模一樣。如果你之前沒有本地訓(xùn)練的經(jīng)驗直接上云可能會被報錯牽著走反過來如果你已經(jīng)有一套完整的本地訓(xùn)練流程遷到這類平臺其實很快核心就是把數(shù)據(jù)路徑、代碼入口、模型輸出這幾個約定對齊。最后再分享一個小技巧首次部署時先用最小的規(guī)格和最小的模型跑通端到端流程確認(rèn)請求、返回、日志都正常后再切換到大規(guī)格模型你會省掉很多等待時間。