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