restructure project
This commit is contained in:
807
README.md
807
README.md
@@ -8,7 +8,25 @@
|
||||
|
||||
1. **自动标注**: 利用已有 CSV 结构化数据 + OCR 自动生成 YOLO 训练标注
|
||||
2. **模型训练**: 使用 YOLOv11 训练字段检测模型
|
||||
3. **推理提取**: 检测字段区域 → OCR 提取文本 → 字段规范化
|
||||
3. **推理提取**: 检测字段区域 -> OCR 提取文本 -> 字段规范化
|
||||
|
||||
### 架构
|
||||
|
||||
项目采用 **monorepo + 三包分离** 架构,训练和推理可独立部署:
|
||||
|
||||
```
|
||||
packages/
|
||||
├── shared/ # 共享库 (PDF, OCR, 规范化, 匹配, 工具)
|
||||
├── training/ # 训练服务 (GPU, 按需启动)
|
||||
└── inference/ # 推理服务 (常驻运行)
|
||||
```
|
||||
|
||||
| 服务 | 部署目标 | GPU | 生命周期 |
|
||||
|------|---------|-----|---------|
|
||||
| **Inference** | Azure App Service | 可选 | 常驻 7x24 |
|
||||
| **Training** | Azure ACI | 必需 | 按需启动/销毁 |
|
||||
|
||||
两个服务通过共享 PostgreSQL 数据库通信。推理服务通过 API 触发训练任务,训练服务从数据库拾取任务执行。
|
||||
|
||||
### 当前进度
|
||||
|
||||
@@ -16,6 +34,8 @@
|
||||
|------|------|
|
||||
| **已标注文档** | 9,738 (9,709 成功) |
|
||||
| **总体字段匹配率** | 94.8% (82,604/87,121) |
|
||||
| **测试** | 922 passed |
|
||||
| **模型 mAP@0.5** | 93.5% |
|
||||
|
||||
**各字段匹配率:**
|
||||
|
||||
@@ -42,24 +62,83 @@
|
||||
|------|------|
|
||||
| **WSL** | WSL 2 + Ubuntu 22.04 |
|
||||
| **Conda** | Miniconda 或 Anaconda |
|
||||
| **Python** | 3.10+ (通过 Conda 管理) |
|
||||
| **Python** | 3.11+ (通过 Conda 管理) |
|
||||
| **GPU** | NVIDIA GPU + CUDA 12.x (强烈推荐) |
|
||||
| **数据库** | PostgreSQL (存储标注结果) |
|
||||
|
||||
## 功能特点
|
||||
## 安装
|
||||
|
||||
- **双模式 PDF 处理**: 支持文本层 PDF 和扫描图 PDF
|
||||
- **自动标注**: 利用已有 CSV 结构化数据自动生成 YOLO 训练数据
|
||||
- **多策略字段匹配**: 精确匹配、子串匹配、规范化匹配
|
||||
- **数据库存储**: 标注结果存储在 PostgreSQL,支持增量处理和断点续传
|
||||
- **YOLO 检测**: 使用 YOLOv11 检测发票字段区域
|
||||
- **OCR 识别**: 使用 PaddleOCR v5 提取检测区域的文本
|
||||
- **统一解析器**: payment_line 和 customer_number 采用独立解析器模块
|
||||
- **交叉验证**: payment_line 数据与单独检测字段交叉验证,优先采用 payment_line 值
|
||||
- **文档类型识别**: 自动区分 invoice (有 payment_line) 和 letter (无 payment_line)
|
||||
- **Web 应用**: 提供 REST API 和可视化界面
|
||||
- **增量训练**: 支持在已训练模型基础上继续训练
|
||||
- **内存优化**: 支持低内存模式训练 (--low-memory)
|
||||
```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. 安装三个包 (editable mode)
|
||||
pip install -e packages/shared
|
||||
pip install -e packages/training
|
||||
pip install -e packages/inference
|
||||
```
|
||||
|
||||
## 项目结构
|
||||
|
||||
```
|
||||
invoice-master-poc-v2/
|
||||
├── packages/
|
||||
│ ├── shared/ # 共享库
|
||||
│ │ ├── setup.py
|
||||
│ │ └── shared/
|
||||
│ │ ├── pdf/ # PDF 处理 (提取, 渲染, 检测)
|
||||
│ │ ├── ocr/ # PaddleOCR 封装 + 机器码解析
|
||||
│ │ ├── normalize/ # 字段规范化 (10 种 normalizer)
|
||||
│ │ ├── matcher/ # 字段匹配 (精确/子串/模糊)
|
||||
│ │ ├── utils/ # 工具 (验证, 清理, 模糊匹配)
|
||||
│ │ ├── data/ # DocumentDB, CSVLoader
|
||||
│ │ ├── config.py # 全局配置 (数据库, 路径, DPI)
|
||||
│ │ └── exceptions.py # 异常定义
|
||||
│ │
|
||||
│ ├── training/ # 训练服务 (GPU, 按需)
|
||||
│ │ ├── setup.py
|
||||
│ │ ├── Dockerfile
|
||||
│ │ ├── run_training.py # 入口 (--task-id 或 --poll)
|
||||
│ │ └── training/
|
||||
│ │ ├── cli/ # train, autolabel, analyze_*, validate
|
||||
│ │ ├── yolo/ # db_dataset, annotation_generator
|
||||
│ │ ├── processing/ # CPU/GPU worker pool, task dispatcher
|
||||
│ │ └── data/ # training_db, autolabel_report
|
||||
│ │
|
||||
│ └── inference/ # 推理服务 (常驻)
|
||||
│ ├── setup.py
|
||||
│ ├── Dockerfile
|
||||
│ ├── run_server.py # Web 服务器入口
|
||||
│ └── inference/
|
||||
│ ├── cli/ # infer, serve
|
||||
│ ├── pipeline/ # YOLO 检测, 字段提取, 解析器
|
||||
│ ├── web/ # FastAPI 应用
|
||||
│ │ ├── api/v1/ # REST API (admin, public, batch)
|
||||
│ │ ├── schemas/ # Pydantic 数据模型
|
||||
│ │ ├── services/ # 业务逻辑
|
||||
│ │ ├── core/ # 认证, 调度器, 限流
|
||||
│ │ └── workers/ # 后台任务队列
|
||||
│ ├── validation/ # LLM 验证器
|
||||
│ ├── data/ # AdminDB, AsyncRequestDB, Models
|
||||
│ └── azure/ # ACI 训练触发器
|
||||
│
|
||||
├── migrations/ # 数据库迁移
|
||||
│ ├── 001_async_tables.sql
|
||||
│ ├── 002_nullable_admin_token.sql
|
||||
│ └── 003_training_tasks.sql
|
||||
├── frontend/ # React 前端 (Vite + TypeScript)
|
||||
├── tests/ # 测试 (922 tests)
|
||||
├── docker-compose.yml # 本地开发 (postgres + inference + training)
|
||||
├── run_server.py # 快捷启动脚本
|
||||
└── runs/train/ # 训练输出 (weights, curves)
|
||||
```
|
||||
|
||||
## 支持的字段
|
||||
|
||||
@@ -76,476 +155,129 @@
|
||||
| 8 | payment_line | 支付行 (机器可读格式) |
|
||||
| 9 | customer_number | 客户编号 |
|
||||
|
||||
## DPI 配置
|
||||
|
||||
**重要**: 系统所有组件统一使用 **150 DPI**,确保训练和推理的一致性。
|
||||
|
||||
DPI(每英寸点数)设置必须在训练和推理时保持一致,否则会导致:
|
||||
- 检测框尺寸失配
|
||||
- mAP显著下降(可能从93.5%降到60-70%)
|
||||
- 字段漏检或误检
|
||||
|
||||
### 配置位置
|
||||
|
||||
| 组件 | 配置文件 | 配置项 |
|
||||
|------|---------|--------|
|
||||
| **全局常量** | `src/config.py` | `DEFAULT_DPI = 150` |
|
||||
| **Web推理** | `src/web/config.py` | `ModelConfig.dpi` (导入自 `src.config`) |
|
||||
| **CLI推理** | `src/cli/infer.py` | `--dpi` 默认值 = `DEFAULT_DPI` |
|
||||
| **自动标注** | `src/config.py` | `AUTOLABEL['dpi'] = DEFAULT_DPI` |
|
||||
| **PDF转图** | `src/web/api/v1/admin/documents.py` | 使用 `DEFAULT_DPI` |
|
||||
|
||||
### 使用示例
|
||||
|
||||
```bash
|
||||
# 训练(使用默认150 DPI)
|
||||
python -m src.cli.autolabel --dual-pool --cpu-workers 3 --gpu-workers 1
|
||||
|
||||
# 推理(默认150 DPI,与训练一致)
|
||||
python -m src.cli.infer -m runs/train/invoice_fields/weights/best.pt -i invoice.pdf
|
||||
|
||||
# 手动指定DPI(仅当需要与非默认训练DPI的模型配合时)
|
||||
python -m src.cli.infer -m custom_model.pt -i invoice.pdf --dpi 150
|
||||
```
|
||||
|
||||
## 安装
|
||||
|
||||
```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. 自动标注
|
||||
### 1. 自动标注
|
||||
|
||||
```bash
|
||||
# 使用双池模式 (CPU + GPU)
|
||||
python -m src.cli.autolabel \
|
||||
python -m training.cli.autolabel \
|
||||
--dual-pool \
|
||||
--cpu-workers 3 \
|
||||
--gpu-workers 1
|
||||
|
||||
# 单线程模式
|
||||
python -m src.cli.autolabel --workers 4
|
||||
python -m training.cli.autolabel --workers 4
|
||||
```
|
||||
|
||||
### 3. 训练模型
|
||||
### 2. 训练模型
|
||||
|
||||
```bash
|
||||
# 从预训练模型开始训练
|
||||
python -m src.cli.train \
|
||||
python -m training.cli.train \
|
||||
--model yolo11n.pt \
|
||||
--epochs 100 \
|
||||
--batch 16 \
|
||||
--name invoice_fields \
|
||||
--dpi 150
|
||||
|
||||
# 低内存模式 (适用于内存不足场景)
|
||||
python -m src.cli.train \
|
||||
# 低内存模式
|
||||
python -m training.cli.train \
|
||||
--model yolo11n.pt \
|
||||
--epochs 100 \
|
||||
--name invoice_fields \
|
||||
--low-memory \
|
||||
--workers 4 \
|
||||
--no-cache
|
||||
--low-memory
|
||||
|
||||
# 从检查点恢复训练 (训练中断后)
|
||||
python -m src.cli.train \
|
||||
# 从检查点恢复训练
|
||||
python -m training.cli.train \
|
||||
--model runs/train/invoice_fields/weights/last.pt \
|
||||
--epochs 100 \
|
||||
--name invoice_fields \
|
||||
--resume
|
||||
```
|
||||
|
||||
### 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. 推理
|
||||
### 3. 推理
|
||||
|
||||
```bash
|
||||
# 命令行推理
|
||||
python -m src.cli.infer \
|
||||
python -m inference.cli.infer \
|
||||
--model runs/train/invoice_fields/weights/best.pt \
|
||||
--input path/to/invoice.pdf \
|
||||
--output result.json \
|
||||
--gpu
|
||||
|
||||
# 批量推理
|
||||
python -m src.cli.infer \
|
||||
--model runs/train/invoice_fields/weights/best.pt \
|
||||
--input invoices/*.pdf \
|
||||
--output results/ \
|
||||
--gpu
|
||||
```
|
||||
|
||||
**推理结果包含**:
|
||||
- `fields`: 提取的字段值 (InvoiceNumber, Amount, payment_line, customer_number 等)
|
||||
- `confidence`: 各字段的置信度
|
||||
- `document_type`: 文档类型 ("invoice" 或 "letter")
|
||||
- `cross_validation`: payment_line 交叉验证结果 (如果有)
|
||||
|
||||
### 6. Web 应用
|
||||
|
||||
**在 WSL 环境中启动**:
|
||||
### 4. Web 应用
|
||||
|
||||
```bash
|
||||
# 方法 1: 从 Windows PowerShell 启动 (推荐)
|
||||
# 从 Windows PowerShell 启动
|
||||
wsl bash -c "source ~/miniconda3/etc/profile.d/conda.sh && conda activate invoice-py311 && cd /mnt/c/Users/yaoji/git/ColaCoder/invoice-master-poc-v2 && python run_server.py --port 8000"
|
||||
|
||||
# 方法 2: 在 WSL 内启动
|
||||
conda activate invoice-py311
|
||||
cd /mnt/c/Users/yaoji/git/ColaCoder/invoice-master-poc-v2
|
||||
python run_server.py --port 8000
|
||||
|
||||
# 方法 3: 使用启动脚本
|
||||
./start_web.sh
|
||||
# 启动前端
|
||||
cd frontend && npm install && npm run dev
|
||||
# 访问 http://localhost:5173
|
||||
```
|
||||
|
||||
**服务启动后**:
|
||||
- 访问 **http://localhost:8000** 使用 Web 界面
|
||||
- 服务会自动加载模型 `runs/train/invoice_fields/weights/best.pt`
|
||||
- GPU 默认启用,置信度阈值 0.5
|
||||
### 5. Docker 本地开发
|
||||
|
||||
#### Web API 端点
|
||||
```bash
|
||||
docker-compose up
|
||||
# inference: http://localhost:8000
|
||||
# training: 轮询模式自动拾取任务
|
||||
```
|
||||
|
||||
## 训练触发流程
|
||||
|
||||
推理服务通过 API 触发训练,训练在独立的 GPU 实例上执行:
|
||||
|
||||
```
|
||||
Inference API PostgreSQL Training (ACI)
|
||||
| | |
|
||||
POST /admin/training/trigger | |
|
||||
|-> INSERT training_tasks ------>| status=pending |
|
||||
|-> Azure SDK: create ACI --------------------------------> 启动
|
||||
| | |
|
||||
| |<-- SELECT pending -----+
|
||||
| |--- UPDATE running -----+
|
||||
| | 执行训练...
|
||||
| |<-- UPDATE completed ---+
|
||||
| | + model_path |
|
||||
| | + metrics 自动关机
|
||||
| | |
|
||||
GET /admin/training/{id} | |
|
||||
|-> SELECT training_tasks ------>| |
|
||||
+-- return status + metrics | |
|
||||
```
|
||||
|
||||
## Web API 端点
|
||||
|
||||
**Public API:**
|
||||
|
||||
| 方法 | 端点 | 描述 |
|
||||
|------|------|------|
|
||||
| GET | `/` | Web UI 界面 |
|
||||
| GET | `/api/v1/health` | 健康检查 |
|
||||
| POST | `/api/v1/infer` | 上传文件并推理 |
|
||||
| GET | `/api/v1/results/{filename}` | 获取可视化图片 |
|
||||
| POST | `/api/v1/async/infer` | 异步推理 |
|
||||
| GET | `/api/v1/async/status/{task_id}` | 查询异步任务状态 |
|
||||
|
||||
#### API 响应格式
|
||||
**Admin API** (需要 `X-Admin-Token` header):
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"result": {
|
||||
"document_id": "abc123",
|
||||
"document_type": "invoice",
|
||||
"fields": {
|
||||
"InvoiceNumber": "12345",
|
||||
"Amount": "1234.56",
|
||||
"payment_line": "# 94228110015950070 # > 48666036#14#",
|
||||
"customer_number": "UMJ 436-R"
|
||||
},
|
||||
"confidence": {
|
||||
"InvoiceNumber": 0.95,
|
||||
"Amount": 0.92
|
||||
},
|
||||
"cross_validation": {
|
||||
"is_valid": true,
|
||||
"ocr_match": true,
|
||||
"amount_match": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 训练配置
|
||||
|
||||
### 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)
|
||||
--resume 从检查点恢复训练
|
||||
--low-memory 启用低内存模式 (batch=8, workers=4, no-cache)
|
||||
--workers 数据加载 worker 数 (默认: 8)
|
||||
--cache 缓存图像到内存
|
||||
```
|
||||
|
||||
### 训练最佳实践
|
||||
|
||||
1. **禁用翻转增强** (文本检测):
|
||||
```python
|
||||
fliplr=0.0, flipud=0.0
|
||||
```
|
||||
|
||||
2. **使用 Early Stopping**:
|
||||
```python
|
||||
patience=20
|
||||
```
|
||||
|
||||
3. **启用 AMP** (混合精度训练):
|
||||
```python
|
||||
amp=True
|
||||
```
|
||||
|
||||
4. **保存检查点**:
|
||||
```python
|
||||
save_period=10
|
||||
```
|
||||
|
||||
### 训练结果示例
|
||||
|
||||
**最新训练结果** (100 epochs, 2026-01-22):
|
||||
|
||||
| 指标 | 值 |
|
||||
|------|-----|
|
||||
| **mAP@0.5** | 93.5% |
|
||||
| **mAP@0.5-0.95** | 83.0% |
|
||||
| **训练集** | ~10,000 张标注图片 |
|
||||
| **字段类型** | 10 个字段 (新增 payment_line, customer_number) |
|
||||
| **模型位置** | `runs/train/invoice_fields/weights/best.pt` |
|
||||
|
||||
**各字段检测性能**:
|
||||
- 发票基础信息 (InvoiceNumber, InvoiceDate, InvoiceDueDate): >95% mAP
|
||||
- 支付信息 (OCR, Bankgiro, Plusgiro, Amount): >90% mAP
|
||||
- 组织信息 (supplier_org_number, customer_number): >85% mAP
|
||||
- 支付行 (payment_line): >80% mAP
|
||||
|
||||
**模型文件**:
|
||||
```
|
||||
runs/train/invoice_fields/weights/
|
||||
├── best.pt # 最佳模型 (mAP@0.5 最高) ⭐ 推荐用于生产
|
||||
└── last.pt # 最后检查点 (用于继续训练)
|
||||
```
|
||||
|
||||
> 注:目前仍在持续标注更多数据,预计最终将有 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 封装
|
||||
│ │ └── machine_code_parser.py # 机器可读付款行解析器
|
||||
│ ├── normalize/ # 字段规范化
|
||||
│ ├── matcher/ # 字段匹配
|
||||
│ ├── yolo/ # YOLO 相关
|
||||
│ │ ├── annotation_generator.py
|
||||
│ │ └── db_dataset.py
|
||||
│ ├── inference/ # 推理管道
|
||||
│ │ ├── pipeline.py # 主推理流程
|
||||
│ │ ├── yolo_detector.py # YOLO 检测
|
||||
│ │ ├── field_extractor.py # 字段提取
|
||||
│ │ ├── payment_line_parser.py # 支付行解析器
|
||||
│ │ └── customer_number_parser.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 # 数据模型
|
||||
│ ├── utils/ # 工具模块
|
||||
│ │ ├── text_cleaner.py # 文本清理
|
||||
│ │ ├── validators.py # 字段验证
|
||||
│ │ ├── fuzzy_matcher.py # 模糊匹配
|
||||
│ │ └── ocr_corrections.py # OCR 错误修正
|
||||
│ └── data/ # 数据处理
|
||||
├── tests/ # 测试文件
|
||||
│ ├── ocr/ # OCR 模块测试
|
||||
│ │ └── test_machine_code_parser.py
|
||||
│ ├── inference/ # 推理模块测试
|
||||
│ ├── normalize/ # 规范化模块测试
|
||||
│ └── utils/ # 工具模块测试
|
||||
├── docs/ # 文档
|
||||
│ ├── REFACTORING_SUMMARY.md
|
||||
│ └── TEST_COVERAGE_IMPROVEMENT.md
|
||||
├── config.py # 配置文件
|
||||
├── run_server.py # Web 服务器启动脚本
|
||||
├── runs/ # 训练输出
|
||||
│ └── train/
|
||||
│ └── invoice_fields/
|
||||
│ └── 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 (默认: 150, 必须与训练DPI一致)
|
||||
--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 调试模式
|
||||
```
|
||||
| 方法 | 端点 | 描述 |
|
||||
|------|------|------|
|
||||
| POST | `/api/v1/admin/auth/login` | 管理员登录 |
|
||||
| GET | `/api/v1/admin/documents` | 文档列表 |
|
||||
| POST | `/api/v1/admin/documents/upload` | 上传 PDF |
|
||||
| GET | `/api/v1/admin/documents/{id}` | 文档详情 |
|
||||
| PATCH | `/api/v1/admin/documents/{id}/status` | 更新文档状态 |
|
||||
| POST | `/api/v1/admin/documents/{id}/annotations` | 创建标注 |
|
||||
| POST | `/api/v1/admin/training/trigger` | 触发训练任务 |
|
||||
| GET | `/api/v1/admin/training/{id}/status` | 查询训练状态 |
|
||||
|
||||
## Python API
|
||||
|
||||
```python
|
||||
from src.inference.pipeline import InferencePipeline
|
||||
from inference.pipeline import InferencePipeline
|
||||
|
||||
# 初始化
|
||||
pipeline = InferencePipeline(
|
||||
@@ -559,41 +291,25 @@ pipeline = InferencePipeline(
|
||||
# 处理 PDF
|
||||
result = pipeline.process_pdf('invoice.pdf')
|
||||
|
||||
# 处理图片
|
||||
result = pipeline.process_image('invoice.png')
|
||||
|
||||
# 获取结果
|
||||
print(result.fields)
|
||||
# {
|
||||
# 'InvoiceNumber': '12345',
|
||||
# 'Amount': '1234.56',
|
||||
# 'payment_line': '# 94228110015950070 # > 48666036#14#',
|
||||
# 'customer_number': 'UMJ 436-R',
|
||||
# ...
|
||||
# }
|
||||
# {'InvoiceNumber': '12345', 'Amount': '1234.56', ...}
|
||||
|
||||
print(result.confidence) # {'InvoiceNumber': 0.95, 'Amount': 0.92, ...}
|
||||
print(result.to_json()) # JSON 格式输出
|
||||
print(result.confidence)
|
||||
# {'InvoiceNumber': 0.95, 'Amount': 0.92, ...}
|
||||
|
||||
# 访问交叉验证结果
|
||||
# 交叉验证
|
||||
if result.cross_validation:
|
||||
print(f"OCR match: {result.cross_validation.ocr_match}")
|
||||
print(f"Amount match: {result.cross_validation.amount_match}")
|
||||
print(f"Details: {result.cross_validation.details}")
|
||||
```
|
||||
|
||||
### 统一解析器使用
|
||||
|
||||
```python
|
||||
from src.inference.payment_line_parser import PaymentLineParser
|
||||
from src.inference.customer_number_parser import CustomerNumberParser
|
||||
from inference.pipeline.payment_line_parser import PaymentLineParser
|
||||
from inference.pipeline.customer_number_parser import CustomerNumberParser
|
||||
|
||||
# Payment Line 解析
|
||||
parser = PaymentLineParser()
|
||||
result = parser.parse("# 94228110015950070 # 15658 00 8 > 48666036#14#")
|
||||
print(f"OCR: {result.ocr_number}")
|
||||
print(f"Amount: {result.amount}")
|
||||
print(f"Account: {result.account_number}")
|
||||
print(f"OCR: {result.ocr_number}, Amount: {result.amount}")
|
||||
|
||||
# Customer Number 解析
|
||||
parser = CustomerNumberParser()
|
||||
@@ -601,156 +317,38 @@ result = parser.parse("Said, Shakar Umj 436-R Billo")
|
||||
print(f"Customer Number: {result}") # "UMJ 436-R"
|
||||
```
|
||||
|
||||
## DPI 配置
|
||||
|
||||
系统所有组件统一使用 **150 DPI**。DPI 必须在训练和推理时保持一致。
|
||||
|
||||
| 组件 | 配置位置 |
|
||||
|------|---------|
|
||||
| 全局常量 | `packages/shared/shared/config.py` -> `DEFAULT_DPI = 150` |
|
||||
| Web 推理 | `packages/inference/inference/web/config.py` -> `ModelConfig.dpi` |
|
||||
| CLI 推理 | `python -m inference.cli.infer --dpi 150` |
|
||||
| 自动标注 | `packages/shared/shared/config.py` -> `AUTOLABEL['dpi']` |
|
||||
|
||||
## 数据库架构
|
||||
|
||||
| 数据库 | 用途 | 存储内容 |
|
||||
|--------|------|----------|
|
||||
| **PostgreSQL** | 标注结果 | `documents`, `field_results`, `training_tasks` |
|
||||
| **SQLite** (AdminDB) | Web 应用 | 文档管理, 标注编辑, 用户认证 |
|
||||
|
||||
## 测试
|
||||
|
||||
### 测试统计
|
||||
|
||||
| 指标 | 数值 |
|
||||
|------|------|
|
||||
| **测试总数** | 688 |
|
||||
| **通过率** | 100% |
|
||||
| **整体覆盖率** | 37% |
|
||||
|
||||
### 关键模块覆盖率
|
||||
|
||||
| 模块 | 覆盖率 | 测试数 |
|
||||
|------|--------|--------|
|
||||
| `machine_code_parser.py` | 65% | 79 |
|
||||
| `payment_line_parser.py` | 85% | 45 |
|
||||
| `customer_number_parser.py` | 90% | 32 |
|
||||
|
||||
### 运行测试
|
||||
|
||||
```bash
|
||||
# 运行所有测试
|
||||
wsl bash -c "source ~/miniconda3/etc/profile.d/conda.sh && conda activate invoice-py311 && cd /mnt/c/Users/yaoji/git/ColaCoder/invoice-master-poc-v2 && pytest"
|
||||
DB_PASSWORD=xxx pytest tests/ -q
|
||||
|
||||
# 运行并查看覆盖率
|
||||
wsl bash -c "source ~/miniconda3/etc/profile.d/conda.sh && conda activate invoice-py311 && cd /mnt/c/Users/yaoji/git/ColaCoder/invoice-master-poc-v2 && pytest --cov=src --cov-report=term-missing"
|
||||
|
||||
# 运行特定模块测试
|
||||
wsl bash -c "source ~/miniconda3/etc/profile.d/conda.sh && conda activate invoice-py311 && cd /mnt/c/Users/yaoji/git/ColaCoder/invoice-master-poc-v2 && pytest tests/ocr/test_machine_code_parser.py -v"
|
||||
DB_PASSWORD=xxx pytest tests/ --cov=packages --cov-report=term-missing
|
||||
```
|
||||
|
||||
### 测试结构
|
||||
|
||||
```
|
||||
tests/
|
||||
├── ocr/
|
||||
│ ├── test_machine_code_parser.py # 支付行解析 (79 tests)
|
||||
│ └── test_ocr_engine.py # OCR 引擎测试
|
||||
├── inference/
|
||||
│ ├── test_payment_line_parser.py # 支付行解析器
|
||||
│ └── test_customer_number_parser.py # 客户编号解析器
|
||||
├── normalize/
|
||||
│ └── test_normalizers.py # 字段规范化
|
||||
└── utils/
|
||||
└── test_validators.py # 字段验证
|
||||
```
|
||||
|
||||
## 开发状态
|
||||
|
||||
**已完成功能**:
|
||||
- [x] 文本层 PDF 自动标注
|
||||
- [x] 扫描图 OCR 自动标注
|
||||
- [x] 多策略字段匹配 (精确/子串/规范化)
|
||||
- [x] PostgreSQL 数据库存储 (断点续传)
|
||||
- [x] 信号处理和超时保护
|
||||
- [x] YOLO 训练 (93.5% mAP@0.5, 10 个字段)
|
||||
- [x] 推理管道
|
||||
- [x] 字段规范化和验证
|
||||
- [x] Web 应用 (FastAPI + REST API)
|
||||
- [x] 增量训练支持
|
||||
- [x] 内存优化训练 (--low-memory, --resume)
|
||||
- [x] Payment Line 解析器 (统一模块)
|
||||
- [x] Customer Number 解析器 (统一模块)
|
||||
- [x] Payment Line 交叉验证 (OCR, Amount, Account)
|
||||
- [x] 文档类型识别 (invoice/letter)
|
||||
- [x] 单元测试覆盖 (688 tests, 37% coverage)
|
||||
|
||||
**进行中**:
|
||||
- [ ] 完成全部 25,000+ 文档标注
|
||||
- [ ] 多源融合增强 (Multi-source fusion)
|
||||
- [ ] OCR 错误修正集成
|
||||
- [ ] 提升测试覆盖率到 60%+
|
||||
|
||||
**计划中**:
|
||||
- [ ] 表格 items 提取
|
||||
- [ ] 模型量化部署 (ONNX/TensorRT)
|
||||
- [ ] 多语言支持扩展
|
||||
|
||||
## 关键技术特性
|
||||
|
||||
### 1. Payment Line 交叉验证
|
||||
|
||||
瑞典发票的 payment_line (支付行) 包含完整的支付信息:OCR 参考号、金额、账号。我们实现了交叉验证机制:
|
||||
|
||||
```
|
||||
Payment Line: # 94228110015950070 # 15658 00 8 > 48666036#14#
|
||||
↓ ↓ ↓
|
||||
OCR Number Amount Bankgiro Account
|
||||
```
|
||||
|
||||
**验证流程**:
|
||||
1. 从 payment_line 提取 OCR、Amount、Account
|
||||
2. 与单独检测的字段对比验证
|
||||
3. **payment_line 值优先** - 如有不匹配,采用 payment_line 的值
|
||||
4. 返回验证结果和详细信息
|
||||
|
||||
**优势**:
|
||||
- 提高数据准确性 (payment_line 是机器可读格式,更可靠)
|
||||
- 发现 OCR 或检测错误
|
||||
- 为数据质量提供信心指标
|
||||
|
||||
### 2. 统一解析器架构
|
||||
|
||||
采用独立解析器模块处理复杂字段:
|
||||
|
||||
**PaymentLineParser**:
|
||||
- 解析瑞典标准支付行格式
|
||||
- 提取 OCR、Amount (包含 Kronor + Öre)、Account + Check digits
|
||||
- 支持多种变体格式
|
||||
|
||||
**CustomerNumberParser**:
|
||||
- 支持多种瑞典客户编号格式 (`UMJ 436-R`, `JTY 576-3`, `FFL 019N`)
|
||||
- 从混合文本中提取 (如地址行中的客户编号)
|
||||
- 大小写不敏感,输出统一大写格式
|
||||
|
||||
**优势**:
|
||||
- 代码模块化、可测试
|
||||
- 易于扩展新格式
|
||||
- 统一的解析逻辑,减少重复代码
|
||||
|
||||
### 3. 文档类型自动识别
|
||||
|
||||
根据 payment_line 字段自动判断文档类型:
|
||||
|
||||
- **invoice**: 包含 payment_line 的发票文档
|
||||
- **letter**: 不含 payment_line 的信函文档
|
||||
|
||||
这个特性帮助下游系统区分处理流程。
|
||||
|
||||
### 4. 低内存模式训练
|
||||
|
||||
支持在内存受限环境下训练:
|
||||
|
||||
```bash
|
||||
python -m src.cli.train --low-memory
|
||||
```
|
||||
|
||||
自动调整:
|
||||
- batch size: 16 → 8
|
||||
- workers: 8 → 4
|
||||
- cache: disabled
|
||||
- 推荐用于 GPU 内存 < 8GB 或系统内存 < 16GB 的场景
|
||||
|
||||
### 5. 断点续传训练
|
||||
|
||||
训练中断后可从检查点恢复:
|
||||
|
||||
```bash
|
||||
python -m src.cli.train --resume --model runs/train/invoice_fields/weights/last.pt
|
||||
```
|
||||
| 指标 | 数值 |
|
||||
|------|------|
|
||||
| **测试总数** | 922 |
|
||||
| **通过率** | 100% |
|
||||
|
||||
## 技术栈
|
||||
|
||||
@@ -762,32 +360,7 @@ python -m src.cli.train --resume --model runs/train/invoice_fields/weights/last.
|
||||
| **数据库** | PostgreSQL + psycopg2 |
|
||||
| **Web 框架** | FastAPI + Uvicorn |
|
||||
| **深度学习** | PyTorch + CUDA 12.x |
|
||||
|
||||
## 常见问题
|
||||
|
||||
**Q: 为什么必须在 WSL 环境运行?**
|
||||
|
||||
A: PaddleOCR 和某些依赖在 Windows 原生环境存在兼容性问题。WSL 提供完整的 Linux 环境,确保所有依赖正常工作。
|
||||
|
||||
**Q: 训练过程中出现 OOM (内存不足) 错误怎么办?**
|
||||
|
||||
A: 使用 `--low-memory` 模式,或手动调整 `--batch` 和 `--workers` 参数。
|
||||
|
||||
**Q: payment_line 和单独检测字段不匹配时怎么处理?**
|
||||
|
||||
A: 系统默认优先采用 payment_line 的值,因为 payment_line 是机器可读格式,通常更准确。验证结果会记录在 `cross_validation` 字段中。
|
||||
|
||||
**Q: 如何添加新的字段类型?**
|
||||
|
||||
A:
|
||||
1. 在 `src/inference/constants.py` 添加字段定义
|
||||
2. 在 `field_extractor.py` 添加规范化方法
|
||||
3. 重新生成标注数据
|
||||
4. 从头训练模型
|
||||
|
||||
**Q: 可以用 CPU 训练吗?**
|
||||
|
||||
A: 可以,但速度会非常慢 (慢 10-50 倍)。强烈建议使用 GPU 训练。
|
||||
| **部署** | Docker + Azure ACI (训练) / App Service (推理) |
|
||||
|
||||
## 许可证
|
||||
|
||||
|
||||
Reference in New Issue
Block a user