简介Monash FIT5217 自然语言处理期末复习整理详解中文版是一份面向莫纳什大学该课程学生的中文备考资料内容覆盖 Moodle 课后全部习题答案和每周课件重点知识点旨在帮助系统巩固课堂所学、高效备考期末考试。从 n-gram 模型参数估算、马尔可夫假设与链式法则到 MLE 概率计算、add-1 平滑、OOV 率及未知词处理再到 perplexity 困惑度公式与三种平滑技术比较均配有中英对照的推导过程和答题模板并额外收录 Self-Assessment 练习详解帮助理解 NLP 语言模型的核心脉络。资源以 PDF 格式呈现共 1 个文件压缩包仅 7.45MB下载后可在电脑或手机端直接阅读也方便打印成纸质材料背诵。目前已有 813 人学习适合考前突击梳理、查漏补缺。相比零散课件这份整理将考试高频考点集中归纳每道题都给出完整解题步骤和原理说明不仅能让读者快速掌握答题方法还便于举一反三应对题目变形显著提升期末复习效率。1. FIT5217 自然语言处理期末复习先定主线再谈记忆FIT5217 自然语言处理这门课的期末复习大部分人第一个动作是打开课件从头翻一遍。我的建议是先别翻。这门课覆盖的范围很广——从 n-gram 语言模型、词向量到 HMM/CRF 序列标注再到 RNN、注意力机制和 Transformer中间还穿插文本分类、机器翻译、问答等应用方向的选讲内容。如果按页数平均用力复习到后半段基本会变成“每个名词都眼熟每道题都手生”。更务实的做法是先画出一条贯穿全课的复习主干把“统计 NLP 的理论底子”和“深度学习 NLP 的结构骨架”串成一条线再把各章考点挂到这条线上。这篇整理就是按这个思路写的适合正在备考 FIT5217 的同学也适合想快速补一遍 NLP 核心知识栈的从业者。它的价值不在于罗列多少名词而在于帮你分辨哪些内容必须手推、哪些只需读懂结论别把宝贵的复习时间花在性价比低的地方。2. 语言模型与词向量从 n-gram 到预训练模型一张表理清主线语言模型是 NLP 课程的第一个大考点也是后面词向量、序列标注、Transformer 的认知地基。这一章把它拆成三个递进的层次统计语言模型、静态词向量、上下文相关的动态词向量。复习时按这个顺序走能明显感觉到每一层解决的是上一层遗留的哪个问题——这个“问题驱动”的视角考试时很有用。2.1 n-gram 语言模型困惑度公式与平滑方法一张表拿走n-gram 语言模型的核心假设是马尔可夫性当前词的概率只依赖前 n-1 个词。用极大似然估计条件概率写为P(w_i | w_{i-n1}^{i-1}) count(ngram) / count(prefix)。这个公式本身不难真正的考点藏在两个地方一是数据稀疏时怎么办二是怎么评估模型好坏。数据稀疏对应平滑方法。拉普拉斯平滑1 平滑是最基础的但 FIT5217 的考题通常不会停在“给每个计数加 1”这种直觉层面而是会问它为什么对长 n-gram 效果差——因为长 n-gram 的计数基数大加 1 对概率分布的扭曲更明显。回退backoff和插值interpolation是考得最多的两类思路回退是“高阶 n-gram 计数为 0 就退回 (n-1)-gram”插值是“把不同阶的 n-gram 概率加权求和”。如果课件里还讲了Good-Turing 和 Kneser-Ney至少要把它们的核心直觉记住Good-Turing 用“出现 r 次的事类数量”来估计“出现 0 次”的概率Kneser-Ney 则是在回退时考虑词的“延续性”——比如“San Francisco”里的 Francisco 很少单独出现但它不是通用词。这两点是我复习时容易忽略、但考试和作业都容易出现的细节。评估语言模型用的是困惑度perplexity公式写成PPL exp(-(1/N) * Σ log P(w_i | w_{i-n1}^{i-1}))越小的困惑度代表模型对测试集的预测越自信。复习这个公式时有个容易踩的坑困惑度必须在同一个词表、同一种分词方式、同一个测试集上比较才有意义。跨词表比较困惑度数值差异可能完全来自分词粒度而不是模型质量。这一点在第 5 章还会单独展开。平滑方法核心思路典型考点易错点1 平滑所有 n-gram 计数加 1写出修正后的概率公式忘记分母也要加 V 倍回退计数为 0 时退到低阶说明回退的顺序与系数混淆“回退”和“插值”插值各阶概率加权求和权重的学习方式EM 或网格搜索把权重写成固定值Kneser-Ney用延续概率做回退修正说明延续概率的作用说不清它和 Good-Turing 的区别2.2 Word2Vec 的 CBOW 与 Skip-gram负采样为什么是神来之笔从 n-gram 到词向量解决的核心问题是“如何让词义变成可计算的向量”。Word2Vec 有两种结构CBOW 用上下文词向量预测中心词适合小语料、训练更快Skip-gram 用中心词预测上下文词对低频词更友好。复习时别只背这个结论要能从“谁预测谁”的图里推出损失函数的形式。Skip-gram 的原始目标是最大化log P(context | center)这里的 P 如果直接用 softmax 对整个词表归一化词表动辄几十万每次更新都要算一遍全词表完全没法落地。负采样把问题改写成二分类给定 (center, context) 对正样本的得分要尽量高随机采样的 k 个负样本得分要尽量低。损失变成L log σ(v_c · v_t) Σ_{n∈neg} log σ(-v_c · v_n)其中σ是 sigmoidv_c是中心词向量v_t是正样本上下文词向量v_n是负样本向量。这个公式的推导细节在作业和考试里都值得亲手写一遍对正样本项求导得到(1 - σ(v_c · v_t)) * v_t对负样本项求导得到-σ(v_c · v_n) * v_n两者方向相反、含义清晰。复习时另一个高频考点是“一个词为什么有两套向量”每个词既是中心词也是上下文词所以训练完得到W_in和W_out两套矩阵实际使用时常取两套向量的平均或者只取输入矩阵。这个细节我在作业里踩过坑——用输出矩阵做近邻查询结果和预期差很远因为输出矩阵的分语义没有被直接训练目标约束。2.3 从静态向量到上下文向量ELMo 与 BERT 考什么Word2Vec 最大的局限是词向量静态不变bank 在“河岸”和“银行”两种语境里是同一个向量。FIT5217 的课程后半段一定会讲如何用上下文信息解决这个问题最常见的是 ELMo 和 BERT 两个代表。ELMo 的答案是双向 LSTM分别从左到右和从右到左训练语言模型每个词的表示是两层 LSTM 隐状态的加权和。注意它是一个特征抽取器下游任务把它的输出拼到自己的输入里。BERT 则换成了 Transformer encoder核心是掩码语言模型MLM随机遮住一部分词让模型根据上下文预测被遮住的词。复习时建议自己说一遍“如果一个句子里出现‘我 [MASK] 苹果’模型如何用双向信息做预测”——能把这个流程讲清楚预训练原理这类的题目基本就稳了。这里有个方向性的建议BERT 的 NSP下一句预测在很多版本里已被证伪甚至移除如果课件里讲了就按课件复习课件没提就别拓展以免把复习重心带偏。真正要抓住的主线是**“动态词向量 语境相关的表示”**这个思想而不是某个具体模型的结构参数。考试如果给一段代码或一个句子问它应该用 Word2Vec 还是 BERT 得到向量表示答案的关键就是看“是否要考虑上下文信息”。3. 序列标注与文本分类HMM、CRF 与序列建模的边界序列标注是 NLP 里最典型的任务形态词性标注、命名实体识别、组块分析都长这样输入一串词输出一串等长的标签。这一章是 FIT5217 复习的重头戏HMM 和 CRF 几乎是必考对比组——课程考它们的概率远比考某个具体深度学习模型高因为它们能很好地考察“模型假设”和“算法推导”的功底。3.1 HMM 三问前向、Viterbi、Baum-Welch 的复习优先级HMM 假设观测序列由一条不可见的隐状态链生成每步状态转移只依赖前一个状态每个观测只依赖当前状态。五元组(S, O, A, B, π)必须背熟。围绕它有三个经典问题复习优先级明确区分评估问题给定模型和观测求P(O | λ)用前向算法。迭代式是从 t 时刻的α推到 t1 时刻公式α_{t1}(j) [Σ_i α_t(i) * a_{ij}] * b_j(o_{t1})。这个公式要能默写。解码问题给定模型和观测求最可能的隐状态序列用 Viterbi 算法。核心是递推式δ_t(j) max_i δ_{t-1}(i) * a_{ij} * b_j(o_t)并且必须保存回溯指针。这是考试最容易出大题的地方建议亲手实现一遍下一节给出最小代码。学习问题给定观测估计模型参数(A, B, π)用 Baum-WelchEM 特例。复习重点是说出 E 步算后验概率、M 步重估参数的思路不要死记公式但要能说出它和普通 EM 的对应关系。复习顺序上解码 评估 学习。Viterbi 几乎每年都出现前向算法经常和 Viterbi 对比出题学习问题通常只考概念。如果你时间不够优先把 Viterbi 的手推和实现练熟。3.2 CRF 与 HMM 的本质差别判别式 vs 生成式CRF 在 FIT5217 里通常和 HMM 放在一起对比讲解。最精炼的回答是HMM 是生成式模型CRF 是判别式模型。HMM 建模P(x, y)的联合分布CRF 直接建模P(y | x)的条件分布因此在特征上更自由——HMM 必须把特征编码进独立的发射概率里CRF 可以把任意重叠、任意相关的特征直接塞进特征函数。线性链 CRF 的标签依赖是一阶马尔可夫结构但观测特征可以是很长的窗口。CRF 的损失函数是带全局归一化的对数似然log P(y | x) Σ_k θ_k f_k(y_{i-1}, y_i, x, i) - log Z(x)Z(x)是配分函数对所有可能的标签序列求和。这个归一化是 CRF 和 HMM 在“全局最优”上的关键差异HMM 的标签转移靠局部概率连乘CRF 的转移特征在整个序列上联合打分因此能克服标签偏置问题。考试常问“为什么标注任务里 CRF 往往比 HMM 好”答题要点就是这三条判别式建模、特征灵活、全局归一化。对比记忆可以用这个表对比维度HMM线性链 CRF模型类型生成式判别式建模对象P(x, y) 联合分布P(y | x) 条件分布特征只能通过发射概率间接表达特征函数直接定义支持重叠特征归一化局部概率连乘天然归一全局配分函数避免标签偏置解码Viterbi同样是 Viterbi但用的是 CRF 的势函数3.3 复习时值得手写的 Viterbi 解码最小实现手写一遍 Viterbi 的价值不是“以后工作会用到”而是把课本上那行递推式变成肌肉记忆。考试手推时最容易丢分的点不是公式本身而是回溯步骤——很多人算出最大概率却拿不到对应路径。import numpy as np def viterbi(obs, states, start_p, trans_p, emit_p): 解码最可能的隐状态序列。 obs: list[int]观测序列每个元素是词在词表中的索引 states: list[str]状态列表 start_p: dict初始概率 P(state) trans_p: dict[dict]转移概率 A[i][j] P(i - j) emit_p: dict[dict]发射概率 B[i][o] P(o | i) 返回: list[str]最优状态序列 T len(obs) n_states len(states) # dp[t][j]: 到 t 时刻为止、当前状态为 j 的最大 log 概率 dp np.full((T, n_states), -1e9) # back[t][j]: 到达 dp[t][j] 时t-1 时刻的最优状态索引 back np.zeros((T, n_states), dtypeint) # 初始化 t0初始概率 发射概率 for s in range(n_states): dp[0][s] np.log(start_p[s] 1e-12) np.log(emit_p[s][obs[0]] 1e-12) # 递推dp[t][j] max_i dp[t-1][i] log(trans[i][j]) log(emit[j][obs[t]]) for t in range(1, T): for j in range(n_states): probs dp[t-1] np.log(trans_p[:, j] 1e-12) np.log(emit_p[j][obs[t]] 1e-12) dp[t][j] np.max(probs) back[t][j] np.argmax(probs) # 回溯从最后一刻概率最大的状态往回找整条路径 path_idx [np.argmax(dp[T-1])] for t in range(T-1, 0, -1): path_idx.insert(0, back[t][path_idx[0]]) return [states[i] for i in path_idx]这段代码做了两个关键设计。一是用log概率把连乘变成连加避免概率连乘后下溢成 0——考试手推时如果题目给的概率很小建议先取log再算思路会清爽很多。二是保留back回溯矩阵每步记录“当前最优是来自上一时刻的哪个状态”最后从后往前还原整条路径。参数trans_p是n_states × n_states的二维数组emit_p是n_states × vocab_size的二维数组初始概率start_p是长度n_states的向量这三个参数换算成 log 域时都要加一个小常数1e-12防止对 0 取 log 报错。复习时建议把这段代码和课本上的递推式对照着看一遍然后把输入换成题目里的数字手动跑两个时间步基本就能把 Viterbi 的机制吃透。4. 深度 NLP 骨架RNN、注意力与 Transformer 的复习路径从统计模型跳到深度模型很多同学是在这里开始掉队的——前面的 HMM 好歹公式明确到了 RNN 和 Transformer符号一多、维度一变就觉得像在看黑匣子。这一章的目标是把骨架拆开让你知道每个模块的“输入 shape、输出 shape、为什么需要它”。4.1 RNN 反向传播的梯度悬崖与 LSTM 门控直觉RNN 的本质是在时间步上共享参数h_t tanh(W_hh * h_{t-1} W_xh * x_t b)。它的反向传播 BPTT 沿着时间展开误差信号从最后一步一路乘回第一步中间是连续的雅可比矩阵连乘。复习时一定要亲手推导一下“为什么长序列会导致梯度消失”——因为每一步都乘一个tanh的导数而tanh的值域是(0, 1]大部分时候小于 1连乘几十步就趋于 0。梯度爆炸则对应特征值大于 1 的情况常靠梯度裁剪gradient clipping缓解。LSTM 的复习重点不是背三个门的名字而是理解它为什么能缓解梯度消失遗忘门让网络学会“记住”或“忘记”信息细胞状态C_t提供了一条从远处直接传回梯度的“捷径”。门控的值域在(0, 1)之间使得误差信号在细胞状态这条通路上的衰减可控。考试常给一张 LSTM 结构图让你标出每一步的公式复习时把输入门i、遗忘门f、输出门o、候选值g和细胞更新公式默写一遍比什么都管用。GRU 只需知道它是 LSTM 的简化版两个门参数更少课程若没展开就不用深挖。4.2 注意力机制Seq2Seq 瓶颈与两种打分方式Seq2Seq 的标准结构是 encoder 把整个源序列压缩成一个固定长度的上下文向量decoder 从这个向量开始逐词生成。这个设计的短板很明显句子长一点前面词的信息就在压缩过程中被挤掉了。注意力机制的思路是不再依赖那一个固定向量而是让 decoder 每一步自己决定“该回头看源序列的哪些位置”。核心公式先记住score(q, k)的打分方式再记权重。最常见的两种加性注意力score(q, k) v^T * tanh(W_q * q W_k * k)点积注意力score(q, k) q · k。点积注意力的好处是实现简单、可以矩阵化并行但维度增大时点积值会变得很大softmax 梯度趋近于 0所以 Transformer 里要用缩放点积score q · k / sqrt(d_k)。这个缩放的动机是考试高频考点务必能说清“为什么除以√d_k”。注意力权重的计算流程可以写成α softmax(score(q, K))然后context Σ α_i * v_i。复习时建议对照一个机器翻译例子的注意力可视化图自己手算一遍权重——这类题基本是送分题前提是你真的理解权重是对所有 encoder 位置归一化得到的而不是只看当前词对齐的那个位置。4.3 Transformer 必考细节位置编码、多头注意力的维度与残差Transformer 是 FIT5217 课程后半程的高潮也是考试最容易出“细节题”的部分。三个高频考点位置编码为什么需要、多头注意力维度怎么切、残差和 LayerNorm 的作用。位置编码是因为 self-attention本身不感知顺序。如果把“我爱你”和“你爱我”的词向量序列分别输入自注意力打出来的权重是一样的组合方式模型分不清语序。Transformer 原文用正弦余弦函数生成位置编码频率随维度变化后期也有用可学习位置编码的变体。复习重点是能说出“为什么选择正弦编码”——它可以泛化到训练时未见过的长度而且不同维度的频率可以表征不同尺度的位置关系。多头注意力的维度要会算。设输入维度d_model 512头数h 8则每个头的Q、K、V线性投影维度是d_k d_model / h 64。每头独立进行缩放点积注意力得到64维输出再拼接成512维最后过一个输出投影。复习时拿张纸画一遍[batch, seq_len, 512] - 分成 8 个头 - 每头 [batch, seq_len, 64] - 注意力 - 拼接 - [batch, seq_len, 512]维度脉络就清楚了。残差连接x sublayer(x)解决的是深层网络退化问题LayerNorm 在序列维度上做归一化与 BatchNorm 在 batch 维度上归一化有本质区别——这个对比也常考。5. 期末复习避坑指南五个让复习翻车的认知误区这一章的每一条都是我在复习和带同学复习时真实遇到过的坑按“现象 → 原因 → 解决”写出来。它不是知识点的补充而是复习策略的纠偏——很多同学不是不努力是把努力用在了错误的方向上。5.1 现象公式背得很熟一写代码就不知道维度怎么初始化背得出softmax(QK^T / √d_k)V但真让写 PyTorch 代码时不知道Q、K、V怎么从输入得到不知道多头注意力里view怎么切。原因是公式层面的“符号”没有落到“shape”上——你记住了运算关系但没记住每个张量的形状如何随层变化。解决方法是复习时给每个模型配一张“维度流转表”。对 Transformer从输入[batch, seq_len, d_model]开始写出经过W_q投影后的[batch, seq_len, d_model]再切成h个头变成[batch, h, seq_len, d_k]注意力输出后再合并回d_model。多花十分钟把这条链走通考场上遇到“计算图填写题”和“写代码题”都会稳很多。5.2 现象报告 F1 分数和课程评测的分数对不上作业里自己写评测脚本得到的 F1 和助教给的分数差异很大。原因几乎都是评测口径不一致标注任务是按 token 级算还是按实体/短语级算token 级 F1 把每个标签当作独立样本统计实体级 F1 必须整段实体识别正确才算一个 Positive两者数值可以差出十几个点。解决方法是先明确定义。写评测脚本时先用混淆矩阵数清楚 True Positive 到底是什么——是一个 token 的标签对了还是一整段连续标注框对了。课程作业一般会在说明文档里定义评测口径如果没写尽早问清楚。这个坑在新手作业里出现的频率极高属于“不算难题但特别冤”的丢分类型。5.3 现象拿困惑度横向比较不同模型得出反直觉结论你训练了一个 3-gram 模型困惑度 80同学用 Word2Vec 加一个分类器报告里说困惑度 10于是你觉得自己模型很菜。实际上两者可能根本不能比——困惑度只在语言模型之间、且词表和分词一致时才有比较意义。如果对方的词表更小、OOV 词直接标unk困惑度天然偏低如果用了不同分词工具同一句话的 token 数都不同PPL 更是没法对齐。解决方法是把困惑度当作“同一份测试集、同一套预处理”下的模型调试指标而不是跨设置对比的通用分数。跨模型比较那个任务应该用下游任务的实际效果比如 BLEU、文本分类准确率来衡量。5.4 现象模型训练好了但生成的句子质量差不知道是模型问题还是参数问题很多课程作业会包含文本生成任务比如用 RNN/LSTM 生成句子。同学把焦点全放在训练损失上损失降下来了但生成的文本重复、空洞。原因是复习时只关注了训练阶段的损失函数忽略了推理阶段的解码策略。解决方法是把解码策略当独立考点复习。贪婪搜索每步选概率最高的词容易陷入重复束搜索保留 top-k 条路径beam_width越大越接近全局最优但计算量上升温度采样通过temperature调整 softmax 分布的尖锐程度温度越低越确定、越高越发散。考试如果给一个解码结果的例子让你分析为什么质量差通常要往解码策略而不是模型结构上答。5.5 现象在中文数据上跑 HMM/CRF效果比课程报告的英文结果差很多中文没有天然空格分词词性标注和 NER 的表现受分词质量影响极大。很多同学把英文课程的评测数据直接换成中文数据集发现 F1 掉了一大截就开始怀疑模型实现有 bug。实际上这是 NLP 落地时的常见情况——序列标注模型的性能上限很大程度被上游的分词或词边界定义限制。解决方法是先确认评测口径对中文是否公平实体边界怎么定义、“O”标签怎么划分、未登录词占多少。如果课程作业允许自定义数据集优先选领域匹配、标注规范的数据不要在数据清洗上省时间。这个坑在工作后尤其常见——模型调参两个小时最后发现是数据分错了属于典型的“事前不检查、事后白熬夜”。6. 把期末复习变成可复现能力一个 50 行的负采样词向量 Demo复习到最后最有价值的验证方式不是再翻一遍课件而是动手把某个算法从零实现一遍。这里给你一个我当年期末之后仍在用的自检项目用纯 NumPy 实现 Skip-gram 负采样词向量训练。它能同时验证你对负采样损失的理解、对梯度方向的把握以及对训练迭代的认知——比背公式深刻得多。以下代码去掉注释约 50 行语料就用几行中文句子跑起来几秒钟。import numpy as np def sigmoid(x): return 1.0 / (1.0 np.exp(-x)) corpus [ 我 喜欢 自然 语言 处理, 自然 语言 是 人工 智能 的 明珠, 这是 一 门 适合 系统 学习 的 课程, 深度 学习 在 自然 语言 处理 中 应用 广泛, ] def build_pairs(corpus, window2): vocab {} for sent in corpus: for w in sent.split(): vocab.setdefault(w, len(vocab)) pairs [] for sent in corpus: toks [vocab[w] for w in sent.split()] for i, center in enumerate(toks): ctxs toks[max(0, i - window): i] toks[i 1: i 1 window] for ctx in ctxs: pairs.append((center, ctx)) return vocab, pairs def train(corpus, embed_dim16, neg_k3, epochs50, lr0.05): vocab, pairs build_pairs(corpus) V len(vocab) rng np.random.default_rng(0) W_in rng.normal(0, 0.01, (V, embed_dim)) W_out rng.normal(0, 0.01, (V, embed_dim)) for epoch in range(epochs): for center, ctx in pairs: vec_c W_in[center] vec_ctx W_out[ctx] score vec_c.dot(vec_ctx) grad_pos 1.0 - sigmoid(score) # d/dscore log sigma(score) W_in[center] lr * (grad_pos * vec_ctx - 0.01 * W_in[center]) W_out[ctx] lr * (grad_pos * vec_c - 0.01 * W_out[ctx]) for _ in range(neg_k): neg rng.integers(V) while neg in (center, ctx): neg rng.integers(V) vec_neg W_out[neg] score_neg vec_c.dot(vec_neg) grad_neg -sigmoid(score_neg) # d/dscore log sigma(-score) W_in[center] lr * (grad_neg * vec_neg - 0.01 * W_in[center]) W_out[neg] lr * (grad_neg * vec_c - 0.01 * W_out[neg]) return vocab, W_in, W_out vocab, W_in, W_out train(corpus) def nearest(word, topk3): idx vocab[word] sims W_in W_in[idx] order np.argsort(-sims)[:topk 1] return [list(vocab.keys())[i] for i in order if i ! idx][:topk] print(“自然”的近邻词, nearest(自然))逻辑说明grad_pos 1 - sigmoid(score)来自正样本项log σ(score)对 score 的导数grad_neg -sigmoid(score_neg)来自负样本项log σ(-score_neg)对 score_neg 的导数。每个训练对分三条更新路径中心词向量、正样本上下文向量、负样本向量。这里的neg_k是负采样个数通常取 520语料小时取 3 就够lr是学习率语料小且迭代次数多时可以适当调低。L2 正则项系数 0.01 防止向量把训练语料“背”下来导致过拟合。训练结束后W_in作为查询向量W_out也可以拼进来取平均但小语料下用W_in更直观。验证方法看nearest(自然)能否出“语言”“处理”这类语义相近词。语料太小结果带一定随机性但结构是对的。想看更直观的把W_in交给 sklearn 的 TSNE 降到二维每个词画一个点语义相近的词会聚在一起。这个 Demo 不追求效果它像一张试纸能测出你是否真的理解了负采样——我当时就是用这种方式发现自己“以为懂了但写不对梯度符号”的问题。现在每次复习 NLP 基础我都会重跑一遍这个小脚本把损失函数的每一个符号重新推一遍这个习惯帮我省了后面很多翻车时间。希望帮到你。本文还有配套的精品资源点击获取