Doccano自动标注实战:我用它3天搞定了一个NER项目的数据标注
1. 项目背景与挑战
上个月接到了一个从新闻文本中抽取公司名和职位的NER任务,标注量约5000条。作为独立开发者,既没有专业标注团队,也没有充足预算购买商业标注服务。传统人工标注效率太低,而完全依赖预训练模型又担心准确率。经过技术选型,最终决定采用Doccano+轻量级模型的半自动方案,没想到3天就完成了全部工作。
这个方案的核心优势在于:
- 80%标注工作由模型自动完成,人工仅需校验和修正
- Doccano的协同功能支持多人并行校验
- 全流程可视化,标注进度和质量实时可控
关键决策点:选择UIE模型而非spaCy,因为前者支持zero-shot自定义实体类型,更适合本项目快速启动
2. 环境搭建与数据准备
2.1 快速部署Doccano 1.6.2
在Ubuntu 20.04服务器上部署仅需三步:
# 创建Python3.8虚拟环境 python -m venv doccano_env source doccano_env/bin/activate # 安装指定版本 pip install doccano==1.6.2 # 初始化服务 doccano init # 设置管理员账号 doccano webserver --port 8000 & # 后台运行 doccano task & # 启动任务队列常见踩坑点:
- 端口冲突导致服务无法启动(建议先用
netstat -tulnp检查) - 未安装SQLite3开发库导致数据库初始化失败(Ubuntu需
apt install libsqlite3-dev)
2.2 原始数据处理技巧
新闻数据往往包含大量HTML标签和特殊字符,需要预处理:
import re from bs4 import BeautifulSoup def clean_text(text): # 去除HTML标签 text = BeautifulSoup(text, 'html.parser').get_text() # 合并连续空格 text = re.sub(r'\s+', ' ', text) # 过滤特殊字符 return text.encode('ascii', 'ignore').decode().strip()预处理后的数据按每行一条文本的格式保存为news_data.txt,方便后续导入。
3. 自动标注系统搭建
3.1 模型选型对比
| 模型 | 准确率 | 推理速度 | 显存占用 | 定制难度 |
|---|---|---|---|---|
| UIE-base | 78% | 15ms/条 | 2.3GB | ★★☆☆☆ |
| spaCy-zh | 65% | 8ms/条 | 1.1GB | ★★★★☆ |
| BERT-CRF | 82% | 45ms/条 | 4.5GB | ★★★★★ |
最终选择UIE模型,因其在少样本场景下表现最佳:
from paddlenlp import Taskflow schema = ['公司名', '职位'] ie = Taskflow('information_extraction', schema=schema, device_id=0) # 使用GPU # 测试样例 text = "阿里巴巴CEO张勇宣布组织架构调整" print(ie(text)) # 输出: [{'公司名': [{'text': '阿里巴巴', 'start': 0, 'end': 4}], '职位': [{'text': 'CEO', 'start': 4, 'end': 7}]}]3.2 自动化接口开发
使用FastAPI构建高性能标注接口:
from fastapi import FastAPI from pydantic import BaseModel app = FastAPI() class RequestData(BaseModel): text: str @app.post("/predict") async def predict(data: RequestData): result = ie(data.text) return { "entities": [ { "label": label, "start_offset": ent['start'], "end_offset": ent['end'], "text": ent['text'] } for label in result[0] for ent in result[0][label] ] }启动服务:
uvicorn api:app --host 0.0.0.0 --port 57394. 标注流程优化实战
4.1 Doccano配置关键步骤
项目创建:
- 选择"Sequence Labeling"类型
- 标签设置:
公司名(蓝色)、职位(红色)
自动标注集成:
// REST接口配置 { "url": "http://your_server_ip:5739/predict", "method": "POST", "headers": {"Content-Type": "application/json"}, "body": {"text": "{{text}}"} }标签映射:
- 接口返回的
公司名→ Doccano标签公司名 - 接口返回的
职位→ Doccano标签职位
- 接口返回的
4.2 效率提升技巧
第一天:完成2000条自动标注
- 发现模型对缩写公司名识别差(如"阿里"→"阿里巴巴")
- 解决方案:在接口层添加别名映射表
company_alias = { "阿里": "阿里巴巴", "腾讯": "腾讯控股", # ... } def normalize_company(name): return company_alias.get(name, name)第二天:团队协作校验
- 3人同时工作,通过Doccano的"Assign"功能分配任务
- 使用
Comments功能标记有争议的标注
第三天:质量检查与导出
- 随机抽样500条检查,准确率达到92%
- 导出格式选择
JSONL,包含原始文本和标注span
5. 关键问题与解决方案
5.1 典型错误案例
复合职位识别:
- 错误示例:"高级产品经理"被拆分为"高级"和"产品经理"
- 修复方法:在UIE的schema中添加
复合职位类别
公司名包含职位:
- 错误示例:"腾讯研究院"被误标为公司名
- 解决方案:添加否定词表过滤
5.2 性能优化
当处理长文本时(>1000字),UIE速度明显下降。采用分块处理策略:
def chunk_text(text, max_len=500): sentences = text.split('。') chunks = [] current_chunk = "" for sent in sentences: if len(current_chunk) + len(sent) < max_len: current_chunk += sent + "。" else: chunks.append(current_chunk) current_chunk = sent + "。" if current_chunk: chunks.append(current_chunk) return chunks6. 项目成果与经验
最终获得的标注数据质量:
- 准确率:91.2%(人工评估)
- 覆盖率:88.7%(相比纯人工标注)
成本对比:
| 方式 | 耗时 | 人力成本 | 工具成本 |
|---|---|---|---|
| 纯人工 | 2周 | ¥6000 | ¥0 |
| 本方案 | 3天 | ¥1500 | ¥300 |
几个实用建议:
- 自动标注前先人工标注100条作为模型微调数据
- 设置明确的标注规范文档(特别是边界案例)
- 定期导出数据备份,防止意外丢失