想象你来当一部戏的导演:你面前站着好多演员(词语),他们的外貌、气质各不相同。“苹果”是个漂亮的姑娘,既可以是“道具”(名词),也可以当“品牌代言人”(专有名词);“爱”是个热情的小伙,既能演“动作戏”(动词),也能客串一下名词。要决定每个人演什么角色,导演必须通读剧本(整个句子)、参考人物关系(上下文),最终给每个演员贴上角色标签(词性)。这就是词性标注——NLP 里最基础也最考验功力的“选角”流程。 这篇文章里,我们会从“手忙脚乱的助理导演”一步步升级为“运筹帷幄的 AI 导演”:先学最基本的选角规则(词性标注基础),再体验只看上一场戏就做决定的副导演(HMM),学习用它高效彩排(Viterbi 解码),接着结识统筹全局的资深监制(CRF),再跟随过目不忘的 AI 导演(深度学习),最后掌握剧组管理与落地的实战心法(工程优化与案例)。

岗位关联

词性标注是 NLP 流水线的“地基”:从 HMM、CRF 到深度学习的进阶路线,是高级 NLP 工程师的必修课,直接决定你在搜索引擎、智能客服、舆情分析等场景中的实战质量。

目录
  1. 可视化演示
  2. 词性标注基础
  3. HMM模型原理
  4. Viterbi解码
  5. CRF模型原理
  6. 深度学习标注
  7. 工程优化技巧
  8. 实战应用案例
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

展示HMM、CRF、BiLSTM-CRF三种序列标注模型的核心机制。切换模式查看不同模型如何为句子中的每个词分配词性标签。

本节配有交互动效(进入视口后自动加载,约 3KB)。

HMM 隐马尔可夫模型 词性标签为隐状态,词语为观测序列 转移概率建模标签间依赖,发射概率建模标签到词的映射 P(Y|X) ∝ π·B·A·B Viterbi算法求解最优标签序列

词性标注基础

导演拿到新剧本的第一步,是建立“角色名册”。你不能让“苹果”今天演道具、明天演主角,结果观众(下游任务)看得莫名其妙。词性标注的任务,就是给剧本里的每个词贴上一个明确的“角色类型”——名词、动词、形容词、副词……有了这套名册,后续的语法分析、语义理解才有依据。

从技术上看,词性标注是典型的序列标注问题:给定词序列 X = (x₁, x₂, …, x_T),模型要为每个词 x_t 找到一个最合适的标签 y_t。标签集合 T 在中文里常用“北大词性标注集”,英文里则是“宾州树库标记集”。标注的难点不在词典,而在歧义:“学习”在“学习雷锋”中是动词,在“学习成绩”里却是名词;“苹果”在“吃苹果”里是普通名词,在“苹果发布会”里却是专有名词的一部分。词典只能提供候选,真正的决策要交给上下文。

怎么把“结合上下文选角色”变成可计算的公式?一个直观的做法是:对当前词 w,从标签集合中挑选后验概率最大的标签:

这一步让导演掌握了基本功:拿到任何台词,手边都有一份完整的角色名册,也知道该结合上下文来定角色。但如果一句台词里同时有十几个词,组合方案指数级增长,单靠“逐个词挑最大概率”远远不够——真正的考验才刚刚开始。

y∗=arg⁡max⁡y∈T P(y∣w)y^{*} = \arg\max_{y \in T} \ P(y \mid w)
这就是“选角规则”的数学化:在角色名册中,挑出那个让当前台词成立概率最高的角色。
  1. 标签集合 TT 是剧组的“角色名册”,涵盖所有可能分配的角色类别。
    T={代词,动词,名词,⋯ }T = \{ \text{代词}, \text{动词}, \text{名词}, \cdots \}
  2. 条件概率 P(y|w)看到词 w 后,某标签 y 成为正确角色的概率,由贝叶斯公式拆解为似然与先验的乘积。
    P(y∣w)=P(w∣y) P(y)P(w)P(y \mid w) = \frac{P(w \mid y)\, P(y)}{P(w)}
  3. arg max 决策在所有候选标签中,选择概率最大的那个作为最终标注。
    y∗=arg⁡max⁡y∈T P(y∣w)y^{*} = \arg\max_{y \in T} \ P(y \mid w)

HMM模型原理

资历尚浅的副导演有个偷懒的诀窍:他只记住上一场戏发生了什么,就凭经验决定下一场该怎么演。你问他“为什么这场让'苹果'演名词?”他说“因为上一场'吃'是动词,动词后面通常跟名词呀。”这套“走一步看一步”的逻辑,正是 HMM(隐马尔可夫模型)的思路。

HMM 把词性标注当作“生成故事”的过程:先在心里走一遍角色链(标签序列 Y),再依据每个角色的特点写出具体台词(词序列 X)。因为要描述的是联合概率 P(X, Y),HMM 属于生成式模型——它先虚构“剧本大纲”,再落成“台词文本”。

想让这个生成过程可计算,HMM 做了两大简化:一阶马尔可夫假设(当前标签只看前一个标签)和观测独立性假设(当前词只由当前标签决定)。于是模型参数变得非常清爽——一个转移概率矩阵 A 管“角色与角色如何衔接”,一个发射概率矩阵 B 管“角色与台词如何匹配”:

副导演的“走一步看一步”虽然省力,却埋下隐患:如果一场戏要隔几个词才能看出人物关系,他只靠上一场戏就会判断失误。更致命的是,联合概率里所有组合都要枚举,计算量爆炸。于是我们迫切需要一套高效的“彩排算法”。

P(X,Y)=∏t=1TP(yt∣yt−1)×P(xt∣yt)P(X, Y) = \prod_{t=1}^{T} P(y_t \mid y_{t-1}) \times P(x_t \mid y_t)
副导演的完整思路:整出戏的“角色链与台词链”同时成立的概率,等于每个位置的“衔接分 × 演技分”全部连乘。
  1. 一阶马尔可夫性当前标签只由上一个标签决定,这是“副导演只看上一场戏”的数学化。
    P(yt∣y1,y2,⋯ ,yt−1)=P(yt∣yt−1)P(y_t \mid y_1, y_2, \cdots, y_{t-1}) = P(y_t \mid y_{t-1})
  2. 观测独立性每个词的出现只由它对应的标签决定,与其他位置的词和标签都无关。
    P(xt∣y1,⋯ ,yT,x1,⋯ ,xt−1)=P(xt∣yt)P(x_t \mid y_1, \cdots, y_T, x_1, \cdots, x_{t-1}) = P(x_t \mid y_t)
  3. 联合概率分解把整句“剧本”的概率拆成“转移概率 × 发射概率”的连乘。
    P(X,Y)=∏t=1TP(yt∣yt−1)×P(xt∣yt)P(X, Y) = \prod_{t=1}^{T} P(y_t \mid y_{t-1}) \times P(x_t \mid y_t)

Viterbi解码

副导演终于要正式排出整出戏的演员表了。他灵机一动:“我把所有可能的角色组合都彩排一遍,再挑一条最顺的戏路不就行了?”可组合爆炸太可怕——标签数 N 的句子长度 T 次方,连神仙都算不完。于是他改用动态规划:每演到一场戏,只保留“以每种角色结尾的最好彩排得分”,一路淘汰明显差劲的搭配。这就是 Viterbi 算法。

Viterbi 维护一张 DP 表:δ_t(j) 表示走到第 t 个词、且第 t 个词被标成标签 j 时,前面所有路径中的最高分;同时用一个回溯指针记录“这个最高分来自上一个哪个标签”,方便最后倒推还原完整路径。

递推式清晰写出了“上一步累积最优分 × 角色衔接分,再乘当前演技分”的完整流程。最后一列取最大值的标签,一路回溯,就得到整体最优的标签序列。整个算法的时间复杂度为 O(N²·T),从指数级降到多项式级,这才是能上生产的算法:

副导演用动态规划彩排,效率大幅提升。但别高兴太早:HMM 的强假设让他只能看到“上一场戏”,遇到依赖长距离线索的复杂剧本,依旧会翻车。导演组决定请来一位更资深的监制。

δt(j)=max⁡i[δt−1(i)×aij]×bj(ot)\delta_t(j) = \max_{i} \left[ \delta_{t-1}(i) \times a_{ij} \right] \times b_j(o_t)
副导演的彩排大法:每一步只保留最好的“戏路片段”,其余统统剪掉,既能保证全局最优,又不用排完全部组合。
  1. 累积分数 δ记录到第 t 个词为止、以标签 j 结尾的所有路径中最高的那一条的分值。
    δt(j)\delta_t(j)
  2. 转移概率 a_ij前一个标签 i 变成当前标签 j 的“出场衔接分”。
    aij=P(yt=j∣yt−1=i)a_{ij} = P(y_t = j \mid y_{t-1} = i)
  3. 发射概率 b_j(o_t)当前标签 j 演好当前词 o_t 的“演技分”。
    bj(ot)=P(xt=ot∣yt=j)b_j(o_t) = P(x_t = o_t \mid y_t = j)
  4. 取最大值从所有前序标签 i 中挑出累积最优的结果,再乘上当前的发射概率,得到本步最优分。
    δt(j)=max⁡i[δt−1(i)×aij]×bj(ot)\delta_t(j) = \max_{i} \left[ \delta_{t-1}(i) \times a_{ij} \right] \times b_j(o_t)

CRF模型原理

资深监制最讨厌“分段决策”。他认为,给每个角色定妆时必须通读整本剧本——前面的铺垫、后面的呼应、角色的前后照应,全都要一起权衡。他拍板的方式是开讨论会:每条证据(特征函数)请一位评委打分,权重越高、音量越大,最终全体评委票数最高的整套演员表才被通过。这就是条件随机场(CRF)的气质。

与 HMM 不同,CRF 不建模“故事是怎么生成的”,而是直接建模在已知台词 X 的条件下,角色链 Y 的条件概率 P(Y|X)。它不要求你遵守“观测独立”之类的硬性假设,只要求你定义特征函数——你想让评委关注什么,就定义什么样的特征。

特征函数 f_k 可以写“当前词是否首字母大写”“前一个标签是不是介词”“当前词的前缀是什么”“当前词左右各一个词是谁”……每个特征配一个权重 λ_k,通过训练数据自动学习。由于 CRF 是对整条标签序列同时打分,它能在全局层面协调“这一个词”与“前后文”的关系:

有资深监制坐镇,选角准确率大幅提升。但他仍有一个弱项——需要手工设计特征,告诉评委们该关注什么。剧组老板问:“能不能训练一个会自己看剧本的导演?”于是 AI 导演登场。

P(Y∣X)=1Z(X)exp⁡(∑t=1T∑k=1Kλkfk(yt−1,yt,X,t))P(Y \mid X) = \frac{1}{Z(X)} \exp\left( \sum_{t=1}^{T} \sum_{k=1}^{K} \lambda_k f_k(y_{t-1}, y_t, X, t) \right)
监制的拍板公式:全体评委对整条路径投票,得票总分取指数后再归一化,得到一条路径的相对概率。
  1. 特征函数 f_k一位评委负责检查一项细节,比如“前一个标签是不是动词”“当前词是不是大写”。
    fk(yt−1,yt,X,t)f_k(y_{t-1}, y_t, X, t)
  2. 权重 λ_k每个评委的话有多大分量,通过训练数据学习得到。
    λk\lambda_k
  3. 加权求和后取指数把全体评委的分数汇总成路径能量,再用 exp 映射成正比例的分值。
    exp⁡(∑t=1T∑k=1Kλkfk(yt−1,yt,X,t))\exp\left( \sum_{t=1}^{T} \sum_{k=1}^{K} \lambda_k f_k(y_{t-1}, y_t, X, t) \right)
  4. 配分函数 Z(X)把所有可能的标签序列得分全部相加做归一化,保证概率总和为 1。
    Z(X)=∑Y′exp⁡(∑t=1T∑k=1Kλkfk(yt−1′,yt′,X,t))Z(X) = \sum_{Y'} \exp\left( \sum_{t=1}^{T} \sum_{k=1}^{K} \lambda_k f_k(y'_{t-1}, y'_t, X, t) \right)

深度学习标注

AI 导演从不喊累,他阅片无数(海量语料),不需要你手工告诉他要看前缀、看上下文。你只需把剧本原封不动扔给他,他就能自动提炼每句台词的气质,再结合“角色不能冲突”的硬性要求(CRF 层),瞬间排出最优演员表。

深度学习方案的标准配置是 BiLSTM-CRF。第一层词嵌入把每个词映射成稠密向量;第二层用双向 LSTM 从正反两个方向“通读”全句,生成每个位置融合了全句信息的隐藏状态 h_t;最后一层 CRF 解码层对标签路径做全局合法性约束——比如“介词后面不能直接跟动词”这类标签搭配规则,由转移矩阵自动学习。

在得分公式里,BiLSTM 为第 t 个词给出“每个标签的初始得分”,CRF 的转移矩阵 T 负责对相邻标签的搭配加分或扣分。训练时整个网络端到端反向传播,词向量、LSTM 参数、转移矩阵一起被优化。最终路径得分是两类分数的总和:

AI 导演让选角效率和质量都上了新台阶。可剧组要真正运转起来,还差最后一公里——数据、评估指标、部署调优这些“剧组管理”琐事,决定了明星导演的活儿能不能真正落地。

Score(X,Y)=∑t=1T(Wyt⊤ht+Tyt−1,yt)\text{Score}(X, Y) = \sum_{t=1}^{T} \left( \mathbf{W}_{y_t}^{\top} \mathbf{h}_t + T_{y_{t-1}, y_t} \right)
AI 导演的快速选角:每个位置把“导演直觉分”和“角色衔接分”加起来,总分最高的那套演员表就是最终答案。
  1. 隐藏状态 h_t模型把整句话读进脑海后,第 t 个位置浓缩出的完整上下文理解。
    ht=[ht→⊕ht←]\mathbf{h}_t = [\overrightarrow{h_t} \oplus \overleftarrow{h_t}]
  2. 标签得分 W^T h把隐藏状态投影到标签空间,得到每个标签对当前词的“适合度分”。
    Wyt⊤ht\mathbf{W}_{y_t}^{\top} \mathbf{h}_t
  3. 转移得分 T相邻标签间的合法性约束分,比如“介词后不能直接接动词”。
    Tyt−1,ytT_{y_{t-1}, y_t}
  4. 求和得到路径总分把所有位置的两类分数加起来,就是整条标签路径的总得分。
    Score(X,Y)=∑t=1T(Wyt⊤ht+Tyt−1,yt)\text{Score}(X, Y) = \sum_{t=1}^{T} \left( \mathbf{W}_{y_t}^{\top} \mathbf{h}_t + T_{y_{t-1}, y_t} \right)

工程优化技巧

再好的导演也需要靠谱的制片主任。数据就是剧照素材,模型是导演,可素材脏乱差、演员表经常错位,导演再牛也白搭。工程优化,就是当好这个制片主任。

第一个任务是数据与标签的规范化。分词结果不统一、标点符号处理粗糙、标注集混用(一会儿北大集一会儿宾州集),都会让模型崩溃。第二个大难题是未登录词——剧本里冒出个从没见过的新词,模型不能直接“罢演”,必须借助词形特征(前缀、后缀、大小写、数字模式)硬着头皮给出合理标签。在 CRF 里这些是手工特征;在深度模型里,可以引入字符级嵌入来自动捕捉。

评估模型时更不能只看整体准确率。因为标签分布极不均衡——绝大多数词都是名词和动词,模型就算把所有词都标成名词,准确率也能到 60%。更科学的做法是分别计算每个标签的精确率 P、召回率 R 和 F1,对“成语”“拟声词”这类低频标签专门监控。F1 是两者的调和平均:

制片主任到位后,剧组终于可以接真实的商业项目了。调参时还要关注学习率、dropout 和 mini-batch 大小——这些细节就像剧组的排班表,安排好了一切顺畅,安排不好全员加班。

F1=2×P×RP+RF1 = \frac{2 \times P \times R}{P + R}
制片主任的考核表:既不能“宁可错杀一千”刷精确率,也不能“宁可漏掉一万”刷召回率,F1 逼着两个指标均衡发展。
  1. 精确率 P标注为某标签的结果中,真正正确的比例。
    P=TPTP+FPP = \frac{TP}{TP + FP}
  2. 召回率 R所有应为该标签的样本中,被正确标注出来的比例。
    R=TPTP+FNR = \frac{TP}{TP + FN}
  3. F1 调和平均两个指标都不偏废,取一个综合分数。
    F1=2×P×RP+RF1 = \frac{2 \times P \times R}{P + R}

实战应用案例

现在剧组接到了三个大项目,每个项目都考验着不同的“选角”组合和调度方式,而词性标注就是剧组里的定妆照环节。

项目一:命名实体识别(NER)。从“苹果在北京发布了 iPhone 15”中抽出“苹果”(公司)和“北京”(地点)。词性标注先给出候选角色:这里的“苹果”是专有名词而不是普通名词。再配合 NER 的序列标注模型,就能把专有名词与普通名词清晰区分开,避免把“苹果”当成水果去搜。

项目二:句法分析辅助。从“我爱吃苹果”到“我/代词,爱/动词,吃/动词,苹果/名词”,句法分析器依靠这些标注结果快速搭建依存树。没有词性序列,句法分析器就无法判断“吃苹果”是动宾结构还是偏正结构。

项目三:分词工具协作。像 jieba 这类工具本身就内置了词性标注功能,支持离线批量处理和在线 API 调用。在正式训练大模型之前,先用成熟工具产出一份初步标注结果,再人工抽样修正,能极大降低标注成本——这就是工业界的“快速原型法”。

从选角名册到完整的剧组体系,你已经掌握了从传统统计模型到深度学习模型的全部核心方法。无论在搜索引擎还是智能客服里,这套“选角”能力都是中文 NLP 的地基。下一部戏,就靠你亲自执导了。

AI 应用场景

搜索引擎查询理解

阶段:文本预处理 / 查询解析

解决的问题:传统词典法无法区分“苹果发布会”里的“苹果”是公司名还是水果名。CRF 词性标注结合命名实体识别,能借助上下文全局决策,把查询中的专有名词单独标记,显著提升搜索召回准确率。

具体案例:用户搜索“苹果发布会时间”,标注系统对“苹果”输出专有名词、“发布会”输出名词、“时间”输出名词,再触发品牌实体识别,迅速返回苹果公司官方活动页,而不是水果种植教程。

金融舆情情感分析

阶段:特征工程 / 情感分析

解决的问题:情感极性常受修饰关系和否定结构影响。传统词袋模型只看到“下跌”就判负面,忽略了“长期向好”的转承结构。BiLSTM-CRF 先为每个词配好词性标签,让情感分类模型能利用词性序列识别转折句。

具体案例:从“市场短期出现下跌,但长期向好趋势不变”中,词性标注输出“下跌/动词、向好/形容词”,情感分析系统据此识别出“短期负面、长期正面”的细粒度结论,帮助基金经理把握舆论节奏。

智能客服意图识别

阶段:自然语言理解

解决的问题:用户说“我要申请退款”和“退款失败怎么办”,句式结构完全不同。直接用词匹配容易误判,而词性序列“动词+名词”能帮助意图模板泛化到相似表达。

具体案例:金融客服机器人接入词性标注模块,将“信用卡 提额 失败 的 原因”统一标记为“名词 名词 动词 助词 名词”,再匹配到“提额问题咨询”意图,准确率比纯关键词匹配提升约 12 个百分点。

Python 演示

python69 行
# -*- 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(["我", "爱", "香蕉", "苹果"]))
目的:用微型语料训练一个 HMM 词性标注器,并通过 Viterbi 解码输出最优标签序列。
  1. self.trans = defaultdict(lambda: defaultdict(float))转移概率表用二级 defaultdict 存储,避免稀疏矩阵的繁琐判断
  2. self.emit[tag][word] += 1统计发射频次:某个词性下每个词出现了多少次
  3. total_start = sum(self.start.values())归一化初始概率,让“第一个词是某词性”的概率和为 1
  4. dp[i, 0] = self.start.get(tag, 1e-8) * self.emit[tag].get(words[0], 1e-8)Viterbi 初始化:首词的初始概率 × 发射概率
  5. probs = dp[:, t - 1] * np.array([...])递推核心:上一步所有标签的最优分 × 转移到当前标签的概率
  6. back[j, t] = np.argmax(probs)记录当前最优分来自上一列的哪个标签,为了最后回溯
  7. dp[j, t] = probs[back[j, t]] * ...乘上当前词的发射概率,完成本步的最优分计算
  8. last = np.argmax(dp[:, T - 1])末尾取全局最大分对应的标签,作为回溯起点
  9. 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. 词性标注的本质任务是什么?

单选题 · easy

  • A.给词语分配合适的词性标签← 正确答案
  • B.把句子切分成词序列
  • C.将词语映射为稠密向量
  • D.将词语翻译为另一种语言

答案:A。词性标注是序列标注任务,核心是给每个词分配一个最合适的词性标签。分词是前置任务,向量化是表示学习,翻译是机器翻译任务,都偏离了词性标注的定义。

Q2. HMM 模型的两个核心假设是什么?

单选题 · mid

  • A.一阶马尔可夫性与观测独立性← 正确答案
  • B.状态无限多且无后效性
  • C.特征无限展开且权重共享
  • D.观测序列独立且标签可以交叉

答案:A。HMM 的两个核心假设是一阶马尔可夫性(当前标签只看前一个标签)和观测独立性(当前词只由当前标签决定)。其他选项分别对应无限状态、特征展开等不相关概念。

Q3. 关于 Viterbi 算法,以下哪些说法正确?

多选题 · mid

  • A.利用动态规划避免指数级枚举← 正确答案
  • B.需要回溯过程来还原最优路径← 正确答案
  • C.每一时刻只需保留最优来源← 正确答案
  • D.只能用于 HMM 不能用于 CRF

答案:ABC。Viterbi 通过动态规划在每个时刻保留最优路径片段,避免指数级组合枚举,最后用回溯指针还原完整路径。它不仅能用于 HMM,也能用于 CRF 和 BiLSTM-CRF 等任意“有路径得分”的模型,所以 D 是错的。

Q4. CRF 中配分函数 Z(X) 的作用是什么?

单选题 · hard

  • A.对全局概率做归一化处理← 正确答案
  • B.加速训练时的收敛速度
  • C.构造有效的特征函数
  • D.保存当前最优的搜索路径

答案:A。配分函数 Z(X) 把所有可能的标签序列的得分求和,做归一化,保证输出是一个合法的概率分布。它不负责加速、构造特征或搜索路径,那些分别由优化器、特征工程和 Viterbi 算法负责。

Q5. 下列哪些是 CRF 相比 HMM 的优势?

多选题 · mid

  • A.可以灵活加入丰富上下文特征← 正确答案
  • B.直接建模条件概率 P(Y|X)← 正确答案
  • C.不要求观测序列的独立性← 正确答案
  • D.模型训练不需要标注数据

答案:ABC。CRF 是判别式模型,直接建模 P(Y|X),可以自由添加重叠特征,不需要观测独立性假设。但 CRF 是有监督学习,依然需要人工标注语料训练,所以 D 错误。

Q6. 在 HMM 词性标注中,“发射概率”描述的是什么?

单选题 · easy

  • A.当前词在给定词性下出现的概率← 正确答案
  • B.前一个词性转移到当前词性的概率
  • C.句子起始处某个词性出现的概率
  • D.所有词性标签共享同一个概率值

答案:A。发射概率 b_j(o_t) = P(x_t = o_t | y_t = j),表示在标签 j 下出现词 o_t 的概率,类似“演员在某个角色下说出某句台词的几率”。转移概率管标签间衔接,初始概率管句首,都不是发射概率。

Q7. BiLSTM-CRF 模型中 CRF 层的主要作用是什么?

单选题 · mid

  • 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 代表什么?

单选题 · hard

  • 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. 解决未登录词词性标注的常用方法有哪些?

多选题 · hard

  • A.利用前缀后缀等词形特征← 正确答案
  • B.在 CRF 中加入词形特征← 正确答案
  • C.使用上下文词组成类词典特征← 正确答案
  • D.直接丢弃未登录的稀有词

答案:ABC。未登录词没有词典信息,但可以通过词形特征(前缀、后缀、大小写)、CRF 中手工加入的词形特征、以及左右上下文词的投票来推断标签。直接丢弃未登录词会丢失信息,严重影响下游任务,是错误的做法。

Q10. F1 分数的计算公式是什么?

单选题 · easy

  • A.精确率与召回率的调和平均← 正确答案
  • B.精确率与召回率的算术平均
  • C.精确率与召回率的两倍之和
  • D.精确率与召回率的简单加法

答案:A。F1 = 2PR/(P+R),是精确率和召回率的调和平均。算术平均、两倍之和、简单加法都无法体现“两个指标都要高”的约束,因此不是正确的 F1 定义。