词性标注与句法分析


文档摘要

词性标注与句法分析 本节摘要:语法有一阵子不时髦。后来每个 LLM 流水线都要校验结构化抽取,它就回来了。第 01 节承诺过:词形还原需要词性标签。不知道 是动词,词形还原器就无法把它还原成 ;不知道 是形容词,就无法还原成 。这个承诺背后藏着一整个子领域。词性标注(POS Tagging)分配语法类别,句法分析(Syntactic Parsing)还原句子的树结构:哪个词修饰哪个词、哪个动词管哪些论元。古典 NLP 花了二十年打磨二者,然后深度学习把它们压成预训练 Transformer 顶上的一个 token 分类任务,研究界就转向了。应用界没有。每个结构化抽取流水线底下仍在用词性和依存树。本节介绍标签集、基线,以及那个「从零实现到此为止,接下来调 spaCy」的临界点。

词性标注与句法分析

本节摘要:语法有一阵子不时髦。后来每个 LLM 流水线都要校验结构化抽取,它就回来了。第 01 节承诺过:词形还原需要词性标签。不知道 running 是动词,词形还原器就无法把它还原成 run;不知道 better 是形容词,就无法还原成 good。这个承诺背后藏着一整个子领域。词性标注(POS Tagging)分配语法类别,句法分析(Syntactic Parsing)还原句子的树结构:哪个词修饰哪个词、哪个动词管哪些论元。古典 NLP 花了二十年打磨二者,然后深度学习把它们压成预训练 Transformer 顶上的一个 token 分类任务,研究界就转向了。应用界没有。每个结构化抽取流水线底下仍在用词性和依存树。本节介绍标签集、基线,以及那个「从零实现到此为止,接下来调 spaCy」的临界点。

对应原课程:Phase 5 · Lesson 07 · pos-tagging-parsing(原英文 phases/05-nlp-foundations-to-advanced/07-pos-tagging-parsing/docs/en.md)。前置依赖:第 01 节(文本处理)、Phase 2 · 14(朴素贝叶斯)。

学习目标

阅读完本节,你应当能够:

  1. 说清词性标注与句法分析各自产出什么,以及 Penn Treebank 与 Universal Dependencies 两套标签集的差异。
  2. 从零实现最频繁标签基线与二元 HMM 标注器(含维特比解码),理解准确率为何从 85% 跳到 93%。
  3. 区分成分分析依存分析,知道何时该调 spaCy 而非自己造解析器。
  4. 识别词性/依存分析在 2026 年仍不可替代的场景(词形还原、LLM 输出校验、方面级情感、查询理解)。

一、问题与直觉

第 01 节承诺过:词形还原需要词性标签。不知道 running 是动词,词形还原器无法把它还原成 run;不知道 better 是形容词,无法还原成 good

这个承诺藏着一整个子领域。词性标注分配语法类别,句法分析还原句子的树结构:哪个词修饰哪个、哪个动词管哪些论元。古典 NLP 花了二十年打磨二者,然后深度学习把它们压成预训练 Transformer 顶上的一个 token 分类任务,研究界就转向了。

应用界没转。每个结构化抽取流水线底下仍在用词性和依存树:LLM 生成的 JSON 要拿语法约束校验,问答系统用依存树分解查询,机器翻译质量评估器检查依存树对齐。

词性标注给每个 token 标一个语法类别。Penn Treebank(PTB) 标签集是英语默认,36 个标签,区分之细让外行觉得啰嗦:NN 单数名词、NNS 复数名词、NNP 专有名词单数、VBD 动词过去式、VBZ 动词第三人称单数现在式,等等。Universal Dependencies(UD) 标签集更粗(17 个标签)且语言无关,成了跨语言工作的默认。

The/DET cats/NOUN were/AUX running/VERB at/ADP 3pm/NOUN ./PUNCT

句法分析产出一棵树,主要有两种风格:

  • 成分分析:名词短语、动词短语、介词短语层层嵌套。输出是非终结类别(NP、VP、PP)的树,词是叶子。
  • 依存分析:每个词有一个它所依存的中心词,边标注语法关系。输出是一棵树,每条边是一个 (中心, 依存, 关系) 三元组。

依存分析在 2010 年代胜出,因为它能干净地跨语言泛化,尤其是自由语序语言。

running 是 ROOT cats 是 running 的 nsubj(主语) were 是 running 的 aux(助动词) at 是 running 的 prep(介词) 3pm 是 at 的 pobj(介词宾语)

二、从零实现

第 1 步:最频繁标签基线

最笨但管用的词性标注器。对每个词,预测它在训练里最常有的标签。

from collections import Counter, defaultdict def train_mft(train_examples): word_tag_counts = defaultdict(Counter) all_tags = Counter() for tokens, tags in train_examples: for token, tag in zip(tokens, tags): word_tag_counts[token.lower()][tag] += 1 all_tags[tag] += 1 word_best = {w: c.most_common(1)[0][0] for w, c in word_tag_counts.items()} default_tag = all_tags.most_common(1)[0][0] return word_best, default_tag def predict_mft(tokens, word_best, default_tag): return [word_best.get(t.lower(), default_tag) for t in tokens]

在 Brown 语料上,这个基线约 85% 准确率。不高,但是任何严肃模型都不该掉到它下面。

第 2 步:二元 HMM 标注器

建模序列的联合概率:

P(tags, words) = prod P(tag_i | tag_{i-1}) * P(word_i | tag_i)

两张表:转移概率(给定前一标签的当前标签)、发射概率(给定标签的词)。两者都从计数估,加拉普拉斯平滑。用维特比解码(在标签格上的动态规划)。

import math def train_hmm(train_examples, alpha=0.01): transitions = defaultdict(Counter) emissions = defaultdict(Counter) tags = set() vocab = set() for tokens, ts in train_examples: prev = "<BOS>" for token, tag in zip(tokens, ts): transitions[prev][tag] += 1 emissions[tag][token.lower()] += 1 tags.add(tag) vocab.add(token.lower()) prev = tag transitions[prev]["<EOS>"] += 1 return transitions, emissions, tags, vocab def log_prob(table, given, key, smooth_denom, alpha): return math.log((table[given].get(key, 0) + alpha) / smooth_denom) def viterbi(tokens, transitions, emissions, tags, vocab, alpha=0.01): tags_list = list(tags) n = len(tokens) V = [[0.0] * len(tags_list) for _ in range(n)] back = [[0] * len(tags_list) for _ in range(n)] for j, tag in enumerate(tags_list): em_denom = sum(emissions[tag].values()) + alpha * (len(vocab) + 1) tr_denom = sum(transitions["<BOS>"].values()) + alpha * (len(tags_list) + 1) tr = log_prob(transitions, "<BOS>", tag, tr_denom, alpha) em = log_prob(emissions, tag, tokens[0].lower(), em_denom, alpha) V[0][j] = tr + em back[0][j] = 0 for i in range(1, n): for j, tag in enumerate(tags_list): em_denom = sum(emissions[tag].values()) + alpha * (len(vocab) + 1) em = log_prob(emissions, tag, tokens[i].lower(), em_denom, alpha) best_prev = 0 best_score = -1e30 for k, prev_tag in enumerate(tags_list): tr_denom = sum(transitions[prev_tag].values()) + alpha * (len(tags_list) + 1) tr = log_prob(transitions, prev_tag, tag, tr_denom, alpha) score = V[i - 1][k] + tr + em if score > best_score: best_score = score best_prev = k V[i][j] = best_score back[i][j] = best_prev last_best = max(range(len(tags_list)), key=lambda j: V[n - 1][j]) path = [last_best] for i in range(n - 1, 0, -1): path.append(back[i][path[-1]]) return [tags_list[j] for j in reversed(path)]

二元 HMM 在 Brown 上约 93% 准确率。从 85% 到 93% 的跳跃主要靠转移概率——模型学到 DET NOUN 常见、NOUN DET 罕见。

第 3 步:为何现代标注器更强

转移 + 发射概率是局部的,捕捉不到 saw 在 "I bought a saw" 里是名词、在 "I saw the movie" 里是动词。带任意特征(后缀、词形、前后词、词本身)的 CRF 约 97%,BiLSTM-CRF 或 Transformer 约 98%+。

这个任务的天花板由标注者分歧决定。Penn Treebank 上人类标注者约 97% 一致,超过 98% 的模型多半在过拟合测试集。

第 4 步:依存分析草图

完整的依存分析从零实现超出范围,经典教材论述见 Jurafsky 与 Martin。两个要了解的古典家族:

  • 基于转移的解析器(arc-eager、arc-standard)像移进-归约解析器:读 token、移进栈、应用归约动作建弧。贪心解码快,经典实现是 MaltParser,现代神经版是 Chen 与 Manning 的转移式解析器。
  • 基于图的解析器(Eisner 算法、Dozat-Manning 双仿射)给每条可能的中心-依存边打分,挑最大生成树。慢但更准。

大多数应用活儿,调 spaCy:

import spacy nlp = spacy.load("en_core_web_sm") doc = nlp("The cats were running at 3pm.") for token in doc: print(f"{token.text:10s} tag={token.tag_:5s} pos={token.pos_:6s} dep={token.dep_:10s} head={token.head.text}")
The tag=DT pos=DET dep=det head=cats cats tag=NNS pos=NOUN dep=nsubj head=running were tag=VBD pos=AUX dep=aux head=running running tag=VBG pos=VERB dep=ROOT head=running at tag=IN pos=ADP dep=prep head=running 3pm tag=NN pos=NOUN dep=pobj head=at . tag=. pos=PUNCT dep=punct head=running

从下往上读 dep 列,句子的语法结构就出来了。

三、框架对比

每个生产 NLP 库都把词性与依存解析器作为标准流水线的一部分。

  • spaCy(en_core_web_sm/md/lg/trf):快、准,与分词、NER、词形还原集成。token.tag_(Penn)、token.pos_(UD)、token.dep_(依存关系)。
  • Stanford NLP(stanza):Stanford 接 CoreNLP 的班,60+ 语言上领先。
  • trankit:基于 Transformer,UD 准确率好。
  • NLTK:pos_tag,可用、慢、老,教学还行。

2026 年这仍重要在哪

  • 词形还原:第 01 节需要词性才能正确还原,永远如此。
  • LLM 输出的结构化抽取:校验生成的句子尊重语法约束(主谓一致、必需修饰语)。
  • 方面级情感:依存树告诉你哪个形容词修饰哪个名词。
  • 查询理解:"movies directed by Wes Anderson starring Bill Murray" 通过依存树分解成结构化约束。
  • 跨语言迁移:UD 标签与依存关系语言无关,能对新语言做零样本结构分析。
  • 低算力流水线:上不了 Transformer 时,词性 + 依存 + 地名词典能走很远。

四、可复用产物

保存为 outputs/skill-grammar-pipeline.md:

--- name: grammar-pipeline description: Design a classical POS + dependency pipeline for a downstream NLP task. version: 1.0.0 phase: 5 lesson: 07 tags: [nlp, pos, parsing] --- Given a downstream task (information extraction, rewrite validation, query decomposition, lemmatization), you output: 1. Tagset to use. Penn Treebank for English-only legacy pipelines, Universal Dependencies for multilingual or cross-lingual. 2. Library. spaCy for most production, stanza for academic-grade multilingual, trankit for highest UD accuracy. Name the specific model ID. 3. Integration pattern. Show the 3-5 lines that call the library and consume the needed attributes (`.pos_`, `.dep_`, `.head`). 4. Failure mode to test. Noun-verb ambiguity (`saw`, `book`, `can`) and PP-attachment ambiguity are the classical traps. Sample 20 outputs and eyeball. Refuse to recommend rolling your own parser. Building parsers from scratch is a research project, not an application task. Flag any pipeline that consumes POS tags without handling lowercase/uppercase variants as fragile.

五、练习

  1. 基础:在一个小标注语料(如 NLTK 的 Brown 子集)上用最频繁标签基线,测留出句准确率,验证约 85% 的结果。
  2. 进阶:训上面的二元 HMM,报每标签精确率/召回率。HMM 最容易混的是哪些标签?
  3. 挑战:用 spaCy 的依存树从 1000 句样本里抽主-谓-宾三元组。在 50 个手工标注的三元组上评估,记录抽取失败的点(常在被动、并列、省略主语处)。

本节要点回顾

  1. 语法回来了:LLM 流水线要校验结构化抽取,词性与依存树仍是底层役马。
  2. 两套标签集:Penn Treebank(36 个,英语细粒度)、Universal Dependencies(17 个,语言无关,跨语言默认)。
  3. 两种句法树:成分分析(NP/VP/PP 嵌套)、依存分析(每词一中心+关系,跨语言更干净)。
  4. 最频繁标签基线约 85%,是任何严肃模型的地板。
  5. 二元 HMM 约 93%,跳跃主要靠转移概率学到 DET NOUN 常见。
  6. 天花板由标注者分歧决定:人类约 97% 一致,超 98% 多半过拟合。
  7. 现代标注器靠任意特征:CRF 约 97%、BiLSTM-CRF/Transformer 约 98%+,捕捉 saw 名动歧义。
  8. 依存分析两家族:转移式(快,MaltParser)、图式(准,Eisner/双仿射)。
  9. 应用活儿调 spaCy:.tag_/.pos_/.dep_/.head 从下往上读出结构。
  10. 2026 年仍不可替代:词形还原、LLM 输出校验、方面级情感、查询理解、跨语言迁移、低算力。

下一节,我们让文本第一次走进神经网络——进入「文本的 CNN 与 RNN」,看卷积与循环如何捕捉局部与序列模式。


发布者: 作者: Rohit Gupta 转发
评论区 (0)
U