模型放得下嗎?從體量計算到量化壓縮實戰(zhàn))
1. 先把體量賬算清楚端側(cè)設(shè)備的容量天花板在哪1.1 參數(shù)量、精度和文件大小的換算關(guān)系標題里問“端側(cè)本地模型放得下嗎”這問題沒法用一句“能”或者“不能”回答因為“模型規(guī)模”和“能不能放下”之間隔著好幾層換算。很多人看到“XX億參數(shù)”就緊張其實先要搞懂一個最小單元一個參數(shù)在內(nèi)存里占多少個字節(jié)。同一個模型用不同精度存儲大小差得很遠。FP32單精度浮點每個參數(shù)占 4 字節(jié)FP16半精度浮點每個參數(shù)占 2 字節(jié)INT88比特整數(shù)每個參數(shù)占 1 字節(jié)常見的 4bit 量化每個參數(shù)約 0.5 字節(jié)所以一個 10 億參數(shù)1B的模型原始 FP32 權(quán)重是 4GB轉(zhuǎn)成 FP16 是 2GB量化到 INT8 是 1GB再激進一點壓到 4bit 只剩約 500MB。這個換算關(guān)系是整個話題的地基后面所有討論都建立在它上面。很多人只盯“文件大小”但真正決定能不能跑起來的是“運行時峰值內(nèi)存”。權(quán)重文件能塞進存儲不代表推理時能塞進內(nèi)存。手機 App 拿到一個 1GB 的模型文件加載時權(quán)重、激活值、KV Cache自回歸模型里的緩存、中間特征圖是同時在內(nèi)存里的實際占用往往是文件大小的 1.5 到 2 倍。1.2 端側(cè)設(shè)備的“三堵墻”存儲墻、內(nèi)存墻、算力墻判斷一個模型能不能本地跑要同時看三堵墻。第一堵墻是存儲。普通手機現(xiàn)在 128GB 起步空間看起來不缺但系統(tǒng)、照片、App 把空間瓜分完之后留給單個應用離線模型包的往往只有幾個 GB。嵌入式設(shè)備更緊張很多工業(yè)邊緣盒子存儲就是 8GB 或 16GB還要分一部分給日志和系統(tǒng)能放模型的區(qū)域非常有限。第二堵墻是內(nèi)存。這是最常被低估的。手機內(nèi)存看著有 12GB、16GB但操作系統(tǒng)、當前 App、后臺進程都在搶。一個 App 能穩(wěn)定申請到的內(nèi)存很多時候只有 2GB 到 4GB。模型推理是內(nèi)存密集型操作峰值一沖上去輕則掉幀重則直接被系統(tǒng)殺掉。第三堵墻是算力。端側(cè) NPU/DSP 現(xiàn)在跑 CNN 這類結(jié)構(gòu)很擅長但 Transformer 里的注意力矩陣、Softmax、Gelu 這類算子在不同芯片上的表現(xiàn)方差非常大。有些 NPU 對動態(tài)形狀支持很差詞表檢索、beam search 這類操作可能根本加速不了。算力不夠模型就算裝得下推理速度也會讓人無法接受。1.3 端側(cè)模型常見的量級區(qū)間結(jié)合我實際做過和見過的設(shè)備大致有這么幾個舒適區(qū)模型量級FP16 大小INT8 大小4bit 量化后端側(cè)可行性100M0.1B200MB100MB50MB非常輕松任何設(shè)備都能跑300M0.3B600MB300MB150MB主流手機無壓力嵌入式需評估1B2GB1GB500MB手機上的上限區(qū)需配合優(yōu)化3B6GB3GB1.5GB高端機勉強普通設(shè)備吃力7B14GB7GB3.5GB基本告別普通端側(cè)除非特殊硬件所以“端側(cè)能不能放下”不是一個絕對概念。200M 級別的模型幾乎放哪都能跑1B 級別開始要精打細算再往上就屬于硬件專項定制范疇。有了這張尺子我們再回頭量“萬物識物”和“多語言翻譯”這兩個具體需求。2. “萬物識物”的本質(zhì)為什么模型規(guī)模會失控2.1 從固定 1000 類到開放詞表任務(wù)定義改變了模型結(jié)構(gòu)傳統(tǒng)圖像分類模型比如大家熟悉的 ResNet、MobileNet 那套思路邏輯很簡單圖片輸入網(wǎng)絡(luò)提取特征最后接一個全連接分類頭輸出神經(jīng)元個數(shù)等于類別數(shù)。你訓練一個 1000 類模型分類頭就是 1000 個神經(jīng)元想識別 1 萬類就把分類頭改成 1 萬維。模型主體不動動的只是一個線性層。但“萬物識物”的訴求完全不是這樣?,F(xiàn)實里的物體遠遠超出固定列表今天加一個品類、明天加一個品牌分類頭方案永遠追不上。于是業(yè)界走向了開放詞表Open Vocabulary路線代表性的做法就是類 CLIP 的對比學習架構(gòu)一個圖像編碼器一個文本編碼器把圖片和文字映射到同一個向量空間通過計算圖像特征和文本特征之間的相似度來判斷“圖片里是什么”。這個架構(gòu)帶來的第一個變化是模型不再是一個網(wǎng)絡(luò)而是兩個網(wǎng)絡(luò)。圖像編碼器負責把像素變成向量文本編碼器負責把候選標簽變成向量。兩個網(wǎng)絡(luò)的參數(shù)量加在一起規(guī)模立刻比單純分類模型大了一圈。2.2 類CLIP架構(gòu)的端側(cè)版本視覺和文本兩條鏈路各占多少很多人以為 CLIP 類模型“只有”幾百兆這個印象來自那些被裁剪過、只做推理用的版本。實際上完整訓練態(tài)模型的體量視覺編碼器加文本編碼器動輒 1B 以上。不過剛才那張尺子告訴我們端側(cè)更關(guān)心的是“能不能壓縮后運行”所以我們要拆開看兩條鏈路的壓縮空間。視覺編碼器通常是 CNN 或 ViTCNN 類結(jié)構(gòu)對端側(cè)非常友好卷積算子在各家 NPU 上都有深度優(yōu)化壓縮到 INT8 甚至 4bit 后精度損失相對可控。文本編碼器是 Transformer算子和顯式/隱式的注意力機制相關(guān)端側(cè)支持度取決于芯片但 300M 到 600M 的文本編碼器壓到 150MB 左右并不夸張。更隱蔽的開銷在“萬物”這個詞本身。你真正部署的時候不能把整本詞典都當作候選標簽一次丟進去算相似度。假設(shè)一次識別要比較 100 個候選類別的文本特征那至少要把 100 個文本向量全部加載到內(nèi)存如果候選詞表有 10 萬個詞把全部文本特征算一遍時間和內(nèi)存都會失控。所以實際工程里要做候選挖掘Candidate Mining先用一個輕量模型粗篩再對候選子集精細比較。2.3 “萬物”還需要詞表兜底候選標簽本身也要占內(nèi)存這里還要補一個很多人忽略的點文本編碼器不是吃進去一個詞而是吃進去一段經(jīng)過 tokenizer 切分的 token 序列。多語言場景下詞表里每種語言都要有對應的子詞單元標簽文本越長編碼器計算量越大。我遇到過最頭疼的情況是“萬物”清單里放了大量長尾名詞比如一條商品的完整標題“2024 新款戶外防水登山鞋男防滑耐磨徒步鞋”tokenizer 切完可能是一長串 token。識別時每一幀都要把這一長串文本編碼一遍計算量不是線性的是隱性地暴漲。所以“萬物識物”放在端側(cè)真正決定成敗的不是那個視覺編碼器而是文本編碼器和詞表帶來的附加內(nèi)存與延遲。如果把視覺端壓到 200M文本端卻因為詞表膨脹到 300M總賬依然不好看。3. 多語言翻譯的真正瓶頸詞表膨脹比模型深度更麻煩3.1 BPE和子詞為什么多語言詞表按“語言數(shù)”放大多語言翻譯模型和單語翻譯模型的差異和“模型深度”關(guān)系不大最核心的差異在詞表Vocabulary。單語詞表通常 3 萬到 5 萬個子詞就夠用了。多語言場景下每種語言都有自己獨特的字符體系中文的漢字、日文的假名、韓文的諺文、阿拉伯文的連寫字母、各種拉丁字母變體……這些字符無法直接共享詞表只能跟著語言數(shù)量往上膨脹?,F(xiàn)在的做法主流是 BPE / SentencePiece 這類子詞切分算法好處是把常見詞拆成更小片段讓不同語言之間可以共享一部分子詞。比如“telephone”和“téléphone”可以共享一部分 token。但跨語系的共享非常有限中文漢字和英文子詞之間幾乎無法共享所以詞表膨脹是硬約束。一個典型的多語言詞表規(guī)模覆蓋歐洲十余種語言時詞表大約 100k 到 150k覆蓋亞歐非數(shù)十種語言時詞表往往沖到 250k 到 500k。這里每個 token 不是一個簡單的整型它還對應一個向量。3.2 詞表與隱藏維度決定的 Embedding 權(quán)重賬Embedding 矩陣的體量很好算詞表大小 × 隱藏維度 × 參數(shù)精度假設(shè)詞表 300k、隱藏維度 1024、用 FP16300000 × 1024 × 2B 614MB這只是一個 Embedding 矩陣。Transformer 模型通常輸入和輸出層各有一次 Embedding 操作很多實現(xiàn)是共享權(quán)重的也就是輸入層和輸出層用同一個矩陣這時還好如果不共享上面這個 614MB 就要乘以 2變成 1.2GB。更麻煩的是Embedding 矩陣在推理時不像普通權(quán)重層那樣可以連續(xù)計算它涉及查表操作內(nèi)存訪問模式對端側(cè)緩存很不友好。翻譯模型解碼的時候每一步都要在輸出層做一次“詞表規(guī)模 × 隱藏維度”的矩陣乘法。詞表越大這一步越慢。還有一個小細節(jié)多語言模型通常還要給每種語言加一個語言 ID 嵌入Language Embedding雖然體量不大但同樣會占用維度空間。所以在多語言模型里幾百兆的參數(shù)可能不是核心網(wǎng)絡(luò)而是詞表嵌入矩陣。3.3 多語言翻譯模型的整體體量實測區(qū)間結(jié)合開源社區(qū)做過的一系列多語言翻譯模型大致落在這些區(qū)間模型配置詞表規(guī)??倕?shù)量FP16 文件INT8 文件小型6層d_model 51260k200M-300M400-600MB200-300MB中型12層d_model 768150k800M-1.2B1.6-2.4GB0.8-1.2GB大型24層d_model 1024300k3B-5B6-10GB3-5GB結(jié)論很明確用中等規(guī)模的量化版本多語言翻譯模型是可以放進端側(cè)的尤其是詞表控制在 150k 以內(nèi)、參數(shù)量在 1B 左右、量化到 INT8 或 4bit 時總占用能壓到 1GB 上下。對一部主流手機這是可行的對 8GB 存儲的嵌入式盒子就要斟酌了。4. 端側(cè)放不下時的實戰(zhàn)壓縮手段量化、蒸餾、分片的取舍4.1 量化從FP16到4bit的真實收益與精度回退既然原始模型放不下第一個想到的就是量化。量化不是新鮮概念但很多人把“量化”當成一鍵壓縮忽略了精度回退問題的分布不均。對翻譯模型來說量化掉的經(jīng)驗是Embedding 層對量化非常敏感因為詞向量直接決定語義空間的位置從 FP16 強行砍到 4bit近義詞之間的距離會發(fā)生漂移翻譯出來的詞經(jīng)?!安钜豢跉狻?。而 Transformer 里的注意力層和前饋層對量化相對寬容。實操上建議混合精度Embedding 層保留 INT8 甚至 FP16計算密集的線性層用 4bit注意力矩陣用 8bit。這樣既保住了語義質(zhì)量又把整體占用壓下來。每次量化之后都要在真實數(shù)據(jù)上跑一遍雙語 BLEU 或者 chrF 指標不要只看困惑度。4.2 蒸餾與結(jié)構(gòu)化裁剪讓端側(cè)模型“本來就不大”量化是在“已訓練好的大模型”上做減法蒸餾則是從源頭上訓練一個小的。做法是用一個千億或者幾十億參數(shù)的大模型當老師讓幾百 M 的小模型去擬合老師模型的輸出分布。蒸餾出來的小模型在指定語言對上的效果往往比直接拿小模型在數(shù)據(jù)上硬訓要好很多。我特別推薦在多語言場景里做“分組蒸餾”不是讓一個小模型同時學 100 種語言而是按語系分組蒸餾出一組小模型每個小組負責若干種相近語言。翻譯時先判斷語言類型再路由到對應的小模型。這樣每個模型詞表更小精度更高加載和切換也靈活。結(jié)構(gòu)化裁剪剪枝在端側(cè)的實際收益沒有想象中大。Transformer 里過度剪枝容易導致整體性能崩塌而且剪枝后的稀疏矩陣在很多 NPU 上不能直接用還要再轉(zhuǎn)回稠密折騰一圈還不如直接蒸餾一個小模型來得干凈。4.3 模型分片與詞表動態(tài)加載運行時再摳內(nèi)存如果模型總量還是超了可以考慮“并不全部加載進內(nèi)存”的思路。翻譯模型里最常見的做法是“分片加載”把模型拆成 Embedding 層、若干 Transformer 層、輸出層幾個部分按需從存儲映射進內(nèi)存。端側(cè)跑在第一個 token 之前只加載前幾層解碼到中途再加載后面的層。配合操作系統(tǒng)的內(nèi)存映射mmap機制可以讓系統(tǒng)按頁讀取權(quán)重而不是一次性把整個文件讀進來。對詞表特別大的模型可以做“詞表裁剪快照”模型包里放一個完整詞表用于離線檢索但推理時只加載當前語言對相關(guān)的那部分 token 向量。比如一個 300k 詞表的模型如果用戶只切換中英、中日翻譯只把中文和英文相關(guān)的 60k token 的子詞向量加載進來Embedding 內(nèi)存直接少一大塊。4.4 分層卸載不是丟人方案端側(cè)云端的混合架構(gòu)純端側(cè)方案一旦要覆蓋幾十種語言和“萬物識別”成本會非常難看。我見過不少團隊最后選擇了混合架構(gòu)端側(cè)放一個輕量級模型應對離線需求和常見場景云端放完整大模型兜底。聯(lián)網(wǎng)時自動走云端斷網(wǎng)時降級到端側(cè)模型。很多人覺得“端側(cè)云端”是妥協(xié)不“純粹”。但在實際產(chǎn)品里用戶在乎的是“能不能離線用”“快不快”“準不準”而不是“模型是否真的全部裝在本地”。一個合理的降級鏈路往往比“死磕端側(cè)”更能照顧體驗。關(guān)鍵是降級策略要做到用戶無感端側(cè)模型負責大概率常見的識別和翻譯當置信度低或遇到陌生語言對時自動切換到云端。這個置信度閾值需要反復調(diào)調(diào)太高則離線模式頻繁失敗調(diào)太低則云端流量和延遲白白增加。5. 務(wù)實決策框架什么必須放端側(cè)什么應該留在云端5.1 判定標準延遲、隱私、帶寬、離線到底哪些能力要放端側(cè)哪些可以交給云端我習慣用四個維度打分。延遲敏感度實時翻譯、實時識別用戶等不起一次 800ms 的網(wǎng)絡(luò)往返這類優(yōu)先放端側(cè)。隱私敏感度醫(yī)療影像、身份證件、聊天內(nèi)容等數(shù)據(jù)不出設(shè)備是硬需求必須本地處理。帶寬成本視頻流、大批量圖片識別在弱網(wǎng)環(huán)境下傳不動端側(cè)可以先兜底。離線可用性明確要求飛行模式、野外環(huán)境也能用的場景比如巡檢、戶外導航端側(cè)是唯一選擇。四項里只要中了兩項就應該考慮端側(cè)方案。如果一項都不占那上云端是更經(jīng)濟的選擇沒必要把模型塞進設(shè)備折磨自己。5.2 參考架構(gòu)一個雙級緩存系統(tǒng)我自己比較偏好的參考架構(gòu)是“雙級緩存”第一級端側(cè)常駐一個覆蓋高頻需求的輕量模型比如 200M 的識別模型加一個 500M 左右、覆蓋頭部十幾種語言的翻譯模型。系統(tǒng)啟動時就加載保證基礎(chǔ)功能離線可用。第二級按需下載的語言包或品類包。用戶第一次使用某種冷門語言或識別某個垂直品類時提示下載一個幾十 MB 的增量模塊。下載后替換或補充到第一級模型旁邊。這個方案的好處是不是一開始就讓用戶下載一個 3GB 的“全家桶”而是讓用戶只為他需要的部分付費存儲和流量。增量模塊之間通過版本號管理服務(wù)端可以隨時發(fā)布新的語言包而不需要用戶升級整個 App。5.3 低資源場景下的降級方案詞典、檢索、模板翻譯如果連 500M 的小模型都不想放或者設(shè)備太陳舊還有一條更輕的路線用檢索式方案代替生成式模型。物體識別可以退化為“特征向量 本地檢索庫”每張圖像抽一個向量提前給每個候選類別抽好向量部署時只放一個“類別向量庫”幾十 MB 搞定。翻譯可以退化為“短語詞典 模板匹配”離線詞典命中高頻短語模板負責句子結(jié)構(gòu)替換。這種方案效果當然不如大模型但作為應急降級、特定垂直領(lǐng)域比如工業(yè)設(shè)備銘牌識別、餐廳菜單翻譯完全夠用。這里我特別想強調(diào)一個工程心態(tài)端側(cè)方案不是“替代”云端方案而是“分層兜底”。很多時候低資源方案解決的是“有和沒有”的問題不是“好和更好”的問題。5.4 給三類團隊的選型建議團隊資源不同策略完全不同。個人開發(fā)者別輕易碰多語言全家桶。選一個 300M 以下的單一語言翻譯模型加一個輕量識別模型只支持中英等兩三個核心語言對先把閉環(huán)跑通。中小團隊采用“按需下載 雙級緩存”架構(gòu)主推 5 到 10 種高頻語言冷門語言走云端 API云端失敗再提示離線不可用。大團隊有硬件和算法資源可以考慮自研蒸餾管線針對自家設(shè)備做混合精度量化把模型壓到極限后常駐端側(cè)。6. 我踩過的坑和給你的實操建議6.1 詞表統(tǒng)一設(shè)計能一石二鳥我參與過的項目里有一個關(guān)鍵決策把“萬物識物”的文本編碼器詞表和多語言翻譯的詞表統(tǒng)一起來。好處很明顯同一個 tokenizer、同一套子詞視覺語言和翻譯模型共享 Embedding 矩陣整體包體直接小一截。但代價是詞表設(shè)計階段很痛苦。視覺標簽用詞偏名詞翻譯用詞偏句式表達兩者對子詞切分的偏好不完全一致。你需要在大規(guī)模語料上做好詞頻統(tǒng)計平衡視覺和翻譯兩個域的子詞覆蓋。一旦定下來后續(xù)再做任何語言擴展都要小心翼翼因為改詞表等于改模型輸入空間。6.2 多語言質(zhì)量退化加語言要雨露均沾多語言模型的經(jīng)典坑是語言越加越多每種語言的質(zhì)量都在掉尤其是資源稀缺的小語種會被高資源語言“擠占”。原因不難理解模型總?cè)萘坑邢拊~表、層參數(shù)要分給更多語言。我的建議是加語言不能“只加詞表、不訓練”要把新語言的語料和已有語言的語料按比例混合重新做一輪全語言微調(diào)。每次加語言之后都要在已有的主要語言上重新跑一遍回歸測試。否則容易出現(xiàn)“新增中文翻譯很好但英文翻譯突然變差”的尷尬情況。6.3 別只看文件大小要看峰值內(nèi)存和首Token延遲模型放進設(shè)備之后判斷能不能用不要只看安裝包大小。要實測兩件事峰值內(nèi)存用內(nèi)存分析工具跑一段連續(xù)翻譯任務(wù)看加載模型 推理時的峰值 RSSResident Set Size。如果超過 App 可用內(nèi)存上限的一半大概率會在低內(nèi)存設(shè)備上被殺。首 Token 延遲多語言翻譯是自回歸生成用戶從輸入文字到屏幕上出現(xiàn)第一個譯文 token 的等待時間直接影響體驗。這個數(shù)字受算力、量化方案、詞表大小影響很大實測比理論估算可靠。我自己吃過虧一個 INT8 模型文件挺小但在某款 NPU 上因為嵌入查表和反量化算子不支持硬件加速首 token 延遲沖到 1.5 秒只能重做算子融合。6.4 熱更新設(shè)計詞表不能隨便加模型版本要兼容端側(cè)模型很方便做熱更新但詞表版本不兼容是個暗坑。云端如果發(fā)布了新詞表舊模型不認識新 token新模型不認識舊緩存結(jié)果就是翻譯質(zhì)量暴跌或者直接索引越界。建議在模型包里帶上 tokenizer 的版本號服務(wù)端下發(fā)增量包時校驗詞表版本一致性。模型文件命名務(wù)必帶 hash不要用“model_v2_final_really”這種詭異命名發(fā)布流程會亂到你懷疑人生。多語言翻譯和萬物識別同時上端側(cè)沒有銀彈但也不是天方夜譚。核心策略就一句話能剪的剪、能量化的量化、能拆的拆、能降級的降級把用戶最常用的那部分留在本地把長尾需求交給云端。端側(cè)模型這個方向上我會持續(xù)跟下去下次再分享更多實測數(shù)據(jù)和踩坑記錄。