才是核心)
很多人學AI第一眼看到的是模型代碼幾行PyTorch、一個訓練函數(shù)、一個準確率。但我從零開始做AI工程這半年最大的感悟是——模型只是冰山一角。真正花時間的是數(shù)據(jù)怎么管、環(huán)境怎么搭、模型怎么部署、線上效果怎么監(jiān)控。如果你正打算從零開始搭一條AI工程鏈路或者你已經(jīng)在跑模型但總覺得“離上線差一口氣”這篇內(nèi)容應該能給你一張清晰的地圖。我會從工程視角而不是算法視角來講把從零起步搭建AI系統(tǒng)會踩的坑、該做的事、值得投錢的工具全部攤開來說。1. AI工程不是“寫模型”而是一套端到端的生產(chǎn)系統(tǒng)先統(tǒng)一一個概念AI工程英文是AI engineering和機器學習算法的“寫模型”是完全兩個維度的東西。算法崗關(guān)心的是模型結(jié)構(gòu)、損失函數(shù)、指標怎么漲上去AI工程關(guān)心的是一個模型從實驗到落地怎么穩(wěn)定、可靠、可持續(xù)地跑在真實場景里。你訓練出一個準確率95%的模型如果不解決數(shù)據(jù)漂移、推理延遲、版本回滾這些問題那它在生產(chǎn)環(huán)境里就是一顆定時炸彈。我自己的體會是從零開始做AI工程本質(zhì)上是在搭建一條“數(shù)據(jù)-模型-服務-反饋”的閉環(huán)流水線。這個過程里你至少要經(jīng)歷這么幾步數(shù)據(jù)從哪來、怎么清洗、怎么標注、怎么做版本管理模型怎么訓練、怎么評估、怎么在多個版本之間對比模型怎么打成服務、怎么暴露API、怎么做并發(fā)和限流上線之后怎么監(jiān)控指標、怎么發(fā)現(xiàn)效果退化、怎么快速回滾所以“ai-engineering-from-scratch”這個標題我的理解不是“從數(shù)學公式開始學習”而是“作為工程師把AI能力真正放進你的系統(tǒng)架構(gòu)里”。你需要的是工程化工具箱而不是更多的論文。因為AI工程是個實踐型技能下面我直接從我的實操路徑講起。2. 環(huán)境搭建所有AI工程的地基都是“可復現(xiàn)性”2.1 Python環(huán)境不要信任全局環(huán)境我最早犯的錯就是在服務器上直接pip install一堆依賴結(jié)果訓練了一個模型過了兩周再去復現(xiàn)版本沖突到懷疑人生。從零開始做AI工程第一步一定是建立隔離且可復現(xiàn)的運行環(huán)境。推薦的做法是用conda或venv創(chuàng)建獨立的Python環(huán)境每個項目一個環(huán)境用requirements.txt或poetry.lock鎖定精確版本號記錄Python版本和硬件環(huán)境的依賴例如CUDA版本、cuDNN版本我現(xiàn)在的標準流程是conda create -n ai-project python3.11 conda activate ai-project pip install torch2.1.0 torchvision0.16.0 pip freeze requirements.lock鎖文件的價值在你需要在新機器上復現(xiàn)實驗、或者同事接手項目的時候才會真正體現(xiàn)。依賴不一致造成的“在我這兒能跑”是AI工程里最耗時間的隱形殺手。2.2 硬件環(huán)境CPU和GPU的使用邊界要提前劃清很多人以為AI工程必須用GPU其實不對。數(shù)據(jù)預處理、特征統(tǒng)計、小規(guī)模驗證CPU足夠只有模型訓練和推理需要GPU。所以你從一開始就要把“哪些環(huán)節(jié)走CPU、哪些環(huán)節(jié)走GPU”規(guī)劃清楚否則會白白燒錢。我自己常用的環(huán)境開發(fā)機Mac或者普通Linux工作站負責數(shù)據(jù)探索和代碼編寫訓練機帶NVIDIA GPU的服務器負責訓練和驗證推理環(huán)境可以是GPU也可以是CPU取決于延遲和吞吐要求把這個邊界理清楚之后你的工作流才會順暢。提示GPU不是越快越好而是越匹配越好。如果你的服務只有偶爾的推理請求用GPU閑置成本反而高。這一點在后面部署章節(jié)我會展開說。2.3 工作目錄結(jié)構(gòu)從第一天就按標準來AI工程的目錄我建議這樣組織可以直接抄作業(yè)ai-project/ ├── data/ │ ├── raw/ # 原始數(shù)據(jù)只讀 │ ├── processed/ # 清洗后的數(shù)據(jù) │ └── features/ # 特征工程產(chǎn)物 ├── models/ # 模型文件或者模型注冊表指針 ├── src/ │ ├── data_processing.py │ ├── train.py │ ├── evaluate.py │ └── serve.py ├── configs/ # 所有參數(shù)配置而不是硬編碼 │ ├── train.yaml │ └── serve.yaml ├── tests/ ├── scripts/ └── README.md這個結(jié)構(gòu)的好處是數(shù)據(jù)、代碼、配置、模型四層分離。任何一個人接手你的項目都能在10分鐘內(nèi)搞清楚哪塊代碼在干什么而且不會誤改數(shù)據(jù)或配置。3. 數(shù)據(jù)工程AI模型的質(zhì)量上限由數(shù)據(jù)決定3.1 數(shù)據(jù)采集要帶著“工程化”思維做模型實驗的時候數(shù)據(jù)隨便下載一個數(shù)據(jù)集就行做AI工程你還得考慮數(shù)據(jù)的來源穩(wěn)定性、更新頻率、格式兼容性。我常用的方式用腳本定時拉取數(shù)據(jù)到data/raw/記錄數(shù)據(jù)版本建議用dvc或者就是把data.parquet存進對象存儲并在代碼里記錄哈希值每次訓練前先跑數(shù)據(jù)校驗腳本檢查字段缺失率、類別分布有一次我處理一個文本分類任務數(shù)據(jù)管道沒加校驗跑了一個月之后發(fā)現(xiàn)新增數(shù)據(jù)的標簽分布發(fā)生了偏移但是我完全沒有留意到模型效果就默默掉了。從那時起我養(yǎng)成了必寫數(shù)據(jù)校驗腳本的習慣。3.2 數(shù)據(jù)清洗比模型調(diào)參重要十倍我的經(jīng)驗是你花80%的時間清洗數(shù)據(jù)模型訓練只需要20%。剛開始很不服氣后來發(fā)現(xiàn)真實場景里的數(shù)據(jù)臟得超出想象。常見坑包括重復樣本導致模型過擬合時間字段格式不一致導致特征錯亂文本數(shù)據(jù)有多語言混用沒有預處理部分字段大量缺失填補策略錯誤為了處理這些問題你的數(shù)據(jù)清洗代碼要像流水線一樣設(shè)計一段清洗對應一個環(huán)節(jié)而且要寫測試。def clean_text(text: str) - str: # 去除噪音字符、統(tǒng)一大小寫 text text.lower() text re.sub(r[^\w\s\u4e00-\u9fff], , text) return text關(guān)鍵是清洗邏輯必須可復現(xiàn)。不要在一個notebook里隨手改一下下一次跑結(jié)果又不一樣。所有清洗代碼都要固化到src/data_processing.py中輸入輸出都是確定的。3.3 訓練集、驗證集、測試集的切分邊界很多人隨便train_test_split一下就去訓練了。但AI工程里切分必須考慮數(shù)據(jù)的時間順序和業(yè)務邏輯。舉例來說做用戶行為預測一定不能隨機切分否則會“未來穿越”造成評估指標虛高。正確做法是按時間切分前80%的時間段做訓練后20%做驗證。我的建議是正樣本和負樣本要在切分前做分層處理避免某一集合里只有一種類別切分的結(jié)果要落盤保存而不是每次隨機生成這樣可以讓實驗結(jié)果可比寫清楚切分規(guī)則到配置里后來的人才知道怎么對齊實驗4. 模型訓練與評估把每個實驗做成可審計的過程4.1 訓練腳本要“參數(shù)化”不要“硬編碼”初期做AI工程最容易踩的坑就是把關(guān)鍵超參數(shù)直接寫在代碼里。換個學習率還要改代碼跑完一個實驗想?yún)R報都說不清自己用的什么參數(shù)。正確做法是把訓練參數(shù)抽到配置文件里。我用的是yaml配置# configs/train.yaml model: name: text_cnn embedding_dim: 128 hidden_size: 256 data: batch_size: 64 max_seq_len: 128 train: epochs: 10 learning_rate: 0.001 optimizer: adam seed: 42訓練腳本只讀取配置with open(configs/train.yaml) as f: config yaml.safe_load(f) model build_model(config[model]) trainer Trainer(config[train])好處是每個實驗都對應一份配置文件復現(xiàn)實驗就是跑同一份config與不同團隊溝通時直接提到配置文件名不用聊半天“我代碼里那個參數(shù)”我還會在每個實驗輸出目錄里存一份當時的配置副本相當于給實驗做“快照”。4.2 指標不要只看準確率要看業(yè)務指標模型評估階段AI工程師最容易犯的錯誤就是拿一個“準確率”當全部。在真實場景里你需要關(guān)注的是模型上線之后能不能帶來業(yè)務價值。比如推薦系統(tǒng)看的是CTR、轉(zhuǎn)化率而不是訓練集上的AUC風控模型看的是誤殺率和召回率你要在成本和體驗之間做權(quán)衡文本生成模型看的是語義正確率和流暢度需要人工評測我現(xiàn)在的評估體系是兩層第一層模型標準的離線指標準確率、F1、AUC、BLEU等第二層業(yè)務指標吞吐、延遲、人工抽樣通過率、線上A/B測試結(jié)果。離線指標只是篩選候選模型的篩子業(yè)務指標才是最終決策依據(jù)。4.3 模型版本管理把你的模型當成代碼一樣管理模型也是代碼所以要有版本。我常做的操作是訓練結(jié)束后將模型文件上傳到對象存儲命名規(guī)則包含實驗名和git提交哈希在模型注冊表里記錄訓練數(shù)據(jù)版本、配置文件、評估指標模型上線前必須經(jīng)過“提交-審查-驗證”流程如果沒有這套流程當你部署了一個模型發(fā)現(xiàn)效果不好想回滾到上一個版本卻發(fā)現(xiàn)上一個版本已經(jīng)找不著了只能從頭再訓練這就是災難?,F(xiàn)在我的做法很簡單用統(tǒng)一的存儲路徑和命名規(guī)則models/ ├── text_cnn_20250101/ │ ├── model.pt │ ├── config.yaml │ └── metrics.json ├── text_cnn_20250115/ │ ├── model.pt │ ├── config.yaml │ └── metrics.json有了模型版本部署哪個、回滾到哪個單憑文件名就能決定。5. 模型部署與上線從notebook到實時服務跨越的不只是代碼5.1 選擇推理服務的方式不要一上來就上K8s很多AI工程初學者會直接想把模型放進Kubernetes做一個微服務。我建議不要這樣。從零開始最樸素的部署方式就是把模型封裝成一個簡單的HTTP服務比如用FastAPI加載模型文件對外提供predict接口。# serve.py from fastapi import FastAPI, Request import torch import joblib app FastAPI() model None vectorizer None app.on_event(startup) def load_model(): global model, vectorizer model torch.load(models/model.pt, map_locationcpu) vectorizer joblib.load(models/vectorizer.joblib) app.post(/predict) async def predict(request: Request): payload await request.json() text payload[text] features vectorizer.transform([text]) pred model.predict(features) return {prediction: int(pred[0])}這個方案的優(yōu)勢無需額外基礎(chǔ)設(shè)施一臺普通服務器就能跑快速驗證模型在線上的真實表現(xiàn)后續(xù)再演進到容器化和集群調(diào)度5.2 推理延遲和吞吐你必須做的三件小事部署模型之后你要立刻關(guān)注三件事第一延遲單次請求平均耗時多少95分位是多少。如果超過業(yè)務容忍度你需要考慮模型剪枝、量化或者換輕量模型。第二吞吐每秒能處理多少個請求。如果吞吐不夠你可能需要多實例部署或者用異步推理。第三資源占用內(nèi)存和CPU/GPU使用率。我遇到過模型文件太大上線后內(nèi)存幾乎耗盡的情況。這里有一個優(yōu)化技巧很多人不知道在CPU上推理時把模型設(shè)為eval模式且關(guān)閉梯度計算能明顯降低顯存和內(nèi)存開銷。model.eval() with torch.no_grad(): output model(input_tensor)5.3 模型的A/B測試與灰度發(fā)布直接全量上線的做法在AI工程里風險太高。正確的姿勢是灰度發(fā)布。流程大致是先部署新模型到一小部分流量上搭配一個路由參數(shù)比如model_versionlatest在后臺同時調(diào)用新舊模型比較輸出差異和業(yè)務指標確認沒有問題后再逐步擴大流量比例灰度發(fā)布的好處是即使新模型有問題影響范圍也是可控的。你可以隨時切回舊版本。之前我參與的一個項目新版模型在離線評估時指標全面領(lǐng)先結(jié)果灰度到10%流量時發(fā)現(xiàn)特定用戶群組的負面反饋明顯上升。如果沒有灰度直接全量那就是一次事故。6. 監(jiān)控與維護AI系統(tǒng)上線之后工作才真正開始6.1 預測偏差漂移離線效果好線上為什么會跪很多AI工程新手做“從零到上線”時覺得部署完了就萬事大吉其實不是。模型上線后最大的風險來自數(shù)據(jù)漂移data drift。所謂數(shù)據(jù)漂移就是線上遇到的數(shù)據(jù)分布和訓練時的數(shù)據(jù)分布不一致了。比如用戶行為習慣變了、新詞出現(xiàn)了、季節(jié)性波動來了。這種情況下模型的表現(xiàn)會逐漸退化。解決思路是記錄每次線上請求的特征數(shù)據(jù)樣本定期將線上特征分布和訓練集特征分布做對比計算PSIPopulation Stability Index當漂移超過閾值時觸發(fā)模型重新訓練6.2 日志、追蹤和告警把AI服務當成正規(guī)系統(tǒng)運維要讓AI服務穩(wěn)定必須有日志和監(jiān)控不能只靠“感覺”。我會在推理代碼中加上結(jié)構(gòu)化日志記錄請求內(nèi)容、模型版本、延遲、預測結(jié)果以及打分值。比如{ timestamp: 2025-02-01T12:00:00Z, model_version: text_cnn_20250115, latency_ms: 32, prediction: 0, confidence: 0.92, request_id: abc123 }同時設(shè)置告警閾值比如接口請求量跌到0說明路由可能有故障平均延遲超過200ms需要檢查資源模型輸出“不確定”類的概率升高可能是數(shù)據(jù)漂移這樣做之后你的AI系統(tǒng)才像個體面的生產(chǎn)系統(tǒng)而不是一次性的研究原型。6.3 模型更新策略定時訓練還是觸發(fā)訓練模型需要持續(xù)更新但更新策略要結(jié)合業(yè)務場景來設(shè)計。我建議分兩種情況一種是數(shù)據(jù)變化快的場景如搜索、推薦、廣告建議設(shè)置定時訓練任務每日或每周將新數(shù)據(jù)灌入重訓。另一種是數(shù)據(jù)相對穩(wěn)定的場景比如立案分類、文本審核可以用觸發(fā)式訓練當監(jiān)控指標下滑到閾值以下再重新訓練。還有一個經(jīng)驗不要每次都從零訓練??梢栽谂f模型基礎(chǔ)上做增量訓練或微調(diào)這樣既節(jié)省資源又保留已學到的知識。7. 自動化與工具鏈讓AI工程走向成熟7.1 使用編排工具串聯(lián)流程到這一步你會想如果數(shù)據(jù)清洗、訓練、評估、部署都能自動串聯(lián)就好了。對的這就是AI工程里的流水線編排。我的個人實踐是用Shell腳本加Cron先做輕量級自動化用到復雜的依賴關(guān)系時再上Airflow或Prefect一個典型的定時訓練任務大致是# 1. 拉取新數(shù)據(jù) python src/data_processing.py --config configs/train.yaml # 2. 訓練模型 python src/train.py --config configs/train.yaml # 3. 評估指標 python src/evaluate.py --config configs/train.yaml # 4. 打包上傳模型 python scripts/upload_model.py這些腳本可以逐行執(zhí)行也可以集成到CI/CD里。關(guān)鍵是先把每個步驟做成命令化、可獨立運行這樣自動化才有基礎(chǔ)。7.2 測試AI工程里的測試和傳統(tǒng)軟件測試不一樣很多做軟件工程的人轉(zhuǎn)AI工程會把傳統(tǒng)的單元測試照搬過來這沒錯但AI的測試有它的特殊性。除了傳統(tǒng)的代碼測試你還需要數(shù)據(jù)測試校驗數(shù)據(jù)完整性、字段類型、分布模型測試在固定的驗證集上跑出穩(wěn)定的指標服務測試模擬請求驗證接口返回的結(jié)構(gòu)和延遲我至少會在項目里寫這么幾類測試# 測試數(shù)據(jù)校驗 def test_raw_data_has_expected_columns(): df load_data() assert {text, label}.issubset(df.columns) # 測試模型輸出 def test_model_output_range(): prediction model.predict(sample_input) assert prediction in (0, 1)這樣做的目的是每次改動代碼或者更新數(shù)據(jù)之后都有一次“安全網(wǎng)”兜底避免“模型調(diào)漏了”這種低級事故。7.3 協(xié)作與知識沉淀AI工程需要文檔化最后我想強調(diào)一點AI工程是非常吃團隊協(xié)作的領(lǐng)域。一個數(shù)據(jù)科學家的實驗代碼如果不整理、不寫文檔其他人根本無法復用。所以在項目啟動之初就要有意識地去寫README說明項目目標、運行方式、數(shù)據(jù)來源配置說明記錄每個參數(shù)的默認值和調(diào)優(yōu)經(jīng)驗變更記錄記錄模型版本和更新時間我個人還會在項目根目錄寫一個DECISIONS.md記錄關(guān)鍵的技術(shù)選型決策比如為什么用FastAPI而不是Flask、為什么選用這個切分方式等。這些在當時看來很容易忘但對后來者極其有價值。8. 最后一點實戰(zhàn)心得從零開始AI工程的正確心態(tài)這條路上有太多信息很容易把你導向“學一堆算法論文”的方向。但作為工程師我更建議你以終為始從“跑通一個真實場景”出發(fā)去學。先選一個具體的小問題例如垃圾短信分類。然后你一步步往下走收集數(shù)據(jù)、清洗、訓練一個普通模型、寫接口、部署到服務器、加監(jiān)控。這一輪做完之后你對AI工程的理解會比看十本書更扎實。很多AI工程的坑不真正上過一次線是不可能體會到的。比如本地運行好好的模型一到線上就報“維度不匹配”比如請求并發(fā)上來之后進程不停重啟再比如數(shù)據(jù)管道凌晨3點跑掛了而你還在睡覺。解決這些問題的過程才是從新手到工程師的軌道。如果讓我總結(jié)一個最實用的建議那就是從第一天起就把代碼寫成可復現(xiàn)、可配置、可依賴管理的樣子。一開始你可能覺得慢了但到項目后期你會慶幸自己當初沒隨手把東西糊在一起。AI工程沒有魔法它就是把每一個細節(jié)做到位然后讓系統(tǒng)在你看不見的地方持續(xù)穩(wěn)定運行。