This commit is contained in:
Yaojia Wang
2026-02-01 00:08:40 +01:00
parent 33ada0350d
commit a516de4320
90 changed files with 11642 additions and 398 deletions

239
README.md
View File

@@ -7,8 +7,9 @@
本项目实现了一个完整的发票字段自动提取流程:
1. **自动标注**: 利用已有 CSV 结构化数据 + OCR 自动生成 YOLO 训练标注
2. **模型训练**: 使用 YOLOv11 训练字段检测模型
2. **模型训练**: 使用 YOLOv11 训练字段检测模型,支持数据增强
3. **推理提取**: 检测字段区域 -> OCR 提取文本 -> 字段规范化
4. **Web 管理**: React 前端 + FastAPI 后端,支持文档管理、数据集构建、模型训练和版本管理
### 架构
@@ -16,15 +17,17 @@
```
packages/
├── shared/ # 共享库 (PDF, OCR, 规范化, 匹配, 工具)
├── shared/ # 共享库 (PDF, OCR, 规范化, 匹配, 存储, 训练)
├── training/ # 训练服务 (GPU, 按需启动)
└── inference/ # 推理服务 (常驻运行)
frontend/ # React 前端 (Vite + TypeScript + TailwindCSS)
```
| 服务 | 部署目标 | GPU | 生命周期 |
|------|---------|-----|---------|
| **Inference** | Azure App Service | 可选 | 常驻 7x24 |
| **Training** | Azure ACI | 必需 | 按需启动/销毁 |
| **Frontend** | Vercel / Nginx | | 常驻 |
| **Inference** | Azure App Service / AWS | 可选 | 常驻 7x24 |
| **Training** | Azure ACI / AWS ECS | 必需 | 按需启动/销毁 |
两个服务通过共享 PostgreSQL 数据库通信。推理服务通过 API 触发训练任务,训练服务从数据库拾取任务执行。
@@ -34,7 +37,8 @@ packages/
|------|------|
| **已标注文档** | 9,738 (9,709 成功) |
| **总体字段匹配率** | 94.8% (82,604/87,121) |
| **测试** | 922 passed |
| **测试** | 1,601 passed |
| **测试覆盖率** | 28% |
| **模型 mAP@0.5** | 93.5% |
**各字段匹配率:**
@@ -97,6 +101,9 @@ invoice-master-poc-v2/
│ │ ├── ocr/ # PaddleOCR 封装 + 机器码解析
│ │ ├── normalize/ # 字段规范化 (10 种 normalizer)
│ │ ├── matcher/ # 字段匹配 (精确/子串/模糊)
│ │ ├── storage/ # 存储抽象层 (Local/Azure/S3)
│ │ ├── training/ # 共享训练组件 (YOLOTrainer)
│ │ ├── augmentation/ # 数据增强 (DatasetAugmenter)
│ │ ├── utils/ # 工具 (验证, 清理, 模糊匹配)
│ │ ├── data/ # DocumentDB, CSVLoader
│ │ ├── config.py # 全局配置 (数据库, 路径, DPI)
@@ -129,12 +136,29 @@ invoice-master-poc-v2/
│ ├── 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)
├── frontend/ # React 前端 (Vite + TypeScript + TailwindCSS)
│ ├── src/
│ ├── api/ # API 客户端 (axios + react-query)
│ ├── components/ # UI 组件
├── Dashboard.tsx # 文档管理面板
├── Training.tsx # 训练管理 (数据集/任务)
│ │ │ ├── Models.tsx # 模型版本管理
│ │ │ ├── DatasetDetail.tsx # 数据集详情
│ │ │ └── InferenceDemo.tsx # 推理演示
│ │ └── hooks/ # React Query hooks
│ └── package.json
├── migrations/ # 数据库迁移 (SQL)
│ ├── 003_training_tasks.sql
│ ├── 004_training_datasets.sql
│ ├── 005_add_group_key.sql
│ ├── 006_model_versions.sql
│ ├── 007_training_tasks_extra_columns.sql
│ ├── 008_fix_model_versions_fk.sql
│ ├── 009_add_document_category.sql
│ └── 010_add_dataset_training_status.sql
├── tests/ # 测试 (1,601 tests)
├── docker-compose.yml # 本地开发 (postgres + inference + training)
├── run_server.py # 快捷启动脚本
└── runs/train/ # 训练输出 (weights, curves)
@@ -270,9 +294,32 @@ Inference API PostgreSQL Training (ACI)
| POST | `/api/v1/admin/documents/upload` | 上传 PDF |
| GET | `/api/v1/admin/documents/{id}` | 文档详情 |
| PATCH | `/api/v1/admin/documents/{id}/status` | 更新文档状态 |
| PATCH | `/api/v1/admin/documents/{id}/category` | 更新文档分类 |
| GET | `/api/v1/admin/documents/categories` | 获取分类列表 |
| POST | `/api/v1/admin/documents/{id}/annotations` | 创建标注 |
| POST | `/api/v1/admin/training/trigger` | 触发训练任务 |
| GET | `/api/v1/admin/training/{id}/status` | 查询训练状态 |
**Training API:**
| 方法 | 端点 | 描述 |
|------|------|------|
| POST | `/api/v1/admin/training/datasets` | 创建数据集 |
| GET | `/api/v1/admin/training/datasets` | 数据集列表 |
| GET | `/api/v1/admin/training/datasets/{id}` | 数据集详情 |
| DELETE | `/api/v1/admin/training/datasets/{id}` | 删除数据集 |
| POST | `/api/v1/admin/training/tasks` | 创建训练任务 |
| GET | `/api/v1/admin/training/tasks` | 任务列表 |
| GET | `/api/v1/admin/training/tasks/{id}` | 任务详情 |
| GET | `/api/v1/admin/training/tasks/{id}/logs` | 训练日志 |
**Model Versions API:**
| 方法 | 端点 | 描述 |
|------|------|------|
| GET | `/api/v1/admin/models` | 模型版本列表 |
| GET | `/api/v1/admin/models/{id}` | 模型详情 |
| POST | `/api/v1/admin/models/{id}/activate` | 激活模型 |
| POST | `/api/v1/admin/models/{id}/archive` | 归档模型 |
| DELETE | `/api/v1/admin/models/{id}` | 删除模型 |
## Python API
@@ -332,8 +379,41 @@ print(f"Customer Number: {result}") # "UMJ 436-R"
| 数据库 | 用途 | 存储内容 |
|--------|------|----------|
| **PostgreSQL** | 标注结果 | `documents`, `field_results`, `training_tasks` |
| **SQLite** (AdminDB) | Web 应用 | 文档管理, 标注编辑, 用户认证 |
| **PostgreSQL** | 主数据库 | 文档、标注、训练任务、数据集、模型版本 |
### 主要表
| 表名 | 说明 |
|------|------|
| `admin_documents` | 文档管理 (PDF 元数据, 状态, 分类) |
| `admin_annotations` | 标注数据 (YOLO 格式边界框) |
| `training_tasks` | 训练任务 (状态, 配置, 指标) |
| `training_datasets` | 数据集 (train/val/test 分割) |
| `dataset_documents` | 数据集-文档关联 |
| `model_versions` | 模型版本管理 (激活/归档) |
| `admin_tokens` | 管理员认证令牌 |
| `async_requests` | 异步推理请求 |
### 数据集状态
| 状态 | 说明 |
|------|------|
| `building` | 正在构建数据集 |
| `ready` | 数据集就绪,可开始训练 |
| `trained` | 已完成训练 |
| `failed` | 构建失败 |
| `archived` | 已归档 |
### 训练状态
| 状态 | 说明 |
|------|------|
| `pending` | 等待执行 |
| `scheduled` | 已计划 |
| `running` | 正在训练 |
| `completed` | 训练完成 |
| `failed` | 训练失败 |
| `cancelled` | 已取消 |
## 测试
@@ -347,8 +427,114 @@ DB_PASSWORD=xxx pytest tests/ --cov=packages --cov-report=term-missing
| 指标 | 数值 |
|------|------|
| **测试总数** | 922 |
| **测试总数** | 1,601 |
| **通过率** | 100% |
| **覆盖率** | 28% |
## 存储抽象层
统一的文件存储接口,支持多后端切换:
| 后端 | 用途 | 安装 |
|------|------|------|
| **Local** | 本地开发/测试 | 默认 |
| **Azure Blob** | Azure 云部署 | `pip install -e "packages/shared[azure]"` |
| **AWS S3** | AWS 云部署 | `pip install -e "packages/shared[s3]"` |
### 配置文件 (storage.yaml)
```yaml
backend: ${STORAGE_BACKEND:-local}
presigned_url_expiry: 3600
local:
base_path: ${STORAGE_BASE_PATH:-./data/storage}
azure:
connection_string: ${AZURE_STORAGE_CONNECTION_STRING}
container_name: ${AZURE_STORAGE_CONTAINER:-documents}
s3:
bucket_name: ${AWS_S3_BUCKET}
region_name: ${AWS_REGION:-us-east-1}
```
### 使用示例
```python
from shared.storage import get_storage_backend
# 从配置文件加载
storage = get_storage_backend("storage.yaml")
# 上传文件
storage.upload(Path("local.pdf"), "documents/invoice.pdf")
# 获取预签名 URL (前端访问)
url = storage.get_presigned_url("documents/invoice.pdf", expires_in_seconds=3600)
```
### 环境变量
| 变量 | 后端 | 说明 |
|------|------|------|
| `STORAGE_BACKEND` | 全部 | `local`, `azure_blob`, `s3` |
| `STORAGE_BASE_PATH` | Local | 本地存储路径 |
| `AZURE_STORAGE_CONNECTION_STRING` | Azure | 连接字符串 |
| `AZURE_STORAGE_CONTAINER` | Azure | 容器名称 |
| `AWS_S3_BUCKET` | S3 | 存储桶名称 |
| `AWS_REGION` | S3 | 区域 (默认: us-east-1) |
## 数据增强
训练时支持多种数据增强策略:
| 增强类型 | 说明 |
|----------|------|
| `perspective_warp` | 透视变换 (模拟扫描角度) |
| `wrinkle` | 皱纹效果 |
| `edge_damage` | 边缘损坏 |
| `stain` | 污渍效果 |
| `lighting_variation` | 光照变化 |
| `shadow` | 阴影效果 |
| `gaussian_blur` | 高斯模糊 |
| `motion_blur` | 运动模糊 |
| `gaussian_noise` | 高斯噪声 |
| `salt_pepper` | 椒盐噪声 |
| `paper_texture` | 纸张纹理 |
| `scanner_artifacts` | 扫描伪影 |
增强配置示例:
```json
{
"augmentation": {
"gaussian_blur": { "enabled": true, "kernel_size": 5 },
"perspective_warp": { "enabled": true, "intensity": 0.1 }
},
"augmentation_multiplier": 2
}
```
## 前端功能
React 前端提供以下功能模块:
| 模块 | 功能 |
|------|------|
| **Dashboard** | 文档列表、上传、标注状态管理、分类筛选 |
| **Training** | 数据集创建/管理、训练任务配置、增强设置 |
| **Models** | 模型版本管理、激活/归档、指标查看 |
| **Inference Demo** | 实时推理演示、结果可视化 |
### 启动前端
```bash
cd frontend
npm install
npm run dev
# 访问 http://localhost:5173
```
## 技术栈
@@ -357,10 +543,27 @@ DB_PASSWORD=xxx pytest tests/ --cov=packages --cov-report=term-missing
| **目标检测** | YOLOv11 (Ultralytics) |
| **OCR 引擎** | PaddleOCR v5 (PP-OCRv5) |
| **PDF 处理** | PyMuPDF (fitz) |
| **数据库** | PostgreSQL + psycopg2 |
| **数据库** | PostgreSQL + SQLModel |
| **Web 框架** | FastAPI + Uvicorn |
| **前端** | React + TypeScript + Vite + TailwindCSS |
| **状态管理** | React Query (TanStack Query) |
| **深度学习** | PyTorch + CUDA 12.x |
| **部署** | Docker + Azure ACI (训练) / App Service (推理) |
| **部署** | Docker + Azure/AWS (训练) / App Service (推理) |
## 环境变量
| 变量 | 必需 | 说明 |
|------|------|------|
| `DB_PASSWORD` | 是 | PostgreSQL 密码 |
| `DB_HOST` | 否 | 数据库主机 (默认: localhost) |
| `DB_PORT` | 否 | 数据库端口 (默认: 5432) |
| `DB_NAME` | 否 | 数据库名 (默认: docmaster) |
| `DB_USER` | 否 | 数据库用户 (默认: docmaster) |
| `STORAGE_BASE_PATH` | 否 | 存储路径 (默认: ~/invoice-data/data) |
| `MODEL_PATH` | 否 | 模型路径 |
| `CONFIDENCE_THRESHOLD` | 否 | 置信度阈值 (默认: 0.5) |
| `SERVER_HOST` | 否 | 服务器主机 (默认: 0.0.0.0) |
| `SERVER_PORT` | 否 | 服务器端口 (默认: 8000) |
## 许可证