[LLM-deploy] 第一次部署 Qwen VLM

利用 Runpod 部署 Qwen VLM

Posted by 李杰諹 on Thursday, July 16, 2026

背景

由於專案要求的緣故,不能使用閉源模型來進行 PDF OCR,而傳統 OCR 的效果沒有很好,所以只好使用開源模型來處理。在 OCR 領域中,我看排名前面幾乎都是 qwen 的版本,所以就先用 qwen 的最新 3.5 來進行測試。

過程

技術選型

首先我先比較市面上解析 PDF 圖片的幾個方案,傳統 Tesseract / EasyOCRPaddleOCR、以及多模態模型VLM(Qwen)的方法,然後選取PDF 文件中比較複雜的20頁進行測試:

  • Tesseract / EasyOCR:傳統 OCR。但它本身不含中文,要另外裝語言包;裝了繁中包(tessdata_best)跑下來,還是有點不符合需求——繁中字有些會被切開、字形也有認錯,像 「辯護人」 被解析成 「拓護人」。單純只比較純文字的解析,只有大約 73% 和 VLM 的解析結果一致。
  • PaddleOCR:來自百度的OCR 模型,其純文字的解析結果幾乎追平 VLM,但它唯一明顯的軟肋是流程圖、表格、模糊頁——一張審判程序的流程圖,它把箭頭圖元認成 全房品品房品品机、方塊的閱讀順序也亂掉了。
  • VLM(Qwen):不只認字,還「看得懂版面」——流程圖、表格能按邏輯順序讀,模糊處也處理得比較好;更關鍵的是它還能夠接收prompt來調整(忽略背面透印、看不清的標【模糊】不要亂猜、保留條號結構等等)

為了 PDF 解析的完整度,所以選擇自己部署 VLM 來對專案的文件進行解析。

計算過程

規劃需要什麼規格的GPU,需要先大致預估一下模型的使用:

模型權重

先查看 Qwen3.5-35B-A3B,其模型權重為 35B:

精度 每個參數 35B 權重
FP32(全精度) 4 bytes 140GB
BF16 / FP16(不量化) 2 bytes 70GB
FP8 1 bytes 35GB
INT4 0.5 bytes ~17.5GB

所以用 INT4(AWQ/GPTQ)的量化版本,權重需要預留快20GB 的容量。

圖片轉換token

Qwen VL 系列的 vision encoder (ViT) 是以 14x14 pixel 切一個 patch,然後才做 2x2 merge—把相鄰的 4 個 patch 合併成 1 個 token 推送給模型。 因此一張 1024x 1024 的圖片,生成 1300+ token,跟著文字 token 一樣佔入 KV cache。

KV cache

算完權重,還有第二個吃 VRAM 的東西:KV cache。

模型是一個字一個字往外吐的,每吐一個新字,都要「回頭看」前面所有字。如果每次都重算前面每個字,太浪費,所以模型會把每個 token 算過的東西(Key 和 Value)先存起來、後面直接拿來用——這份存起來的東西就是 KV cache。

它和權重最大的不同是:權重是固定的(就是那 ~18GB,跑一頁跑一百頁都一樣),但 KV cache 是會變化的,長度取決於「一次餵多少 token × 同時處理幾個請求」。所以一張卡能同時服務幾個請求、能吃多長的內容,其實是看「權重佔完後,剩多少空間留給 KV cache」。而 KV cache 的算法為:

每個 token = 2(Key + Value)× 層數 × KV 頭數 × 每頭維度 × 每個數字幾 bytes

根據 Qwen 3 的config:

"text_config": {
    "head_dim": 128,
    "num_hidden_layers": 64,
    "num_key_value_heads": 8,
  },

代進去(64 層、8 個 KV 頭、每頭 128 維、KV cache 用 FP16 存所以 2 bytes——這是 KV_CACHE_DTYPE 決定的,和權重是不是 INT4 無關):

2 × 64 × 8 × 128 × 2 ≈ 256 KB / 每個 token

所以粗估一下:

情境 token 數 KV cache
一個 OCR 請求(圖 ~1300 + 文字 + 輸出) ~4000 ~1 GB
塞滿 16K 的 context 16384 ~4 GB

也就是說,一個 OCR 請求大概吃 1GB 的 KV cache。假設權重佔完後還剩 30GB,那大概能同時跑 30 個請求。

(補充:KV cache 也可以另外量化——把 KV_CACHE_DTYPE 設成 fp8 就變成 1 byte、直接砍一半。這是獨立於權重量化的另一個可調整的地方。)

而前面提到那 ~1300 個 vision token 也一起算在裡面,所以圖片解析度越高、token 越多、KV cache 越大、prefill 也越慢

GPU 估算

把前面兩塊加起來,大概就知道要租多大的卡了:

  • 權重:Int4 量化後 ~18GB,這塊是固定的
  • KV cache + 其他:留點空間給並發,再加上激活、vision encoder、框架本身的 overhead,抓個十幾 GB

粗估下來大概要 30GB 以上的 GPU。

運氣問題

一開始部署完成後,我在本地隨機挑選20頁比較複雜的PDF 文檔頁面、呼叫 Runpod endpoint 去解析,都很成功;但當我把服務上雲(upload image -> GPU pod -> callback -> R2 bucket)的時候,發現光是解析5頁,總會有一頁遇到 OOM/ time out 的問題。但我也不想隨便往上調GPU容量,總覺得是哪裡有問題,所以修改一些 vllm 的環境變量:

  • GPU_MEMORY_UTILIZATION(0.9 → 0.75):一開始以為是顯存留太少,就把 vLLM 佔用的比例調低、想多留點餘量給它。結果 OOM 照樣發生。

  • MAX_NUM_SEQS(8 → 2):接著以為是一次塞太多請求進同一張卡、KV cache 被塞爆,就把 「同時處理幾個序列」 往下調。但翻 log 才發現 KV cache 根本用不完,也不是這條。

  • GDN_PREFILL_BACKEND=triton:這個應該是比較接近真相的原因——這個模型(Qwen3.5 用的是比較新的注意力架構)的核在 worker 冷啟動時要「即時編譯」,而編譯過程本身十分佔用記憶體、結果被系統砍掉。設了之後確實好一些,但還是有些 worker 沒吃到這個設定、繼續OOM/timeout。 vllm 的 log:

    FlashInfer GDN prefill kernel is JIT-compiled; first run may take a while
    to compile. Set `--gdn-prefill-backend triton` to avoid JIT compile time
    

推理導致的,降級

往回看、翻 log 才發現:不同的 worker 跑的核居然不一樣——有的印 Using FlashInfer、有的印 Using Triton。而 FlashInfer 那種要在冷啟動即時編譯(編譯目標是 sm_90a,也就是 Hopper H100/H200 這類卡),編譯本身吃爆記憶體被砍(exit 137)。叫AI 查看 vLLM 源碼才知道,選哪種核是看 GPU 型號決定的——只有 Hopper 卡才會走 FlashInfer,而 RunPod 每次長 worker 給的卡不固定。所以同一批 5 頁,哪一頁不巧落到用 FlashInfer 的 worker 上就掛,落到 Triton 的就正常。失敗看起來隨機,但根源其實在「GPU / worker 不一致」,不是模型自己在選核。

再加上這個模型是推理模型,每次回答前會先跑一大段思考(<think>),一頁要多花好幾倍時間,就算沒 OOM 也很容易撞到執行超時。等於是「不穩的核」+「慢的推理」兩個問題疊在一起。

想清楚之後就決定:與其繼續跟這個新架構硬碰,不如換一個更成熟、更單純的版本。所以測試了上一代的 Qwen3-VL-32B

  • 它是 dense 架構(不是那個要即時編譯的新注意力),不管 worker 落到哪張卡,都不會有那個會 OOM 的核;
  • 它是 non-reasoning 版,不會先跑一段 <think>,每頁快很多、也不容易超時。

結果一換上去,OOM 和超時都不見了,速度還快了好幾倍。

利用辨識完成的20頁進行 eval

換了模型,還是要確認一下「降級」有沒有讓辨識品質變差。我拿本文一開始提到的 20 頁測試文本來餵給 Qwen3-VL-32B,結果還不錯:

  • 整體字元一致度 ~94%。
  • 不一致的地方,多半是排版差異(換行、空白),或者是新模型其實表現更好——比如它用的是標準字形( 而不是舊體的 )、會把上標的註腳()正確標出來、表格也還原得比較整齊。
  • 最關鍵的是:法律條文中需要注意幾個地方——條號(第 X 條)、還有「得 / 應」這種一字之差就是「可以」變「必須」的字——兩個模型完全一致。

所以這樣看起來,降級到 Qwen3-VL 並沒有付出品質的代價,因此我就拿去跑全量的PDF OCR。

結論

有了AI coding agent,在做技術選型的時候真的方便很多,叫AI 寫個demo 然後人類評估一下好壞就好;而在部署 VLM 的時候,由於不太懂 Qwen3.5-35B-A3B 導致花了很多時間在處理 OOM 的問題,最後沒想到降了一級、選擇non-reasoning 的版本就可以快速完成。

而且未來有關LLM、VLM 的任務,真的不一定要選擇最新最強的模型,畢竟一切都還在變化。就依照自己業務邏輯所需要的、和目前市面上模型的版本找到局部最優解。

Ref