測(cè)與性能邊界分析)
1. 8GB 顯卡跑 27B 模型這事到底靠不靠譜先把結(jié)論擺在前面能跑但跑完之后你大概率會(huì)和我一樣把它放進(jìn)技術(shù)驗(yàn)證成功、日常使用放棄的文件夾里。Ternary Bonsai 2 27B 這個(gè)模型最近在圈子里討論度不低核心賣(mài)點(diǎn)就一個(gè)——三元量化也就是權(quán)重被壓到只有三種取值狀態(tài)配合 llama.cpp 的推理后端理論上能把一個(gè) 270 億參數(shù)級(jí)別的模型塞進(jìn) 8GB 顯存里跑起來(lái)。注意我說(shuō)的是塞進(jìn)顯存不是流暢運(yùn)行這兩者之間的差距就是這篇博文想聊清楚的東西。我自己手上是一張 8GB 顯存的卡平時(shí)跑 7B、13B 的量化模型算是家常便飯Q4_K_M 級(jí)別的 13B 大概占 7GB 出頭勉強(qiáng)能全量上卡。27B 這個(gè)體量按常規(guī) Q4 量化算光權(quán)重就要 13GB 到 15GB8GB 卡想都別想。三元量化的意義就在這兒把每個(gè)權(quán)重從 16 位浮點(diǎn)壓到接近 1.58 位的信息量權(quán)重體積直接砍到原來(lái)的十分之一左右27B 的模型文件能壓到 7GB 上下這才有了8GB 顯卡硬塞的可能性。但能塞進(jìn)去和能用是兩碼事。這篇文章我會(huì)把整個(gè)折騰過(guò)程拆開(kāi)講三元量化到底是什么原理、llama.cpp 怎么加載這種模型、8GB 卡上實(shí)際跑起來(lái)是什么體驗(yàn)、哪些參數(shù)決定了你能不能跑動(dòng)、以及為什么我最后沒(méi)把它當(dāng)成日常主力。適合手里有中低端顯卡、想搞清楚量化推理邊界在哪的朋友也適合單純好奇27B 塞 8GB這個(gè)噱頭背后有多少水分的人??赐昴阒辽倌芘袛噙@事值不值得你花一個(gè)下午去折騰。2. 三元量化到底是個(gè)什么東西2.1 從 FP16 到三值權(quán)重壓縮的極限在哪要理解 Ternary Bonsai 2 27B 為什么能塞進(jìn) 8GB得先搞清楚三元這個(gè)詞的分量。常規(guī)模型權(quán)重是 FP16每個(gè)參數(shù)占 2 字節(jié)主流的 Q4 量化是 4 位每個(gè)參數(shù)占 0.5 字節(jié)而三元量化每個(gè)權(quán)重只有三種可能取值——通常是 -1、0、1理論上每個(gè)參數(shù)只需要 log2(3) ≈ 1.58 位。這就是為什么它能把體積壓到 Q4 的三分之一左右。這里有個(gè)關(guān)鍵點(diǎn)很多人會(huì)誤解三元量化不是簡(jiǎn)單地把權(quán)重四舍五入到三個(gè)值就完事。如果直接粗暴地截?cái)嗄P途葧?huì)崩得一塌糊涂輸出全是胡言亂語(yǔ)。真正能用的三元模型訓(xùn)練階段就要做量化感知訓(xùn)練QAT讓模型在訓(xùn)練時(shí)就知道自己最終會(huì)被壓成三值從而把關(guān)鍵信息擠到那三種狀態(tài)里。Ternary Bonsai 2 27B 屬于這類經(jīng)過(guò)專門(mén)訓(xùn)練的三元模型不是拿現(xiàn)成模型事后硬壓的產(chǎn)物這是它能保持基本可用性的前提。那 1.58 位是怎么算出來(lái)的信息論里三種等概率狀態(tài)的信息熵是 log2(3)約等于 1.5849。但實(shí)際存儲(chǔ)時(shí)不可能真的用 1.58 位去存工程上通常用 2 位來(lái)存一個(gè)權(quán)重浪費(fèi)一點(diǎn)空間換實(shí)現(xiàn)簡(jiǎn)單或者用更緊湊的打包方式把多個(gè)三值權(quán)重塞進(jìn)一個(gè)字節(jié)。llama.cpp 對(duì)這類模型的支持走的是專門(mén)的量化類型加載時(shí)會(huì)做解包。所以你在文件系統(tǒng)里看到的模型大小和理論上的 1.58 位會(huì)有出入這是正常的。2.2 為什么是 llama.cpp 而不是別的推理框架熱詞里 llama.cpp 和 CUDA 同時(shí)出現(xiàn)這不是巧合。目前對(duì)三元量化模型支持最成熟的推理后端就是 llama.cpp原因有幾個(gè)。第一llama.cpp 的量化體系本來(lái)就是圍繞極致壓縮 CPU/GPU 混合推理設(shè)計(jì)的它支持從 Q2 到 Q8 一整條量化譜系擴(kuò)展到三元量化是順理成章的事。第二llama.cpp 的 GGUF 格式對(duì)自定義量化類型很友好加一種新的 tensor 類型不需要?jiǎng)诱麄€(gè)加載框架。第三也是最實(shí)際的——它能在顯存不夠時(shí)自動(dòng)把部分層卸載到內(nèi)存用 CPU 補(bǔ)算這對(duì) 8GB 卡跑 27B 是剛需。相比之下主流的 PyTorch transformers 路線對(duì)三元量化的支持要弱得多你得自己寫(xiě)反量化 kernel還得處理 CUDA 上的算子兼容問(wèn)題。熱詞里那一堆cuda安裝cuda版本cuda toolkit的搜索其實(shí)反映了很多人在這個(gè)環(huán)節(jié)卡住——想用 GPU 加速結(jié)果光環(huán)境配置就耗掉半天。llama.cpp 的好處是它把 CUDA 后端封裝得相對(duì)干凈編譯時(shí)開(kāi)-DGGML_CUDAON就能用上顯卡不用你去手動(dòng)裝一堆 CUDA 組件。提示如果你只是想驗(yàn)證三元模型能不能跑優(yōu)先用 llama.cpp 的預(yù)編譯版本或者官方 release別一上來(lái)就自己編譯 CUDA 后端。編譯環(huán)節(jié)的坑足夠單獨(dú)寫(xiě)一篇文章。2.3 三元模型的精度代價(jià)省下來(lái)的空間從哪來(lái)天下沒(méi)有免費(fèi)的午餐。三元量化把體積壓到十分之一代價(jià)必然是精度損失。這里要區(qū)分兩個(gè)概念困惑度perplexity上升和實(shí)際可用性下降。前者是客觀指標(biāo)三元模型的困惑度通常比同規(guī)模的 Q4 模型高不少后者是主觀體驗(yàn)表現(xiàn)為模型更容易跑偏、邏輯鏈條更短、復(fù)雜推理任務(wù)上翻車(chē)率更高。我實(shí)測(cè)下來(lái)的感受是三元 27B 在簡(jiǎn)單問(wèn)答、文本改寫(xiě)、信息抽取這類任務(wù)上表現(xiàn)大概相當(dāng)于一個(gè) Q4 量化的 7B 到 13B 模型但一旦涉及多步推理、代碼生成、長(zhǎng)上下文理解它的短板就暴露得很明顯。換句話說(shuō)你付出了 27B 的推理成本哪怕壓縮后計(jì)算量還是 27B 級(jí)別的換來(lái)的效果可能還不如一個(gè)老老實(shí)實(shí)的 13B Q4。這就是我標(biāo)題里說(shuō)大概率不會(huì)真用的核心原因——性價(jià)比不劃算。3. 8GB 顯卡上的實(shí)操?gòu)沫h(huán)境到跑通3.1 環(huán)境準(zhǔn)備CUDA 版本和 llama.cpp 編譯先說(shuō)環(huán)境。我用的是一張 8GB 顯存的卡驅(qū)動(dòng)版本比較新CUDA 用的是 12.x 系列。這里有個(gè)經(jīng)驗(yàn)llama.cpp 對(duì) CUDA 版本的要求沒(méi)那么苛刻只要你的驅(qū)動(dòng)支持編譯時(shí)它能自己找到合適的 toolkit。熱詞里很多人搜4060ti 支持的 cuda 版本gtx1070 cuda 版本其實(shí)沒(méi)必要死磕某個(gè)特定版本裝一個(gè)和驅(qū)動(dòng)匹配的就行。編譯 llama.cpp 的 CUDA 后端核心命令大概是這樣git clone https://github.com/ggerganov/llama.cpp cd llama.cpp cmake -B build -DGGML_CUDAON cmake --build build --config Release -j編譯過(guò)程中最常見(jiàn)的坑是找不到 CUDA toolkit報(bào)錯(cuò)類似Could NOT find CUDAToolkit。這時(shí)候檢查兩個(gè)東西nvcc --version能不能正常輸出以及CUDA_HOME環(huán)境變量有沒(méi)有指向正確的路徑。如果用的是 Windows 上的 WSL2還要確認(rèn) WSL 里的 CUDA 驅(qū)動(dòng)是透?jìng)鞯膭e在 WSL 里再裝一遍顯卡驅(qū)動(dòng)那樣會(huì)沖突。注意編譯時(shí)-j后面的數(shù)字別開(kāi)太大CUDA 編譯很吃內(nèi)存我 16GB 內(nèi)存的機(jī)器開(kāi)-j8直接 OOM 過(guò)。穩(wěn)妥點(diǎn)用-j4。3.2 模型下載與顯存分配策略模型文件從對(duì)應(yīng)的發(fā)布渠道拿到 GGUF 格式后第一件事是看文件大小。Ternary Bonsai 2 27B 的三元量化版本文件大概在 7GB 上下。這個(gè)數(shù)字很關(guān)鍵因?yàn)樗鼪Q定了你的顯存策略。8GB 顯存系統(tǒng)和其他進(jìn)程要占掉 1GB 左右實(shí)際可用大概 7GB。模型文件 7GB如果全部加載到顯存加上 KV cache 和計(jì)算中間變量肯定爆。所以必須用部分卸載策略把一部分層放在 GPU 上剩下的放內(nèi)存用 CPU 算。llama.cpp 里控制這個(gè)的參數(shù)是-nglnumber of GPU layers。我的做法是從小往大試。先-ngl 10看顯存占用和速度然后逐步加到 20、30直到顯存快滿為止。27B 模型通常有 40 到 60 層8GB 卡上能卸載的層數(shù)大概在 20 到 30 層之間具體取決于你的 KV cache 設(shè)置。下面是我實(shí)測(cè)的一組數(shù)據(jù)GPU 層數(shù) (-ngl)顯存占用生成速度 (tokens/s)體驗(yàn)10約 3.5GB2-3慢但穩(wěn)定20約 5.5GB4-5可接受28約 6.8GB6-7接近上限32爆顯存-失敗可以看到即使把能卸載的層都放上去速度也就 6-7 tokens/s。這個(gè)速度什么概念你打一句話等它一個(gè)字一個(gè)字往外蹦讀起來(lái)比它生成得還快。日常對(duì)話勉強(qiáng)能用長(zhǎng)文本生成就是折磨。3.3 關(guān)鍵參數(shù)KV cache 和上下文長(zhǎng)度除了-ngl還有兩個(gè)參數(shù)直接決定你能不能跑起來(lái)上下文長(zhǎng)度-c和 KV cache 的量化。上下文越長(zhǎng)KV cache 占的顯存越多。默認(rèn)的 FP16 KV cache 在長(zhǎng)上下文下非常吃顯存8GB 卡上必須做量化。llama.cpp 支持--cache-type-k和--cache-type-v參數(shù)可以分別指定 K 和 V 的緩存類型。我一般設(shè)成q8_0或者q4_0。設(shè)成q4_0能省不少顯存但對(duì)輸出質(zhì)量有輕微影響。實(shí)測(cè)下來(lái)q8_0是質(zhì)量和顯存的平衡點(diǎn)。./build/bin/llama-cli \ -m ternary-bonsai-2-27b.gguf \ -ngl 28 \ -c 4096 \ --cache-type-k q8_0 \ --cache-type-v q8_0 \ -p 你的提示詞上下文我建議先設(shè) 4096跑通了再往上加。設(shè) 8192 的話KV cache 會(huì)多吃 1GB 多顯存很可能就把你從能跑推到爆顯存。這里有個(gè)反直覺(jué)的點(diǎn)上下文長(zhǎng)度對(duì)顯存的影響是非線性的因?yàn)樽⒁饬C(jī)制的計(jì)算中間變量也隨長(zhǎng)度增長(zhǎng)。所以別看著 4096 能跑就以為 8192 只是多一點(diǎn)點(diǎn)。4. 實(shí)際體驗(yàn)?zāi)芘艿珵槭裁次也幌胗?.1 速度與質(zhì)量的真實(shí)權(quán)衡跑通之后我做了幾組對(duì)比測(cè)試拿 Ternary Bonsai 2 27B 和一個(gè)常規(guī)的 13B Q4 模型比。任務(wù)包括中文問(wèn)答、英文摘要、簡(jiǎn)單代碼補(bǔ)全、多輪對(duì)話。結(jié)果是在中文問(wèn)答上三元 27B 的回答更啰嗦信息密度反而低經(jīng)常繞圈子英文摘要任務(wù)上兩者接近但三元模型偶爾會(huì)漏掉關(guān)鍵信息代碼補(bǔ)全上三元模型明顯吃力生成的代碼經(jīng)常有語(yǔ)法錯(cuò)誤或者邏輯不完整多輪對(duì)話里三元模型更容易忘記前面說(shuō)過(guò)的話上下文保持能力弱。速度上13B Q4 在 8GB 卡上能全量卸載跑到 20 tokens/s體驗(yàn)流暢。三元 27B 只有 6-7 tokens/s還得忍受部分層在 CPU 上算帶來(lái)的延遲波動(dòng)。這個(gè)差距在日常使用中是壓倒性的——你不會(huì)愿意為了一個(gè)效果更差的模型去忍受三倍以上的等待時(shí)間。4.2 那些讓人抓狂的細(xì)節(jié)問(wèn)題除了速度和質(zhì)量還有幾個(gè)細(xì)節(jié)讓我最終放棄把它當(dāng)主力。第一是首 token 延遲因?yàn)椴糠謱釉?CPU 上第一次生成前的等待特別長(zhǎng)有時(shí)候要等五六秒才開(kāi)始出字。第二是顯存波動(dòng)跑一段時(shí)間后顯存占用會(huì)慢慢爬升可能是內(nèi)存碎片或者緩存沒(méi)釋放干凈跑久了偶爾會(huì) OOM。第三是溫度參數(shù)敏感三元模型對(duì) temperature 和 top_p 的變化比常規(guī)模型敏感得多調(diào)不好就容易輸出重復(fù)內(nèi)容或者直接崩壞。實(shí)操心得如果你非要試三元模型把 temperature 設(shè)在 0.6 到 0.8 之間top_p 設(shè) 0.9repeat_penalty 稍微調(diào)高到 1.1。這套參數(shù)是我試了十幾組之后相對(duì)穩(wěn)定的組合但依然不能保證每次都正常。4.3 什么場(chǎng)景下它還有點(diǎn)用說(shuō)了這么多缺點(diǎn)也得客觀講它有用的地方。三元模型最大的價(jià)值在于極端資源受限下的可行性驗(yàn)證。比如你只有 8GB 顯存又想跑一個(gè)參數(shù)量看起來(lái)很大的模型來(lái)做實(shí)驗(yàn)、寫(xiě)論文、做 demo三元量化給了你一個(gè)能跑起來(lái)的選項(xiàng)。另外在離線環(huán)境、邊緣設(shè)備上三元模型的體積優(yōu)勢(shì)是實(shí)打?qū)嵉?GB 的文件比 15GB 的 Q4 好傳輸、好部署。但如果你只是想要一個(gè)日常能用的本地模型我的建議很直接8GB 卡老老實(shí)實(shí)跑 7B 或 13B 的 Q4/Q5 量化速度和質(zhì)量都比硬塞 27B 三元模型強(qiáng)。省下來(lái)的折騰時(shí)間夠你多跑幾百條推理了。5. 踩過(guò)的坑和排查清單5.1 編譯與加載階段的常見(jiàn)報(bào)錯(cuò)折騰過(guò)程中我遇到不少報(bào)錯(cuò)整理成表格方便對(duì)照排查報(bào)錯(cuò)信息原因解決方法CUDA error: out of memory顯存不夠?qū)訑?shù)設(shè)太多降低-ngl或減小-cunknown model architecturellama.cpp 版本太舊更新到最新版重新編譯failed to load model模型文件損壞或格式不對(duì)校驗(yàn)文件哈希確認(rèn)是 GGUFCUDA driver version is insufficient驅(qū)動(dòng)太舊更新顯卡驅(qū)動(dòng)生成速度極慢1 tokens/s層幾乎全在 CPU增大-ngl檢查 CUDA 是否啟用其中unknown model architecture這個(gè)坑我踩得最冤。三元模型用的量化類型比較新老版本的 llama.cpp 不認(rèn)識(shí)加載直接報(bào)錯(cuò)。解決辦法就是拉最新代碼重新編譯別用半年前的 release。5.2 顯存優(yōu)化的幾個(gè)野路子除了常規(guī)參數(shù)還有幾個(gè)偏方可以榨出一點(diǎn)顯存。第一關(guān)掉桌面環(huán)境或者減少后臺(tái)程序尤其是瀏覽器Chrome 開(kāi)幾個(gè)標(biāo)簽就能吃掉 1GB 顯存。第二用--no-mmap有時(shí)候反而能減少內(nèi)存碎片但會(huì)增加加載時(shí)間看情況取舍。第三把--cache-type-v設(shè)成q4_0而 K 保持q8_0V 緩存對(duì)精度的影響比 K 小這樣能再省幾百 MB。還有一個(gè)容易被忽略的點(diǎn)batch size。llama.cpp 的-b參數(shù)控制批處理大小默認(rèn)值在顯存緊張時(shí)可能偏大。設(shè)成 128 或 256 能降低峰值顯存代價(jià)是吞吐量下降。在 8GB 卡上我一般設(shè)-b 256。5.3 三元模型值不值得折騰我的判斷標(biāo)準(zhǔn)最后說(shuō)說(shuō)我的判斷邏輯。判斷一個(gè)模型值不值得用我會(huì)看三個(gè)指標(biāo)速度是否超過(guò)閱讀速度大概 10 tokens/s 是底線、質(zhì)量是否達(dá)到任務(wù)要求、資源占用是否可持續(xù)。Ternary Bonsai 2 27B 在這三項(xiàng)上第一項(xiàng)不達(dá)標(biāo)6-7 tokens/s第二項(xiàng)勉強(qiáng)簡(jiǎn)單任務(wù)可以復(fù)雜任務(wù)不行第三項(xiàng)勉強(qiáng)顯存吃緊跑久了會(huì) OOM。三項(xiàng)里兩項(xiàng)勉強(qiáng)一項(xiàng)不達(dá)標(biāo)結(jié)論就很清楚了。它適合的是我就是要驗(yàn)證這個(gè)技術(shù)路線的場(chǎng)景而不是我需要一個(gè)能干活的模型的場(chǎng)景。這兩者的區(qū)別決定了你會(huì)不會(huì)在跑通之后像我一樣把它歸檔然后繼續(xù)用回那個(gè)老老實(shí)實(shí)的 13B Q4。如果你手里是 12GB 或 16GB 的卡情況會(huì)好很多三元 27B 可能能全量卸載速度上到 15 tokens/s 以上那時(shí)候它的性價(jià)比就值得重新評(píng)估了。但 8GB 這個(gè)檔位我的經(jīng)驗(yàn)是別跟硬件較勁選對(duì)模型規(guī)模比硬塞大模型重要得多。