想象你来当一部戏的导演:你面前站着好多演员(词语),他们的外貌、气质各不相同。“苹果”是个漂亮的姑娘,既可以是“道具”(名词),也可以当“品牌代言人”(专有名词);“爱”是个热情的小伙,既能演“动作戏”(动词),也能客串一下名词。要决定每个人演什么角色,导演必须通读剧本(整个句子)、参考人物关系(上下文),最终给每个演员贴上角色标签(词性)。这就是词性标注——NLP 里最基础也最考验功力的“选角”流程。 这篇文章里,我们会从“手忙脚乱的助理导演”一步步升级为“运筹帷幄的 AI 导演”:先学最基本的选角规则(词性标注基础),再体验只看上一场戏就做决定的副导演(HMM),学习用它高效彩排(Viterbi 解码),接着结识统筹全局的资深监制(CRF),再跟随过目不忘的 AI 导演(深度学习),最后掌握剧组管理与落地的实战心法(工程优化与案例)。
词性标注是 NLP 流水线的“地基”:从 HMM、CRF 到深度学习的进阶路线,是高级 NLP 工程师的必修课,直接决定你在搜索引擎、智能客服、舆情分析等场景中的实战质量。
可视化演示
展示HMM、CRF、BiLSTM-CRF三种序列标注模型的核心机制。切换模式查看不同模型如何为句子中的每个词分配词性标签。
本节配有交互动效(进入视口后自动加载,约 3KB)。
HMM 隐马尔可夫模型 词性标签为隐状态,词语为观测序列 转移概率建模标签间依赖,发射概率建模标签到词的映射 P(Y|X) ∝ π·B·A·B Viterbi算法求解最优标签序列
词性标注基础
导演拿到新剧本的第一步,是建立“角色名册”。你不能让“苹果”今天演道具、明天演主角,结果观众(下游任务)看得莫名其妙。词性标注的任务,就是给剧本里的每个词贴上一个明确的“角色类型”——名词、动词、形容词、副词……有了这套名册,后续的语法分析、语义理解才有依据。
从技术上看,词性标注是典型的序列标注问题:给定词序列 X = (x₁, x₂, …, x_T),模型要为每个词 x_t 找到一个最合适的标签 y_t。标签集合 T 在中文里常用“北大词性标注集”,英文里则是“宾州树库标记集”。标注的难点不在词典,而在歧义:“学习”在“学习雷锋”中是动词,在“学习成绩”里却是名词;“苹果”在“吃苹果”里是普通名词,在“苹果发布会”里却是专有名词的一部分。词典只能提供候选,真正的决策要交给上下文。
怎么把“结合上下文选角色”变成可计算的公式?一个直观的做法是:对当前词 w,从标签集合中挑选后验概率最大的标签:
这一步让导演掌握了基本功:拿到任何台词,手边都有一份完整的角色名册,也知道该结合上下文来定角色。但如果一句台词里同时有十几个词,组合方案指数级增长,单靠“逐个词挑最大概率”远远不够——真正的考验才刚刚开始。
- 标签集合 TT 是剧组的“角色名册”,涵盖所有可能分配的角色类别。
- 条件概率 P(y|w)看到词 w 后,某标签 y 成为正确角色的概率,由贝叶斯公式拆解为似然与先验的乘积。
- arg max 决策在所有候选标签中,选择概率最大的那个作为最终标注。
HMM模型原理
资历尚浅的副导演有个偷懒的诀窍:他只记住上一场戏发生了什么,就凭经验决定下一场该怎么演。你问他“为什么这场让'苹果'演名词?”他说“因为上一场'吃'是动词,动词后面通常跟名词呀。”这套“走一步看一步”的逻辑,正是 HMM(隐马尔可夫模型)的思路。
HMM 把词性标注当作“生成故事”的过程:先在心里走一遍角色链(标签序列 Y),再依据每个角色的特点写出具体台词(词序列 X)。因为要描述的是联合概率 P(X, Y),HMM 属于生成式模型——它先虚构“剧本大纲”,再落成“台词文本”。
想让这个生成过程可计算,HMM 做了两大简化:一阶马尔可夫假设(当前标签只看前一个标签)和观测独立性假设(当前词只由当前标签决定)。于是模型参数变得非常清爽——一个转移概率矩阵 A 管“角色与角色如何衔接”,一个发射概率矩阵 B 管“角色与台词如何匹配”:
副导演的“走一步看一步”虽然省力,却埋下隐患:如果一场戏要隔几个词才能看出人物关系,他只靠上一场戏就会判断失误。更致命的是,联合概率里所有组合都要枚举,计算量爆炸。于是我们迫切需要一套高效的“彩排算法”。
- 一阶马尔可夫性当前标签只由上一个标签决定,这是“副导演只看上一场戏”的数学化。
- 观测独立性每个词的出现只由它对应的标签决定,与其他位置的词和标签都无关。
- 联合概率分解把整句“剧本”的概率拆成“转移概率 × 发射概率”的连乘。
Viterbi解码
副导演终于要正式排出整出戏的演员表了。他灵机一动:“我把所有可能的角色组合都彩排一遍,再挑一条最顺的戏路不就行了?”可组合爆炸太可怕——标签数 N 的句子长度 T 次方,连神仙都算不完。于是他改用动态规划:每演到一场戏,只保留“以每种角色结尾的最好彩排得分”,一路淘汰明显差劲的搭配。这就是 Viterbi 算法。
Viterbi 维护一张 DP 表:δ_t(j) 表示走到第 t 个词、且第 t 个词被标成标签 j 时,前面所有路径中的最高分;同时用一个回溯指针记录“这个最高分来自上一个哪个标签”,方便最后倒推还原完整路径。
递推式清晰写出了“上一步累积最优分 × 角色衔接分,再乘当前演技分”的完整流程。最后一列取最大值的标签,一路回溯,就得到整体最优的标签序列。整个算法的时间复杂度为 O(N²·T),从指数级降到多项式级,这才是能上生产的算法:
副导演用动态规划彩排,效率大幅提升。但别高兴太早:HMM 的强假设让他只能看到“上一场戏”,遇到依赖长距离线索的复杂剧本,依旧会翻车。导演组决定请来一位更资深的监制。
- 累积分数 δ记录到第 t 个词为止、以标签 j 结尾的所有路径中最高的那一条的分值。
- 转移概率 a_ij前一个标签 i 变成当前标签 j 的“出场衔接分”。
- 发射概率 b_j(o_t)当前标签 j 演好当前词 o_t 的“演技分”。
- 取最大值从所有前序标签 i 中挑出累积最优的结果,再乘上当前的发射概率,得到本步最优分。
CRF模型原理
资深监制最讨厌“分段决策”。他认为,给每个角色定妆时必须通读整本剧本——前面的铺垫、后面的呼应、角色的前后照应,全都要一起权衡。他拍板的方式是开讨论会:每条证据(特征函数)请一位评委打分,权重越高、音量越大,最终全体评委票数最高的整套演员表才被通过。这就是条件随机场(CRF)的气质。
与 HMM 不同,CRF 不建模“故事是怎么生成的”,而是直接建模在已知台词 X 的条件下,角色链 Y 的条件概率 P(Y|X)。它不要求你遵守“观测独立”之类的硬性假设,只要求你定义特征函数——你想让评委关注什么,就定义什么样的特征。
特征函数 f_k 可以写“当前词是否首字母大写”“前一个标签是不是介词”“当前词的前缀是什么”“当前词左右各一个词是谁”……每个特征配一个权重 λ_k,通过训练数据自动学习。由于 CRF 是对整条标签序列同时打分,它能在全局层面协调“这一个词”与“前后文”的关系:
有资深监制坐镇,选角准确率大幅提升。但他仍有一个弱项——需要手工设计特征,告诉评委们该关注什么。剧组老板问:“能不能训练一个会自己看剧本的导演?”于是 AI 导演登场。
- 特征函数 f_k一位评委负责检查一项细节,比如“前一个标签是不是动词”“当前词是不是大写”。
- 权重 λ_k每个评委的话有多大分量,通过训练数据学习得到。
- 加权求和后取指数把全体评委的分数汇总成路径能量,再用 exp 映射成正比例的分值。
- 配分函数 Z(X)把所有可能的标签序列得分全部相加做归一化,保证概率总和为 1。
深度学习标注
AI 导演从不喊累,他阅片无数(海量语料),不需要你手工告诉他要看前缀、看上下文。你只需把剧本原封不动扔给他,他就能自动提炼每句台词的气质,再结合“角色不能冲突”的硬性要求(CRF 层),瞬间排出最优演员表。
深度学习方案的标准配置是 BiLSTM-CRF。第一层词嵌入把每个词映射成稠密向量;第二层用双向 LSTM 从正反两个方向“通读”全句,生成每个位置融合了全句信息的隐藏状态 h_t;最后一层 CRF 解码层对标签路径做全局合法性约束——比如“介词后面不能直接跟动词”这类标签搭配规则,由转移矩阵自动学习。
在得分公式里,BiLSTM 为第 t 个词给出“每个标签的初始得分”,CRF 的转移矩阵 T 负责对相邻标签的搭配加分或扣分。训练时整个网络端到端反向传播,词向量、LSTM 参数、转移矩阵一起被优化。最终路径得分是两类分数的总和:
AI 导演让选角效率和质量都上了新台阶。可剧组要真正运转起来,还差最后一公里——数据、评估指标、部署调优这些“剧组管理”琐事,决定了明星导演的活儿能不能真正落地。
- 隐藏状态 h_t模型把整句话读进脑海后,第 t 个位置浓缩出的完整上下文理解。
- 标签得分 W^T h把隐藏状态投影到标签空间,得到每个标签对当前词的“适合度分”。
- 转移得分 T相邻标签间的合法性约束分,比如“介词后不能直接接动词”。
- 求和得到路径总分把所有位置的两类分数加起来,就是整条标签路径的总得分。
工程优化技巧
再好的导演也需要靠谱的制片主任。数据就是剧照素材,模型是导演,可素材脏乱差、演员表经常错位,导演再牛也白搭。工程优化,就是当好这个制片主任。
第一个任务是数据与标签的规范化。分词结果不统一、标点符号处理粗糙、标注集混用(一会儿北大集一会儿宾州集),都会让模型崩溃。第二个大难题是未登录词——剧本里冒出个从没见过的新词,模型不能直接“罢演”,必须借助词形特征(前缀、后缀、大小写、数字模式)硬着头皮给出合理标签。在 CRF 里这些是手工特征;在深度模型里,可以引入字符级嵌入来自动捕捉。
评估模型时更不能只看整体准确率。因为标签分布极不均衡——绝大多数词都是名词和动词,模型就算把所有词都标成名词,准确率也能到 60%。更科学的做法是分别计算每个标签的精确率 P、召回率 R 和 F1,对“成语”“拟声词”这类低频标签专门监控。F1 是两者的调和平均:
制片主任到位后,剧组终于可以接真实的商业项目了。调参时还要关注学习率、dropout 和 mini-batch 大小——这些细节就像剧组的排班表,安排好了一切顺畅,安排不好全员加班。
- 精确率 P标注为某标签的结果中,真正正确的比例。
- 召回率 R所有应为该标签的样本中,被正确标注出来的比例。
- F1 调和平均两个指标都不偏废,取一个综合分数。
实战应用案例
现在剧组接到了三个大项目,每个项目都考验着不同的“选角”组合和调度方式,而词性标注就是剧组里的定妆照环节。
项目一:命名实体识别(NER)。从“苹果在北京发布了 iPhone 15”中抽出“苹果”(公司)和“北京”(地点)。词性标注先给出候选角色:这里的“苹果”是专有名词而不是普通名词。再配合 NER 的序列标注模型,就能把专有名词与普通名词清晰区分开,避免把“苹果”当成水果去搜。
项目二:句法分析辅助。从“我爱吃苹果”到“我/代词,爱/动词,吃/动词,苹果/名词”,句法分析器依靠这些标注结果快速搭建依存树。没有词性序列,句法分析器就无法判断“吃苹果”是动宾结构还是偏正结构。
项目三:分词工具协作。像 jieba 这类工具本身就内置了词性标注功能,支持离线批量处理和在线 API 调用。在正式训练大模型之前,先用成熟工具产出一份初步标注结果,再人工抽样修正,能极大降低标注成本——这就是工业界的“快速原型法”。
从选角名册到完整的剧组体系,你已经掌握了从传统统计模型到深度学习模型的全部核心方法。无论在搜索引擎还是智能客服里,这套“选角”能力都是中文 NLP 的地基。下一部戏,就靠你亲自执导了。
AI 应用场景
搜索引擎查询理解
阶段:文本预处理 / 查询解析
解决的问题:传统词典法无法区分“苹果发布会”里的“苹果”是公司名还是水果名。CRF 词性标注结合命名实体识别,能借助上下文全局决策,把查询中的专有名词单独标记,显著提升搜索召回准确率。
具体案例:用户搜索“苹果发布会时间”,标注系统对“苹果”输出专有名词、“发布会”输出名词、“时间”输出名词,再触发品牌实体识别,迅速返回苹果公司官方活动页,而不是水果种植教程。
金融舆情情感分析
阶段:特征工程 / 情感分析
解决的问题:情感极性常受修饰关系和否定结构影响。传统词袋模型只看到“下跌”就判负面,忽略了“长期向好”的转承结构。BiLSTM-CRF 先为每个词配好词性标签,让情感分类模型能利用词性序列识别转折句。
具体案例:从“市场短期出现下跌,但长期向好趋势不变”中,词性标注输出“下跌/动词、向好/形容词”,情感分析系统据此识别出“短期负面、长期正面”的细粒度结论,帮助基金经理把握舆论节奏。
智能客服意图识别
阶段:自然语言理解
解决的问题:用户说“我要申请退款”和“退款失败怎么办”,句式结构完全不同。直接用词匹配容易误判,而词性序列“动词+名词”能帮助意图模板泛化到相似表达。
具体案例:金融客服机器人接入词性标注模块,将“信用卡 提额 失败 的 原因”统一标记为“名词 名词 动词 助词 名词”,再匹配到“提额问题咨询”意图,准确率比纯关键词匹配提升约 12 个百分点。
Python 演示
# -*- coding: utf-8 -*-"""基于 HMM + Viterbi 的词性标注器"""import numpy as npfrom collections import defaultdict class HMMTagger: def __init__(self): self.tags = [] self.start = {} self.trans = defaultdict(lambda: defaultdict(float)) self.emit = defaultdict(lambda: defaultdict(float)) def train(self, corpus): """corpus: [[(词, 词性), ...], ...]""" for sent in corpus: for i, (word, tag) in enumerate(sent): if i == 0: self.start[tag] += 1 else: prev_tag = sent[i - 1][1] self.trans[prev_tag][tag] += 1 self.emit[tag][word] += 1 total_start = sum(self.start.values()) for tag in self.start: self.start[tag] /= total_start for prev_tag in self.trans: total = sum(self.trans[prev_tag].values()) for tag in self.trans[prev_tag]: self.trans[prev_tag][tag] /= total for tag in self.emit: total = sum(self.emit[tag].values()) for word in self.emit[tag]: self.emit[tag][word] /= total self.tags = list(self.emit.keys()) def viterbi(self, words): """维特比解码:返回最优词性序列""" T = len(words) dp = np.zeros((len(self.tags), T)) back = np.zeros((len(self.tags), T), dtype=int) for i, tag in enumerate(self.tags): dp[i, 0] = self.start.get(tag, 1e-8) * self.emit[tag].get(words[0], 1e-8) for t in range(1, T): for j, tag_j in enumerate(self.tags): probs = dp[:, t - 1] * np.array([ self.trans.get(self.tags[i], {}).get(tag_j, 1e-8) for i in range(len(self.tags)) ]) back[j, t] = np.argmax(probs) dp[j, t] = probs[back[j, t]] * self.emit[tag_j].get(words[t], 1e-8) best_path = [] last = np.argmax(dp[:, T - 1]) best_path.append(last) for t in range(T - 1, 0, -1): last = back[last, t] best_path.append(last) best_path.reverse() return [self.tags[idx] for idx in best_path] if __name__ == '__main__': corpus = [ [("我", "代词"), ("爱", "动词"), ("吃", "动词"), ("苹果", "名词")], [("他", "代词"), ("喜欢", "动词"), ("香蕉", "名词")], ] model = HMMTagger() model.train(corpus) print(model.viterbi(["我", "爱", "香蕉", "苹果"]))
self.trans = defaultdict(lambda: defaultdict(float))转移概率表用二级 defaultdict 存储,避免稀疏矩阵的繁琐判断self.emit[tag][word] += 1统计发射频次:某个词性下每个词出现了多少次total_start = sum(self.start.values())归一化初始概率,让“第一个词是某词性”的概率和为 1dp[i, 0] = self.start.get(tag, 1e-8) * self.emit[tag].get(words[0], 1e-8)Viterbi 初始化:首词的初始概率 × 发射概率probs = dp[:, t - 1] * np.array([...])递推核心:上一步所有标签的最优分 × 转移到当前标签的概率back[j, t] = np.argmax(probs)记录当前最优分来自上一列的哪个标签,为了最后回溯dp[j, t] = probs[back[j, t]] * ...乘上当前词的发射概率,完成本步的最优分计算last = np.argmax(dp[:, T - 1])末尾取全局最大分对应的标签,作为回溯起点for t in range(T - 1, 0, -1)从后往前回溯,还原整条最优标签路径
常见误区
误区“词性标注不就是查词典吗?把每个词标成词典里的第一个释义就好,词性标注工具都是这么做的。”
正解词典只能给出候选,无法解决一词多义和上下文依赖。比如“苹果”在“吃苹果”和“苹果发布会”中指代完全不同的事物,“学习”在“学习雷锋”和“学习成绩”中词性也不同。HMM、CRF 和深度模型的核心价值恰恰在于利用上下文做全局决策,而不是死板查表。
为何常见:早期词性标注工具确实以词典和规则为主,容易给人留下“查表即可”的刻板印象。
误区“HMM 和 CRF 都是序列模型,输入输出都是词和标签,换哪个都差不多。”
正解两者建模思路完全不同。HMM 是生成式,假设观测独立,只能建模转移概率和发射概率的乘积;CRF 是判别式,直接建模条件概率 P(Y|X),可以自由组合重叠特征(前缀、后缀、大小写、上下文词)。在特征丰富的数据上,CRF 通常明显优于 HMM。这就像副导演只看上一场戏安排下一场,而监制通读全剧统筹全局。
为何常见:两者都处理序列标注问题,教科书常放在一起讲,初学者容易混淆模型类型与任务类型。
误区“BiLSTM 已经能编码上下文信息了,最后一层直接 softmax 分类就行,CRF 层是多余的。”
正解softmax 对每个词独立决策,会输出“动-动-动-名-动”这类不合语法的标签跳变。CRF 层通过转移矩阵约束标签序列的合法性(比如“介词后面不能直接接动词”),把非法路径直接一票否决。BiLSTM-CRF 是词性标注的标准配置,这不是锦上添花,而是硬性的序列约束。
为何常见:直觉上认为 LSTM 已经“看到”了上下文,容易忽视标签之间的转移约束。
误区“Viterbi 是 HMM 专属的解码算法,CRF 和深度模型用的是别的方法。”
正解Viterbi 是通用的最优序列解码算法,只要模型能给出路径得分,就可以用它找最优路径。HMM 的得分是概率乘积,CRF 的得分是特征加权和,BiLSTM-CRF 的得分是神经网络输出加转移分——三者最终都可以用 Viterbi 解码。
为何常见:教科书总是以 HMM 为例子推导 Viterbi,导致很多人把它和 HMM 绑定。
误区“模型整体准确率到了 97%,肯定可以安全上线了。”
正解词性标注的标签分布极不均衡,绝大多数词是名词和动词,即使把所有词都标成名词也能获得较高准确率。真正危险的是低频但重要的标签(如“拟声词”“成语”“处所词”)错误率很高。要按标签分别计算精确率、召回率和 F1,尤其关注下游任务是否对特定词性敏感。
为何常见:只盯着整体准确率一个数字,忽略了不均衡分布下的“幸存者偏差”。
面试题
Q1. 词性标注的本质任务是什么?
- A.给词语分配合适的词性标签← 正确答案
- B.把句子切分成词序列
- C.将词语映射为稠密向量
- D.将词语翻译为另一种语言
答案:A。词性标注是序列标注任务,核心是给每个词分配一个最合适的词性标签。分词是前置任务,向量化是表示学习,翻译是机器翻译任务,都偏离了词性标注的定义。
Q2. HMM 模型的两个核心假设是什么?
- A.一阶马尔可夫性与观测独立性← 正确答案
- B.状态无限多且无后效性
- C.特征无限展开且权重共享
- D.观测序列独立且标签可以交叉
答案:A。HMM 的两个核心假设是一阶马尔可夫性(当前标签只看前一个标签)和观测独立性(当前词只由当前标签决定)。其他选项分别对应无限状态、特征展开等不相关概念。
Q3. 关于 Viterbi 算法,以下哪些说法正确?
- A.利用动态规划避免指数级枚举← 正确答案
- B.需要回溯过程来还原最优路径← 正确答案
- C.每一时刻只需保留最优来源← 正确答案
- D.只能用于 HMM 不能用于 CRF
答案:ABC。Viterbi 通过动态规划在每个时刻保留最优路径片段,避免指数级组合枚举,最后用回溯指针还原完整路径。它不仅能用于 HMM,也能用于 CRF 和 BiLSTM-CRF 等任意“有路径得分”的模型,所以 D 是错的。
Q4. CRF 中配分函数 Z(X) 的作用是什么?
- A.对全局概率做归一化处理← 正确答案
- B.加速训练时的收敛速度
- C.构造有效的特征函数
- D.保存当前最优的搜索路径
答案:A。配分函数 Z(X) 把所有可能的标签序列的得分求和,做归一化,保证输出是一个合法的概率分布。它不负责加速、构造特征或搜索路径,那些分别由优化器、特征工程和 Viterbi 算法负责。
Q5. 下列哪些是 CRF 相比 HMM 的优势?
- A.可以灵活加入丰富上下文特征← 正确答案
- B.直接建模条件概率 P(Y|X)← 正确答案
- C.不要求观测序列的独立性← 正确答案
- D.模型训练不需要标注数据
答案:ABC。CRF 是判别式模型,直接建模 P(Y|X),可以自由添加重叠特征,不需要观测独立性假设。但 CRF 是有监督学习,依然需要人工标注语料训练,所以 D 错误。
Q6. 在 HMM 词性标注中,“发射概率”描述的是什么?
- A.当前词在给定词性下出现的概率← 正确答案
- B.前一个词性转移到当前词性的概率
- C.句子起始处某个词性出现的概率
- D.所有词性标签共享同一个概率值
答案:A。发射概率 b_j(o_t) = P(x_t = o_t | y_t = j),表示在标签 j 下出现词 o_t 的概率,类似“演员在某个角色下说出某句台词的几率”。转移概率管标签间衔接,初始概率管句首,都不是发射概率。
Q7. BiLSTM-CRF 模型中 CRF 层的主要作用是什么?
- A.约束输出标签序列的合法性← 正确答案
- B.增加词嵌入向量的原始维度
- C.降低梯度下降中的损失值
- D.提高预训练词向量的质量
答案:A。CRF 层通过转移矩阵约束相邻标签的组合是否合法,避免 softmax 逐词独立决策导致的非法标签跳变。词嵌入维度由嵌入层决定,损失由损失函数定义,词向量质量由预训练决定,都与 CRF 层的核心功能无关。
Q8. 在 Viterbi 递推公式 δ_t(j) = max_i[δ_{t-1}(i) × a_ij] × b_j(o_t) 中,a_ij 代表什么?
- A.从标签 i 转移至标签 j 的概率← 正确答案
- B.从标签 j 转移至标签 i 的概率
- C.标签 j 产生当前观测词的概率
- D.t 时刻状态选择标签 j 的概率
答案:A。a_ij = P(y_t = j | y_{t-1} = i),表示从上一时刻的标签 i 转移到当前时刻标签 j 的转移概率。B 的方向反了,C 是发射概率 b_j(o_t),D 是当前状态本身的含义,都不对应 a_ij。
Q9. 解决未登录词词性标注的常用方法有哪些?
- A.利用前缀后缀等词形特征← 正确答案
- B.在 CRF 中加入词形特征← 正确答案
- C.使用上下文词组成类词典特征← 正确答案
- D.直接丢弃未登录的稀有词
答案:ABC。未登录词没有词典信息,但可以通过词形特征(前缀、后缀、大小写)、CRF 中手工加入的词形特征、以及左右上下文词的投票来推断标签。直接丢弃未登录词会丢失信息,严重影响下游任务,是错误的做法。
Q10. F1 分数的计算公式是什么?
- A.精确率与召回率的调和平均← 正确答案
- B.精确率与召回率的算术平均
- C.精确率与召回率的两倍之和
- D.精确率与召回率的简单加法
答案:A。F1 = 2PR/(P+R),是精确率和召回率的调和平均。算术平均、两倍之和、简单加法都无法体现“两个指标都要高”的约束,因此不是正确的 F1 定义。