2026-01-27 23:58:17 +01:00
2026-01-27 23:58:17 +01:00
2026-01-27 23:58:17 +01:00
2026-01-27 23:58:17 +01:00
2026-01-27 23:58:17 +01:00
2026-01-27 23:58:17 +01:00
2026-01-27 23:58:17 +01:00
2026-01-27 23:58:17 +01:00
2026-01-27 23:58:17 +01:00
2026-01-25 16:17:39 +01:00
2026-01-27 23:58:17 +01:00
2026-01-25 16:17:39 +01:00
WIP
2026-01-27 00:47:10 +01:00
2026-01-27 23:58:17 +01:00
2026-01-27 23:58:17 +01:00
2026-01-27 23:58:17 +01:00
WIP
2026-01-27 00:47:10 +01:00
2026-01-27 23:58:17 +01:00
2026-01-27 23:58:17 +01:00
2026-01-25 16:17:39 +01:00
WIP
2026-01-27 00:47:10 +01:00

Invoice Master POC v2

自动发票字段提取系统 - 使用 YOLOv11 + PaddleOCR 从瑞典 PDF 发票中提取结构化数据。

项目概述

本项目实现了一个完整的发票字段自动提取流程:

  1. 自动标注: 利用已有 CSV 结构化数据 + OCR 自动生成 YOLO 训练标注
  2. 模型训练: 使用 YOLOv11 训练字段检测模型
  3. 推理提取: 检测字段区域 -> OCR 提取文本 -> 字段规范化

架构

项目采用 monorepo + 三包分离 架构,训练和推理可独立部署:

packages/
├── shared/      # 共享库 (PDF, OCR, 规范化, 匹配, 工具)
├── training/    # 训练服务 (GPU, 按需启动)
└── inference/   # 推理服务 (常驻运行)
服务 部署目标 GPU 生命周期
Inference Azure App Service 可选 常驻 7x24
Training Azure ACI 必需 按需启动/销毁

两个服务通过共享 PostgreSQL 数据库通信。推理服务通过 API 触发训练任务,训练服务从数据库拾取任务执行。

当前进度

指标 数值
已标注文档 9,738 (9,709 成功)
总体字段匹配率 94.8% (82,604/87,121)
测试 922 passed
模型 mAP@0.5 93.5%

各字段匹配率:

字段 匹配率 说明
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.11+ (通过 Conda 管理)
GPU NVIDIA GPU + CUDA 12.x (强烈推荐)
数据库 PostgreSQL (存储标注结果)

安装

# 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)

支持的字段

类别 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 供应商组织号
8 payment_line 支付行 (机器可读格式)
9 customer_number 客户编号

快速开始

1. 自动标注

# 使用双池模式 (CPU + GPU)
python -m training.cli.autolabel \
    --dual-pool \
    --cpu-workers 3 \
    --gpu-workers 1

# 单线程模式
python -m training.cli.autolabel --workers 4

2. 训练模型

# 从预训练模型开始训练
python -m training.cli.train \
    --model yolo11n.pt \
    --epochs 100 \
    --batch 16 \
    --name invoice_fields \
    --dpi 150

# 低内存模式
python -m training.cli.train \
    --model yolo11n.pt \
    --epochs 100 \
    --name invoice_fields \
    --low-memory

# 从检查点恢复训练
python -m training.cli.train \
    --model runs/train/invoice_fields/weights/last.pt \
    --epochs 100 \
    --name invoice_fields \
    --resume

3. 推理

# 命令行推理
python -m inference.cli.infer \
    --model runs/train/invoice_fields/weights/best.pt \
    --input path/to/invoice.pdf \
    --output result.json \
    --gpu

4. Web 应用

# 从 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"

# 启动前端
cd frontend && npm install && npm run dev
# 访问 http://localhost:5173

5. Docker 本地开发

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 /api/v1/health 健康检查
POST /api/v1/infer 上传文件并推理
GET /api/v1/results/{filename} 获取可视化图片
POST /api/v1/async/infer 异步推理
GET /api/v1/async/status/{task_id} 查询异步任务状态

Admin API (需要 X-Admin-Token header):

方法 端点 描述
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

from inference.pipeline import InferencePipeline

# 初始化
pipeline = InferencePipeline(
    model_path='runs/train/invoice_fields/weights/best.pt',
    confidence_threshold=0.25,
    use_gpu=True,
    dpi=150,
    enable_fallback=True
)

# 处理 PDF
result = pipeline.process_pdf('invoice.pdf')

print(result.fields)
# {'InvoiceNumber': '12345', 'Amount': '1234.56', ...}

print(result.confidence)
# {'InvoiceNumber': 0.95, 'Amount': 0.92, ...}

# 交叉验证
if result.cross_validation:
    print(f"OCR match: {result.cross_validation.ocr_match}")
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}, Amount: {result.amount}")

# Customer Number 解析
parser = CustomerNumberParser()
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 应用 文档管理, 标注编辑, 用户认证

测试

# 运行所有测试
DB_PASSWORD=xxx pytest tests/ -q

# 运行并查看覆盖率
DB_PASSWORD=xxx pytest tests/ --cov=packages --cov-report=term-missing
指标 数值
测试总数 922
通过率 100%

技术栈

组件 技术
目标检测 YOLOv11 (Ultralytics)
OCR 引擎 PaddleOCR v5 (PP-OCRv5)
PDF 处理 PyMuPDF (fitz)
数据库 PostgreSQL + psycopg2
Web 框架 FastAPI + Uvicorn
深度学习 PyTorch + CUDA 12.x
部署 Docker + Azure ACI (训练) / App Service (推理)

许可证

MIT License

Description
No description provided
Readme 2.1 MiB
Languages
Python 91.5%
TypeScript 7.7%
Shell 0.7%