语言学基础


文档摘要

语言学基础 语言学为 NLP 系统提供了一套结构性词汇,而这些系统会在学习中隐式地利用它们。本文件涵盖形态学、句法学、语义学、语用学、音系学、成分与依存分析,以及分布式假设——这门研究人类语言的科学,正是 AI 中分词、语法和意义的根基。 在构建能够理解或生成语言的系统之前,我们先得弄清楚语言本身是怎么运作的。 语言学是对语言的科学研究,它提供了一整套概念词汇,而 NLP 一直在源源不断地向它借用。 即便是从原始数据中学习语言的现代神经网络,也会隐式地重新发现语言学家几十年来已经记录在案的那些结构。 语言在每一个层次上都有结构:构成单词的语音、构成单词的语素、把单词组合成句子的规则、句子所承载的意义,以及语境如何塑造理解。我们会自底向上,一层一层地讲下去。

语言学基础

语言学为 NLP 系统提供了一套结构性词汇,而这些系统会在学习中隐式地利用它们。本文件涵盖形态学、句法学、语义学、语用学、音系学、成分与依存分析,以及分布式假设——这门研究人类语言的科学,正是 AI 中分词、语法和意义的根基。

  • 在构建能够理解或生成语言的系统之前,我们先得弄清楚语言本身是怎么运作的。

  • 语言学是对语言的科学研究,它提供了一整套概念词汇,而 NLP 一直在源源不断地向它借用。

  • 即便是从原始数据中学习语言的现代神经网络,也会隐式地重新发现语言学家几十年来已经记录在案的那些结构。

  • 语言在每一个层次上都有结构:构成单词的语音、构成单词的语素、把单词组合成句子的规则、句子所承载的意义,以及语境如何塑造理解。我们会自底向上,一层一层地讲下去。

  • **形态学(morphology)**研究单词的内部结构。词不是原子,它们由更小的、有意义的单位——语素(morpheme)——构成。

  • 单词 "unhappiness" 包含三个语素:"un-"(一个表示"不"的前缀)、"happy"(词根)和 "-ness"(一个把形容词变成名词的后缀)。每个语素都对意义有贡献。

  • **词根(root,或称词干 stem)**是承载主要意义的核心语素。"happy"、"run"、"compute" 都是词根。

  • **词缀(affix)**是附着在词根上、用来改变其含义或用法的语素。

  • 英语里有前缀(prefix)(在词根之前:un-、re-、pre-)和后缀(suffix)(在词根之后:-ing、-ed、-tion)。有些语言还有中缀(插在词根内部)和环缀(前后包夹)。

语素树:"unhappiness" 被拆分为前缀 "un"、词根 "happy"、后缀 "ness"

  • 形态学过程分两大类。**屈折变化(inflection)**改变单词的语法属性,但不改变核心意义或词类:"run" 变成 "runs"(第三人称)、"running"(进行时)、"ran"(过去时)。这个词仍然是动词,意思也没变。

  • **派生(derivation)**则会创造一个新词,常常改变词类:"happy"(形容词)变成 "happiness"(名词),"compute"(动词)变成 "computation"(名词)再变成 "computational"(形容词)。每一次派生都会改变意义和语法类别。

  • 各种语言的形态复杂度差异巨大。英语相对偏分析语(analytic)(每个词的语素少,靠词序传情达意)。

  • 土耳其语和芬兰语是黏着语(agglutinative)(一个词里可以串起很多语素)。阿拉伯语和希伯来语采用模板形态(templatic morphology)(词根是辅音骨架,比如 k-t-b 表示"写",再往里塞入不同的元音模式就能造出不同的词:kitab "书"、kataba "他写了"、maktub "被写下的")。

  • 形态学之所以对 NLP 重要,是因为它会影响分词。一个词级别的分词器会把 "run"、"runs"、"running"、"ran" 当作四个毫不相关的符号。

  • 一个有形态学意识的系统则会识别出它们共享同一个词根。我们将在第 2 节讲到的子词分词(BPE、WordPiece),就是对形态分析的一种统计近似。

  • **句法学(syntax)**研究单词如何组合成短语和句子。每种语言都有支配词序和结构的规则;违反这些规则就会产生胡言乱语。

  • "The cat sat on the mat" 是合乎英语语法的;"Mat the on sat cat the" 则不是。

  • 描述句法结构主要有两套框架。

  • **短语结构语法(phrase structure grammar,也叫成分语法 constituency grammar)**认为,句子是通过短语嵌套短语构建出来的。一个句子(S)由一个名词短语(NP)和一个动词短语(VP)构成。

  • 一个名词短语可能是一个限定词(Det)加上一个名词(N)。一个动词短语可能是一个动词(V)加上一个名词短语。这些规则会构建出一棵树:

"the cat sat on the mat" 的成分树:S 分解为 NP 和 VP,NP 分解为 Det "the" 和 N "cat",VP 分解为 V "sat" 和 PP,PP 分解为 P "on" 和 NP

  • 这棵树被称为成分树(constituency tree,或语法分析树 parse tree)。每个内部节点都是一个短语类型,每个叶子都是一个词。这棵树刻画了层次化的分组关系:"on the mat" 是一个整体(介词短语),"sat on the mat" 是一个整体(动词短语),而整棵树就是一个句子。

  • **上下文无关文法(context-free grammar,CFG)**把这些规则形式化了。它由一组产生式规则组成,每条规则形如 A \to \alpha,其中 A 是一个非终结符(诸如 NP 或 VP 这样的短语类型),\alpha 是一串终结符(词)和非终结符。例如:

S → NP VP NP → Det N NP → Det N PP VP → V NP VP → V PP PP → P NP Det → "the" | "a" N → "cat" | "mat" | "dog" V → "sat" | "chased" P → "on" | "under"
  • 从 S 出发不断套用规则,你就能生成这套文法允许的所有句子。而分析(parsing)是反过来:给定一个句子,找出产生它的那棵树(或多棵树)。如果一个句子有多棵合法的分析树,它就是有句法歧义的(syntactically ambiguous)。"I saw the man with the telescope" 就有两种分析:要么是我用望远镜看到了那个男人,要么是我看到了一个拿着望远镜的男人。

  • 依存语法(dependency grammar)换了一个视角。它不谈短语嵌套,而是直接描述词与词之间的关系。句子中的每个词都恰好依存于另一个词(它的中心词 head),只有句子的根除外。结果就是一棵依存树(dependency tree),其中的边带有语法关系标签(主语、宾语、修饰语等)。

"the cat sat on the mat" 的依存树:箭头从 "sat" 指向 "cat"(nsubj)、指向 "on"(prep),从 "on" 指向 "mat"(pobj),从 "cat" 指向 "the"(det),从 "mat" 指向 "the"(det)

  • 在依存的视角下,"sat" 是根。"Cat" 作为主语(nsubj)依存于 "sat"。"On" 作为介词修饰语依存于 "sat"。"Mat" 作为介词宾语依存于 "on"。每个词都恰好挂在一个中心词下面,由此构成一棵树。

  • 依存语法已经成为现代 NLP 的主流框架,因为依存树更容易用统计解析器生成,而且这些关系能更直接地映射到语义角色(谁对谁做了什么)。

  • **配价(valency)描述一个动词需要几个论元。"Sleep" 是不及物(intransitive)的(一个论元:睡觉的人)。"Eat" 是及物(transitive)的(两个:吃的人和被吃的东西)。"Give" 是双及物(ditransitive)**的(三个:给的人、给出的东西、接收者)。知道一个动词的配价,就能约束哪些分析树是合法的。

  • **语义学(semantics)**研究意义。句法告诉你句子是怎么组织的;语义告诉你它是什么意思。

  • **词汇语义(lexical semantics)**关心单个词的意义。词与词之间以系统化的方式相互关联:

    • 同义(synonymy):(几乎)意义相同的词。"Big" 和 "large" 是同义词。真正完美的同义很罕见,几乎总存在内涵或用法上的微妙差异。
    • 反义(antonymy):意义相反的词。"Hot" 与 "cold","buy" 与 "sell"。
    • 上下义(hypernymy/hyponymy):"是一种"的关系。"Dog" 是 "animal" 的下位词(狗是一种动物)。"Animal" 是 "dog" 的上位词。这些关系构成分类层级。
    • 整体-部分(meronymy):"是一部分"的关系。"Wheel" 是 "car" 的一个部分。
    • 多义(polysemy):一个词有多个相关意义。"Bank" 既可指金融机构,也可指河岸。具体是哪个,要靠语境消歧。
  • **词义消歧(word sense disambiguation,WSD)**的任务,就是判定在某个具体语境下,一个多义词到底取哪个意义。在 "I deposited money at the bank" 里,金融意义是对的;在 "We sat by the river bank" 里,地理意义才是对的。WSD 是早期 NLP 的核心问题;现代上下文词嵌入(ELMo、BERT)通过为同一个词的不同用法生成不同的向量表示,已经基本解决了它。

  • **组合语义(compositional semantics)**追问:单个词的意义如何组合成短语或句子的意义?**组合性原理(compositionality,归功于 Frege)**认为,一个复杂表达式的意义,由其各部分的意义以及组合它们的规则共同决定。"The cat chased the dog" 和 "the dog chased the cat" 意义不同,正是因为句法结构(谁是主语、谁是宾语)与词义发生了不同的相互作用。

  • 并非所有意义都是组合性的。习语(idiom),比如 "kick the bucket"(意思是"死"),其意义无法从字面各部分推导出来。这对任何组合性方法都是个挑战。

  • **分布式语义(distributional semantics)**是支撑现代 NLP 的、关于意义的计算路线。**分布式假设(distributional hypothesis,Firth,1957)**说道:"You shall know a word by the company it keeps."(你将通过一个词所处的语境来认识它。)出现在相似语境中的词,往往意义也相似。这正是词嵌入(Word2Vec、GloVe)的理论基础,我们会在第 3 节深入探讨。

  • **语用学(pragmatics)**研究语境如何影响意义。同样一句话,由不同的人、在不同的时间、地点和动机下说出来,意思可能完全不同。

  • "Can you pass the salt?" 在句法上是一个关于能力的是非问句。但在语用上,它是一个请求。你不会回答 "Yes, I can" 然后坐着不动。理解这一点需要超越字面词汇的知识,具体说就是**言语行为(speech acts)**的惯例。

  • **言语行为理论(speech act theory,Austin、Searle)**区分了三种行为:

    • 言内行为(locutionary act):字面内容("Can you pass the salt?")
    • 言外行为(illocutionary act):意图的功能(一个请求)
    • 言后行为(perlocutionary act):对听者产生的效果(对方把盐递了过来)
  • **含义(implicature,Grice)指的是暗示出来、但并未明说的意义。如果有人问 "Is John a good cook?",你回答 "He's British",你字面上并没有回答这个问题,但听者可以推断(借助文化刻板印象,无论公不公平)你的意思是"不"。Grice 的合作原则(cooperative principle)**认为,说话人通常会努力做到 informative(信息充分)、truthful(诚实)、relevant(切题)、clear(清楚),而听者也是在假定这些准则成立的前提下来解读话语的。

  • **共指(coreference)**是一种语用现象:不同的表达指的是同一个实体。在 "Alice went to the store. She bought milk." 中,"she" 指的是 Alice。解决共指对理解跨句文本至关重要,也是 NLP 的一项关键任务。

  • **话语结构(discourse structure)**描述句子如何衔接成连贯的文本。叙事有开头、中间、结尾;论证有主张和证据。**修辞结构理论(Rhetorical Structure Theory,RST)**把文本分析成一棵由话语关系(elaboration 详述、contrast 对比、cause 因果等)组成的树,关系存在于各个文本片段之间。

  • 语用学是 NLP 最难啃的地方。现代语言模型能通过训练数据隐式地处理掉大部分句法和语义问题,但语用推理——理解讽刺、言外之意和依赖语境的意义——仍然是一个前沿难题。

  • **音系学(phonology)**研究语言的语音系统。虽然本章聚焦文本,但简短地过一遍,可以衔接到音频与语音那一章(第 9 章)。

  • 音位(phoneme)是区分意义的最小语音单位。英语大约有 44 个音位。"bat" 和 "pat" 之间只差一个音位(/b/ 对 /p/),意义却完全不同。这就叫做最小对立对(minimal pair)

  • **音位变体(allophone)**是同一个音位的不同物理实现,它们不区分意义。"pin" 中的 "p"(送气,带一股气流)和 "spin" 中的 "p"(不送气)在英语里是 /p/ 的音位变体;母语者把它们当作同一个音。

  • **国际音标(International Phonetic Alphabet,IPA)**为所有语言的音位提供了一套标准化的记法。单词 "cat" 转写成 /kæt/。IPA 是书面文本和语音系统之间的桥梁。

  • **韵律(prosody)**涵盖言语的节奏、重音和语调。"I didn't say he stole the money" 这句话,根据重读的是哪个词,可以有七种不同的意思。韵律携带了纯文本会丢失的信息,这就是为什么文本转语音系统必须仔细地建模它。

  • 在 NLP 中,音系知识出现在文本转语音(字素到音位的转换)、语音识别(把声学信号映射到音位),甚至拼写纠错和音译里。

编程练习(使用 CoLab 或 notebook)

  1. 构建一个简单的形态分析器,用常见前缀和后缀列表把英语单词拆分成可能的语素。
prefixes = ['un', 're', 'pre', 'dis', 'mis', 'over', 'under', 'out', 'non'] suffixes = ['ing', 'ed', 'ly', 'ness', 'ment', 'tion', 'able', 'ible', 'er', 'est', 'ful', 'less', 'ous'] def analyse_morphemes(word): """用已知词缀做简单的语素分析。""" parts = [] remaining = word.lower() # 检查前缀 for p in sorted(prefixes, key=len, reverse=True): if remaining.startswith(p) and len(remaining) > len(p) + 2: parts.append(f"[prefix: {p}]") remaining = remaining[len(p):] break # 检查后缀 for s in sorted(suffixes, key=len, reverse=True): if remaining.endswith(s) and len(remaining) > len(s) + 2: root = remaining[:-len(s)] parts.append(f"[root: {root}]") parts.append(f"[suffix: {s}]") remaining = None break if remaining is not None: parts.append(f"[root: {remaining}]") return parts for word in ['unhappiness', 'reusable', 'disconnected', 'overreacting', 'kindness']: print(f"{word:20s} → {' + '.join(analyse_morphemes(word))}")
  1. 用递归下降实现一个简单的上下文无关文法分析器。定义一套小型文法,把一个句子分析成一棵成分树。
class CFGParser: """针对一套小型英语文法的递归下降分析器。""" def __init__(self, tokens): self.tokens = tokens self.pos = 0 def peek(self): return self.tokens[self.pos] if self.pos < len(self.tokens) else None def consume(self, expected=None): tok = self.peek() if expected and tok != expected: return None self.pos += 1 return tok def parse_det(self): if self.peek() in ('the', 'a'): return ('Det', self.consume()) return None def parse_noun(self): if self.peek() in ('cat', 'dog', 'mat', 'man'): return ('N', self.consume()) return None def parse_verb(self): if self.peek() in ('sat', 'chased', 'saw'): return ('V', self.consume()) return None def parse_prep(self): if self.peek() in ('on', 'under', 'with'): return ('P', self.consume()) return None def parse_np(self): save = self.pos det = self.parse_det() noun = self.parse_noun() if det and noun: # 检查是否有可选的 PP pp = self.parse_pp() if pp: return ('NP', det, noun, pp) return ('NP', det, noun) self.pos = save return None def parse_pp(self): save = self.pos prep = self.parse_prep() np = self.parse_np() if prep and np: return ('PP', prep, np) self.pos = save return None def parse_vp(self): save = self.pos verb = self.parse_verb() if verb: np = self.parse_np() if np: return ('VP', verb, np) pp = self.parse_pp() if pp: return ('VP', verb, pp) self.pos = save return None def parse_sentence(self): np = self.parse_np() vp = self.parse_vp() if np and vp and self.pos == len(self.tokens): return ('S', np, vp) return None def print_tree(tree, indent=0): if isinstance(tree, str): print(' ' * indent + tree) elif isinstance(tree, tuple): print(' ' * indent + tree[0]) for child in tree[1:]: print_tree(child, indent + 2) sentences = [ "the cat sat on the mat", "a dog chased the cat", ] for sent in sentences: tokens = sent.split() parser = CFGParser(tokens) tree = parser.parse_sentence() print(f"\n'{sent}':") if tree: print_tree(tree) else: print(" (no parse found)")
  1. 通过构建一个简单的词图来探索词汇关系。给定一个带有同义、反义和上下义关系的小词库,找出词与词之间的路径。
relations = { ('big', 'large'): 'synonym', ('big', 'small'): 'antonym', ('small', 'tiny'): 'synonym', ('dog', 'animal'): 'hypernym', ('cat', 'animal'): 'hypernym', ('puppy', 'dog'): 'hypernym', ('happy', 'glad'): 'synonym', ('happy', 'sad'): 'antonym', ('hot', 'cold'): 'antonym', ('hot', 'warm'): 'synonym', } # 构建邻接表 from collections import defaultdict, deque graph = defaultdict(list) for (w1, w2), rel in relations.items(): graph[w1].append((w2, rel)) graph[w2].append((w1, rel)) def find_path(start, end): """用 BFS 在关系图中找出两个词之间的路径。""" queue = deque([(start, [(start, None)])]) visited = {start} while queue: node, path = queue.popleft() if node == end: return path for neighbor, rel in graph[node]: if neighbor not in visited: visited.add(neighbor) queue.append((neighbor, path + [(neighbor, rel)])) return None pairs = [('big', 'tiny'), ('puppy', 'cat'), ('happy', 'sad')] for w1, w2 in pairs: path = find_path(w1, w2) if path: steps = " → ".join(f"{w}({r})" if r else w for w, r in path) print(f"{w1} → {w2}: {steps}") else: print(f"{w1} → {w2}: no path found")

作者与出处
原作者: HenryNdubuaku
来源:HenryNdubuaku
许可证:Apache-2.0
整理: 灏天文库整理
由灏天文库结构化整理,提供目录导航、全文检索与在线阅读,便于系统化学习
发布者: 作者: HenryNdubuaku 转发
评论区 (0)
U