同實(shí)戰(zhàn):從模型部署到推理性能優(yōu)化的完整指南)
模型在本地調(diào)得再漂亮上了線才發(fā)現(xiàn)GPU利用率不到30%接口P99延遲飄到幾百毫秒算法同學(xué)說“我代碼沒問題”infra同學(xué)說“我資源也沒問題”兩邊對(duì)著一個(gè)黑盒互相甩鍋——這種場(chǎng)面我在不止一個(gè)團(tuán)隊(duì)里見過。算法和infra的協(xié)同聽起來是個(gè)工程管理話題實(shí)際操作起來全是技術(shù)細(xì)節(jié)推理框架怎么選、batch size怎么定、數(shù)據(jù)管道怎么設(shè)計(jì)、profile的結(jié)果怎么解讀。這篇東西不聊虛的把我自己在一線做“算法-infra協(xié)同”的踩坑經(jīng)驗(yàn)、設(shè)計(jì)思路和排查方法整成一份可復(fù)用的參考給正在被模型部署和性能調(diào)優(yōu)折磨的算法工程師、以及被各種“優(yōu)化需求”打擾的infra工程師都做個(gè)參照。我前幾年主導(dǎo)過一個(gè)推薦模型的推理服務(wù)優(yōu)化項(xiàng)目從最開始的兩邊互相看不懂到后面沉淀出一套配合模式中間的過程很能說明問題。下面按我自己的實(shí)踐路徑把整個(gè)協(xié)同過程拆開講。1. 為什么“算法-infra協(xié)同”成了必須面對(duì)的問題1.1 算法模型跑通只是第一步上線才是真正的門檻很多算法團(tuán)隊(duì)習(xí)慣的交付狀態(tài)是模型在離線評(píng)測(cè)集上指標(biāo)達(dá)標(biāo)然后在Jupyter Notebook里跑通一個(gè)推理demo就認(rèn)為“算法已經(jīng)完成”。但實(shí)際情況是從Notebook到生產(chǎn)服務(wù)之間有一道巨大的鴻溝這道鴻溝幾乎全是infra的領(lǐng)域。拿我自己踩過的坑來說。有一次我們訓(xùn)練好的CTR模型離線AUC比舊模型高了三個(gè)千分點(diǎn)算法同學(xué)很開心覺得這個(gè)版本穩(wěn)了。但一到線上壓測(cè)就傻眼了單機(jī)QPS只有預(yù)期的四分之一P99延遲超過500毫秒線上流量稍微一抖服務(wù)直接超時(shí)雪崩。那時(shí)候算法和infra互相都覺得是對(duì)方的問題。算法覺得“模型邏輯就這么簡(jiǎn)單無非就是個(gè)矩陣乘法加激活函數(shù)怎么可能這么慢”infra覺得“機(jī)器配置給夠了是你模型代碼寫得爛”。后來profile一查問題根本不在模型計(jì)算本身特征拼接時(shí)反復(fù)做內(nèi)存拷貝、Python側(cè)循環(huán)處理請(qǐng)求、每個(gè)請(qǐng)求都重新加載一次詞表這些代碼層面的問題全被忽略了。模型本身可能只占整個(gè)推理鏈路的20%時(shí)間剩下80%都被數(shù)據(jù)處理、框架調(diào)度、IO等待吃掉了。這件事給我的教訓(xùn)是算法模型的“正確性”和系統(tǒng)的“可用性”是兩回事。算法同學(xué)的交付物不應(yīng)該只是模型權(quán)重還應(yīng)該包含對(duì)推理鏈路的整體理解以及和infra一起把系統(tǒng)調(diào)優(yōu)到可用的狀態(tài)的意愿。1.2 協(xié)同斷點(diǎn)到底卡在哪里要說算法和infra的協(xié)同難難就難在兩邊的心智模型不一樣。我觀察下來斷點(diǎn)主要集中在這幾個(gè)地方。第一個(gè)斷點(diǎn)是語(yǔ)言不通。算法同學(xué)張口閉口是“AUC”、“l(fā)oss收斂”、“Embedding維度”infra同學(xué)關(guān)心的是“QPS”、“P99時(shí)延”、“CPU水位”、“內(nèi)存占用”。兩邊都在說性能但說的根本不是一回事。算法眼里的“性能”是模型效果指標(biāo)infra眼里的“性能”是系統(tǒng)資源指標(biāo)中間缺一個(gè)翻譯層。第二個(gè)斷點(diǎn)是交付物邊界不清。算法覺得“我把模型文件發(fā)給你就行了”infra覺得“你給我的模型我跑不起來”。模型用什么框架導(dǎo)出、有沒有帶預(yù)處理邏輯、依賴的Python包版本、詞表文件在哪、特征維度是多少這些信息如果不掉清楚infra同學(xué)就得靠猜。猜的代價(jià)就是反復(fù)試錯(cuò)時(shí)間全耗在溝通上。第三個(gè)斷點(diǎn)是資源評(píng)估全靠拍腦袋。算法說“給我4張A100”問他要4張卡的理由他說“可能不夠多要點(diǎn)保險(xiǎn)”。infra那邊資源池本來就不寬裕一看這種申請(qǐng)直接打回。其實(shí)完全可以做精細(xì)化評(píng)估跑一下profile看顯存峰值和計(jì)算密度再用公式估算一下吞吐需求有理有據(jù)地填申請(qǐng)單。第四個(gè)斷點(diǎn)是性能問題定位的方式不對(duì)。我見過有團(tuán)隊(duì)遇到線上延遲高第一反應(yīng)是“加機(jī)器”結(jié)果加了兩倍的機(jī)器延遲一點(diǎn)沒降因?yàn)槠款i在CPU上的數(shù)據(jù)預(yù)處理不在GPU推理。按加機(jī)器來擴(kuò)容完全擴(kuò)錯(cuò)了方向。這些斷點(diǎn)單獨(dú)看都是小事但疊在一起就會(huì)形成巨大的協(xié)作成本。我后來總結(jié)了一個(gè)觀點(diǎn)算法和infra的協(xié)同本質(zhì)上是要找到一種方式把彼此的領(lǐng)域知識(shí)翻譯成對(duì)方能理解的語(yǔ)言比如用可量化的指標(biāo)、標(biāo)準(zhǔn)的交付格式、清晰的流程節(jié)點(diǎn)把這些知識(shí)編碼下來。2. 我在協(xié)同項(xiàng)目里的整體設(shè)計(jì)思路2.1 從“交付模型”轉(zhuǎn)向“交付服務(wù)”這個(gè)轉(zhuǎn)變是我做協(xié)同優(yōu)化時(shí)第一個(gè)確立的原則。如果算法團(tuán)隊(duì)的交付物只是模型文件那infra團(tuán)隊(duì)的職責(zé)就成了“把模型跑起來”至于跑得好不好、快不快全是infra的事算法自然就不關(guān)心推理性能。這樣分工從根上就有問題因?yàn)橥评硇阅艿暮艽笠徊糠秩Q于模型結(jié)構(gòu)、算子實(shí)現(xiàn)和特征處理方式這些都在算法團(tuán)隊(duì)的控制范圍內(nèi)。正確的做法是把交付邊界往后推算法團(tuán)隊(duì)交付的不只是模型文件還包括一套推理服務(wù)的可運(yùn)行版本。這套版本要有明確的性能指標(biāo)要求比如目標(biāo)QPS、目標(biāo)延遲、吞吐上限并且要附帶模型對(duì)資源需求的估算說明比如建議的batch size范圍、顯存占用預(yù)估、CPU內(nèi)存需求。infra團(tuán)隊(duì)基于這套交付物做容量規(guī)劃和部署調(diào)優(yōu)雙方的責(zé)任邊界從“模型文件”移動(dòng)到了“性能指標(biāo)”。你說這不是算法團(tuán)隊(duì)得多干不少活嗎確實(shí)多了但這種“多”是必要的。因?yàn)橹挥袑懩P偷膱F(tuán)隊(duì)才最清楚模型的計(jì)算圖、內(nèi)存占用和算子特性這些信息轉(zhuǎn)述給infra要么失真要么說不全。把性能要求寫進(jìn)交付規(guī)范雙方對(duì)話就有了共同基準(zhǔn)。2.2 接口規(guī)范化讓算法和infra解耦的關(guān)鍵第二個(gè)原則是接口規(guī)范化。我之前出現(xiàn)過不少讓人崩潰的協(xié)作問題算法同學(xué)說“這個(gè)實(shí)現(xiàn)依賴Python 3.8的一個(gè)特性”infra同學(xué)部署的生產(chǎn)環(huán)境是Python 3.7直接跑不起來或者模型輸入的特征順序算法和工程端理解的不一樣上線之后預(yù)測(cè)結(jié)果全亂了。后來我們做了一個(gè)標(biāo)準(zhǔn)化的模型服務(wù)接口規(guī)范把模型推理的輸入輸出格式固定下來特征按有序字典傳入模型版本用語(yǔ)義化版本號(hào)管理配置信息如batch size、并發(fā)數(shù)、超時(shí)時(shí)間全部外置到配置文件里。這樣算法迭代模型時(shí)不需要改動(dòng)服務(wù)框架infra升級(jí)基礎(chǔ)設(shè)施時(shí)也不需要跟算法確認(rèn)每個(gè)細(xì)節(jié)。模型文件、特征配置、服務(wù)框架、資源規(guī)格四層各管各的通過標(biāo)準(zhǔn)化接口銜接。這個(gè)設(shè)計(jì)很像軟件工程里的依賴倒置原則高層的算法邏輯不要依賴底層的具體基礎(chǔ)設(shè)施底層基礎(chǔ)設(shè)施也不要反哺高層的模型代碼兩邊只依賴抽象接口。實(shí)踐下來這個(gè)解耦帶來的好處是兩邊可以并行推進(jìn)不再互相阻塞。2.3 用性能基線和透明數(shù)據(jù)建立信任第三個(gè)原則是用數(shù)據(jù)說話建立性能基線。在協(xié)同項(xiàng)目里最怕的不是性能差而是性能數(shù)據(jù)不透明。算法說“我這個(gè)模型很快”沒有任何數(shù)據(jù)支撐infra說“服務(wù)器有瓶頸”也說不清瓶頸在哪。最后只能靠嗓門大。我們的做法是建立一套標(biāo)準(zhǔn)的性能評(píng)測(cè)流程每次模型迭代上線前都要在固定的評(píng)測(cè)環(huán)境下跑一次壓測(cè)記錄吞吐量、P99時(shí)延、GPU利用率、顯存占用、CPU開銷等核心指標(biāo)把這些指標(biāo)記錄到一份自動(dòng)生成的評(píng)測(cè)報(bào)告里和上一版本做對(duì)比。誰(shuí)快誰(shuí)慢、快在哪慢在哪一目了然不再有“我覺得”“我感覺”的空間。這套評(píng)測(cè)流程跑起來之后我明顯感覺到兩邊溝通順暢多了。infra說“你的服務(wù)GPU利用率低”直接拿出監(jiān)控截圖和nvidia-smi數(shù)據(jù)算法說“這個(gè)算子慢”直接拿出profiling報(bào)告指出熱點(diǎn)。數(shù)據(jù)代替了猜測(cè)協(xié)作自然就高效了。3. 推理服務(wù)協(xié)同優(yōu)化的實(shí)操全過程3.1 環(huán)境準(zhǔn)備階段選對(duì)工具鏈后面能省一半的麻煩這個(gè)項(xiàng)目的目標(biāo)是讓一個(gè)推薦模型的在線推理服務(wù)達(dá)到線上要求支撐2000 QPS的流量P99延遲不超過100毫秒。模型用的是DeepFM架構(gòu)特征規(guī)模大約5000萬(wàn)維稀疏特征加200維稠密特征服務(wù)于用戶點(diǎn)擊率預(yù)估場(chǎng)景。環(huán)境選型上我們做了一輪比較。模型原本是用PyTorch訓(xùn)練的原計(jì)劃直接用PyTorch做推理服務(wù)但壓測(cè)發(fā)現(xiàn)Python側(cè)的開銷太高GIL讓多線程并發(fā)受限后來?yè)Q成了ONNX Runtime加TensorRT的后端方案。ONNX Runtime負(fù)責(zé)從PyTorch導(dǎo)出的模型做轉(zhuǎn)換和執(zhí)行TensorRT在NVIDIA GPU上做算子融合和精度校準(zhǔn)。實(shí)測(cè)下來單卡的推理吞吐比純PyTorch提升了接近3倍P99時(shí)延也降了一個(gè)數(shù)量級(jí)。工具鏈方面profile用的是PyTorch自帶的torch.profiler配合NVIDIA的Nsight Systems資源監(jiān)控用的Prometheus加Grafana部署走的是Docker加Kubernetes。這套組合的好處是覆蓋了從模型算子級(jí)別到系統(tǒng)資源級(jí)別的全鏈路觀測(cè)兩邊拿到的數(shù)據(jù)是同一套不會(huì)再出現(xiàn)“我這邊看到CPU滿了你那邊說進(jìn)程很閑”這種數(shù)據(jù)對(duì)不上的情況。3.2 從單卡推理到服務(wù)化部署這一步隱藏了最多的坑模型導(dǎo)出之后第一版直接拿ONNX Runtime的Python接口做了個(gè)Wrap然后用Flask啟動(dòng)了一個(gè)HTTP服務(wù)。簡(jiǎn)單測(cè)了一下能跑通但很快就暴露了問題Python接口的調(diào)用開銷很大每次請(qǐng)求進(jìn)來都要做Python和C之間的數(shù)據(jù)交換而且不支持真正的多進(jìn)程并行推理CPU并發(fā)利用不起來。后來改成了ONNX Runtime的C部署方案配合TensorRT的engine來做推理。這一步改動(dòng)帶來的性能提升非常直接因?yàn)槭〉袅薖ython解釋器這層開銷每次推理調(diào)用的延遲直接砍掉了三分之一。同時(shí)我們用內(nèi)存映射的方式預(yù)加載了模型的Embedding參數(shù)避免了每個(gè)請(qǐng)求都從磁盤讀取參數(shù)導(dǎo)致的IO抖動(dòng)。服務(wù)端的并發(fā)模型也做了一輪重構(gòu)。最初是每個(gè)請(qǐng)求一個(gè)小線程線程切換開銷大后來改成了線程池加有界隊(duì)列的模式根據(jù)目標(biāo)QPS動(dòng)態(tài)調(diào)整worker數(shù)量配合OMP_NUM_THREADS環(huán)境變量控制底層并行度。這么改完單實(shí)例的QPS從原來的不到400提升到了接近1500CPU和GPU的使用率也均衡了很多。這里有個(gè)數(shù)據(jù)值得記錄一下同樣的模型和機(jī)器純Python部署的P99時(shí)延是180ms換成C后端之后降到了58ms再配合TensorRT的FP16精度優(yōu)化進(jìn)一步降到了35ms。這三步優(yōu)化每一步單獨(dú)拿出來看都不算大工程但疊加起來就是數(shù)量級(jí)的差別。3.3 壓測(cè)過程和瓶頸分析把一切問題擺到明面上壓測(cè)工具用的是wrk加自研的流量回放腳本。wrk負(fù)責(zé)模擬高并發(fā)HTTP請(qǐng)求流量回放腳本則從線上Nginx日志抽取真實(shí)請(qǐng)求樣本按時(shí)間戳順序重放到測(cè)試服務(wù)上盡量還原線上請(qǐng)求的特征包括請(qǐng)求大小分布、特征稀疏度、以及峰值時(shí)刻的流量形態(tài)。壓測(cè)分為三輪。第一輪是小流量驗(yàn)證用100 QPS跑10分鐘確認(rèn)功能正確、內(nèi)存沒有泄漏、日志沒有報(bào)錯(cuò)。第二輪是加壓測(cè)試從500 QPS起步每2分鐘增加250 QPS觀察各項(xiàng)指標(biāo)的變化趨勢(shì)找到服務(wù)崩潰的臨界點(diǎn)。第三輪是持續(xù)穩(wěn)定性測(cè)試用目標(biāo)負(fù)載的80%1600 QPS連續(xù)跑8小時(shí)確認(rèn)長(zhǎng)時(shí)間運(yùn)行下沒有內(nèi)存膨脹、句柄泄漏等問題。瓶頸分析階段我們抓到了好幾個(gè)經(jīng)典問題。第一個(gè)是第一個(gè)版本在1000 QPS左右時(shí)P99延遲突然從60ms跳升到400ms查下來發(fā)現(xiàn)是Python側(cè)的特征預(yù)處理線程變成了瓶頸CPU打滿GPU反而在空轉(zhuǎn)。解決辦法是預(yù)處理部分全部向C側(cè)下沉用ONNX Runtime的custom operator實(shí)現(xiàn)特征拼接把CPU和GPU的工作重新配平。第二個(gè)是壓測(cè)跑到中段出現(xiàn)周期性延遲尖峰后來發(fā)現(xiàn)是日志寫的太頻繁磁盤IO被打滿改成異步日志之后尖峰消失。第三個(gè)是Embedding查表在GPU上表現(xiàn)為顯存訪問不連續(xù)用class-wise的存儲(chǔ)布局優(yōu)化之后這塊耗時(shí)又降了一些。這些瓶頸沒有一個(gè)能被直覺直接發(fā)現(xiàn)全靠profile數(shù)據(jù)才能定位。所以我強(qiáng)烈建議優(yōu)化性能的時(shí)候一定要先把“當(dāng)前時(shí)間花在哪兒”搞清楚再談怎么優(yōu)化千萬(wàn)不要憑感覺。4. 協(xié)同過程中必踩的坑與解決方案實(shí)錄4.1 “GPU利用率上不去”到底是誰(shuí)的鍋這是我在協(xié)同項(xiàng)目里被問過最多的問題也是算法和infra最容易吵架的導(dǎo)火索。很多團(tuán)隊(duì)拿“GPU利用率”作為模型服務(wù)效率的核心指標(biāo)但GPU利用率低的原因遠(yuǎn)比想象的復(fù)雜不一定是模型的錯(cuò)。我見過的GPU利用率低的典型原因有這么幾類數(shù)據(jù)加載和預(yù)處理太慢GPU在等數(shù)據(jù)利用率自然上不去batch size設(shè)得太小GPU的并行能力沒有充分釋放CPU側(cè)的Python邏輯占了太多時(shí)間GPU一直在空轉(zhuǎn)模型本身有串行依賴計(jì)算圖并行度不夠。每一類對(duì)應(yīng)的解法完全不同。定位方法說起來很簡(jiǎn)單打開Nsight Systems或者nvidia-smi的監(jiān)控看GPU到底是“計(jì)算密集”還是“空閑等待”。如果是等待就去看CPU側(cè)在忙什么如果是計(jì)算密集但利用率依舊低那就去看模型的計(jì)算圖是不是存在大量的串行節(jié)點(diǎn)。有一次我們排查一個(gè)BERT的服務(wù)發(fā)現(xiàn)GPU利用率一直只有20%多profile一看問題在CPU側(cè)的分詞器上——每次請(qǐng)求都要重新做一次分詞一個(gè)毫秒級(jí)的操作在并發(fā)量大的時(shí)候被放大成了巨大的等待。改成預(yù)分詞加緩存之后GPU利用率直接翻倍。這類問題暴露出來以后我最大的感觸是千萬(wàn)不要把“GPU利用率低”當(dāng)作某個(gè)人的責(zé)任問題它更接近一個(gè)系統(tǒng)問題。GPU利用率是結(jié)果指標(biāo)不是原因指標(biāo)單純盯著它沒什么用還是要一級(jí)一級(jí)往下拆。4.2 模型精度復(fù)現(xiàn)不了的隱形元兇模型從PyTorch導(dǎo)出到ONNX再到TensorRT之后經(jīng)常有算法同學(xué)跑回來反饋“線上預(yù)測(cè)結(jié)果和離線對(duì)不上?!币婚_始我們以為是模型導(dǎo)出過程出了錯(cuò)后來才發(fā)現(xiàn)最隱蔽的問題來自“數(shù)值精度”和“算子實(shí)現(xiàn)差異”。PyTorch在GPU訓(xùn)練時(shí)默認(rèn)用FP32推理轉(zhuǎn)成TensorRT的FP16后部分算子比如LayerNorm、Softmax的數(shù)值精度會(huì)有微小差異在大多數(shù)場(chǎng)景下不影響排序但有些邊界樣本的預(yù)測(cè)值會(huì)變化幾個(gè)百分點(diǎn)。算法同學(xué)如果拿著離線全精度的輸出和線上半精度的輸出逐條比對(duì)就會(huì)看到差異。解決的辦法是分類處理對(duì)精度有嚴(yán)格要求的任務(wù)比如風(fēng)控、金融場(chǎng)景保持FP32推理用算子融合和優(yōu)化內(nèi)存訪問來提速對(duì)排序、推薦這類對(duì)絕對(duì)概率值不敏感、只看相對(duì)順序的任務(wù)可以用FP16甚至INT8配合校準(zhǔn)集把精度損失控制在一個(gè)很小的范圍內(nèi)。還有個(gè)容易被忽略的點(diǎn)是框架版本差異PyTorch 1.x和2.x導(dǎo)出的ONNX算子集不一樣TensorRT版本不同也可能導(dǎo)致同樣的模型在部署時(shí)行為不同。所以必須把“訓(xùn)練框架版本、導(dǎo)出工具版本、推理框架版本”這三項(xiàng)固定下來做成一個(gè)部署配置的“鐵三角”任何一項(xiàng)升級(jí)都要重新回歸。4.3 資源申請(qǐng)被駁回其實(shí)是可以避免的“申請(qǐng)4張A100被infra駁回了說我浪費(fèi)資源怎么辦”這種問題我隔三差五就能遇到。實(shí)際情況是不少算法同學(xué)根本不估算自己到底需要多少資源申請(qǐng)的時(shí)候全靠感覺往大了填。infra那邊資源池是共享的肯定優(yōu)先批給那些能說清楚需求的團(tuán)隊(duì)。資源估算其實(shí)沒那么難。第一步在目標(biāo)機(jī)器上跑一次真實(shí)的訓(xùn)練或推理任務(wù)記錄峰值顯存和平均顯存這就是“單實(shí)例基線”。第二步根據(jù)訓(xùn)練數(shù)據(jù)量和目標(biāo)訓(xùn)練周期估算需要的總算力公式大概是總算力需求FLOPs 單樣本前向計(jì)算量 × 訓(xùn)練樣本數(shù) × epoch數(shù)。第三步結(jié)合單卡算力算出需要的顯卡數(shù)量和訓(xùn)練天數(shù)再留出20%到30%的冗余應(yīng)對(duì)波動(dòng)。拿這個(gè)思路去和infra溝通交流起來完全不同。你說“我需要4張卡跑完需要3天實(shí)測(cè)單卡峰值顯存是38GB考慮數(shù)據(jù)加載波動(dòng)留了10%的buffer需要40GB以上的卡”這種申請(qǐng)沒有理由被駁回。說到底資源申請(qǐng)不只是填個(gè)數(shù)字它本身就是一種工程能力是算法和infra之間建立信任的重要一環(huán)。5. 把協(xié)同能力沉淀成團(tuán)隊(duì)資產(chǎn)5.1 給算法同學(xué)的建議不要把“跑通了”當(dāng)作交付完成我自己也是算法出身太知道“跑通”和“交付”之間的差距了。模型在Notebook里跑通了只是證明你的思路在二三十個(gè)樣本上可行真正讓模型產(chǎn)生價(jià)值的是它能穩(wěn)定地服務(wù)線上流量還能在流量翻倍時(shí)不崩在資源減半時(shí)依然可控。這些要求都不是算法同學(xué)的“分內(nèi)事”但如果完全不關(guān)心你的模型就永遠(yuǎn)活不過上線那一天。所以至少要做到交付模型時(shí)附帶一份“模型說明卡”內(nèi)容包括模型結(jié)構(gòu)、輸入輸出的字段和類型、依賴的框架及版本、詞表在線獲取方式、推薦的batch size范圍、估計(jì)的顯存占用、冷啟動(dòng)時(shí)間、對(duì)CPU和內(nèi)存的依賴。這份卡片不要求多規(guī)范但至少能讓infra同學(xué)不用靠猜來部署你的模型。另外學(xué)會(huì)讀profile是算法同學(xué)性價(jià)比最高的技能投資。一個(gè)模型服務(wù)慢90%的情況下用profile都能找到原因。我見過太多人拿著性能問題去找infra連自己程序的火焰圖都沒看過一眼。不要只會(huì)跑訓(xùn)練要學(xué)會(huì)看推理的計(jì)算熱點(diǎn)。掌握這一項(xiàng)技能你在協(xié)同中的話語(yǔ)權(quán)會(huì)高很多。5.2 給基礎(chǔ)設(shè)施同學(xué)的建議把“不行”變成“怎么才行”infra同學(xué)在這類協(xié)同里也有一個(gè)常見的立場(chǎng)問題因?yàn)殚L(zhǎng)期被各種不靠譜的資源申請(qǐng)和不規(guī)范的服務(wù)代碼折磨容易變成一個(gè)“什么都說不行”的狀態(tài)。這樣短期看是保護(hù)了資源池長(zhǎng)期看會(huì)壓制業(yè)務(wù)的迭代速度最后被業(yè)務(wù)方集體吐槽“基礎(chǔ)設(shè)施跟不上業(yè)務(wù)”。我的建議是infra團(tuán)隊(duì)的重點(diǎn)不應(yīng)該放在審批和拒絕上而是應(yīng)該放在提供“自助能力”上。比如做一個(gè)標(biāo)準(zhǔn)的模型部署平臺(tái)算法同學(xué)上傳模型文件、選定推理框架和后端配置平臺(tái)自動(dòng)生成Docker鏡像、分配測(cè)試環(huán)境、跑一輪標(biāo)準(zhǔn)壓測(cè)、輸出性能報(bào)告?;A(chǔ)設(shè)施把通用的能力沉淀成工具和流程就能把“每個(gè)項(xiàng)目都要人肉介入支持一次”的重復(fù)勞動(dòng)省掉。這需要在思想上做一個(gè)轉(zhuǎn)變infra的kpi不是“管住了多少資源”而是“業(yè)務(wù)能多快地把模型部署上線并且用盡量少的資源跑起來”。如果基礎(chǔ)設(shè)置的同學(xué)能往這個(gè)方向做算法和infra的摩擦?xí)『芏唷?.3 把復(fù)盤和知識(shí)沉淀變成日常動(dòng)作項(xiàng)目結(jié)束之后我們做了一次總結(jié)把整個(gè)協(xié)同過程中的經(jīng)驗(yàn)和工具沉淀成了幾樣?xùn)|西一份模型交付標(biāo)準(zhǔn)文檔、一個(gè)自動(dòng)化壓測(cè)腳本、一套性能基準(zhǔn)數(shù)據(jù)表、一段線上問題排查的SOP。這些資產(chǎn)的價(jià)值在后面的幾次迭代里被充分證明了——新模型上線的時(shí)間從一開始的三天壓縮到了半天性能問題的定位時(shí)間也從按小時(shí)計(jì)縮短到了按分鐘計(jì)。復(fù)盤形式上我們沒有搞那種全員參加的PPT大會(huì)就是內(nèi)部拉了一個(gè)文檔把從模型開發(fā)到部署調(diào)優(yōu)到線上穩(wěn)定的過程中所有關(guān)鍵決策、踩坑記錄、數(shù)據(jù)截圖、最終結(jié)論全部寫進(jìn)去想到什么記什么。每周項(xiàng)目例會(huì)上花15分鐘過一遍。三個(gè)月后回頭翻這份文檔里面很多記錄在當(dāng)下看來已經(jīng)成了團(tuán)隊(duì)共識(shí)但如果沒有當(dāng)時(shí)的記錄這些共識(shí)根本建立不起來。還有一點(diǎn)很關(guān)鍵知識(shí)沉淀要落到代碼和腳本里不能只活在文檔里。比如“壓測(cè)時(shí)必須監(jiān)控GPU利用率”這個(gè)原則寫在文檔里可能過幾周就沒人看了但如果寫進(jìn)壓測(cè)腳本里每次壓測(cè)結(jié)束自動(dòng)出一份指標(biāo)報(bào)告這個(gè)原則就變成了流程的一部分天然被強(qiáng)制執(zhí)行。制度化永遠(yuǎn)是比自覺更可靠的保障。6. 一個(gè)更長(zhǎng)期的視角算法與基礎(chǔ)設(shè)施雙輪驅(qū)動(dòng)的演進(jìn)路徑6.1 模型迭代和基礎(chǔ)設(shè)施演進(jìn)要配套走我們剛開始做這個(gè)協(xié)同項(xiàng)目的時(shí)候總覺得模型迭代是最重要的infra只是“陪跑”。但做到后面才發(fā)現(xiàn)兩者其實(shí)是在互相牽引的。模型側(cè)有個(gè)新想法比如從單模態(tài)走向多模態(tài)或者模型結(jié)構(gòu)要從2D卷積換成3D卷積這對(duì)基礎(chǔ)設(shè)施提出的要求是完全不同的反過來基礎(chǔ)設(shè)施這邊推理框架升級(jí)支持了新的算子和精度模式也會(huì)反過來影響模型側(cè)怎么做結(jié)構(gòu)設(shè)計(jì)和調(diào)優(yōu)。所以現(xiàn)在我們?cè)谧黾夹g(shù)規(guī)劃時(shí)會(huì)把算法和infra放在同一個(gè)盤子里看。每次新模型立項(xiàng)的時(shí)候算法團(tuán)隊(duì)要寫清楚預(yù)期的推理需求和資源消耗infra團(tuán)隊(duì)根據(jù)這個(gè)需求反饋基礎(chǔ)設(shè)施的改造計(jì)劃兩邊一起排期而不是等算法做完了再丟給infra“看看能不能跑”。這種平行推進(jìn)的節(jié)奏看起來很慢實(shí)際算總時(shí)間反而更快因?yàn)樗鼫p少了大量返工和等待。6.2 “算法-infra協(xié)同”的三種模式根據(jù)團(tuán)隊(duì)的大小和業(yè)務(wù)復(fù)雜度我觀察到的協(xié)同模式大概有三類你可以看看自己團(tuán)隊(duì)現(xiàn)在卡在哪一類。第一種是“工程師一肩挑”小團(tuán)隊(duì)里算法同學(xué)順便把自己寫的服務(wù)部署上線了這種模式最靈活但對(duì)個(gè)人能力要求極高而且隨著業(yè)務(wù)變大很難維持。第二種是“接口人協(xié)作”算法和infra各派一個(gè)人負(fù)責(zé)對(duì)接互相轉(zhuǎn)述需求這種模式對(duì)接口人的能力依賴極大接口人一換人就可能崩。第三種是“流程化協(xié)同”通過標(biāo)準(zhǔn)的交付物、性能指標(biāo)、部署流程來協(xié)作人和人之間對(duì)具體細(xì)節(jié)的依賴降到最低這也是我在這篇文章里主要推薦的做法。三種模式之間不是互斥的團(tuán)隊(duì)小的時(shí)候可以用第一種起家逐漸過渡到第二種最后在業(yè)務(wù)量上來之后用第三種方式做長(zhǎng)治久安的保障。關(guān)鍵是心里要有個(gè)譜現(xiàn)在的協(xié)同方式在小規(guī)模下夠用但它不會(huì)自動(dòng)適配更大體量的團(tuán)隊(duì)和更復(fù)雜的業(yè)務(wù)。6.3 數(shù)據(jù)驅(qū)動(dòng)的協(xié)同能力評(píng)估最后一個(gè)想聊的心得是協(xié)同本身也要量化不能被“最近好像合作好多了”這種模糊感覺帶過去。我們每個(gè)月會(huì)看幾個(gè)關(guān)鍵數(shù)字平均模型上線周期、壓測(cè)通過率、資源超用率、線上重大性能事件次數(shù)。這組數(shù)字能直觀反映算法和infra之間的配合是否在變好而不是憑某幾次印象做判斷。我第一次提出這個(gè)想法時(shí)有同事開玩笑說“連協(xié)同都要上指標(biāo)了是不是卷過頭了”。但實(shí)際跑下來效果還挺好。因?yàn)橐坏?shù)字看得到就總有人會(huì)想辦法改進(jìn)數(shù)字。比如那陣子壓測(cè)通過率只有60%大家分析一圈發(fā)現(xiàn)是模型交付的“模型說明卡”寫得不完整infra拿到模型以后各種猜浪費(fèi)了不少時(shí)間。后來把說明卡模板和交付工具打通壓測(cè)通過率很快升了上來。如果沒有數(shù)據(jù)這種問題可能永遠(yuǎn)藏在“溝通不太順”的模糊說法里誰(shuí)也看不到具體的短板在哪里。所以我的建議是如果你的團(tuán)隊(duì)也經(jīng)常因?yàn)樗惴ê蚷nfra的邊界吵架、推諉先別急著開“加強(qiáng)溝通”的會(huì)不如一起定一套可量化的協(xié)同指標(biāo)把問題擺到明面上。數(shù)據(jù)到位了協(xié)作自然會(huì)改善。我個(gè)人在帶著團(tuán)隊(duì)跑完這個(gè)項(xiàng)目之后最大的感受就是算法和infra的協(xié)同說到底是兩個(gè)知識(shí)體系之間的翻譯工作。算法同學(xué)需要理解一點(diǎn)系統(tǒng)原理和工程約束infra同學(xué)也要懂一點(diǎn)模型推理的基本邏輯中間用標(biāo)準(zhǔn)化的接口、可量化的指標(biāo)和數(shù)據(jù)化的復(fù)盤來搭橋。不要指望靠幾次團(tuán)建或幾輪溝通就能根治協(xié)作問題真正管用的還是把這些協(xié)同點(diǎn)固化到流程和技術(shù)里讓流程替人去記憶、去執(zhí)行。這一步做到了團(tuán)隊(duì)的能力才算真正沉淀下來。