- Add MachineCodeParser for Swedish invoice payment line parsing - Fix OCR Reference extraction by normalizing account number spaces - Add cross-validation tests for pipeline and field_extractor - Update UI layout for compact upload and full-width results Key changes: - machine_code_parser.py: Handle spaces in Bankgiro numbers (e.g. "78 2 1 713") - pipeline.py: OCR and Amount override from payment_line, BG/PG comparison only - field_extractor.py: Improved invoice number normalization - app.py: Responsive UI layout changes Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
465 lines
12 KiB
Markdown
465 lines
12 KiB
Markdown
# Invoice Master POC v2
|
||
|
||
自动发票字段提取系统 - 使用 YOLOv11 + PaddleOCR 从瑞典 PDF 发票中提取结构化数据。
|
||
|
||
## 项目概述
|
||
|
||
本项目实现了一个完整的发票字段自动提取流程:
|
||
|
||
1. **自动标注**: 利用已有 CSV 结构化数据 + OCR 自动生成 YOLO 训练标注
|
||
2. **模型训练**: 使用 YOLOv11 训练字段检测模型
|
||
3. **推理提取**: 检测字段区域 → OCR 提取文本 → 字段规范化
|
||
|
||
### 当前进度
|
||
|
||
| 指标 | 数值 |
|
||
|------|------|
|
||
| **已标注文档** | 9,738 (9,709 成功) |
|
||
| **总体字段匹配率** | 94.8% (82,604/87,121) |
|
||
|
||
**各字段匹配率:**
|
||
|
||
| 字段 | 匹配率 | 说明 |
|
||
|------|--------|------|
|
||
| supplier_accounts(Bankgiro) | 100.0% | 供应商 Bankgiro |
|
||
| supplier_accounts(Plusgiro) | 100.0% | 供应商 Plusgiro |
|
||
| Plusgiro | 99.4% | 支付 Plusgiro |
|
||
| OCR | 99.1% | OCR 参考号 |
|
||
| Bankgiro | 99.0% | 支付 Bankgiro |
|
||
| InvoiceNumber | 98.9% | 发票号码 |
|
||
| InvoiceDueDate | 95.9% | 到期日期 |
|
||
| InvoiceDate | 95.5% | 发票日期 |
|
||
| Amount | 91.3% | 金额 |
|
||
| supplier_organisation_number | 78.2% | 供应商组织号 (CSV 数据质量问题) |
|
||
|
||
## 运行环境
|
||
|
||
本项目**必须**在 **WSL + Conda** 环境中运行。
|
||
|
||
### 系统要求
|
||
|
||
| 环境 | 要求 |
|
||
|------|------|
|
||
| **WSL** | WSL 2 + Ubuntu 22.04 |
|
||
| **Conda** | Miniconda 或 Anaconda |
|
||
| **Python** | 3.10+ (通过 Conda 管理) |
|
||
| **GPU** | NVIDIA GPU + CUDA 12.x (强烈推荐) |
|
||
| **数据库** | PostgreSQL (存储标注结果) |
|
||
|
||
## 功能特点
|
||
|
||
- **双模式 PDF 处理**: 支持文本层 PDF 和扫描图 PDF
|
||
- **自动标注**: 利用已有 CSV 结构化数据自动生成 YOLO 训练数据
|
||
- **多策略字段匹配**: 精确匹配、子串匹配、规范化匹配
|
||
- **数据库存储**: 标注结果存储在 PostgreSQL,支持增量处理和断点续传
|
||
- **YOLO 检测**: 使用 YOLOv11 检测发票字段区域
|
||
- **OCR 识别**: 使用 PaddleOCR v5 提取检测区域的文本
|
||
- **Web 应用**: 提供 REST API 和可视化界面
|
||
- **增量训练**: 支持在已训练模型基础上继续训练
|
||
|
||
## 支持的字段
|
||
|
||
| 类别 ID | 字段名 | 说明 |
|
||
|---------|--------|------|
|
||
| 0 | invoice_number | 发票号码 |
|
||
| 1 | invoice_date | 发票日期 |
|
||
| 2 | invoice_due_date | 到期日期 |
|
||
| 3 | ocr_number | OCR 参考号 (瑞典支付系统) |
|
||
| 4 | bankgiro | Bankgiro 号码 |
|
||
| 5 | plusgiro | Plusgiro 号码 |
|
||
| 6 | amount | 金额 |
|
||
| 7 | supplier_organisation_number | 供应商组织号 |
|
||
|
||
## 安装
|
||
|
||
```bash
|
||
# 1. 进入 WSL
|
||
wsl -d Ubuntu-22.04
|
||
|
||
# 2. 创建 Conda 环境
|
||
conda create -n invoice-py311 python=3.11 -y
|
||
conda activate invoice-py311
|
||
|
||
# 3. 进入项目目录
|
||
cd /mnt/c/Users/yaoji/git/ColaCoder/invoice-master-poc-v2
|
||
|
||
# 4. 安装依赖
|
||
pip install -r requirements.txt
|
||
|
||
# 5. 安装 Web 依赖
|
||
pip install uvicorn fastapi python-multipart pydantic
|
||
```
|
||
|
||
## 快速开始
|
||
|
||
### 1. 准备数据
|
||
|
||
```
|
||
~/invoice-data/
|
||
├── raw_pdfs/
|
||
│ ├── {DocumentId}.pdf
|
||
│ └── ...
|
||
├── structured_data/
|
||
│ └── document_export_YYYYMMDD.csv
|
||
└── dataset/
|
||
└── temp/ (渲染的图片)
|
||
```
|
||
|
||
CSV 格式:
|
||
```csv
|
||
DocumentId,InvoiceDate,InvoiceNumber,InvoiceDueDate,OCR,Bankgiro,Plusgiro,Amount
|
||
3be53fd7-...,2025-12-13,100017500321,2026-01-03,100017500321,53939484,,114
|
||
```
|
||
|
||
### 2. 自动标注
|
||
|
||
```bash
|
||
# 使用双池模式 (CPU + GPU)
|
||
python -m src.cli.autolabel \
|
||
--dual-pool \
|
||
--cpu-workers 3 \
|
||
--gpu-workers 1
|
||
|
||
# 单线程模式
|
||
python -m src.cli.autolabel --workers 4
|
||
```
|
||
|
||
### 3. 训练模型
|
||
|
||
```bash
|
||
# 从预训练模型开始训练
|
||
python -m src.cli.train \
|
||
--model yolo11n.pt \
|
||
--epochs 100 \
|
||
--batch 16 \
|
||
--name invoice_yolo11n_full \
|
||
--dpi 150
|
||
```
|
||
|
||
### 4. 增量训练
|
||
|
||
当添加新数据后,可以在已训练模型基础上继续训练:
|
||
|
||
```bash
|
||
# 从已训练的 best.pt 继续训练
|
||
python -m src.cli.train \
|
||
--model runs/train/invoice_yolo11n_full/weights/best.pt \
|
||
--epochs 30 \
|
||
--batch 16 \
|
||
--name invoice_yolo11n_v2 \
|
||
--dpi 150
|
||
```
|
||
|
||
**增量训练建议**:
|
||
|
||
| 场景 | 建议 |
|
||
|------|------|
|
||
| 添加少量新数据 (<20%) | 继续训练 10-30 epochs |
|
||
| 添加大量新数据 (>50%) | 继续训练 50-100 epochs |
|
||
| 修正大量标注错误 | 从头训练 |
|
||
| 添加新的字段类型 | 从头训练 |
|
||
|
||
### 5. 推理
|
||
|
||
```bash
|
||
# 命令行推理
|
||
python -m src.cli.infer \
|
||
--model runs/train/invoice_yolo11n_full/weights/best.pt \
|
||
--input path/to/invoice.pdf \
|
||
--output result.json \
|
||
--gpu
|
||
```
|
||
|
||
### 6. Web 应用
|
||
|
||
```bash
|
||
# 启动 Web 服务器
|
||
python run_server.py --port 8000
|
||
|
||
# 开发模式 (自动重载)
|
||
python run_server.py --debug --reload
|
||
|
||
# 禁用 GPU
|
||
python run_server.py --no-gpu
|
||
```
|
||
|
||
访问 **http://localhost:8000** 使用 Web 界面。
|
||
|
||
#### Web API 端点
|
||
|
||
| 方法 | 端点 | 描述 |
|
||
|------|------|------|
|
||
| GET | `/` | Web UI 界面 |
|
||
| GET | `/api/v1/health` | 健康检查 |
|
||
| POST | `/api/v1/infer` | 上传文件并推理 |
|
||
| GET | `/api/v1/results/{filename}` | 获取可视化图片 |
|
||
|
||
## 训练配置
|
||
|
||
### YOLO 训练参数
|
||
|
||
```bash
|
||
python -m src.cli.train [OPTIONS]
|
||
|
||
Options:
|
||
--model, -m 基础模型 (默认: yolo11n.pt)
|
||
--epochs, -e 训练轮数 (默认: 100)
|
||
--batch, -b 批大小 (默认: 16)
|
||
--imgsz 图像尺寸 (默认: 1280)
|
||
--dpi PDF 渲染 DPI (默认: 150)
|
||
--name 训练名称
|
||
--limit 限制文档数 (用于测试)
|
||
--device 设备 (0=GPU, cpu)
|
||
```
|
||
|
||
### 训练最佳实践
|
||
|
||
1. **禁用翻转增强** (文本检测):
|
||
```python
|
||
fliplr=0.0, flipud=0.0
|
||
```
|
||
|
||
2. **使用 Early Stopping**:
|
||
```python
|
||
patience=20
|
||
```
|
||
|
||
3. **启用 AMP** (混合精度训练):
|
||
```python
|
||
amp=True
|
||
```
|
||
|
||
4. **保存检查点**:
|
||
```python
|
||
save_period=10
|
||
```
|
||
|
||
### 训练结果示例
|
||
|
||
使用约 10,000 张训练图片,100 epochs 后的结果:
|
||
|
||
| 指标 | 值 |
|
||
|------|-----|
|
||
| **mAP@0.5** | 98.7% |
|
||
| **mAP@0.5-0.95** | 87.4% |
|
||
| **Precision** | 97.5% |
|
||
| **Recall** | 95.5% |
|
||
|
||
> 注:目前仍在持续标注更多数据,预计最终将有 25,000+ 张标注图片用于训练。
|
||
|
||
## 项目结构
|
||
|
||
```
|
||
invoice-master-poc-v2/
|
||
├── src/
|
||
│ ├── cli/ # 命令行工具
|
||
│ │ ├── autolabel.py # 自动标注
|
||
│ │ ├── train.py # 模型训练
|
||
│ │ ├── infer.py # 推理
|
||
│ │ └── serve.py # Web 服务器
|
||
│ ├── pdf/ # PDF 处理
|
||
│ │ ├── extractor.py # 文本提取
|
||
│ │ ├── renderer.py # 图像渲染
|
||
│ │ └── detector.py # 类型检测
|
||
│ ├── ocr/ # PaddleOCR 封装
|
||
│ ├── normalize/ # 字段规范化
|
||
│ ├── matcher/ # 字段匹配
|
||
│ ├── yolo/ # YOLO 相关
|
||
│ │ ├── annotation_generator.py
|
||
│ │ └── db_dataset.py
|
||
│ ├── inference/ # 推理管道
|
||
│ │ ├── pipeline.py
|
||
│ │ ├── yolo_detector.py
|
||
│ │ └── field_extractor.py
|
||
│ ├── processing/ # 多池处理架构
|
||
│ │ ├── worker_pool.py
|
||
│ │ ├── cpu_pool.py
|
||
│ │ ├── gpu_pool.py
|
||
│ │ ├── task_dispatcher.py
|
||
│ │ └── dual_pool_coordinator.py
|
||
│ ├── web/ # Web 应用
|
||
│ │ ├── app.py # FastAPI 应用
|
||
│ │ ├── routes.py # API 路由
|
||
│ │ ├── services.py # 业务逻辑
|
||
│ │ ├── schemas.py # 数据模型
|
||
│ │ └── config.py # 配置
|
||
│ └── data/ # 数据处理
|
||
├── config.py # 配置文件
|
||
├── run_server.py # Web 服务器启动脚本
|
||
├── runs/ # 训练输出
|
||
│ └── train/
|
||
│ └── invoice_yolo11n_full/
|
||
│ └── weights/
|
||
│ ├── best.pt
|
||
│ └── last.pt
|
||
└── requirements.txt
|
||
```
|
||
|
||
## 多池处理架构
|
||
|
||
项目使用 CPU + GPU 双池架构处理不同类型的 PDF:
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────┐
|
||
│ DualPoolCoordinator │
|
||
│ ┌─────────────────┐ ┌─────────────────┐ │
|
||
│ │ CPU Pool │ │ GPU Pool │ │
|
||
│ │ (3 workers) │ │ (1 worker) │ │
|
||
│ │ │ │ │ │
|
||
│ │ Text PDFs │ │ Scanned PDFs │ │
|
||
│ │ ~50-87 it/s │ │ ~1-2 it/s │ │
|
||
│ └─────────────────┘ └─────────────────┘ │
|
||
│ │
|
||
│ TaskDispatcher: 根据 PDF 类型分配任务 │
|
||
└─────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 关键设计
|
||
|
||
- **spawn 启动方式**: 兼容 CUDA 多进程
|
||
- **as_completed()**: 无死锁结果收集
|
||
- **进程初始化器**: 每个 worker 加载一次模型
|
||
- **协调器持久化**: 跨 CSV 文件复用 worker 池
|
||
|
||
## 配置文件
|
||
|
||
### config.py
|
||
|
||
```python
|
||
# 数据库配置
|
||
DATABASE = {
|
||
'host': '192.168.68.31',
|
||
'port': 5432,
|
||
'database': 'docmaster',
|
||
'user': 'docmaster',
|
||
'password': '******',
|
||
}
|
||
|
||
# 路径配置
|
||
PATHS = {
|
||
'csv_dir': '~/invoice-data/structured_data',
|
||
'pdf_dir': '~/invoice-data/raw_pdfs',
|
||
'output_dir': '~/invoice-data/dataset',
|
||
}
|
||
```
|
||
|
||
## CLI 命令参考
|
||
|
||
### autolabel
|
||
|
||
```bash
|
||
python -m src.cli.autolabel [OPTIONS]
|
||
|
||
Options:
|
||
--csv, -c CSV 文件路径 (支持 glob)
|
||
--pdf-dir, -p PDF 文件目录
|
||
--output, -o 输出目录
|
||
--workers, -w 单线程模式 worker 数 (默认: 4)
|
||
--dual-pool 启用双池模式
|
||
--cpu-workers CPU 池 worker 数 (默认: 3)
|
||
--gpu-workers GPU 池 worker 数 (默认: 1)
|
||
--dpi 渲染 DPI (默认: 150)
|
||
--limit, -l 限制处理文档数
|
||
```
|
||
|
||
### train
|
||
|
||
```bash
|
||
python -m src.cli.train [OPTIONS]
|
||
|
||
Options:
|
||
--model, -m 基础模型路径
|
||
--epochs, -e 训练轮数 (默认: 100)
|
||
--batch, -b 批大小 (默认: 16)
|
||
--imgsz 图像尺寸 (默认: 1280)
|
||
--dpi PDF 渲染 DPI (默认: 150)
|
||
--name 训练名称
|
||
--limit 限制文档数
|
||
```
|
||
|
||
### infer
|
||
|
||
```bash
|
||
python -m src.cli.infer [OPTIONS]
|
||
|
||
Options:
|
||
--model, -m 模型路径
|
||
--input, -i 输入 PDF/图像
|
||
--output, -o 输出 JSON 路径
|
||
--confidence 置信度阈值 (默认: 0.5)
|
||
--dpi 渲染 DPI (默认: 300)
|
||
--gpu 使用 GPU
|
||
```
|
||
|
||
### serve
|
||
|
||
```bash
|
||
python run_server.py [OPTIONS]
|
||
|
||
Options:
|
||
--host 绑定地址 (默认: 0.0.0.0)
|
||
--port 端口 (默认: 8000)
|
||
--model, -m 模型路径
|
||
--confidence 置信度阈值 (默认: 0.3)
|
||
--dpi 渲染 DPI (默认: 150)
|
||
--no-gpu 禁用 GPU
|
||
--reload 开发模式自动重载
|
||
--debug 调试模式
|
||
```
|
||
|
||
## Python API
|
||
|
||
```python
|
||
from src.inference import InferencePipeline
|
||
|
||
# 初始化
|
||
pipeline = InferencePipeline(
|
||
model_path='runs/train/invoice_yolo11n_full/weights/best.pt',
|
||
confidence_threshold=0.3,
|
||
use_gpu=True,
|
||
dpi=150
|
||
)
|
||
|
||
# 处理 PDF
|
||
result = pipeline.process_pdf('invoice.pdf')
|
||
|
||
# 处理图片
|
||
result = pipeline.process_image('invoice.png')
|
||
|
||
# 获取结果
|
||
print(result.fields) # {'InvoiceNumber': '12345', 'Amount': '1234.56', ...}
|
||
print(result.confidence) # {'InvoiceNumber': 0.95, 'Amount': 0.92, ...}
|
||
print(result.to_json()) # JSON 格式输出
|
||
```
|
||
|
||
## 开发状态
|
||
|
||
- [x] 文本层 PDF 自动标注
|
||
- [x] 扫描图 OCR 自动标注
|
||
- [x] 多策略字段匹配 (精确/子串/规范化)
|
||
- [x] PostgreSQL 数据库存储 (断点续传)
|
||
- [x] 信号处理和超时保护
|
||
- [x] YOLO 训练 (98.7% mAP@0.5)
|
||
- [x] 推理管道
|
||
- [x] 字段规范化和验证
|
||
- [x] Web 应用 (FastAPI + 前端 UI)
|
||
- [x] 增量训练支持
|
||
- [ ] 完成全部 25,000+ 文档标注
|
||
- [ ] 表格 items 处理
|
||
- [ ] 模型量化部署
|
||
|
||
## 技术栈
|
||
|
||
| 组件 | 技术 |
|
||
|------|------|
|
||
| **目标检测** | YOLOv11 (Ultralytics) |
|
||
| **OCR 引擎** | PaddleOCR v5 (PP-OCRv5) |
|
||
| **PDF 处理** | PyMuPDF (fitz) |
|
||
| **数据库** | PostgreSQL + psycopg2 |
|
||
| **Web 框架** | FastAPI + Uvicorn |
|
||
| **深度学习** | PyTorch + CUDA |
|
||
|
||
## 许可证
|
||
|
||
MIT License
|