PaddleOCR-VL-1.6_Demo/README.md

165 lines
5.5 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
本地 CPU 部署 [PaddlePaddle/PaddleOCR-VL-1.6](https://github.com/PaddlePaddle/PaddleOCR) 的 OCR 识别项目,包含完整的性能 Benchmark 和多级优化方案。
## 项目结构
```
ocr-VL1.6/
├── main.py # 单图 OCR + Benchmark已集成 set_num_threads 加速)
├── batch_ocr.py # 批量 OCR多进程并行加速
├── pyproject.toml # 项目配置uv 管理)
├── images/
│ └── 手写01.png # 测试图片手写中文1758×646
└── README.md
```
## 技术栈
| 组件 | 版本 | 说明 |
|------|------|------|
| Python | 3.13 | |
| PaddlePaddle | 3.2.1 | CPU 版(无 CUDA已编译 oneDNN/MKL-DNN |
| 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/ --workers 4
```
首次运行会自动从 ModelScope 下载模型文件(约 2GB后续使用缓存。
## 工作原理
`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 实例,同时处理不同图片。
**策略:**
- 每个子进程独立调用 `core.set_num_threads(总核心 / 进程数)`,避免线程争抢
- 例如 4 进程 × 5 线程 = 20 核心全部利用
```bash
# 4 进程并行
uv run python batch_ocr.py images/ --workers 4
```
| 配置 | 适用场景 | 理论加速比 | 内存开销 | 实际限制 |
|------|---------|-----------|---------|---------|
| `set_num_threads(N)` | 单张图片 | ~1.5x | 无额外开销 | 自回归解码瓶颈 |
| `batch_ocr.py` | 批量多图 | ~NxN=进程数) | N × 2GB | 内存/内存带宽 |
> ⚠️ 每个进程独立加载模型(~2GB32GB RAM 建议 `--workers ≤ 4`。
---
### 优化总结
```
初始: 238s/image (单线程, 无优化)
├─ 迭代1: set_num_threads(20) → 162s (1.5x, 单图最优)
└─ 迭代2: batch_ocr.py (4进程) → ~40s/image (5.9x, 批量场景)
```
> 单图理论极限约 2.7 分钟(受自回归解码串行特征限制),批量场景通过粗粒度并行可进一步摊薄。
## 已知局限
| 问题 | 影响 | 说明 |
|------|------|------|
| CPU 推理极慢 | 单图 ~2.7 min优化后 | 0.9B VL 模型不适合 CPU 实时场景 |
| 自回归解码串行 | 无法更细粒度并行 | 生成阶段逐 token 依赖,多线程收益有限 |
| 内存占用大 | 每进程需 ~2GB | 限制了 `batch_ocr.py` 并行度 |
| Windows 控制台乱码 | 中文输出显示为乱码 | GBK 编码问题,文件写入/pipe 正常 |
| ccache 警告 | 无实际影响 | 仅影响首次编译加速,可忽略 |