10 KiB
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(系统友好的多进程版本)
├── pyproject.toml # CPU 项目依赖
├── uv.lock # CPU 锁文件
├── gpu/ # 独立 GPU 子项目
│ ├── main.py # GPU 单图 Benchmark
│ ├── 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
技术栈
| 组件 | 版本 | 说明 |
|---|---|---|
| Python(CPU) | 3.13 | 根目录独立环境 |
| Python(GPU) | 3.11 | gpu/ 独立环境,提升 GPU Wheel 兼容性 |
| PaddlePaddle | 3.2.1 | CPU 使用 paddlepaddle,GPU 使用 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/
首次运行会自动从 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。
工作原理
PaddleOCRVL pipeline 分两阶段:
输入图片 → [PP-DocLayoutV3 版面检测] → [PaddleOCR-VL-1.6-0.9B 文字识别] → 结构化输出
- 版面检测 — PP-DocLayoutV3 检测页面中的文本块区域(坐标 + 标签 + 置信度)
- OCR 识别 — PaddleOCR-VL-1.6-0.9B(GQA 架构视觉语言模型)逐块识别文字
- 结果输出 — 返回
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~15s)② psutil 降低进程优先级 ③ 预留 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 |
批量多图 | ~Nx(N=进程数) | N × 2GB | 内存/内存带宽,需错峰避免打满系统 |
⚠️ 每个进程独立加载模型(~2GB),32GB RAM 建议从
--workers 2开始测试。默认值是相对保守配置,但是否稳定仍取决于可用内存、散热、后台应用和图片复杂度。
迭代 3:独立 GPU 子项目(待实机验证)
为避免 paddlepaddle 和 paddlepaddle-gpu 相互覆盖,在同一仓库新增 gpu/ 子项目,使用独立 Python、虚拟环境、依赖配置和锁文件。
已完成:
gpu/setup_env.py:根据cu118/cu126Wheel 索引创建环境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 警告 | 无实际影响 | 仅影响首次编译加速,可忽略 |