🎯 学习目标
- 理解词性标注(POS)的任务定义与实际用途
- 认识常见中文标注集,熟练使用 jieba.posseg
- 理解命名实体识别(NER)与实体类别
- 理解 BIO/BIOES 序列标注体系与 NER 的技术路线
- 会用 spaCy / LAC / HanLP 进行中文实体抽取
- 能完成"从新闻文本中抽取人名、地名、机构名"的实战
1词性标注是什么
词性标注(Part-of-Speech Tagging,POS Tagging):给分词后每个词标注语法类别(名词、动词、形容词……)。
这/d 座/q 城市/n 建设/v 得/u 很/d 美丽/a
(d=副词 q=量词 n=名词 v=动词 u=助词 a=形容词)
难点在于兼类词:同一个词在不同语境词性不同。"花"可以是名词(一朵花)、动词(花钱)、形容词(花哨);"在"可以是动词(他在家)、介词(在家看书)、副词(在学习)。
POS 的典型用途:① 信息抽取时只保留名词/动词等实词;② 帮助分词与句法分析消歧;③ 文本-to-语音中多音字判断("行长/cháng/zhǎng");④ 知识图谱、问答系统的特征。
2中文主流标注集
不同工具用不同标注规范,读文档时要先确认是哪一套:
| 标注集 | 使用方 | 特点 |
|---|---|---|
| 北大标注集(ICTCLAS) | jieba、HanLP 默认 | 约 40~99 个细粒度标记,nr/ns/nt 专名细分 |
| 宾州中文树库 CTB | LTP、学术语料 | NN/NR/NT/VV/VA 等英文缩写 |
| Universal POS | spaCy、UD 通用 | 17 个跨语言统一标记(NOUN/VERB/PROPN…) |
通用词性(UPOS)17 类速记:NOUN 名词、PROPN 专有名词、VERB 动词、ADJ 形容词、ADV 副词、NUM 数词、PRON 代词、ADP 介词、CONJ 连词、DET 限定词、PART 助词、PUNCT 标点、X 其他。
3jieba.posseg 实战
import jieba
import jieba.posseg as pseg
# 基本用法:得到 (词, 词性) 对
for w, f in pseg.cut("雷军在小米汽车工厂发布了新款SU7"):
print(f"{w}/{f}", end=" ")
# 雷军/nr 在/p 小米/nz 汽车/n 工厂/n 发布/v 了/u 新款/n SU7/eng
# 直接转列表
pairs = [(w.word, w.flag) for w in pseg.cut("他/r 喜欢/v 旅游/vn")]
# 自定义词典时可以指定词性:jieba.add_word(词, freq, 词性)
jieba.add_word("小米汽车", tag="nt") # 标注为机构团体
jieba.add_word("SU7", tag="nz")
for w, f in pseg.cut("雷军在小米汽车发布了新款SU7"):
print(f"{w}-{f}", end=" ")
按词性过滤的实用模式
text = "百度公司在上海建设新的人工智能研发中心,张教授对此发表了评论"
# 只取实体性较强的名词类(n/nr人名/ns地名/nt机构/nz其他专名)
entities_like = [w for w, f in pseg.cut(text)
if f in ("n", "nr", "ns", "nt", "nz", "eng")]
print(entities_like)
# ['百度', '公司', '上海', '人工智能', '研发', '中心', '张', '教授', '评论']
# 只取动词(分析"用户在说什么动作/诉求")
verbs = [w for w, f in pseg.cut(text) if f == "v"]
print(verbs) # ['建设', '发表']
# 批量处理文档
def pos_tag_many(docs):
return [[(w, f) for w, f in pseg.cut(d)] for d in docs]
jieba 的词性标注基于 HMM,准确率中等(85%~90% 上下,随领域变化),够用于特征过滤、粗分析;科研/高精度场景建议用 HanLP、LAC 或 BERT 类模型。
4命名实体识别(NER)
命名实体识别(Named Entity Recognition,NER):从文本中识别出具有特定意义的实体边界与类型。三大经典类(实体名三分):
- PER(人名):张三、马斯克
- LOC(地名):北京、浦东新区
- ORG(机构名):华为技术有限公司、北京大学
垂直领域还有:时间、日期、货币、产品型号、疾病/药物(医疗 NER)、法律条款、股票代码等。NER = 边界检测 + 类型分类,比分词更难,因为实体长度不定、嵌套常见("北京大学"里"北京"也是地名)、新词极多。
BIO / BIOES 标注体系
NER 被建模为序列标注:给每个字一个标签。最常见的 BIO:
句子:马 化 腾 在 深 圳 创 办 了 腾 讯
BIO: B-PER I-PER E-PER O B-LOC I-LOC O O O B-ORG I-ORG
(这里用 BIOES 更细:B=实体首字 I=内部 E=末字 S=单字实体 O=非实体)
B-PER:人名开始(begin)
I-PER:人名内部(inside)
E-PER:人名结束(end) ← BIOES 比 BIO 多 E 和 S
S-ORG:机构名单字(单字实体)
O:与实体无关(outside)
技术演进:规则+词典 → HMM/CRF(特征工程)→ BILSTM-CRF → BERT-CRF / LLM(当前主流,F1 可达 95%+ 通用领域)。
5方法一:规则 + 词性 + 词典抽取
没有训练条件时,"词性模式 + 地名词典 + 正则"可以解决不少封闭场景:
import jieba.posseg as pseg
text = "5月10日,王芳在北京会见了阿里巴巴集团的马云,双方谈到杭州总部扩建。"
# ① 词性线索:nr 人名、ns 地名、nt 机构名
for w, f in pseg.cut(text):
if f in ("nr", "ns", "nt"):
kind = {"nr": "人名", "ns": "地名", "nt": "机构名"}[f]
print(kind, w)
# ② 词典匹配(地名/机构名单维护在文件中)
city_file = {"北京", "上海", "杭州", "深圳", "广州"}
found = [w for w in jieba.lcut(text) if w in city_file]
print("命中城市:", found)
# ③ 正则提取数字/时间类实体
import re
print(re.findall(r"\d{1,2}月\d{1,2}日", text)) # ['5月10日']
print(re.findall(r"\d+(?:\.\d+)?亿?元", "投资额达3.5亿元")) # ['3.5亿元']
模式抽取套路:"动词 + nr/nt"(收购了 OOO、会见了 XXX)、"位于 ns"、地名词典后位词(省/市/路/大厦)、机构后缀词典(公司/集团/大学/医院/局/院)。规则法可解释、零样本,缺点是召回有限、维护成本高。
6方法二:spaCy 工业级 NER
pip install spacy
python -m spacy download zh_core_web_sm
import spacy
nlp = spacy.load("zh_core_web_sm") # 加载中文小模型
text = "华为技术有限公司总部位于深圳,创始人任正非出席了在北京举行的发布会。"
doc = nlp(text)
# 一行代码拿到实体:文本、类型、起止位置
for ent in doc.ents:
print(ent.text, "|", ent.label_, "|", ent.start_char, ent.end_char)
# 华为技术有限公司 | ORG | 0 8
# 深圳 | GPE | 12 14(GPE:地理政治实体,城市/国家)
# 任正非 | PERSON | 19 22
# 北京 | GPE | 28 30
# 词性、依存关系也都有
for token in doc[:6]:
print(token.text, token.pos_, token.dep_)
# 英文模型 en_core_web_sm 的实体类型更丰富:
# PERSON人 / GPE城市国家 / ORG机构 / DATE日期 / MONEY钱 / PRODUCT产品
小模型(sm)在中文上能力有限,复杂文本可换 zh_core_web_trf(Transformer 版,更准更大)。spaCy 的优势是工程化流水线:分句→词性→NER→依存一步到位,易于批量处理与定制规则。
7方法三:LAC / HanLP(中文模型)
# ===== 百度 LAC:词法分析(分词+词性+专名一体)=====
# pip install lac
from LAC import LAC
lac = LAC(mode="lac") # 首次运行自动下载模型
# 单句输入:返回 (词列表, 标签列表) 两个等长列表
words, tags = lac.run("李四在北京大学学习计算机,腾讯总部在深圳")
for w, t in zip(words, tags):
if t in ("PER", "LOC", "ORG", "TIME"):
print(t, w)
# PER 李四
# ORG 北京大学
# ORG 腾讯
# LOC 深圳
# 批量输入:传入列表,返回 (多句的词列表, 多句的标签列表)
# batch_words, batch_tags = lac.run(["句子1", "句子2"])
# LAC 标签:PER人名 LOC地名 ORG机构名 TIME时间
# ===== HanLP 2.x(功能最全,选学)=====
# pip install hanlp
# import hanlp
# tagger = hanlp.load(hanlp.pretrained.ner.MSRA_NER_BERT_BASE_ZH)
# print(tagger([["李四", "在", "北京", "大学", "学习"]]))
# HanLP 支持分词、POS、NER、依存句法、语义角色等一整套流水线
8综合案例:新闻实体三元组抽取
import spacy
from collections import defaultdict
nlp = spacy.load("zh_core_web_sm")
news_list = [
"字节跳动公司在北京和上海设立了新的研发中心。",
"张三从上海乘高铁抵达北京,参观了清华大学。",
"阿里巴巴集团宣布在杭州成立达摩院,李四担任负责人。",
]
bag = defaultdict(set) # 类型 → 实体集合(自动去重)
for news in news_list:
doc = nlp(news)
for ent in doc.ents:
label = {"PERSON": "人名", "ORG": "机构", "GPE": "地名",
"LOC": "地名"}.get(ent.label_, ent.label_)
bag[label].add(ent.text)
for k, v in bag.items():
print(f"{k}:{sorted(v)}")
# 地名:['上海', '北京', '杭州']
# 机构:['字节跳动公司', '研发中心', '清华大学', '阿里巴巴集团', '达摩院']
# 人名:['张三', '李四', '负责人'](小模型噪声,换 trf 模型可改善)
从实体到知识:关系抽取入门
# 最简单的关系模式:"ORG 位于 LOC"
doc = nlp("小米集团总部位于北京市海淀区。")
orgs = [e.text for e in doc.ents if e.label_ == "ORG"]
places = [e.text for e in doc.ents if e.label_ in ("GPE", "LOC")]
for o in orgs:
for p in places:
print((o, "总部位于", p))
# ('小米集团', '总部位于', '北京市海淀区') → 这就是知识图谱的一条三元组
# 成熟的关系抽取用模板/监督模型/大模型,第8章项目篇会再实践
9NER 效果评估要点
- 仍然用 P / R / F1,但必须边界 + 类型都正确才算命中。
- "北京大学"只识别出"北京"算错;类型把 ORG 标成 LOC 也算错。
- 工程上关注:实体级 F1(常用 seqeval 库按块计算)、各类型分别的 F1(人名通常最高,机构名最难)、跨领域迁移效果、速度。
# pip install seqeval(标签序列与金标准按实体块计算,不要按字算准确率!)
from seqeval.metrics import classification_report
y_true = [["B-ORG", "I-ORG", "O", "B-LOC"]]
y_pred = [["B-ORG", "I-ORG", "O", "B-LOC"]]
print(classification_report(y_true, y_pred))
10本章小结与练习
📌 关键结论
- POS 标注兼类词是难点;jieba 用北大标注集(nr/ns/nt),spaCy 用 UPOS。
- NER 三大类:人名 PER、地名 LOC/GPE、机构 ORG;建模为 BIO(ES) 字序列标注。
- 三条技术路线:规则词典(可解释基线)→ spaCy/LAC(统计/深度流水线)→ BERT/LLM(高精度)。
- NER 评估按"边界+类型"的实体块算 F1,不能只看单字准确率。