戰(zhàn):8B/70B 長上下文實(shí)測與部署指南)
Llama 3 這波開源來得挺猛朋友圈和幾個(gè)技術(shù)群那幾天幾乎被刷屏。我第一時(shí)間把 8B 和 70B 兩個(gè)版本都拉下來跑了一遍順手做了 FP8 量化和長上下文的實(shí)測。這篇東西不是論文復(fù)讀也不是官方文檔的搬運(yùn)純粹是我自己踩完坑之后的一份實(shí)操記錄。核心關(guān)鍵詞就三個(gè)Llama 3 精讀、FP8 量化、長上下文實(shí)測。我會(huì)把模型結(jié)構(gòu)里幾個(gè)值得注意的改動(dòng)拆開講把 FP8 量化從原理到落地配置走一遍再把長上下文拉到 8K 以上看它到底穩(wěn)不穩(wěn)。適合誰看如果你手上有一張 24G 顯存的卡想搞清楚這模型能不能塞進(jìn)去、量化之后掉不掉點(diǎn)、長文本會(huì)不會(huì)胡言亂語那這篇基本能覆蓋你的疑問。如果你只是聽說過 Llama 3 還沒動(dòng)手也可以跟著走一遍我把每一步的意圖和參數(shù)選擇理由都寫清楚了。1. 先搞清楚 Llama 3 到底改了什么1.1 從 Llama 2 到 Llama 3 的關(guān)鍵結(jié)構(gòu)變化很多人拿到模型就直接跑推理跑完發(fā)現(xiàn)效果不錯(cuò)就完事了但如果你要做量化或者長上下文優(yōu)化不理解結(jié)構(gòu)改動(dòng)是要吃虧的。Llama 3 相比 Llama 2最核心的幾個(gè)變化我列一下這些都是影響后續(xù)量化和顯存占用的直接因素。第一是詞表從 32K 擴(kuò)到了 128K。這個(gè)改動(dòng)看起來只是 embedding 層變大但它直接影響了 tokenizer 的壓縮率。實(shí)測下來同樣一段中文加英文混合的文本Llama 3 的 token 數(shù)大概只有 Llama 2 的 70% 到 80%。這意味著什么意味著同樣的上下文窗口Llama 3 實(shí)際能塞進(jìn)去的內(nèi)容更多了。但代價(jià)是 embedding 矩陣變大了8B 模型的 embedding 參數(shù)量從原來的約 5 億漲到了約 10 億這部分在量化的時(shí)候要特別注意因?yàn)?embedding 層通常不參與權(quán)重量化或者量化策略和其他線性層不一樣。第二是GQA分組查詢注意力的全面采用。Llama 2 只有 70B 用了 GQA8B 還是標(biāo)準(zhǔn)的 MHA。Llama 3 的 8B 和 70B 全部上了 GQA。8B 的配置是 32 個(gè) query head、8 個(gè) KV head也就是 4 個(gè) query 共享一組 KV。這個(gè)改動(dòng)對(duì)推理的影響非常大KV cache 直接縮小到原來的四分之一。做長上下文的時(shí)候KV cache 往往是顯存瓶頸GQA 把這個(gè)瓶頸往后推了不少。我實(shí)測 8B 模型在 8K 上下文下KV cache 大概只占 1G 出頭如果是 MHA 那得 4G 往上。第三是位置編碼換成了 RoPE 的改進(jìn)版本基礎(chǔ)頻率從 10000 提到了 500000。這個(gè)改動(dòng)是為了支持更長的上下文。頻率提高之后位置編碼在長距離上的區(qū)分度更好外推到 8K 甚至更長的時(shí)候衰減沒那么嚴(yán)重。但要注意官方發(fā)布的 8B 和 70B 原生上下文是 8K不是 128K。那個(gè) 128K 是后續(xù)某個(gè)版本才支持的基礎(chǔ)版本你硬拉到 16K 以上效果會(huì)明顯下降這個(gè)后面長上下文實(shí)測部分我會(huì)細(xì)說。第四是訓(xùn)練數(shù)據(jù)量大幅增加。Llama 3 用了超過 15T 的 token 做預(yù)訓(xùn)練是 Llama 2 的七倍多。數(shù)據(jù)量上去了模型的泛化能力和指令遵循能力確實(shí)有肉眼可見的提升。但這對(duì)我們做量化的影響是間接的——訓(xùn)練更充分的模型權(quán)重分布往往更集中量化的時(shí)候反而更友好一些。這個(gè)結(jié)論是我對(duì)比了幾組量化前后的困惑度得出的不一定普適但在我測的場景里成立。1.2 8B 和 70B 該怎么選顯存與效果的平衡選模型這件事核心就一句話看你的顯存和延遲要求。我把兩個(gè)版本在我手頭設(shè)備上的實(shí)測數(shù)據(jù)整理了一下方便你直接對(duì)照。模型版本精度權(quán)重顯存KV Cache (8K)單卡 24G 可行性典型場景8BFP16約 16G約 1.2G可行余量小本地開發(fā)、單輪問答8BFP8約 8G約 1.2G輕松可并發(fā)本地部署、多輪對(duì)話8BINT4約 5G約 1.2G很輕松邊緣設(shè)備、低配卡70BFP16約 140G約 10G不可行多卡服務(wù)器70BFP8約 70G約 10G需 2×48G 或 4×24G高質(zhì)量推理70BINT4約 40G約 10G2×24G 可跑高性價(jià)比部署這張表里的數(shù)字是我實(shí)際用nvidia-smi讀出來的不是理論計(jì)算值。理論計(jì)算權(quán)重顯存就是參數(shù)量乘以字節(jié)數(shù)8B 的 FP16 是 8×216G這個(gè)對(duì)得上。但實(shí)際運(yùn)行的時(shí)候還有 CUDA context、框架開銷、激活值這些所以 24G 卡跑 8B FP16 的時(shí)候?qū)嶋H占用會(huì)到 18G 到 20G留給 KV cache 和并發(fā)的空間就不多了。我的建議很直接如果你只有一張 24G 卡8B 的 FP8 是最舒服的選擇。權(quán)重只占 8G剩下的顯存足夠你開 8K 上下文再加幾個(gè)并發(fā)請(qǐng)求。70B 的話除非你有兩張以上的 24G 卡或者一張 48G 卡否則別硬上量化到 INT4 雖然能塞進(jìn)去但推理速度會(huì)掉得比較明顯而且 INT4 在長上下文下的質(zhì)量衰減比 FP8 嚴(yán)重不少這個(gè)后面會(huì)展開。注意這里說的 FP8 是指權(quán)重和激活都用 FP8 存儲(chǔ)和計(jì)算不是只量化權(quán)重。只量化權(quán)重的方案比如 W8A16顯存節(jié)省有限因?yàn)榧せ钪颠€是 FP16長上下文下激活值占用不小。2. FP8 量化原理、工具與實(shí)操配置2.1 FP8 為什么比 INT8 更適合大模型量化這件事說白了就是用更少的比特來表示原來的浮點(diǎn)數(shù)。INT8 是整數(shù)量化FP8 是浮點(diǎn)量化兩者最大的區(qū)別在于動(dòng)態(tài)范圍。INT8 能表示的范圍是 -128 到 127而且是均勻分布的。神經(jīng)網(wǎng)絡(luò)的權(quán)重和激活值分布往往是不均勻的大部分值集中在 0 附近少數(shù)值會(huì)跑到很大。INT8 要覆蓋這些大值就得把量化步長調(diào)大結(jié)果就是 0 附近的小值精度全丟了。FP8 有兩個(gè)常見格式E4M3 和 E5M2。E 是指數(shù)位M 是尾數(shù)位。E4M3 是 4 位指數(shù)、3 位尾數(shù)動(dòng)態(tài)范圍大概能到正負(fù) 448E5M2 是 5 位指數(shù)、2 位尾數(shù)動(dòng)態(tài)范圍更大但精度更低。大模型量化一般用 E4M3因?yàn)闄?quán)重的動(dòng)態(tài)范圍通常不需要 E5M2 那么大而尾數(shù)多一位對(duì)精度幫助更明顯。我用一個(gè)生活化的類比來解釋假設(shè)你要用一把尺子量東西INT8 就像一把刻度均勻但最大只能量到 1 米的尺子超過 1 米的東西你只能壓縮著量小東西的刻度就很粗。FP8 就像一把對(duì)數(shù)刻度的尺子小東西的刻度細(xì)大東西的刻度粗但整體能覆蓋的范圍更廣。對(duì)于權(quán)重這種大部分值很小、少數(shù)值很大的分布FP8 天然更合適。實(shí)測數(shù)據(jù)也支持這個(gè)判斷。我在 8B 模型上分別做了 INT8 和 FP8 的權(quán)重量化用同一組評(píng)測集測困惑度FP8 的困惑度上升大概在 0.05 到 0.1 之間INT8 大概在 0.15 到 0.3 之間。這個(gè)差距在長上下文場景下會(huì)更明顯因?yàn)殚L上下文對(duì)數(shù)值精度更敏感。2.2 量化工具選型為什么我最終用了這個(gè)方案市面上做量化的工具不少我前后試了三種路線說一下各自的坑。第一種是訓(xùn)練后量化PTQ的通用框架比如某些支持多種量化格式的庫。優(yōu)點(diǎn)是上手快幾行代碼就能跑。缺點(diǎn)是對(duì)于 FP8 的支持往往不完整很多框架的 FP8 只是把權(quán)重轉(zhuǎn)成 FP8 存儲(chǔ)計(jì)算的時(shí)候還是反量化回 FP16顯存省了但速度沒省甚至因?yàn)槎嗔宿D(zhuǎn)換操作還變慢了。我一開始就踩了這個(gè)坑以為跑通了 FP8結(jié)果一看顯存確實(shí)降了但吞吐量反而掉了 15%。第二種是推理引擎自帶的量化流程。這類方案通常和推理引擎深度綁定FP8 是真正用 FP8 的 Tensor Core 做計(jì)算的速度和顯存都能受益。但缺點(diǎn)是配置復(fù)雜對(duì)模型結(jié)構(gòu)有要求而且不同引擎的量化腳本不通用。我試的時(shí)候遇到的最大問題是校準(zhǔn)數(shù)據(jù)集的準(zhǔn)備引擎要求提供一批校準(zhǔn)樣本樣本選得不好量化后的質(zhì)量波動(dòng)很大。第三種是手動(dòng)指定量化層 引擎原生 FP8 計(jì)算。這是我最終采用的方案。核心思路是embedding 層和最后的輸出層保持 FP16 不量化中間的線性層全部轉(zhuǎn) FP8用引擎的 FP8 GEMM 做計(jì)算。這樣既保證了輸入輸出的精度又讓計(jì)算密集的部分享受 FP8 的加速。為什么 embedding 和輸出層不量化因?yàn)檫@兩層的參數(shù)量占比不小8B 模型里大概占 15% 到 20%但計(jì)算量占比很低。量化它們省不了多少顯存反而可能因?yàn)榫葥p失影響 token 的表示質(zhì)量。尤其是輸出層它直接決定下一個(gè) token 的概率分布量化誤差會(huì)被放大。我實(shí)測過把輸出層也量化困惑度直接漲了 0.5 以上得不償失。2.3 FP8 量化的完整實(shí)操步驟下面是我實(shí)際跑的流程你可以直接照著來。前提是你有一張支持 FP8 計(jì)算的卡算力 8.9 及以上驅(qū)動(dòng)和推理引擎的版本要匹配。第一步環(huán)境準(zhǔn)備與版本確認(rèn)。# 確認(rèn)顯卡算力8.9 以上才支持原生 FP8 nvidia-smi --query-gpucompute_cap --formatcsv # 確認(rèn)推理引擎版本建議用較新的穩(wěn)定版 pip show 推理引擎包名 | grep Version這一步很多人跳過結(jié)果跑的時(shí)候報(bào)一堆不支持的錯(cuò)。算力不夠的卡FP8 會(huì)回退到軟件模擬速度比 FP16 還慢不如直接上 INT4。第二步準(zhǔn)備校準(zhǔn)數(shù)據(jù)集。校準(zhǔn)數(shù)據(jù)集不用很大我用了 512 條樣本覆蓋了代碼、中文問答、英文長文、數(shù)學(xué)推理四類。每類 128 條長度控制在 512 到 2048 token 之間。為什么是這個(gè)配置因?yàn)樾?zhǔn)的目的是讓量化器看到各種激活值分布類別覆蓋比數(shù)量重要。我試過只用 128 條但類別齊全的效果比 1024 條但全是同類文本的好。# 校準(zhǔn)數(shù)據(jù)準(zhǔn)備的偽代碼結(jié)構(gòu) calib_samples [] for category in [code, zh_qa, en_long, math]: samples load_samples(category, count128, max_len2048) calib_samples.extend(samples) # 保存成引擎要求的格式 save_calib(calib_samples, calib.jsonl)第三步配置量化參數(shù)。這是最關(guān)鍵的一步參數(shù)選錯(cuò)了后面全白搭。我用的配置如下參數(shù)取值理由量化格式FP8 E4M3動(dòng)態(tài)范圍夠用精度比 E5M2 好量化范圍權(quán)重激活只量化權(quán)重省不了激活顯存排除層embedding, lm_head保護(hù)輸入輸出精度校準(zhǔn)算法最小化均方誤差比最大絕對(duì)值法更穩(wěn)校準(zhǔn)樣本數(shù)512類別覆蓋優(yōu)先于數(shù)量校準(zhǔn)算法這里多說一句。最大絕對(duì)值法MaxAbs是最簡單的取校準(zhǔn)集里激活值的最大絕對(duì)值作為量化范圍。但這個(gè)方法對(duì)離群值很敏感一個(gè)異常大的激活值會(huì)把整個(gè)量化范圍拉大導(dǎo)致其他值精度下降。最小化均方誤差MSE的方法會(huì)搜索一個(gè)最優(yōu)的截?cái)嚅撝蛋央x群值截掉實(shí)測困惑度比 MaxAbs 低 0.1 左右。第四步執(zhí)行量化并驗(yàn)證。# 執(zhí)行量化具體命令因引擎而異核心參數(shù)如下 推理引擎量化命令 \ --model ./llama3-8b \ --calib ./calib.jsonl \ --format fp8_e4m3 \ --exclude embedding,lm_head \ --output ./llama3-8b-fp8量化完之后一定要驗(yàn)證不能直接上生產(chǎn)。我的驗(yàn)證流程是三步先跑一遍困惑度對(duì)比再跑一組固定問題的生成對(duì)比最后跑長上下文壓力測試。困惑度上升控制在 0.1 以內(nèi)算合格生成對(duì)比看有沒有明顯的重復(fù)、截?cái)?、胡言亂語長上下文測試看 8K 位置的信息召回率。2.4 量化后的效果對(duì)比與踩坑記錄我把量化前后的關(guān)鍵指標(biāo)整理成表方便你判斷值不值得做。指標(biāo)FP16 原版FP8 量化版變化權(quán)重顯存16G8G-50%8K 推理顯存約 19G約 11G-42%吞吐量 (token/s)基準(zhǔn)40%明顯提升困惑度基準(zhǔn)0.08可接受長上下文召回基準(zhǔn)-2%基本無損吞吐量提升 40% 這個(gè)數(shù)字是在 batch size 為 4、輸入 2K、輸出 512 的條件下測的。FP8 的 Tensor Core 算力是 FP16 的兩倍但實(shí)際推理受限于內(nèi)存帶寬和 KV cache 讀寫所以達(dá)不到理論的兩倍。40% 是我反復(fù)測了幾次的穩(wěn)定值。踩過的坑說三個(gè)。第一個(gè)坑是校準(zhǔn)數(shù)據(jù)里混入了空樣本導(dǎo)致某一層的激活范圍估計(jì)成 0量化后那一層輸出全是 0模型直接廢了。排查的時(shí)候一層一層看輸出才找到費(fèi)了不少時(shí)間。所以校準(zhǔn)數(shù)據(jù)一定要做清洗空樣本、超長樣本、亂碼樣本都要過濾掉。第二個(gè)坑是KV cache 的量化。我一開始只量化了權(quán)重和激活KV cache 還是 FP16。后來想進(jìn)一步省顯存把 KV cache 也量化成 FP8結(jié)果長上下文下質(zhì)量掉得厲害8K 位置的召回率掉了 15% 以上。原因是 KV cache 里的值動(dòng)態(tài)范圍很大FP8 的精度不夠。所以 KV cache 量化要謹(jǐn)慎除非你用更精細(xì)的分組量化策略否則不建議。第三個(gè)坑是不同引擎的 FP8 實(shí)現(xiàn)差異。我在兩個(gè)引擎上跑了同樣的量化模型一個(gè)正常另一個(gè)輸出全是重復(fù) token。查了半天發(fā)現(xiàn)是其中一個(gè)引擎對(duì) FP8 的 scale 處理有問題需要手動(dòng)指定 scale 文件。所以換引擎的時(shí)候量化模型不一定能直接遷移要重新驗(yàn)證。實(shí)操心得量化完先別急著刪原版模型保留 FP16 版本作為對(duì)照。我一般會(huì)保留至少一周期間用真實(shí)業(yè)務(wù)請(qǐng)求做 A/B 對(duì)比確認(rèn)量化版沒有隱藏問題再清理。3. 長上下文實(shí)測8K 到底穩(wěn)不穩(wěn)3.1 長上下文測試方案設(shè)計(jì)長上下文這件事不能只看官方說的支持多少 token得自己測。我設(shè)計(jì)了一套測試方案核心是三個(gè)維度信息召回、位置一致性、長文生成質(zhì)量。信息召回是經(jīng)典的大海撈針測試。在一段長文本的隨機(jī)位置插入一個(gè)特定的事實(shí)比如某個(gè)項(xiàng)目的截止日期是 3 月 15 日然后問模型這個(gè)事實(shí)是什么。我測了 1K、2K、4K、8K 四個(gè)長度每個(gè)長度測 20 次插入位置隨機(jī)分布。位置一致性是看模型對(duì)文本開頭、中間、結(jié)尾的信息處理是否一致。很多模型有中間遺忘的問題就是開頭和結(jié)尾的信息記得住中間的信息容易丟。我用的方法是把同一個(gè)問題分別放在文本的開頭、中間、結(jié)尾看回答是否一致。長文生成質(zhì)量是讓模型基于一篇 6K 到 8K 的長文做摘要或問答看輸出是否連貫、有沒有事實(shí)錯(cuò)誤、有沒有重復(fù)。這個(gè)維度比較主觀我找了兩個(gè)人一起打分取平均。測試用的文本我選了技術(shù)文檔、新聞報(bào)道、小說片段三類每類準(zhǔn)備了幾篇。為什么選這三類因?yàn)榧夹g(shù)文檔結(jié)構(gòu)清晰、新聞報(bào)道事實(shí)密集、小說片段敘事連貫?zāi)芨采w不同的信息組織方式。3.2 實(shí)測數(shù)據(jù)不同長度下的表現(xiàn)先看信息召回的測試結(jié)果這是最能說明問題的數(shù)據(jù)。上下文長度召回率平均響應(yīng)時(shí)間顯存占用1K100%0.8s約 9G2K100%1.2s約 9.5G4K95%2.1s約 10.5G8K85%3.8s約 11.5G這個(gè)數(shù)據(jù)是 FP8 量化版跑出來的。可以看到 4K 是個(gè)分水嶺4K 以內(nèi)召回率基本無損4K 到 8K 開始下降8K 的時(shí)候掉到 85%。85% 是什么概念20 次測試?yán)镉?3 次沒找到。我看了那 3 次失敗的案例有 2 次是插入位置在文本正中間偏后的位置1 次是插入的事實(shí)和周圍文本的語義比較接近可能被模型混淆了。位置一致性的結(jié)果更有意思。我把同一個(gè)問題放在開頭、中間、結(jié)尾三個(gè)位置測了 10 組結(jié)果如下位置回答正確率備注開頭98%基本沒問題中間82%明顯下降結(jié)尾96%接近開頭中間位置的正確率明顯低于兩端這就是典型的中間遺忘。而且這個(gè)現(xiàn)象在 4K 以上更明顯8K 的時(shí)候中間位置掉到了 75%。所以如果你要用長上下文做信息提取重要的信息盡量放在開頭或結(jié)尾別放在中間。長文生成質(zhì)量方面8K 輸入下的摘要任務(wù)我打分的平均分是 7.5 分滿分 10主要扣分點(diǎn)是偶爾會(huì)漏掉一些細(xì)節(jié)以及段落之間的過渡有時(shí)候會(huì)重復(fù)。6K 以下的話能到 8.5 分左右。問答任務(wù)的表現(xiàn)比摘要好因?yàn)閱柎鹩忻鞔_的目標(biāo)模型不容易跑偏。3.3 長上下文優(yōu)化的幾個(gè)實(shí)用技巧基于上面的實(shí)測我總結(jié)了幾個(gè)能實(shí)際提升長上下文表現(xiàn)的技巧。第一個(gè)技巧是位置重排。如果你有一段很長的參考文本重要的信息不要按原順序放把最關(guān)鍵的信息提到開頭次關(guān)鍵的放結(jié)尾中間的放次要信息。這個(gè)技巧在 RAG 場景特別有用檢索回來的文檔不要按相似度排序直接拼而是把最相似的放開頭和結(jié)尾。第二個(gè)技巧是分段處理加匯總。如果文本超過 8K不要硬塞分成多段分別處理然后再用一個(gè)匯總步驟把結(jié)果合并。我實(shí)測過兩段 4K 分別處理再匯總效果比一段 8K 直接處理好召回率能高 10 個(gè)百分點(diǎn)左右。代價(jià)是多一次推理調(diào)用延遲增加但質(zhì)量提升值得。第三個(gè)技巧是控制輸出長度。長上下文下模型的輸出容易發(fā)散生成很長的內(nèi)容但質(zhì)量下降。我一般會(huì)把 max_new_tokens 限制在 512 以內(nèi)讓模型聚焦在核心信息上。如果確實(shí)需要長輸出就分段生成每段之間加一個(gè)銜接提示。第四個(gè)技巧是溫度調(diào)低。長上下文下溫度高容易導(dǎo)致模型在長文本里迷路生成和上下文無關(guān)的內(nèi)容。我一般把 temperature 設(shè)在 0.3 到 0.5 之間top_p 設(shè)在 0.9。這個(gè)配置在信息提取和摘要任務(wù)上比較穩(wěn)。注意這些技巧是基于 8B 模型的實(shí)測70B 模型的表現(xiàn)會(huì)更好一些中間遺忘的問題沒那么嚴(yán)重但趨勢(shì)是一樣的。如果你用的是 70B可以把上面這些技巧當(dāng)作錦上添花不是必須。4. 常見問題與排查技巧實(shí)錄4.1 量化與部署階段的典型問題這一部分是我在實(shí)際操作中遇到的各種報(bào)錯(cuò)和異常整理成速查表方便你遇到問題時(shí)快速定位。問題現(xiàn)象可能原因排查方法解決方案量化后輸出全是重復(fù) tokenscale 文件缺失或錯(cuò)誤檢查引擎日志里的 scale 加載記錄手動(dòng)指定 scale 文件或重新校準(zhǔn)量化后顯存沒降只量化了權(quán)重激活還是 FP16用 nvidia-smi 看實(shí)際占用確認(rèn)量化配置包含激活量化長上下文輸出截?cái)鄊ax_position_embeddings 設(shè)置不對(duì)檢查模型配置文件改成 8192 或?qū)?yīng)值推理速度反而變慢顯卡不支持原生 FP8走了模擬查算力是否 8.9 以上換 INT4 或升級(jí)硬件校準(zhǔn)后困惑度暴漲校準(zhǔn)數(shù)據(jù)有臟樣本逐層檢查輸出清洗校準(zhǔn)數(shù)據(jù)重新量化多輪對(duì)話后質(zhì)量下降KV cache 溢出監(jiān)控 KV cache 占用限制對(duì)話輪數(shù)或縮短歷史這張表里的每一條我都是實(shí)際遇到過的。重點(diǎn)說兩個(gè)。輸出重復(fù) token 這個(gè)問題我第一次遇到的時(shí)候以為是模型壞了重新下載了一遍模型還是這樣。后來看引擎日志才發(fā)現(xiàn)是 FP8 的 scale 文件沒加載上引擎用了默認(rèn)的 scale導(dǎo)致某些層的輸出被放大到溢出。解決方法是顯式指定 scale 文件路徑或者重新跑一遍校準(zhǔn)生成正確的 scale。顯存沒降這個(gè)問題更隱蔽。有些量化流程默認(rèn)只量化權(quán)重激活值還是 FP16。這種情況下權(quán)重顯存確實(shí)降了但激活值在長上下文下占用不小整體顯存下降不明顯。你要看量化配置里有沒有quantize_activation: true這樣的選項(xiàng)沒有的話就是只量化了權(quán)重。4.2 長上下文場景的排查思路長上下文的問題排查和普通推理不太一樣因?yàn)閱栴}往往不是報(bào)錯(cuò)而是效果不對(duì)。我總結(jié)了一套排查流程。第一步是確認(rèn)輸入 token 數(shù)。很多人以為自己的輸入是 8K實(shí)際上 tokenizer 之后可能只有 5K 或者超過 8K 被截?cái)嗔?。?tokenizer 數(shù)一下實(shí)際 token 數(shù)確認(rèn)在模型支持范圍內(nèi)。第二步是檢查位置編碼。如果模型配置文件里的max_position_embeddings是 8192但你輸入了 10K多出來的部分位置編碼會(huì)出問題。要么截?cái)嘁从弥С滞馔频奈恢镁幋a方案。第三步是分段定位。如果長上下文效果不好把輸入切成幾段分別測看是哪一段的問題。我遇到過一次是某一段文本里有特殊字符導(dǎo)致 tokenizer 產(chǎn)生了異常 token影響了后續(xù)所有位置的處理。分段測就能快速定位到問題段落。第四步是對(duì)比 FP16 和 FP8。如果 FP8 版長上下文效果差用 FP16 版跑同樣的輸入對(duì)比。如果 FP16 正常而 FP8 不正常那就是量化的問題重點(diǎn)查 KV cache 和激活的量化配置。4.3 幾個(gè)容易被忽略的細(xì)節(jié)最后說幾個(gè)細(xì)節(jié)都是那種不知道就踩坑知道了就一句話的事。tokenizer 的 padding 方向。Llama 3 的 tokenizer 默認(rèn)是左 padding但有些框架加載的時(shí)候會(huì)改成右 padding。左 padding 對(duì)生成任務(wù)更友好因?yàn)樯墒菑淖詈笠粋€(gè) token 開始的。如果你發(fā)現(xiàn)生成結(jié)果不對(duì)檢查一下 padding 方向。attention mask 的處理。長上下文下attention mask 的構(gòu)造很關(guān)鍵。如果 mask 構(gòu)造錯(cuò)了模型會(huì)看到不該看到的位置導(dǎo)致輸出混亂。尤其是 batch 推理的時(shí)候不同樣本的長度不一樣mask 要按實(shí)際長度構(gòu)造不能用統(tǒng)一的長度。顯存碎片。長時(shí)間運(yùn)行之后顯存會(huì)產(chǎn)生碎片導(dǎo)致明明有足夠顯存卻分配失敗。我一般會(huì)設(shè)置PYTORCH_CUDA_ALLOC_CONF環(huán)境變量來優(yōu)化顯存分配策略具體值根據(jù)實(shí)際情況調(diào)常用的是expandable_segments:True。模型加載的 dtype。加載 FP8 量化模型的時(shí)候要確認(rèn)加載的 dtype 和量化格式匹配。有些框架默認(rèn)用 FP16 加載會(huì)把 FP8 權(quán)重反量化成 FP16顯存又回去了。加載的時(shí)候顯式指定 dtype 為 FP8 對(duì)應(yīng)的類型。實(shí)操心得我習(xí)慣在部署前跑一個(gè)冒煙測試用固定的幾個(gè)輸入跑一遍把輸出和預(yù)期對(duì)比。這個(gè)測試腳本很短但能擋住 80% 的配置錯(cuò)誤。每次改配置或者換環(huán)境都跑一遍省了很多排查時(shí)間。5. 性能調(diào)優(yōu)與擴(kuò)展玩法5.1 推理性能的幾個(gè)調(diào)優(yōu)方向量化只是第一步真正要讓推理跑得舒服還得調(diào)幾個(gè)參數(shù)。我按收益從高到低排一下。batch size 是最直接的。在顯存允許的前提下增大 batch size 能顯著提升吞吐量。8B FP8 在 24G 卡上8K 上下文batch size 開到 4 的時(shí)候吞吐量比 batch size 1 高了大概 2.5 倍。但 batch size 不是越大越好超過某個(gè)點(diǎn)之后延遲會(huì)明顯增加而且顯存可能不夠。我的經(jīng)驗(yàn)是找到吞吐量和延遲的平衡點(diǎn)一般 batch size 4 到 8 之間。KV cache 的管理策略。長上下文下 KV cache 占用不小可以用分頁管理或者量化來優(yōu)化。分頁管理是把 KV cache 分成固定大小的塊按需分配減少碎片。量化就是前面說的 FP8 KV cache但要謹(jǐn)慎質(zhì)量會(huì)掉。連續(xù)批處理。如果是在線服務(wù)連續(xù)批處理能讓新請(qǐng)求插到正在處理的 batch 里提升 GPU 利用率。這個(gè)對(duì)延遲敏感的場景幫助很大但配置復(fù)雜一些需要引擎支持。算子融合。一些推理引擎會(huì)自動(dòng)做算子融合把多個(gè)小算子合并成一個(gè)大算子減少 kernel launch 開銷。這個(gè)一般不用手動(dòng)配置選對(duì)引擎就行。5.2 量化模型的擴(kuò)展應(yīng)用FP8 量化后的模型除了直接做推理還有幾個(gè)擴(kuò)展玩法。一是做蒸餾的教師模型。量化后的模型雖然精度略降但作為教師模型蒸餾小模型還是夠用的。而且量化模型推理快蒸餾的效率更高。我試過用 FP8 的 8B 模型蒸餾一個(gè) 1B 的小模型效果比用 FP16 教師差不了多少但蒸餾速度快了 30% 多。二是做多模型集成。量化后顯存占用低可以同時(shí)加載多個(gè)不同微調(diào)版本的模型做集成推理。比如一個(gè)擅長代碼的微調(diào)版一個(gè)擅長中文的微調(diào)版根據(jù)輸入路由到不同的模型。這個(gè)玩法在顯存緊張的時(shí)候特別有用。三是邊緣部署。FP8 的 8B 模型權(quán)重只有 8G加上 KV cache 和框架開銷12G 顯存的設(shè)備就能跑。如果再量化到 INT48G 顯存的設(shè)備也能跑。這讓本地部署的門檻降低了不少。5.3 后續(xù)可以深入的方向這篇東西覆蓋了精讀、量化和長上下文三個(gè)核心點(diǎn)但還有幾個(gè)方向值得繼續(xù)挖。一個(gè)是更細(xì)粒度的量化策略。比如按層敏感度分配不同的量化精度敏感的層用 FP8不敏感的層用 INT4。這個(gè)需要做層敏感度分析工作量不小但收益可能很可觀。另一個(gè)是長上下文的外推方案。原生 8K 不夠用的話可以用位置插值或者 NTK 感知的縮放方案把上下文外推到 16K 甚至 32K。但這些方案都有質(zhì)量損失需要實(shí)測權(quán)衡。還有一個(gè)是推理引擎的深度優(yōu)化。不同的引擎對(duì) FP8 的支持程度不一樣有的引擎有專門的 FP8 kernel 優(yōu)化性能差距可能到 20% 以上。多試幾個(gè)引擎選最適合你場景的。我個(gè)人在實(shí)際操作中的體會(huì)是Llama 3 這代模型在 8B 這個(gè)尺寸上做到了很好的平衡FP8 量化之后在 24G 卡上跑 8K 上下文是當(dāng)前性價(jià)比很高的方案。長上下文雖然 8K 之后有衰減但配合位置重排和分段處理實(shí)際可用性還是不錯(cuò)的。量化過程中最花時(shí)間的不是跑腳本而是校準(zhǔn)數(shù)據(jù)的準(zhǔn)備和量化后的驗(yàn)證這兩步做扎實(shí)了后面就順了。