PaddleOCR-VL-1.6_Demo/README.md

429 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ocr-VL1.6
本地部署 [PaddlePaddle/PaddleOCR-VL-1.6](https://github.com/PaddlePaddle/PaddleOCR) 的实验项目,包含已实测的 CPU 版本和独立隔离、待 NVIDIA GPU 验证的 GPU 版本。
> 在线 Demo: [HuggingFace Space](https://huggingface.co/spaces/PaddlePaddle/PaddleOCR-VL-1.6_Online_Demo) · 模型权重: [HuggingFace](https://huggingface.co/PaddlePaddle/PaddleOCR-VL-1.6)
## 项目结构
```
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 使用 `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](https://github.com/astral-sh/uv) 包管理器
### 安装
```bash
uv sync
```
### 运行
```bash
# 单张图片 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`
```bash
# 查看 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`](gpu/README.md)。
## 运行日志
所有主要入口均使用统一日志格式:
```text
2026-07-16 14:28:02 | INFO | pid=27644 | PAGE_OCR_COMPLETED page=1 seconds=36.345
```
默认日志目录:
```text
logs/
├── single/ # main.py / gpu/main.py
├── batch/ # batch_ocr.py
└── pdf/ # pdf_ocr.py / gpu/pdf_ocr.py
```
默认文件名包含输入名、设备和时间戳,例如:
```text
logs/pdf/sample-cpu-20260716-142802.log
logs/single/手写01-gpu0-20260716-142802.log
```
可用参数:
```bash
# 指定日志文件
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.py``gpu/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.py``gpu/pdf_ocr.py` 记录:
- PDF 预检、打开和 manifest 创建耗时
- 模型初始化耗时
- 每页渲染耗时
- 每页 OCR 推理耗时
- 每页 Markdown/JSON 导出耗时
- manifest 与合并文件保存耗时
- 每页总耗时、累计耗时和预计剩余时间ETA
- 每页图片尺寸、版面框数量和文本块数量
- 完成页、失败页和断点续传前已完成页数
- 各阶段累计值、平均每页耗时和任务总用时
- 从程序启动(含模型加载)到退出的程序总用时
PDF 的 `manifest.json` 同时包含 `summary.timing`
```json
{
"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 使用
```bash
# 处理整个 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 上运行:
```bash
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 使用同样的一基页码记录。
### 输出结构
```text
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_index`、`page_number`、`page_count` 和 `render_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~15s`psutil` 降低进程优先级 ③ 预留 1 核给 OS ④ `imap_unordered` 替代 `starmap` |
**策略:**
- 每个子进程独立调用 `core.set_num_threads((总核心-1) / 进程数)`,预留核心给 OS
- 例如 2 进程 × 9 线程 = 18 核,留 2 核给系统
- `--stagger` 控制错峰窗口,默认 15s
```bash
# 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 子项目(待实机验证)
为避免 `paddlepaddle``paddlepaddle-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 警告 | 无实际影响 | 仅影响首次编译加速,可忽略 |