第一章:语言的数字密码——从规则到统计的魔法
想象一下,你正在和好朋友玩一个填词游戏:老师读出一个句子,“今天天气____”,你需要快速说出一个最可能的词。是“很好”?“晴朗”?还是“下雨”?你之所以能轻松填上,是因为你听过、读过无数个类似的句子,大脑里悄悄记下了“天气”后面常跟着什么词。这就是语言模型最核心的魔法——预测下一个词。从古老的规则系统到现代的神经网络,人工智能走过了同样的学习之路:从死记硬背规则,到统计规律,最终理解语言的深层逻辑。本章,我们将化身“语言侦探”,揭开语言如何被变成一串串“数字密码”,看看机器是如何从“填词游戏”开始,一点点学会说话的。
从规则大师ELIZA到统计革命:语言模型的两次进化
在计算机还像房间一样大的年代,科学家们就梦想让机器像人一样聊天。1966年,麻省理工学院的约瑟夫·魏岑鲍姆教授写出了一个叫ELIZA的程序,它模仿心理治疗师,用简单的规则与用户对话。如果你输入“我觉得很累”,ELIZA可能会回应:“为什么你觉得累?”听起来挺聪明?其实它的“大脑”里只有一堆模式匹配规则,比如:“如果句子包含‘我觉得’和‘累’,就回复‘为什么你觉得累?’”。规则是人工编写的,脆弱得像纸牌屋——如果你说“我感到疲惫”,它可能就懵了,因为规则里没写“疲惫”这个词。
ELIZA的局限性催生了新的思路:与其让人类写无数规则,不如让机器自己从海量文本中发现规律。这个转折点发生在1980年代,统计语言学崛起,人们意识到:语言不是随机的,词与词之间存在着可量化的概率关系。例如,在新闻语料中,“总统”后面跟着“发表”的概率,远高于“吃苹果”。这种概率,就是语言的“数字密码”。基于此,N-gram模型应运而生,成为第一个真正意义上的统计语言模型。
N-gram:用“记忆小本本”预测未来
“N-gram”这个名字听起来很技术,但思想简单极了:一个词的出现, strongly depends on它前面的几个词。比如,“吃”后面常接“饭”,但“吃苹果”比“吃汽车”常见得多。N-gram就是给机器一个“记忆小本本”,专门记录相邻词的组合频率。
- **Unigram(一元模型)**:不管前面是什么,每个词单独出现的概率。比如,语料中“的”出现次数最多,那它概率就最高。这太简单了,几乎没用。
- **Bigram(二元模型)**:只看前一个词。预测下一个词时,只考虑当前词。比如,给定“天气”,看“天气”后面哪些词出现得多。
- **Trigram(三元模型)**:看前两个词。比如,给定“今天天气”,看后面接什么。
- N越大,模型记住的“上下文”越长,但数据需求也指数级增长。
核心公式:给定前面的词序列$w_{1}, w_{2}, ..., w_{n-1}$,预测第$n$个词$w_n$的概率为: $$P(w_n | w_{1}, w_{2}, ..., w_{n-1}) = \frac{C(w_{1}, ..., w_{n})}{C(w_{1}, ..., w_{n-1})}$$ 其中$C(\cdot)$表示计数。在实际中,由于数据稀疏,我们通常用最大似然估计,并且常用马尔可夫假设:下一个词只依赖于前N-1个词,即: $$P(w_n | w_{1}, ..., w_{n-1}) \approx P(w_n | w_{n-N+1}, ..., w_{n-1})$$ 这就是N-gram的本质:用有限的局部历史,近似全局依赖。
概率如何成为“数字密码”?
让我们用一个微型语料动手算一算。假设我们的训练文本只有三句话: 1. “我 爱 吃 苹果” 2. “我 爱 吃 香蕉” 3. “猫 爱 吃 鱼”
我们构建一个bigram模型。首先统计所有词对的出现次数:
| 前一个词 | 下一个词 | 计数 | |----------|----------|------| | 我 | 爱 | 2 | | 爱 | 吃 | 3 | | 吃 | 苹果 | 1 | | 吃 | 香蕉 | 1 | | 吃 | 鱼 | 1 | | 猫 | 爱 | 1 |
然后计算条件概率。例如,给定“吃”,下一个词是“苹果”的概率: $$P(苹果 | 吃) = \frac{C(吃, 苹果)}{C(吃)} = \frac{1}{3} \approx 0.333$$ 同样,$P(香蕉 | 吃) = 1/3$,$P(鱼 | 吃) = 1/3$。
现在,如果我们用这个模型生成句子:从“我”开始,$P(爱 | 我) = 2/2 = 1$,所以下一个一定是“爱”;然后$P(吃 | 爱) = 3/3 = 1$,所以下一个是“吃”;最后从“吃”随机选一个下一个词,有1/3概率选“苹果”。于是,模型很可能生成“我爱吃苹果”。但注意:它永远无法生成“我爱吃西瓜”,因为训练文本中没有“吃西瓜”这个组合。这就是N-gram的根本局限:它只能回忆训练数据中见过的短语,无法泛化到未见的组合。
数据稀疏:小本本记不住的秘密
上面的例子太简单了。真实语言中,词组合的数量是天文数字。假设词汇表有10万个词,bigram的可能组合就有100亿个!而任何语料库都只能覆盖其中一小部分。当我们计算一个未出现过的bigram概率时,按照最大似然估计,概率为0,这意味着模型会认为这个序列“不可能发生”。但现实中,“吃西瓜”明明很常见,只是训练数据里恰好没出现。
为了解决这个问题,统计学家发明了平滑技术,比如加一平滑(Laplace smoothing):给每个计数加1,这样未出现的组合也有微小概率。公式变为: $$P_{smoothed}(w_n | w_{n-1}) = \frac{C(w_{n-1}, w_n) + 1}{C(w_{n-1}) + V}$$ 其中$V$是词汇表大小。但这只是“作弊”——它把概率均匀分配给所有未出现的词,并不真正理解为什么“吃西瓜”合理而“吃汽车”不合理。更高级的平滑如Kneser-Ney会考虑低阶信息,但依然停留在统计表面,没有触及语义。
互动游戏:故事接龙中的N-gram陷阱
现在,请你亲自体验N-gram的魔力与局限。我们来玩一个故事接龙游戏。假设我们用《格林童话》训练一个trigram模型。游戏规则: 1. 我给出故事开头:“从前有座山”。 2. 你根据直觉,接一个词,比如“山里”。 3. 然后,我们的trigram模型,基于“从前有座山”这个三元组,从训练数据中找出所有以这三个词开头的接续词,按概率选一个。 4. 比较你的接法和模型的接法,看看有何不同。
先试试你的直觉:给定“从前有座山”,你可能会接“山里住着一位老人”或“山上有个城堡”。但trigram模型只关心精确匹配“从前有座山”这个三元组。在《格林童话》中,这个三元组可能只出现一次,后面接的是“山脚下”。于是模型固执地输出“山脚下”,然后继续:给定“有座山脚下”,它又找下一个词……很快,它可能会生成“山脚下有一间小屋”、“小屋里有七个小矮人”……听起来还行?但如果你给一个训练数据中从未出现过的开头,比如“在遥远的银河系”,模型会完全卡住,因为没有任何三元组匹配。它就像一个只背熟了几篇课文的学生,一旦题目稍作变化,就不知所措。
更糟糕的是,即使匹配上了,生成的故事也往往缺乏连贯性。因为trigram只看到三个词,无法把握故事的整体脉络。它可能生成:“公主吻了青蛙,青蛙变成了王子。王子高兴地跳起舞。舞会上,王子吃了一个苹果。”——从“王子”到“吃苹果”毫无逻辑,但在训练数据中,“王子”后面可能真出现过“吃”这个词(比如“王子吃早餐”),于是概率不为零。模型没有“王子是主角,不该突然吃苹果”的概念,它只认数字。
动手实验:用10行Python代码打造你的微型N-gram模型
理论说了这么多,不如亲手写一个N-gram模型!打开你的Python编辑器(推荐在线环境如Google Colab),我们只用标准库,不用任何机器学习框架,就能实现一个bigram文本生成器。
第一步:准备训练文本
找一个简单的txt文件,比如《西游记》片段,或者你自己写一段话。为了演示,我们用这个微型语料: ``` 小兔子乖乖 把门开开 快点开开 我要进来 ``` 保存为`story.txt`。
第二步:编写代码
```python from collections import defaultdict import random
1. 读取文本,分词(这里简单按空格和换行)
with open('story.txt', 'r', encoding='utf-8') as f: text = f.read().replace('\n', ' ') # 把换行当空格 words = text.split() # 得到词列表:['小兔子','乖乖','把门','开开','快点','开开','我要','进来']
2. 构建bigram模型:记录每个词后面出现的词及次数
model = defaultdict(lambda: defaultdict(int)) # 嵌套字典:model[前词][后词] = 计数 for i in range(len(words)-1): prev, next_word = words[i], words[i+1] model[prev][next_word] += 1
3. 将计数转换为概率(可选,生成时可以直接按比例随机)
这里为了简单,生成时用加权随机
4. 生成函数:从给定词开始,生成最多10个词
def generate(start_word, max_words=10): result = [start_word] current = start_word for _ in range(max_words-1): if current not in model: # 如果当前词在训练数据中没作为前词出现过,停止 break next_words = list(model[current].keys()) weights = list(model[current].values())
按权重随机选择下一个词
current = random.choices(next_words, weights=weights, k=1)[0] result.append(current) return ''.join(result) # 中文需加空格,这里简单拼接
5. 试试生成!
print("从‘小兔子’开始:", generate('小兔子')) print("从‘把门’开始:", generate('把门')) ```
第三步:运行与观察
运行后,你可能会看到:
- 从“小兔子”开始:`小兔子乖乖把门开开`(完美复现训练数据)
- 从“把门”开始:`把门开开快点开开`(也复现)
但如果我们换一个开头,比如`generate('我要')`,可能输出`我要进来`,然后停止,因为“进来”后面没词了。如果我们把`max_words`设大,它可能会重复“开开”或卡住。
实验升级:把训练文本换成一段新闻或小说片段,观察生成结果。你会发现,模型会机械重复训练数据中的常见短语,但一旦需要创意,它就束手无策。比如,训练数据中“天空”后面常接“是蓝色的”,模型永远生成“天空是蓝色的”,即使你问“天空为什么是蓝色的?”,它也无法推理。
为什么这个微型模型如此“笨”?
1. 记忆有限:bigram只记住相邻词,忽略更远上下文。比如,“虽然……但是……”这种长距离依赖,bigram完全抓瞎。 2. 没有语义:它不知道“苹果”是水果,“汽车”是机器,所以“吃汽车”在统计上可能概率极低,但理论上如果训练数据有 bizarre 句子,它也可能生成。 3. 数据饥渴:词汇表越大,需要的数据越多。对于开放域语言,bigram几乎无法覆盖所有可能组合。 4. 无法处理未知词:训练后,遇到新词(如网络用语)就失效。
这个简单的代码,正是所有现代语言模型的“祖爷爷”。它用最直白的方式告诉我们:概率是语言的数字密码,但仅靠表面统计,无法解锁语言的真正含义。
从密码到理解:统计模型的遗产与未竟之路
N-gram模型在1990年代至2000年代风靡一时,被用于语音识别、机器翻译的早期系统。它取得了实用进展,但天花板清晰可见。语言的美妙在于创造性和泛化能力——我们能听懂从未说过的句子,能写出从未读过的文章。N-gram做不到,因为它没有“理解”,只有“记忆”。
这一章,我们从填词游戏出发,见证了语言模型从规则大师ELIZA到统计小能手N-gram的进化。概率,确实成了语言的数字密码,让我们能用数学描述词与词的关联。但密码只是表象,真正的秘密藏在语言的结构与意义之中。N-gram像一台只会背课文的录音机,可以复述,却无法对话。
那么,机器如何才能超越统计,获得真正的语言理解?下一章,我们将踏入神经网络的世界,看看词向量如何将词语变成空间中的点,让机器第一次“看见”语义的几何;再深入循环神经网络(RNN) 如何尝试捕捉长距离依赖,为Transformer的登场铺垫舞台。从“数字密码”到“意义网络”,旅程才刚刚开始。
> 📱 电子版互动提示:扫描二维码,访问在线N-gram演示工具,输入你自己的文本,实时训练一个bigram模型,并观察生成结果!尝试用不同语料(诗歌、新闻、对话),比较生成风格的差异。 > > ```mermaid > graph LR > A[填词游戏:预测下一个词] --> B[规则系统:ELIZA<br/>关键词匹配] > B --> C[统计革命:N-gram<br/>概率作为数字密码] > C --> D[核心公式:P(下一个词|前文)] > D --> E[数据稀疏问题] > E --> F[平滑技术] > F --> G[局限:无语义/短记忆] > G --> H[互动:故事接龙游戏] > H --> I[代码实践:微型N-gram] > I --> J[总结:统计的遗产与缺陷] > J --> K[下一章:神经网络与词向量] > ```
> 🤖 Markdown 机器标签: > - 概念:语言模型、预测下一个词、ELIZA、N-gram、bigram、trigram、概率、条件概率、最大似然估计、平滑、数据稀疏、马尔可夫假设 > - 技能:Python基础、字典、随机选择、文本处理 > - 公式:$P(w_n | w_{n-1}) = \frac{C(w_{n-1}, w_n)}{C(w_{n-1})}$,加一平滑公式 > - 图表:N-gram演进流程图、bigram结构图 > - 代码:微型N-gram实现 > - 互动:故事接龙游戏设计
语言的数字密码:从规则到统计的魔法(完整扩展版)
想象一下,你正在和好朋友玩一个填词游戏:老师读出一个句子,“今天天气____”,你需要快速说出一个最可能的词。是“很好”?“晴朗”?还是“下雨”?你之所以能轻松填上,是因为你听过、读过无数个类似的句子,大脑里悄悄记下了“天气”后面常跟着什么词。这就是语言模型最核心的魔法——预测下一个词。从古老的规则系统到现代的神经网络,人工智能走过了同样的学习之路:从死记硬背规则,到统计规律,最终理解语言的深层逻辑。本章,我们将化身“语言侦探”,揭开语言如何被变成一串串“数字密码”,看看机器是如何从“填词游戏”开始,一点点学会说话的。
第一次尝试:规则大师ELIZA的“关键词魔术”
在计算机还像房间一样大的年代,科学家们就梦想让机器像人一样聊天。1966年,麻省理工学院的约瑟夫·魏岑鲍姆教授写出了一个叫ELIZA的程序,它模仿心理治疗师,用简单的规则与用户对话。ELIZA的名字来自萧伯纳戏剧《卖花女》中的角色,暗示它能让机器“脱胎换骨”。如果你输入“我觉得很累”,ELIZA可能会回应:“为什么你觉得累?”听起来挺聪明?其实它的“大脑”里只有一堆模式匹配规则,比如:“如果句子包含‘我觉得’和‘累’,就回复‘为什么你觉得累?’”。规则是人工编写的,脆弱得像纸牌屋——如果你说“我感到疲惫”,它可能就懵了,因为规则里没写“疲惫”这个词。
ELIZA如何工作?一个具体例子
让我们看看ELIZA的“内心独白”。它的核心是一种叫脚本(script) 的东西,以`DOCTOR`脚本为例,包含两类规则:
1. 分解规则(Decomposition rules):将用户输入拆解成关键部分。
- 例如:`(0 YOU 0 ME)` 匹配“你”和“我”之间的任何内容,如“你讨厌我”。
- 拆解后,将`0`替换为变量,变成模板`(WHAT MAKES YOU THINK I 0 YOU)`。
2. 重组规则(Reassembly rules):将变量填入预设回应模板。
- 对于上述模板,可能的重组是`"Why do you think I 0 you?"`,生成“你为什么觉得我讨厌你?”
ELIZA还会使用关键词优先策略:为每个关键词(如“母亲”“父亲”“dream”)分配优先级,匹配最高优先级的关键词。如果没有任何关键词匹配,它就会使用默认回应,如“请继续”、“这很有趣,请告诉我更多”,或者重复用户的话:“你在说‘____’,对吗?”
ELIZA的“魔法”为何容易穿帮?
ELIZA的局限性非常明显,它像一个只会背答案的学生:
- **无记忆**:每次对话都是独立的,它不记得几分钟前说过什么。如果你说“我妈妈生病了”,然后说“她很难过”,ELIZA不知道“她”指谁。
- **无理解**:它不解析语法,不捕捉语义。所有“负面情绪”表达都被同等处理。“我恨你”和“我讨厌下雨”可能触发类似回应。
- **规则爆炸**:要覆盖更多话题,需要人工编写海量规则,且规则之间可能冲突。
著名的“ELIZA效应”正是人们 tendency to attribute 理解给简单程序,因为回应听起来合理。但一旦深入追问,漏洞百出。例如:
> 用户:我头疼。 > ELIZA:你为什么头疼? > 用户:因为昨晚没睡好。 > ELIZA:这和你与母亲的关系有关吗?(触发“睡”可能关联“母亲”的荒谬规则)
ELIZA是符号主义AI的典型:试图用人工符号和规则模拟智能。它证明了自然语言对话的复杂性,但也暴露了纯规