Procházet zdrojové kódy

feat(新增GGUF转换方案文档): 新增HF_safetensors->GGUF.md文档,详细说明PaddleOCR-VL 1.6到GGUF的转换步骤与注意事项,提供两条高效路径,解决OTSL结构token过滤问题,提升用户在模型转换过程中的指导性与可操作性。

zhch158_admin před 1 měsícem
rodič
revize
556b67d19f

+ 177 - 0
docs/ocr_tools/universal_doc_parser/HF_safetensors->GGUF.md

@@ -0,0 +1,177 @@
+确认完毕,给你结论和可执行方案。
+
+## 关键事实
+
+1. **llama.cpp 已原生支持 `paddleocr_vl` 架构**(build b8110,2026-02 合入)。你现在能跑 1.5-GGUF 就证明你的 llama.cpp 已支持。
+2. **1.6 与 1.5 架构「完全兼容」**(官方原话 "zero-cost plug-and-play migration")。这意味着 GGUF 转换/推理路径对 1.6 完全适用——没有任何适配成本。
+3. 1.6 的 HF model tree 已显示 "Quantizations 2 models",社区量化正在跟进(`noctrex` 这类作者对 1.5 更新很快)。
+
+所以你**不用等官方**,有两条高效路径。
+
+---
+
+## 方案 A(最省事):直接找/等社区 GGUF
+
+因为架构完全兼容,社区转 1.6 是零成本的。优先去这两个地方蹲:
+
+- [`noctrex/PaddleOCR-VL-1.5-GGUF`](https://huggingface.co/noctrex/PaddleOCR-VL-1.5-GGUF)(看是否出 1.6 仓库)
+- 1.6 官方页的 [Quantizations 链接](https://huggingface.co/PaddlePaddle/PaddleOCR-VL-1.6)(已挂 2 个量化)
+
+找到后,**你现有的 daemon 脚本几乎不用改**,只改 3 个变量(路径 + 别名)即可。
+
+---
+
+## 方案 B(最可控,推荐):自己转换 1.6 → GGUF
+
+模型只有 0.9B,M4 Pro 48G 转换+量化几分钟搞定。步骤:
+
+```bash
+conda activate mineru
+
+# 1. 拿最新 llama.cpp(确保 convert 脚本支持 paddleocr_vl)
+git clone https://github.com/ggml-org/llama.cpp.git
+cd llama.cpp
+pip install -r requirements.txt    # 转换脚本依赖
+cmake -B build -DGGML_METAL=ON && cmake --build build -j
+
+# 2. 下载 1.6 原始权重(safetensors)
+conda activate mineru
+hf download PaddlePaddle/PaddleOCR-VL-1.6 --local-dir ~/models/PaddleOCR-VL-1.6
+hf download opendatalab/MinerU2.5-Pro-2604-1.2B --local-dir ~/models/MinerU2.5-Pro-2604-1.2B
+
+# 3. 转语言模型主体(M4 48G 内存充足,直接 f16 不量化质量最好)
+python convert_hf_to_gguf.py ~/models/PaddleOCR-VL-1.6 \
+    --outfile ~/models/PaddleOCR-VL-1.6-GGUF \
+    --outtype f16
+
+# 4. 转视觉投影器 mmproj(OCR 质量主要看这个,建议 f16/f32)
+python convert_hf_to_gguf.py ~/models/PaddleOCR-VL-1.6 \
+    --mmproj \
+    --outfile ~/models/PaddleOCR-VL-1.6-GGUF/PaddleOCR-VL-1.6-mmproj.gguf \
+    --outtype f16
+cp ~/models/PaddleOCR-VL-1.6/chat_template.jinja ~/models/PaddleOCR-VL-1.6-GGUF/chat_template.jinja
+
+# 5. 转语言模型主体(M4 48G 内存充足,直接 f16 不量化质量最好)
+python convert_hf_to_gguf.py ~/models/MinerU2.5-Pro-2604-1.2B \
+    --outfile ~/models/MinerU2.5-Pro-2604-1.2B-GGUF \
+    --outtype f16
+
+# 6. 转视觉投影器 mmproj(OCR 质量主要看这个,建议 f16/f32)
+python convert_hf_to_gguf.py ~/models/MinerU2.5-Pro-2604-1.2B \
+    --mmproj \
+    --outfile ~/models/MinerU2.5-Pro-2604-1.2B-GGUF/MinerU2.5-Pro-2604-1.2B-mmproj.gguf \
+    --outtype f16
+cp ~/models/MinerU2.5-Pro-2604-1.2B/chat_template.jinja ~/models/MinerU2.5-Pro-2604-1.2B-GGUF/chat_template.jinja
+
+```
+
+> 模型总共才 ~1.5GB,48G 内存下**不建议量化**主模型(Q4 会掉 OCR 精度),直接 f16 即可;mmproj 用 f16 或 f32 质量最佳。
+
+转完后,把你 `paddle_local_daemon.sh` 里这几行指向新文件就行:
+
+```bash
+HF_CACHE="$HOME/models/paddleocr_vl"
+MODEL_PATH="$HF_CACHE/PaddleOCR-VL-1.6.gguf"
+MMPROJ_PATH="$HF_CACHE/PaddleOCR-VL-1.6-mmproj.gguf"
+MODEL_NAME="PaddleOCR-VL-1.6"
+```
+
+#### 验证模型
+```bash
+# llama-server -hf ~/models/PaddleOCR-VL-1.6-GGUF
+# llama-server -hf ~/models/MinerU2.5-Pro-2604-1.2B-GGUF
+ocr_tools/daemons/mineru_local_daemon.sh start
+
+ocr_tools/daemons/curl_local_mineru.sh
+
+```
+
+---
+
+## 一个重要的坑:chat template
+
+社区/自转的 GGUF 如果不带模板,调用时会因为缺少图像占位符 token(`<|IMAGE_START|><|IMAGE_PLACEHOLDER|><|IMAGE_END|>`)而**崩溃**。你当前脚本能跑 1.5 官方 GGUF 是因为模板已内嵌。如果换 1.6 后启动报错或输出异常,给 `llama-server` 加上:
+
+```bash
+--jinja --chat-template-file /path/to/chat_template.jinja
+```
+
+`chat_template.jinja` 从 GGUF 仓库下载,或从 1.6 的 `tokenizer_config.json` 里的 `chat_template` 字段提取。
+
+---
+
+## 又一个更隐蔽的坑:OTSL 结构 token 被当 special 过滤(MinerU2.5 实测)
+
+**现象**:MinerU2.5-Pro-2604 转成 GGUF 后,走 `/v1/chat/completions` 做表格识别,
+返回 `content` 里**文字全对,但完全没有 OTSL 结构 token**(`<fcel>`/`<nl>`/`<ecel>` 一个都没有,
+连字面 `<` 都没有),表格塌成纯文本,后续 `convert_otsl_to_html` 无法还原表格。
+
+**根因**:MinerU2.5 的 OTSL token 在 `tokenizer_config.json` / `tokenizer.json` 中被标记为
+`"special": true` 并列入 `additional_special_tokens`。转 GGUF 后它们成为 **CONTROL/special token**,
+而 **llama-server 的 chat/completions 默认不会把 special token 输出到 `content`**——
+模型其实生成了 OTSL,但在返回时被剥离。
+
+**铁证对比**(两者都用 OTSL,差别只在 special 标记):
+
+| 模型 | OTSL token `special` | chat content 输出 OTSL |
+|---|---|---|
+| PaddleOCR-VL-1.6 | `false`(USER_DEFINED) | ✅ 正常输出 `<fcel>...` |
+| MinerU2.5-Pro-2604 | `true`(CONTROL) | ❌ 被过滤,只剩纯文本 |
+
+> 注意:社区现成 GGUF(如 `mradermacher/MinerU2.5-Pro-2604-1.2B-GGUF`)基于同样的 tokenizer 转换,
+> **同样有这个坑**,换现成包也不行。
+
+**解决(转主模型前,把 7 个 OTSL token 改为非 special,再重转;mmproj 无需重转)**:
+
+```bash
+conda activate mineru
+python - <<'PY'
+import json, os
+d = os.path.expanduser("~/models/MinerU2.5-Pro-2604-1.2B")
+otsl = {"<ched>", "<ecel>", "<fcel>", "<lcel>", "<ucel>", "<xcel>", "<nl>"}
+
+# 1) tokenizer.json:convert_hf_to_gguf 据此决定 token 类型(关键)
+#    注意 indent=2:保持与原始一致的多行格式,避免被压成一行导致 diff 巨大
+p = os.path.join(d, "tokenizer.json")
+t = json.load(open(p, encoding="utf-8"))
+for tok in t.get("added_tokens", []):
+    if tok.get("content") in otsl:
+        tok["special"] = False
+json.dump(t, open(p, "w", encoding="utf-8"), ensure_ascii=False, indent=2)
+
+# 2) tokenizer_config.json:保持一致 + 从 additional_special_tokens 移除
+p = os.path.join(d, "tokenizer_config.json")
+c = json.load(open(p, encoding="utf-8"))
+for v in c.get("added_tokens_decoder", {}).values():
+    if v.get("content") in otsl:
+        v["special"] = False
+c["additional_special_tokens"] = [x for x in c.get("additional_special_tokens", []) if x not in otsl]
+json.dump(c, open(p, "w", encoding="utf-8"), ensure_ascii=False, indent=2)
+print("已将 OTSL token 标记为非 special,可重新转换主模型 GGUF")
+PY
+
+# 重转主模型(mmproj 不用动)
+python convert_hf_to_gguf.py ~/models/MinerU2.5-Pro-2604-1.2B \
+    --outfile ~/models/MinerU2.5-Pro-2604-1.2B-GGUF \
+    --outtype f16
+```
+
+**验证**:重启 daemon 后再跑 `ocr_tools/daemons/curl_local_mineru.sh`,
+`content` 里应能看到 `<fcel>`/`<nl>` 等结构 token。
+
+> 经验法则:**凡是需要出现在模型输出文本里的结构/标记 token(OTSL、Markdown 边界等),
+> 转 GGUF 前都应确保它们是非 special(USER_DEFINED)**,否则会被 llama-server 从 chat content 过滤。
+> PaddleOCR-VL 之所以没踩坑,正是因为它的 OTSL token 本就是 `special: false`。
+
+---
+
+## 不推荐的路径
+
+- **transformers + MPS**:Mac 上 MPS 跑视觉模型慢,且官方明确说 transformers 路径只支持 element-level(单元素识别),**不支持整页解析**,会破坏你现在的流水线。
+- **vLLM**:官方加速方案依赖 CUDA/Docker GPU,Mac 上没有可用 GPU 后端。
+
+---
+
+**建议**:先按方案 B 自转一份 f16,跑通后对比 1.5 看精度提升是否值得切换;同时关注社区 1.6 GGUF,出了直接换方案 A。需要的话我可以(切到 Agent 模式后)帮你把脚本改造成支持 `--jinja` 并参数化版本号,方便 1.5/1.6 一键切换。
+
+Citation: [InsiderLLM 指南](https://insiderllm.com/guides/paddleocr-vl-local-document-ocr/)、[1.6 官方页](https://huggingface.co/PaddlePaddle/PaddleOCR-VL-1.6)、[1.5-GGUF 用法](https://huggingface.co/PaddlePaddle/PaddleOCR-VL-1.5-GGUF)。