ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

NLP教材14章拆解:从分词到知识图谱的工程阅读路线

NLP教材14章拆解:从分词到知识图谱的工程阅读路线

简介:《自然语言处理导论》由张奇、桂韬、黄萱菁三位长期从事NLP教学与科研的学者编写,面向高校高年级本科生、研究生及对该领域感兴趣的入门读者,系统解决NLP知识分散、缺乏全面教材的问题。全书共14章,第一章为绪论,其余按基础技术、核心技术、模型分析三部分展开:基础技术依次讲解词汇分析、句法分析、语义分析、篇章分析与语言模型,其中语言模型一章专门介绍GPT与ChatGPT的实现过程;核心技术覆盖信息抽取、机器翻译、情感分析、智能问答、文本摘要与知识图谱;模型分析部分聚焦大模型的稳健性与可解释性。资源包为1个PDF文件,约35.01MB,内容完整、目录结构清晰,便于按章节检索学习。目前已有1731人学习下载,适合希望建立NLP任务与机器学习算法对应关系、系统梳理从规则方法到预训练模型研究范式的读者参考。

1. 拿到一份 14 章的 NLP 教材 PDF,先别急着从头读

上周有个做搜索排序的同行问我:想系统补一遍自然语言处理,但网上课程要么只讲 Transformer 之后的模型,要么一上来就是公式推导,中间那段“任务怎么定义、语料怎么标、指标怎么算”的空白没人填。我把他拉到工位上,翻出这份《自然语言处理导论》的网络预览版 PDF——14 章,2023 年 4 月定稿,作者是张奇、桂韬、黄萱菁三位长期做 NLP 教学和科研的老师。它最值钱的地方不是讲了多少模型,而是把每个任务都按“任务概述 → 语言学知识 → 规则方法 → 特征机器学习 → 深度网络 → 评测指标 → 常用数据集”这条线铺开,等于把从词法到篇章、从信息抽取到知识图谱的整条工程链路摊在你面前。适合谁?适合已经会调库、但说不清 CRF 为什么能用在分词和词性标注两个任务上的从业者,也适合带课的研究生助教。下面我按自己拆这份 PDF 的顺序讲,怎么读、怎么对照代码、哪些章节可以跳。

2. 拆解全书结构:三部分 14 章怎么对应到工程任务

2.1 基础技术五章:词汇、句法、语义、篇章、语言模型

第一部分是底层技术,按处理粒度从小到大排:第 2 章词汇分析、第 3 章句法分析、第 4 章语义分析、第 5 章篇章分析、第 6 章语言模型。这个顺序不是随便定的,它对应一条真实的数据处理流水线——你拿到一段原始文本,先分词和词性标注(第 2 章),再做句法结构(第 3 章),然后抽语义表示(第 4 章),跨句才谈篇章(第 5 章),最后才是用语言模型给整条链路兜底(第 6 章)。我一般建议读者先读第 2 章和第 6 章,因为这两章是日常调用最频繁的:分词决定了下游所有特征的边界,语言模型决定了你评估生成质量时的口径。

第 2 章里有个细节值得单独拎出来:中文分词一节同时给了最大匹配、线性链条件随机场、感知器、双向 LSTM 四种方法,还配了评价方法和语料库说明。这不是凑篇幅,而是让你看到同一个任务在不同范式下的解法差异。最大匹配是规则派,CRF 是特征机器学习派,BiLSTM 是深度派,三者的评测指标(精确率、召回率、F1)算法完全一致,但错误分布完全不同——规则方法在未登录词上翻车,CRF 在长距离依赖上吃力,BiLSTM 吃语料规模。这种对照在别处很难一次看全。

第 6 章语言模型是全书和当下最贴的一章,正文明确提到 GPT 的基础介绍和 ChatGPT 的实现过程。注意这是 2023 年初的稿子,讲的是 GPT 系列的技术脉络,不是最新模型的参数细节,读的时候把它当“预训练语言模型怎么从 n-gram 走到大规模”的历史线来看,反而更清楚为什么现在的模型要做成那个样子。

2.2 核心技术六章:从信息抽取到知识图谱的任务映射

第二部分是应用任务:信息抽取、机器翻译、情感分析、智能问答、文本摘要、知识图谱。这六章的组织方式和第一部分一样,每个任务都走“目标 → 语言学知识 → 难点 → 各范式算法 → 评测 → 数据集”的模板。我带人读这部分时有个固定动作:先看每章开头的“任务难点”,再看结尾的“评测指标和数据集”,中间算法部分按需跳读。

原因是这样:难点决定了你选什么算法,指标决定了你怎么验收。比如信息抽取的难点在于实体边界和关系类型的歧义,那你就知道为什么序列标注和关系分类要分开做;机器翻译的评测指标是 BLEU 这类 n-gram 重叠度,你就明白为什么有些改动在 BLEU 上涨了但人工看还是别扭。把这两头抓住,中间的算法细节可以等真正上手某个任务时再回来查。

这里有个容易被忽略的点:全书反复强调“同一机器学习算法可以应用于不同任务”,并且刻意在不同任务里选不同类别的算法来讲,避免重复。条件随机场在第 2 章中文分词里出现,在第 2 章词性标注里又出现,在第 3 章句法分析里还可能出现——作者的处理方式是第一次讲透,后面只讲“这个算法在这个任务上怎么改”。读的时候要建立一张“任务—算法”对照表,不然容易觉得内容散。

2.3 模型分析两章:稳健性与可解释性为什么放在最后

第三部分只有两块内容:模型的稳健性和可解释性。序言里说得很直白——大模型参数高达数千亿,人们极难理解这些参数的含义,结果虽好但难解释;而且有些精度很高的大模型,仅改变几个参数性能就下降很多,不稳健。这两章放在最后不是凑数,而是给前面所有任务一个“验收视角”:你前面学的分词、句法、翻译、摘要,最终都要面对“这个模型靠不靠谱、出错能不能查”的问题。

我在实际项目里越来越觉得这两章该提前读。做线上 NLP 服务,模型精度只是及格线,真正让你半夜爬起来的是分布漂移导致的性能骤降,以及出了问题没法向业务方解释“为什么这句话被判成负面”。稳健性那部分讲的是扰动下的表现,可解释性那部分讲的是归因方法,这两块内容在工程落地时的价值不比任何一章算法低。

2.4 用目录页做一张阅读路线表

拿到 PDF 后我做的第一件事不是读正文,而是把目录页截图,按“当前工作是否需要”打标。下面这张表是我自己用的分类方式,你可以照着改:

章节内容建议读法对应工程场景
第 1 章绪论、基本范式通读一遍建立术语和范式坐标系
第 2 章词汇分析、中文分词、词性标注精读加动手文本预处理流水线
第 3 章成分句法、依存句法选读依存部分关系抽取、事件抽取的前置
第 4 章语义表示、谓词逻辑、框架、语义网精读语义表示知识表示、语义匹配
第 5 章篇章分析按需长文档理解、指代消解
第 6 章语言模型、GPT 脉络精读生成任务、预训练选型
第 7-12 章信息抽取、翻译、情感、问答、摘要、知识图谱按任务选读具体业务模块
第 13-14 章稳健性、可解释性提前读线上服务验收、故障排查

这张表的作用是防止你陷入“从第一页读到最后一页”的线性阅读陷阱。教材是按知识体系写的,工程是按任务需求取用的,两者顺序本来就不一致。

3. 把书里的算法落到代码:分词、词性标注、评测三段实操

3.1 用第 2 章的评测口径写一个分词验收脚本

书里第 2 章给了中文分词的评测方法,核心就是精确率、召回率、F1,按词边界对齐计算。很多人调分词库只看“分出来像不像”,没有量化口径,换个领域语料就说不清好坏。我一般会先写一个小的验收脚本,把标准答案和预测结果按词集合比对。下面这段是通用写法,标准答案用空格分隔的词序列,预测结果同样处理:

# 分词评测:按词边界计算 P/R/F1 # 输入:gold 为标准分词(空格分隔),pred 为模型输出(空格分隔) def tokenize_metrics(gold_sentences, pred_sentences): tp = fp = fn = 0 for gold, pred in zip(gold_sentences, pred_sentences): gold_words = gold.strip().split() pred_words = pred.strip().split() # 用位置区间表示词,避免重复词干扰集合比对 gold_spans = set() idx = 0 for w in gold_words: gold_spans.add((idx, idx + len(w))) idx += len(w) pred_spans = set() idx = 0 for w in pred_words: pred_spans.add((idx, idx + len(w))) idx += len(w) tp += len(gold_spans & pred_spans) fp += len(pred_spans - gold_spans) fn += len(gold_spans - pred_spans) precision = tp / (tp + fp) if tp + fp else 0.0 recall = tp / (tp + fn) if tp + fn else 0.0 f1 = 2 * precision * recall / (precision + recall) if precision + recall else 0.0 return precision, recall, f1 # 调用示例 gold = ["自然语言 处理 是 人工智能 的 重要 分支"] pred = ["自然语言 处理 是 人工智能 重要 分支"] print(tokenize_metrics(gold, pred))

这段代码的关键在“用位置区间而不是词本身做集合比对”。如果直接用词做集合,同一句里出现两个“的”,一个分对一个分错,集合运算会把错误吞掉,指标虚高。位置区间把每个词锚定在字符偏移上,错一个位置就算错,和书里讲的边界对齐口径一致。参数上唯一要注意的是 gold 和 pred 必须来自同一份原始文本,字符偏移才能对齐;如果预测结果做过全半角转换或去空格,要先还原再比。

3.2 词性标注:从 HMM 到 CNN 的选型判断

第 2 章词性标注部分给了基于规则、基于隐马尔可夫模型、基于卷积神经网络三种方法。选型时我一般看三个条件:标注体系是否固定、语料规模、是否需要处理未登录词。规则方法适合标注体系小且稳定的场景,比如只分名词、动词、形容词三类,写几十条上下文规则就能跑;HMM 适合有标注语料但规模不大的场景,转移概率和发射概率都能从语料里估出来;CNN 适合语料较大、需要捕捉局部 n-gram 特征的场景,但训练成本高。

书里没有给完整可运行的 HMM 实现,但给了模型定义和参数估计思路。按这个思路补一个最小可跑版本并不难,核心是两步:统计转移矩阵和发射矩阵,然后用维特比解码。下面这段是骨架,重点看参数怎么从语料里估:

# 极简 HMM 词性标注:训练阶段统计转移和发射 from collections import defaultdict def train_hmm(tagged_corpus): # tagged_corpus: [[(word, tag), ...], ...] trans = defaultdict(lambda: defaultdict(int)) emit = defaultdict(lambda: defaultdict(int)) tag_count = defaultdict(int) for sent in tagged_corpus: prev = "<START>" for word, tag in sent: trans[prev][tag] += 1 emit[tag][word] += 1 tag_count[tag] += 1 prev = tag trans[prev]["<END>"] += 1 # 转成概率,加一平滑避免零概率 tags = list(tag_count.keys()) trans_prob = {} for prev, nxts in trans.items(): total = sum(nxts.values()) + len(tags) trans_prob[prev] = {t: (nxts.get(t, 0) + 1) / total for t in tags} emit_prob = {} for tag, words in emit.items(): total = sum(words.values()) + len(words) emit_prob[tag] = {w: c / total for w, c in words.items()} return trans_prob, emit_prob, tags

逻辑说明:转移矩阵记录“上一个词性到当前词性”的频次,发射矩阵记录“某个词性生成某个词”的频次,两者都做加一平滑。参数上要注意<START>和<END>两个虚拟状态必须显式建模,否则句首句尾的词性判断会偏。这个骨架只到训练,解码用维特比,书里第 2 章有算法描述,照着实现即可。实际工程里我不会自己手写 HMM,而是用现成库,但自己写一遍能让你明白为什么 HMM 在长句上会退化——转移概率只依赖前一个状态,长距离依赖信息丢了。

3.3 第 6 章语言模型:用困惑度做一次基线对比

第 6 章讲语言模型,从 n-gram 一路讲到 GPT。读这章时我建议动手算一次困惑度(perplexity),因为它是理解“语言模型在优化什么”最直接的指标。困惑度本质是平均每词负对数似然的指数,值越低说明模型对测试语料的预测越准。下面这段用 n-gram 做一个最小演示:

# 计算 bigram 语言模型在测试集上的困惑度 import math from collections import defaultdict def train_bigram(corpus): counts = defaultdict(lambda: defaultdict(int)) unigram = defaultdict(int) for sent in corpus: tokens = ["<s>"] + sent + ["</s>"] for i in range(len(tokens) - 1): counts[tokens[i]][tokens[i+1]] += 1 unigram[tokens[i]] += 1 return counts, unigram def perplexity(test_corpus, counts, unigram, vocab_size): log_prob = 0.0 n = 0 for sent in test_corpus: tokens = ["<s>"] + sent + ["</s>"] for i in range(len(tokens) - 1): prev, cur = tokens[i], tokens[i+1] # 加一平滑 prob = (counts[prev].get(cur, 0) + 1) / (unigram.get(prev, 0) + vocab_size) log_prob += math.log(prob) n += 1 return math.exp(-log_prob / n)

参数说明:vocab_size是词表大小,加一平滑的分母要用它;<s>和</s>要计入词表。这段代码跑出来的困惑度只能做相对比较,不能和论文里的绝对值比,因为语料和分词口径不同。我一般用它来验证“换一种平滑方式或换一个 n 值,指标往哪个方向走”,这比背公式有用。书里第 6 章讲 GPT 的部分,读的时候把注意力放在“为什么从 n-gram 的局部窗口走向了注意力机制”这条因果链上,而不是记具体模型结构。

4. 避坑:读这份 PDF 和照着做时最容易翻车的五处

4.1 把预览版当最终版,页码和章节号对不上

现象:按目录翻到第 4 章语义分析,发现正文页码和 PDF 阅读器显示的页码差了好几页,交叉引用也对不上。原因:这是网络预览版,排版和正式出版物有差异,目录里的页码是排版稿页码。解决:不要依赖页码定位,用 PDF 阅读器的书签或搜索章节标题跳转;引用书里内容时只写章节号不写页码。

4.2 跳过语言学知识直接看算法,结果看不懂任务难点

现象:直接翻到第 3 章依存句法分析的算法部分,看到“基于转移”和“基于图”两套方法,不知道为什么要分两套。原因:书里每个任务前面都有“相关语言学知识”和“任务难点”,依存句法的难点在于投射性和非投射性结构,不讲这个就理解不了两套方法的适用边界。解决:每个任务先读“任务概述”和“难点”两节,再进算法;语言学知识部分至少扫一遍术语。

4.3 把不同章节的评测指标混用

现象:拿第 2 章分词的 F1 去评价第 7 章信息抽取的实体识别结果,发现数值对不上,怀疑代码写错。原因:分词 F1 按词边界算,实体识别 F1 按实体区间和类型算,口径不同。解决:每个任务用自己章节给的评测口径,跨任务比较前先确认指标定义一致;书里每章末尾的“评价方法”小节就是干这个用的。

4.4 以为书里的算法可以直接上生产

现象:照着第 2 章的 BiLSTM 分词方法实现了一版,在自己的业务语料上效果远不如预期。原因:书里讲的是方法原理和标准数据集上的表现,业务语料的领域分布、标注规范、未登录词比例都不同。解决:把书里的实现当基线,先在业务语料上跑通评测脚本,再决定是调模型还是补标注;不要拿标准数据集指标当验收线。

4.5 忽略第 13、14 章的稳健性和可解释性,上线后被动

现象:模型离线指标很好,上线后遇到输入扰动(错别字、口语化表达)性能明显下降,业务方追问原因答不上来。原因:只关注精度,没做扰动测试和归因分析。解决:上线前按第 13 章思路做一轮扰动测试,按第 14 章思路对典型错误样本做归因;这两章内容不多,但能省掉很多事后排查。

5. 进阶用法:把 14 章拆成一张可执行的学习与验收清单

读这份 PDF 到第三遍时我改了一个习惯:不再按章读,而是按“任务—算法—指标—数据”四列做一张清单,每读一章填一行。填完之后你会发现,全书 14 章其实在反复回答同一个问题——怎么把一个自然语言处理任务转化成机器学习问题,怎么选算法,怎么根据任务特性改算法。这个视角比记具体模型有用得多。

具体做法是这样:拿第 2 章中文分词举例,任务列填“中文分词”,算法列填“最大匹配 / CRF / 感知器 / BiLSTM”,指标列填“P/R/F1,按词边界对齐”,数据列填书里提到的分词语料库类型。填完第 2 章再填第 7 章信息抽取,你会发现实体识别同样可以用序列标注框架,同样用 P/R/F1,但边界定义从“词”变成了“实体区间”。两行一对照,迁移关系就出来了。

下面这张表是我填了一部分的样例,你可以接着往下填:

任务算法族评测指标数据特点迁移提示
中文分词规则 / CRF / BiLSTM词边界 P/R/F1未登录词多序列标注通用框架
词性标注规则 / HMM / CNN标注准确率标注体系固定HMM 可换 CRF
命名实体识别序列标注实体级 P/R/F1实体边界歧义与分词共用标注框架
情感分析分类 / 序列标注分类准确率 / F1标签噪声大可做句子级或方面级
文本摘要抽取式 / 生成式ROUGE长文档生成式需语言模型兜底

填表过程中我踩过的一个坑是:早期我把“算法族”填得太细,把每个变体都列一行,结果表变成论文索引,失去对照价值。后来改成只填算法大类,变体记在备注里,表才真正能用。另一个坑是“迁移提示”列容易写成空话,我的标准是必须写出一条具体的复用关系,比如“与分词共用标注框架”这种能直接指导下一步动作的,写不出就说明这一行还没读透。

验证方法上,我一般用“能不能给一个没读过这本书的同事讲清楚某个任务的难点和指标”作为过关标准。讲不清楚就回去补那一章的“任务概述”和“评价方法”两节。这个方法比做习题更贴近工程场景,因为工程里你就是要向别人解释为什么选这个方案、怎么验收。

从那以后我每次拿到一本教材类 PDF,都强制先做这张四列清单再读正文,读完一章填一行,填不动的地方就是没读懂的地方。这份《自然语言处理导论》的价值不在于它讲了多少模型,而在于它把每个任务的来龙去脉和验收口径都摆出来了,照着清单走一遍,比从头到尾翻三遍管用。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进