PaddleOCR-VL-1.6_Demo/README.md

17 KiB
Raw Blame History

ocr-VL1.6

本地部署 PaddlePaddle/PaddleOCR-VL-1.6 的实验项目,包含已实测的 CPU 版本和独立隔离、待 NVIDIA GPU 验证的 GPU 版本。

在线 Demo: HuggingFace Space · 模型权重: HuggingFace

项目结构

ocr-VL1.6/
├── main.py                 # CPU 单图 OCR + Benchmark
├── batch_ocr.py            # CPU 批量图片 OCR系统友好的多进程版本
├── pdf_ocr.py              # CPU PDF OCR逐页、可恢复
├── pdf_ocr_core.py         # CPU/GPU 共用的 PDF 渲染、恢复和导出逻辑
├── ocr_logging.py          # CPU/GPU 共用的 UTF-8 结构化日志工具
├── pyproject.toml          # CPU 项目依赖
├── uv.lock                 # CPU 锁文件
├── gpu/                    # 独立 GPU 子项目
│   ├── main.py             # GPU 单图 Benchmark
│   ├── pdf_ocr.py          # GPU PDF OCR复用公共核心
│   ├── verify_env.py       # CUDA 环境与计算验证
│   ├── setup_env.py        # 按 CUDA Wheel 类型创建环境
│   ├── pyproject.toml      # GPU 独立依赖
│   ├── .python-version     # GPU 使用 Python 3.11
│   └── README.md           # GPU 安装与运行说明
├── benchmarks/
│   └── gpu/                # GPU Benchmark JSON 输出目录
├── images/
│   └── 手写01.png          # 测试图片手写中文1758×646
└── README.md

技术栈

组件 版本 说明
PythonCPU 3.13 根目录独立环境
PythonGPU 3.11 gpu/ 独立环境,提升 GPU Wheel 兼容性
PaddlePaddle 3.2.1 CPU 使用 paddlepaddleGPU 使用 paddlepaddle-gpu
PaddleOCR 3.7.0 doc-parser extra
PaddleOCR-VL-1.6 0.9B 主 OCR 视觉语言模型(~1.8GB
PP-DocLayoutV3 - 版面检测模型(~126MB

模型缓存目录:~/.paddlex/official_models/

快速开始

前提条件

  • Python ≥ 3.13
  • uv 包管理器

安装

uv sync

运行

# 单张图片 OCR自动使用全部 CPU 核心)
uv run python main.py

# 批量 OCR多进程并行安全默认值
uv run python batch_ocr.py images/

所有 OCR 入口默认同时输出控制台日志和 UTF-8 日志文件,详见“运行日志”章节。

首次运行会自动从 ModelScope 下载模型文件(约 2GB后续使用缓存。

GPU 子项目

状态:已实现、未实测。 当前开发机器只有集成显卡,不能运行 NVIDIA CUDA。GPU 代码已通过语法、CLI 和无 CUDA 安全退出检查,但安装兼容性、显存占用和性能数据必须在目标 NVIDIA GPU 机器上验证。

CPU 与 GPU 使用不同虚拟环境,禁止在根目录 CPU .venv 中安装 paddlepaddle-gpu

# 查看 GPU 安装命令,不实际安装
python gpu/setup_env.py --cuda cu118 --dry-run

# 在目标 NVIDIA GPU 机器创建 gpu/.venv根据官方兼容表选择 cu118 或 cu126
python gpu/setup_env.py --cuda cu118

# 检查 CUDA 构建、GPU 设备和矩阵乘法
uv run --project gpu python gpu/verify_env.py

# 运行 GPU 单图 Benchmark
uv run --project gpu python gpu/main.py --warmup 1 --rounds 3

GPU Benchmark JSON 写入 benchmarks/gpu/。详细说明见 gpu/README.md

运行日志

所有主要入口均使用统一日志格式:

2026-07-16 14:28:02 | INFO     | pid=27644 | PAGE_OCR_COMPLETED page=1 seconds=36.345

默认日志目录:

logs/
├── single/    # main.py / gpu/main.py
├── batch/     # batch_ocr.py
└── pdf/       # pdf_ocr.py / gpu/pdf_ocr.py

默认文件名包含输入名、设备和时间戳,例如:

logs/pdf/sample-cpu-20260716-142802.log
logs/single/手写01-gpu0-20260716-142802.log

可用参数:

# 指定日志文件
uv run python main.py images/手写01.png --log-file logs/custom.log
uv run python pdf_ocr.py documents/sample.pdf --log-file logs/pdf-sample.log
uv run python batch_ocr.py images/ --log-file logs/batch-images.log

# 输出详细异常堆栈和调试日志
uv run python pdf_ocr.py documents/sample.pdf --verbose

日志文件使用 UTF-8 编码。即使 Windows 控制台因 GBK 显示乱码,日志文件中的中文仍可正常查看。

单图日志统计

main.pygpu/main.py 记录:

  • 程序启动与输入图片大小
  • Paddle/PaddleOCR 导入耗时
  • CPU 线程数或 GPU/CUDA 初始化耗时
  • 模型初始化耗时
  • 每轮预热耗时
  • 每轮正式推理耗时
  • min/max/mean/median/stdev
  • 图片尺寸、版面框数量、文本块数量
  • OCR 文本块内容(可用 --no-result 关闭)
  • 从程序启动到结果输出的总用时
  • GPU 入口额外记录显存统计和 Benchmark JSON 路径

批量图片日志统计

batch_ocr.py 记录:

  • 图片扫描耗时和图片数量
  • Worker 数、每 Worker 线程数和预估内存
  • 每个 Worker 的 PID、错峰等待、框架导入、模型初始化和启动总耗时
  • 每张图片的 Worker PID、推理耗时、尺寸、版面框和文本块数量
  • 任务进度、成功数和失败数
  • Pool 总耗时、串行耗时估计、平均每图耗时和并行加速比
  • 从程序启动到全部结果汇总的总用时

PDF 日志统计

pdf_ocr.pygpu/pdf_ocr.py 记录:

  • PDF 预检、打开和 manifest 创建耗时
  • 模型初始化耗时
  • 每页渲染耗时
  • 每页 OCR 推理耗时
  • 每页 Markdown/JSON 导出耗时
  • manifest 与合并文件保存耗时
  • 每页总耗时、累计耗时和预计剩余时间ETA
  • 每页图片尺寸、版面框数量和文本块数量
  • 完成页、失败页和断点续传前已完成页数
  • 各阶段累计值、平均每页耗时和任务总用时
  • 从程序启动(含模型加载)到退出的程序总用时

PDF 的 manifest.json 同时包含 summary.timing

{
  "pdf_open_seconds": 0.01,
  "manifest_prepare_seconds": 0.03,
  "render_total_seconds": 1.2,
  "ocr_total_seconds": 324.5,
  "export_total_seconds": 0.8,
  "state_save_total_seconds": 0.2,
  "page_total_seconds": 326.7,
  "average_ocr_seconds": 162.25,
  "average_page_seconds": 163.35,
  "finalize_seconds": 0.1,
  "task_total_seconds": 327.1
}

task_total_seconds 是 PDF 核心任务总时间,不含入口模型初始化;完整程序总时间记录在日志的 PROGRAM_COMPLETED 事件中。

PDF OCR

PDF 使用 pypdfium2 逐页渲染,再将每一页交给 PaddleOCR-VL。默认采用安全的单进程串行模式页面完成后立即保存适合 CPU 长时间任务。CPU 默认预留 2 个逻辑核心给系统,可通过 --threads 覆盖。

CPU 使用

# 处理整个 PDF默认 DPI 144
uv run python pdf_ocr.py documents/sample.pdf

# 处理指定页1-5、8、10 到末页
uv run python pdf_ocr.py documents/sample.pdf --pages "1-5,8,10-"

# 中断后继续,已完成页不会重复推理
uv run python pdf_ocr.py documents/sample.pdf --resume

# 删除已有输出并重新处理
uv run python pdf_ocr.py documents/sample.pdf --overwrite

# 保留每页渲染后的 PNG便于检查输入质量
uv run python pdf_ocr.py documents/sample.pdf --keep-rendered

# 手动设置 CPU 线程数;长任务建议保留 12 个核心给系统
uv run python pdf_ocr.py documents/sample.pdf --threads 18

GPU 使用

GPU 入口与 CPU 入口使用相同的 PDF 核心逻辑,但必须在 gpu/.venv 和 NVIDIA CUDA GPU 上运行:

uv run --project gpu python gpu/pdf_ocr.py documents/sample.pdf \
  --device-id 0 \
  --pages "1-10" \
  --dpi 144

当前机器无 NVIDIA 独立显卡,因此 GPU PDF 入口仅完成静态检查,尚未实机验证。

页码语法

参数 含义
1 仅第 1 页
1-5 第 15 页
10- 第 10 页到最后一页
1-5,8,10- 多个页码范围组合

用户页码从 1 开始;内部 manifest 使用同样的一基页码记录。

输出结构

outputs/
└── sample/
    ├── manifest.json        # 任务配置、页状态、耗时和错误
    ├── document.md          # 合并后的 Markdown
    ├── document.json        # 合并后的 JSON
    ├── pages/
    │   ├── page-0001.md
    │   ├── page-0001.json
    │   └── ...
    ├── assets/              # 表格、图片等 Markdown 资源
    └── rendered/            # 仅使用 --keep-rendered 时保留

默认不保留中间渲染 PNGOCR 完成后会删除临时图。每页 JSON 会将 input_path 恢复为原 PDF 路径,并记录 page_indexpage_numberpage_countrender_dpi

恢复与错误处理

  • 输出目录已存在时,必须显式使用 --resume--overwrite
  • --resume 会校验 PDF SHA-256 和 DPI防止接续到错误任务
  • 单页失败默认写入 manifest 并继续后续页面
  • --fail-fast 可在第一页失败后立即停止
  • Ctrl+C 会保存当前 manifest下次使用 --resume 继续
  • 逐页文件和 manifest 使用临时文件替换,降低中途退出造成文件损坏的概率

DPI 建议

文档类型 建议 DPI
普通打印文字 120144
小字号文档 150200
手写或低质量扫描件 200250

CPU 当前单图实测约 162 秒。长 PDF 总时间可粗略按 待处理页数 × 单页耗时 估算,因此建议先用 --pages "1" 测试效果和耗时再扩大页码范围。DPI 越高通常越慢,不建议默认使用 300 DPI。

工作原理

PaddleOCRVL pipeline 分两阶段:

输入图片 → [PP-DocLayoutV3 版面检测] → [PaddleOCR-VL-1.6-0.9B 文字识别] → 结构化输出
  1. 版面检测 — PP-DocLayoutV3 检测页面中的文本块区域(坐标 + 标签 + 置信度)
  2. OCR 识别 — PaddleOCR-VL-1.6-0.9BGQA 架构视觉语言模型)逐块识别文字
  3. 结果输出 — 返回 PaddleOCRVLResult,包含布局信息和识别文本

输出结构

字段 类型 说明
layout_det_res.boxes list[dict] 版面文本区域cls_id, label, score, coordinate, polygon_points
parsing_res_list list[PaddleOCRVLBlock] 识别文本块(.label, .bbox, .content, .polygon_points
model_settings dict 推理配置开关(版面检测/图表/印章等)
width / height int 图片尺寸

性能优化迭代

测试机器Windows 11, CPU 20 核(逻辑线程), RAM 32GB, PaddlePaddle 3.2.1 CPU

迭代 0初始状态无任何优化

直接调用 pipeline.predict(),未设置任何线程参数。

阶段 耗时
模型初始化(加载权重) ~60s
首次推理(含 JIT 编译) ~285s
后续推理 ~238s~4 min

迭代 1算子级并行 — core.set_num_threads()

探索过程:

尝试 方法 结果
paddle.set_num_threads() Paddle 3.x 已移除该 API
环境变量 OMP_NUM_THREADS / MKL_NUM_THREADS Paddle 3.x 内部使用 oneDNN不读取这些变量
from paddle import core; core.set_num_threads(N) 有效! oneDNN 底层算子matmul 等)受该 API 控制

矩阵乘法微基准测试4000×4000

线程数 耗时 (matmul) 加速比
1 0.952s 1.0x
4 0.419s 2.3x
8 0.323s 2.9x
16 0.240s 4.0x
20 0.223s 4.3x

应用到 OCR 后的实际效果:

设置 core.set_num_threads(20) 后重新评测:

阶段 优化前 优化后 提速
模型初始化 ~60s ~40s 1.5x
推理 ~238s ~162s~2.7 min 1.5x

为什么不是 4.3x 矩阵乘法只是 OCR pipeline 的一部分。自回归解码(逐 token 生成天然串行、I/O 等待、版面检测中的非矩阵运算等不受线程数影响。


迭代 2批量多进程并行 — batch_ocr.py

思路: 多张图片时,用 multiprocessing.Pool 启动多个独立进程,每个进程加载一份 pipeline 实例,同时处理不同图片。

遇到的问题 & 修复(迭代 2.1

问题 原因 修复
系统卡顿/黑屏/无响应 Pool.starmap 同时启动 N 个进程,同步加载 N×2GB 模型CPU + 内存瞬间打满 ① 进程错峰启动(随机延迟 0~15spsutil 降低进程优先级 ③ 预留 1 核给 OS ④ imap_unordered 替代 starmap

策略:

  • 每个子进程独立调用 core.set_num_threads((总核心-1) / 进程数),预留核心给 OS
  • 例如 2 进程 × 9 线程 = 18 核,留 2 核给系统
  • --stagger 控制错峰窗口,默认 15s
# 2 进程并行(安全默认值)
uv run python batch_ocr.py images/

# 4 进程并行(需 32GB+ RAM
uv run python batch_ocr.py images/ --workers 4

# 自定义错峰窗口(值越大内存峰值越低,但总耗时增加)
uv run python batch_ocr.py images/ --workers 4 --stagger 30
配置 适用场景 理论加速比 内存开销 实际限制
set_num_threads(N) 单张图片 ~1.5x 无额外开销 自回归解码瓶颈
batch_ocr.py 批量多图 ~NxN=进程数) N × 2GB 内存/内存带宽,需错峰避免打满系统

⚠️ 每个进程独立加载模型(~2GB32GB RAM 建议从 --workers 2 开始测试。默认值是相对保守配置,但是否稳定仍取决于可用内存、散热、后台应用和图片复杂度。


迭代 3独立 GPU 子项目(待实机验证)

为避免 paddlepaddlepaddlepaddle-gpu 相互覆盖,在同一仓库新增 gpu/ 子项目,使用独立 Python、虚拟环境、依赖配置和锁文件。

已完成:

  • gpu/setup_env.py:根据 cu118 / cu126 Wheel 索引创建环境
  • gpu/verify_env.py:检查 CUDA 构建、设备数量并执行 GPU 矩阵乘法
  • gpu/main.py:显式指定 device="gpu:N",支持预热、多轮计时和 JSON 输出
  • 无 CUDA 时立即退出,不静默回退到 CPU
  • CPU 环境下已通过 Python 语法、CLI 和安全退出检查

尚未验证:

  • NVIDIA 驱动、CUDA Wheel 与目标 GPU 的兼容性
  • PaddleOCR-VL-1.6 GPU 模型初始化是否正常
  • GPU 显存峰值和真实推理速度
  • FP16/BF16、TensorRT 或批量推理收益

优化总结

迭代 状态 结果
CPU 初始版本 已实测 后续单图约 238s
CPU set_num_threads(20) 已实测 单图约 162s约 1.5x 加速
CPU 多进程批量 已实现,稳定性依机器而定 理论提升批量吞吐;当前没有足够的可靠实测数据支持固定加速比
独立 GPU 子项目 已实现,未实机验证 等待 NVIDIA CUDA GPU 测试

CPU 单图当前实测约 2.7 分钟。批量多进程主要提高总吞吐不会缩短某一张图片自身的推理延迟。GPU 性能在实机验证前不作预测。

已知局限

问题 影响 说明
CPU 推理极慢 单图 ~2.7 min优化后 0.9B VL 模型不适合 CPU 实时场景
自回归解码串行 无法更细粒度并行 生成阶段逐 token 依赖,多线程收益有限
内存占用大 每进程需 ~2GB 限制了 batch_ocr.py 并行度
Windows 控制台乱码 中文输出显示为乱码 GBK 编码问题,文件写入/pipe 正常
GPU 未实机验证 暂无 GPU 性能结论 当前机器只有集成显卡,需 NVIDIA CUDA GPU 验证
ccache 警告 无实际影响 仅影响首次编译加速,可忽略