的完整閉環(huán))
做AI工程這行久了經(jīng)常有朋友問我“怎么從零開始入門”。我一開始沒太當回事覺得照著教程跑通幾個模型就算入門了。直到我親眼看到不少科班出身、算法調(diào)參很熟練的同學在把一個模型真正變成線上服務(wù)時被各種工程問題捶得沒脾氣才意識到所謂的“從零開始”指的根本不是會跑通一個notebook而是能在真實業(yè)務(wù)環(huán)境里把模型從想法變成持續(xù)穩(wěn)定運行的系統(tǒng)。今天這篇內(nèi)容就是基于“從零開始搭建AI工程能力”這個主題把我在實際項目中反復踩過、也反復優(yōu)化過的一套方法論整理出來。它不教你怎么推導Transformer公式也不講怎么刷LeetCode只關(guān)注一件事一個非AI背景的工程師或者一個算法基礎(chǔ)不錯但工程經(jīng)驗為零的人要沿著怎樣一條路線才能把一個AI項目從實驗推到生產(chǎn)并且持續(xù)迭代。1. 從零起步AI工程與算法實驗的本質(zhì)差別很多人會把“AI工程”和“機器學習算法”混為一談這是第一個需要掰開揉碎講清楚的問題。我做過的項目里花在模型結(jié)構(gòu)設(shè)計上的時間通常只占不到30%剩下70%都在處理數(shù)據(jù)質(zhì)量、管道調(diào)度、接口延遲、資源成本、模型版本回滾這些“不入流”的臟活。如果一開始就奔著“我要寫一個高級模型”去大概率會在真實系統(tǒng)的復雜面前被徹底淹沒。算法實驗是一次性的邏輯驗證AI工程是算法系統(tǒng)的全生命周期管理。你可以把算法實驗想象成在廚房里研究一道新菜鍋碗瓢盆隨你折騰做壞了重來一鍋成本極低。而AI工程是開一家餐廳菜譜再好也得考慮供應鏈、冷藏保鮮、出菜速度、服務(wù)員培訓和顧客口味變化。同樣的菜譜在前者的實驗室環(huán)境里能得滿分放進后者的營業(yè)場景里可能連及格都難。所以“從零開始”的第一課不是去找算力買顯卡而是建立一套規(guī)?;墓こ趟季S。我在實際項目中總結(jié)過AI工程能力的最低可行閉環(huán)包括五個環(huán)節(jié)需求拆解把一個模糊的業(yè)務(wù)問題翻譯成可優(yōu)化的機器學習目標數(shù)據(jù)閉環(huán)構(gòu)建訓練數(shù)據(jù)、驗證數(shù)據(jù)、線上反饋數(shù)據(jù)的流動管道訓練管理可復現(xiàn)的實驗記錄、模型版本、超參數(shù)配置服務(wù)化部署模型上線成API、定時任務(wù)或嵌入式模塊監(jiān)控回歸持續(xù)觀察線上指標自動發(fā)現(xiàn)模型衰減。這個閉環(huán)缺了任何一環(huán)后面都會付出代價。我見過很多團隊號稱在“用AI”實際上就是離線跑一次腳本把預測結(jié)果倒成Excel發(fā)出去。這連AI工程的邊都沒摸到最多叫數(shù)據(jù)加工。在這個環(huán)節(jié)里我強烈建議初學者打破一個心理定勢不要把所有問題都當成一個“端到端深度學習”問題。從零開始學AI工程反而是先學會權(quán)衡邏輯規(guī)則能解決的不用模型線性模型能解決的不用GBDTGBDT能解決的別上來就上大模型。工程上每多一分模型復雜度就多十分維護成本。2. 搭建第一套可復用的AI工程腳手架我以前也經(jīng)歷過“隨便開個文件夾寫代碼”的階段。直到有一次需要回滾到一周前的模型卻發(fā)現(xiàn)當時的訓練腳本已經(jīng)改得面目全非連自己都分不清哪份代碼產(chǎn)出過哪份模型我才意識到AI工程的第一步必須是腳手架而不是模型。一套真正可復用的AI工程腳手架至少要覆蓋三個層面的能力代碼層面的模塊邊界、數(shù)據(jù)層面的版本管理、實驗層面的指標追蹤。先說代碼模塊邊界。我建議一個最小項目這樣劃分目錄project/ ├── data/ # 原始數(shù)據(jù)與中間產(chǎn)物 │ ├── raw/ │ └── processed/ ├── src/ │ ├── features/ # 特征工程 │ ├── models/ # 模型定義與訓練 │ ├── serving/ # 模型服務(wù)化接口 │ └── utils/ # 公共工具 ├── configs/ # 配置文件超參、路徑、環(huán)境 ├── experiments/ # 每次實驗的記錄 ├── notebooks/ # 探索性分析 └── tests/ # 單元測試和冒煙測試這個劃分不是拍腦袋定的它的核心原則是“配置與代碼分離、數(shù)據(jù)與邏輯分離、實驗與源碼分離”。你寫一個訓練腳本不應在腳本里寫死一個文件路徑而應該通過config文件讀取。這樣同一個代碼就能跑不同的數(shù)據(jù)集不同超參組合也能追溯。然后是數(shù)據(jù)版本管理。代碼可以進Git但數(shù)據(jù)往往幾個G甚至幾個T不適合直接塞進Git。我實際用的是DVC的輕量思路——把元數(shù)據(jù)哈希值、文件地址交給Git數(shù)據(jù)本體放在共享存儲。這是一個小到可以手工實現(xiàn)的方案每次數(shù)據(jù)更新記錄下數(shù)據(jù)快照hash作為實驗配置的一部分。這樣做的好處是任何一次訓練都能明確回答“我用的是哪份數(shù)據(jù)訓練出來的”。實驗追蹤我用的是MLflow它給我?guī)淼暮诵膬r值不是漂亮的后臺而是“可復現(xiàn)”的保障。每次訓練啟動時自動把以下內(nèi)容記入實驗記錄代碼版本Git commit hash數(shù)據(jù)版本數(shù)據(jù)目錄hash完整超參數(shù)通過命令行或配置文件注入評估指標訓練集、驗證集、測試集產(chǎn)物路徑模型二進制、預處理pipeline文件一套腳手架跑起來的標志是你訓練完一個模型一周后哪怕是別人也能不看任何解釋只靠README和實驗記錄完整重建整個訓練過程和產(chǎn)物。如果你的項目達不到這個程度它還不能被稱為“工程”只是腳本。這里我想額外提一句關(guān)于環(huán)境依賴的管理。Python的依賴地獄是每個AI工程師躲不掉的。不要相信requirements.txt寫到“tensorflow2.0”這種寫法我踩過不少坑發(fā)現(xiàn)就算同一個大版本不同小版本在GPU算子上的行為都會有差異。建議直接鎖定子版本更穩(wěn)妥的是用Docker鏡像鎖定整個操作系統(tǒng)、CUDA、Python和依賴庫的組合。這相當于你給訓練任務(wù)買了一份“環(huán)境保險”。3. 訓練迭代中被低估的數(shù)據(jù)與評估問題訓練誰都會跑幾個epoch誰都能跑。但真正拉開差距的是兩個“隱形問題”數(shù)據(jù)質(zhì)量怎么保障模型好不好怎么度量。先聊數(shù)據(jù)。很多教程用現(xiàn)成的DataLoader糊弄過去現(xiàn)實中的數(shù)據(jù)往往都是臟的、亂的、不平衡的。我見過一個項目線上收益一直上不去最后發(fā)現(xiàn)訓練數(shù)據(jù)里有大量重復樣本同一個用戶的行為被抽樣了多次導致模型嚴重偏置。那之后我把“數(shù)據(jù)畫像”作為訓練前強制環(huán)節(jié)。所謂數(shù)據(jù)畫像就是在特征訓練前用腳本產(chǎn)出數(shù)據(jù)分布報告包括每列特征的缺失率、均值、方差、分位數(shù)目標變量在訓練集和驗證集的分布對比特征與目標之間的簡單相關(guān)性樣本時間戳的跨度與間隔分布。這一步看起來基礎(chǔ)但能防住很多奇怪的模型行為。舉個例子如果訓練集的時間范圍是1月到5月驗證集是6月這兩個月里數(shù)據(jù)分布發(fā)生了明顯漂移那你做出來的指標再高也只是歷史擬合上線后照樣崩。數(shù)據(jù)畫像能讓你提前看到“訓練和驗證來自不同世界”的警告。再說評估。我自己吃過最大的虧是只盯著單一指標做優(yōu)化。分類任務(wù)就只看AUC回歸任務(wù)就只看MAE最后模型上線業(yè)務(wù)方跟我說“你這個預測完全沒用”。后來我學乖了嗎其實沒有。模型離線指標與線上業(yè)務(wù)指標之間存在一條無法完全抹平的鴻溝但可以用一套“分層評估”把它們拉近。我的做法是把評估指標分成三層第一層是算法指標AUC、LogLoss、召回率、精確率這些用于快速比較不同版本模型的優(yōu)劣第二層是業(yè)務(wù)代理指標比如推薦場景的點擊率預估離線算一下預測值和真實點擊的相關(guān)性、AUC分段收益曲線等第三層是灰度指標上線后通過A/B實驗直接看業(yè)務(wù)KPI如成交轉(zhuǎn)化、停留時長、投訴率。這三層不是互相替代而是從不同層面給模型做體檢。很多從零開始的初學者眼里只有第一層指標所以模型“看著好”卻“用著差”。當你把評估體系搭建完整后你就不會再被一個高AUC沖昏頭腦了——因為你知道它能解釋什么不能解釋什么。在訓練迭代策略上還有一條實用的經(jīng)驗不要試圖一次性把模型調(diào)到完美而是固定好基線做增量改進。我習慣的做法是先把最簡單的邏輯回歸或線性模型跑通作為baseline然后一步步增加特征和模型復雜度。每一步都保留到實驗記錄里這樣你能隨時判斷新加的東西到底是正向還是負向貢獻。沒有baseline的優(yōu)化都是在裸泳。4. 部署上線從離線實驗到生產(chǎn)推理的最后一公里模型練出來了離線指標很不錯接下來就是AI工程里最容易翻車的地方——部署上線。離線環(huán)境訓練用的Python版本、依賴庫、系統(tǒng)環(huán)境相對干凈但生產(chǎn)環(huán)境可沒那么聽話。本地上跑得飛快的推理代碼放到線上容器里可能連路徑都找不到。部署方式的選擇要基于你的實際需求而不是追新。我總結(jié)了四種常見的模型上線形態(tài)以及它們的適用場景形態(tài)適用場景延遲要求實現(xiàn)復雜度離線批處理用戶分群、批量推薦、定時報表分鐘級到小時級低在線API實時推薦、風控決策、聊天助手毫秒級到百毫秒級中高嵌入式推理移動端、邊緣設(shè)備、IoT毫秒級高流式處理實時事件流、即刻策略響應秒級高從零開始我建議先掌握離線批處理和在線API兩者。離線批處理最簡單本質(zhì)上是寫好一個推理腳本在特定時間點對一批數(shù)據(jù)運行輸出結(jié)果到數(shù)據(jù)庫或文件。這個過程中最關(guān)鍵的是冪等性設(shè)計同一時刻跑兩次、或者重跑同一個數(shù)據(jù)批次結(jié)果必須是一樣的。不然調(diào)度系統(tǒng)一重試就會出現(xiàn)重復數(shù)據(jù)。在線API則涉及到更多的工程細節(jié)。我拿一個實際的FastAPI推理服務(wù)來說明。假設(shè)你訓練好了一個用于預測用戶點擊概率的XGBoost模型模型的輸入需要經(jīng)過特征工程轉(zhuǎn)換。你不能讓線上服務(wù)每次請求都從頭做一遍特征處理這樣延遲會很高而且容易和訓練時的特征處理不一致。正確做法是把特征處理Pipeline和模型一起打包上線。操作上我把所有特征變換邏輯封裝成一個transform函數(shù)然后把這個函數(shù)所在的模塊和模型文件一起保存。服務(wù)啟動時加載模型和管線推理時先走變換函數(shù)再喂給模型。偽代碼如下import os import pickle import numpy as np from fastapi import FastAPI from pydantic import BaseModel app FastAPI() # 啟動時加載整體Pipeline with open(os.getenv(MODEL_PATH, /models/pipeline_and_model.pkl), rb) as f: pipeline pickle.load(f) class PredictRequest(BaseModel): user_id: str features: list app.post(/predict) def predict(req: PredictRequest): # 做數(shù)據(jù)校驗和特征補全 x np.array(req.features).reshape(1, -1) prob pipeline.predict_proba(x)[0][1] return {user_id: req.user_id, click_prob: round(float(prob), 6)}這段代碼雖然小但避開了幾個大坑模型和特征轉(zhuǎn)換打包在同一個pickle文件里線上和訓練時用的是完全一致的邏輯請求里的features由上游按約定順序傳入模型API不負責解析復雜的業(yè)務(wù)字段進一步還能通過gRPC或者ONNX Runtime來降低預測延遲初期先不用強求。服務(wù)寫好后千萬不能不壓測就直接上線。我用Locust做簡單的并發(fā)請求測試服務(wù)的吞吐量和P99延遲。注意一個細節(jié)很多人的模型推理時間看起來不慢但整個HTTP服務(wù)延遲高是因為請求里帶了大段JSON、做了不必要的日志打印、或者模型對象沒預熱。有一次我發(fā)現(xiàn)首請求延遲高達2秒原因就是模型在進程啟動后第一次預測時才進行XGBoost的線程池初始化。解決方案很簡單加載完模型后先用一條假數(shù)據(jù)“預熱”一次讓底層資源就緒。再談容器化部署。Docker是線上部署繞不開的一環(huán)但不要只是在鏡像里裝一個Python解釋器和依賴庫。生產(chǎn)鏡像必須做到無入侵、無獨立寫權(quán)限、時區(qū)正確、日志輸出到標準輸出。我踩過一個很不值錢的坑Dockerfile里忘了設(shè)置時區(qū)導致線上服務(wù)出的時間戳全部是UTC業(yè)務(wù)查對賬的時候數(shù)據(jù)差了8小時被運維同學噴了一整天。這里給出一個我實際使用的小型API鏡像Dockerfile片段它不復雜但每個指令都針對一個真實事故FROM python:3.10-slim ENV PYTHONUNBUFFERED1 \ PYTHONDONTWRITEBYTECODE1 \ TZAsia/Shanghai WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY ./app /app/app # 使用非root用戶運行降低安全風險 RUN useradd --create-home --shell /bin/bash appuser USER appuser EXPOSE 8080 CMD [uvicorn, app.main:app, --host, 0.0.0.0, --port, 8080]在部署這一節(jié)最后必須強調(diào)一個理念部署不等于上線上線不等于完成。真正嚴謹?shù)牧鞒淌恰安渴?→ 灰度 → 切量 → 監(jiān)控”。我經(jīng)歷過一次事故模型在離線驗證集上F1很高灰度給1%流量時看著也正常等慢慢放到30%后才發(fā)現(xiàn)特定用戶群的預測結(jié)果異常。原因是灰度樣本分布存在偏差前20%流量恰好掩蓋了某些用戶特征。從那以后我再也不信“放量穩(wěn)步提升”這句話沒有監(jiān)控兜底。5. 模型上線后的維護與演進建立持續(xù)監(jiān)控和快速迭代機制模型部署上去這塊工作就結(jié)束了嗎遠遠沒有。現(xiàn)實中模型上線后基本就進入了一個不斷壞死的過程。數(shù)據(jù)變了、用戶行為變了、外部環(huán)境變了模型預測的準確性就會逐漸下降。AI工程中很多團隊都不重視這部分導致業(yè)務(wù)初期效果好幾個月后莫名其妙變差還找不到原因。我做維護介入的第一件事就是給模型建立監(jiān)控面板。監(jiān)控指標分兩類一類是無法避免的技術(shù)指標服務(wù)本身的QPS、延遲、報錯率、超時率。另一類是模型相關(guān)的業(yè)務(wù)指標預測分數(shù)分布、平均值、方差、特征覆蓋率、輸出結(jié)果與正負樣本的比例。這里有個非常實用的技巧模型分數(shù)分布漂移往往比真實業(yè)務(wù)指標下降更早暴露問題。比如一個用戶點擊率預測模型正常情況下預測概率均值在0.3到0.4之間某天突然變成0.1即使目前線上業(yè)務(wù)指標還沒變化也要引起警惕因為模型可能已經(jīng)在面對分布完全不同的數(shù)據(jù)了。我在監(jiān)控面板里專門畫了“預測分數(shù)分布日環(huán)比變化”和“特征缺失率趨勢”兩個圖表故障發(fā)現(xiàn)速度比只看業(yè)務(wù)KPI快得多。用于監(jiān)控模型是否衰減的另一個手段是留存樣本回放。定期抽樣一部分線上真實請求樣本保存下來注意脫敏和合規(guī)然后離線用當前最新模型和歷史模型同時進行預測對比兩者在留存樣本上的分數(shù)變化。這相當于給模型做了一個“歷史考卷”可以量化模型衰減的幅度。當發(fā)現(xiàn)模型衰減后就要啟動新一輪的迭代。這里最忌諱的是把線上模型直接拿下來重新訓練然后一把梭替換。正確做法是建立模型版本管理機制線上有一個穩(wěn)定的生產(chǎn)版本同時有一個正在訓練的候選版本。候選版本在離線評估和影子模式下表現(xiàn)穩(wěn)定后才進入灰度發(fā)布。所謂影子模式Shadow Mode就是讓新模型上線后和舊模型同步接收真實請求但新模型的結(jié)果僅記錄不外發(fā)。跑一段時間后用真實的線上數(shù)據(jù)對比新舊模型的表現(xiàn)這個環(huán)節(jié)非常能說明問題。我見過不少離線指標提升很猛的模型在影子模式下被現(xiàn)實教育得老老實實。版本管理上我建議每個模型都遵循一套命名和元信息規(guī)范否則時間長了根本分不清歷史模型的含義。我的規(guī)范是“模型類型_業(yè)務(wù)場景_序號_日期”比如“xgboost_ctr_v3_20240120”表示2024年1月20日訓練的第3版CTR預測模型。同時每版模型在倉庫里記錄四個必填信息訓練代碼commit、訓練數(shù)據(jù)集hash、訓練配置、評估報告。這套規(guī)范和前面說的實驗追蹤是一脈相承的。一個很多人忽視的維護點是特征對齊。訓練時的特征構(gòu)造代碼和線上推理時的特征構(gòu)造代碼如果分別維護在兩處幾乎一定會因為“改了一行卻忘了改另一邊”而漂移。我在工具鏈上強制要求訓練特征和線上特征必須復用一個特征函數(shù)庫新特征上線前必須跑一遍“訓練時同一份樣本的推理分數(shù)一致性校驗”。這個校驗不復雜就是對同一批歷史數(shù)據(jù)用訓練時的代碼構(gòu)造特征再用線上的代碼構(gòu)造特征計算兩組特征的最大差異。如果差異超過閾值堅決不讓上線。到了這個階段你會發(fā)現(xiàn)從零開始的AI工程逐步形成了一個良性循環(huán)穩(wěn)定的腳手架幫助你高效實驗實時的監(jiān)控讓你了解線上模型狀態(tài)自動化的指標評估幫你作出決策安全的版本管理使你可以快速迭代。這是一條沒有終點的路但走通它你就不再是“會用框架的人”而是真正能夠駕馭AI系統(tǒng)的工程師。最后分享一條私人經(jīng)驗不要給自己規(guī)定“必須學完什么才能開始”。我學習AI工程的過程極其雜亂一開始連Linux常用命令都生疏就敢去改服務(wù)端推理邏輯結(jié)果被進程崩潰按在地上摩擦了幾次才回頭老老實實補基礎(chǔ)。如果你也打算從零開始我建議你直接選一個真實業(yè)務(wù)場景哪怕只是做一個“商品評論情感分析”小程序然后順著這條鏈路往下走數(shù)據(jù)處理→模型訓練→構(gòu)建API→部署上線→監(jiān)控日志。走完這一趟你踩下的每一個坑都會成為你和別人介紹AI工程時最生動的素材。