从人工规则到AI大脑:自然语言处理60年进化全揭秘

你对着手机说“帮我订一张明天去北京的高铁票”,手机立刻照办——这背后,是一场持续了六十多年的技术马拉松。

自然语言处理(NLP)的目标只有一个:让电脑看懂人话、说人话。听起来简单,做起来却难如登天。因为人类语言充满了歧义、省略、反讽和无穷无尽的新说法。

今天,我就带你完整回顾NLP从“手写规则”到“深度学习”的四个进化阶段。全文会穿插大量可运行的代码示例,保证你能一边看一边理解。

一、NLP到底在解决哪些问题?

在聊历史之前,我们先搞清楚NLP通常处理哪几类任务。后面每个阶段的技术都是冲着这些任务去的。

1.1 文本分类

给整段文字贴一个标签。比如:

  • 判断酒店评论是好评还是差评(情感分析)

  • 判断一封邮件是不是垃圾邮件

  • 判断新闻属于体育、财经还是娱乐

1.2 序列标注

给文本中的每个词打上一个标签。最典型的例子是命名实体识别:从一句话里把人名、地名、日期、手机号等找出来。

比如下面这个收货地址信息:

收货人:王雷 手机号:13812345678 地址:西部硅谷大厦6栋201室

我们希望自动标出:王雷(人名)、13812345678(手机号)、西部硅谷大厦(地名)、6栋201室(门牌号)。

序列标注常用的标记法是 BIO

  • B

    (Begin):一个实体的开头

  • I

    (Inside):一个实体的内部

  • O

    (Outside):不是实体

例如“王雷住在深圳”标注为:王(B-人名) 雷(I-人名) 住(O) 在(O) 深(B-地名) 圳(I-地名)

1.3 文本转换

把一种形式的文本变成另一种形式。包括:

  • 机器翻译(中文→英文)

  • 文本摘要(长文章→短摘要)

  • 风格转换(口语→书面语)

理解了这些任务,我们下面来看技术是如何一步步实现它们的。

二、第一阶段:规则系统(1950s–1980s)—— 像教小孩背课文

早期的科学家非常乐观:他们认为语言就是“词典 + 语法规则”。只要把这两样东西写成代码,计算机就能理解语言。

2.1 Georgetown-IBM实验:字典加规则硬翻译

1954年,乔治城大学和IBM搞了一次轰动性的演示:把60多个俄语句子自动翻译成英语。当时的媒体兴奋地预测“三五年内机器翻译将取代人工”。结果呢?六十多年后的今天,机器翻译仍然需要人工校对。

这个系统的工作流程非常直白:

  1. 查词典:每个俄语单词找到对应的英语单词

  2. 调语序:按照英语语法规则重新排列单词顺序

  3. 输出句子

下面用代码模拟这个过程:

# 模拟1954年规则翻译系统的核心逻辑
class RuleBasedTranslator:
    def __init__(self):
        # 俄语 -> 英语 词典
        self.dictionary = {
            'automobilu': 'car',
            'edet': 'goes',
            'bistro': 'fast'
        }
 
    def translate(self, russian_text):
        # 步骤1:按空格切分单词
        tokens = russian_text.split()
        # 步骤2:逐个查词典
        english_words = [self.dictionary.get(token, token) for token in tokens]
        # 步骤3:按英语语序拼接(本例中顺序相同,实际复杂得多)
        return ' '.join(english_words)
 
# 运行示例
translator = RuleBasedTranslator()
print(translator.translate("automobilu edet bistro"))   # 输出: car goes fast

这个系统的缺陷非常明显:

  • 换个语言就要重写词典和所有语法规则

  • 遇到词典里没有的词就直接卡壳

  • 无法处理歧义(比如“bank”可以是银行也可以是河岸)

2.2 ELIZA:世界上第一个“心理医生”聊天机器人

1966年,MIT的Joseph Weizenbaum写了一个叫ELIZA的程序。它假装成一位心理医生——心理医生的特点就是不停反问,ELIZA完美模仿了这一招。

它的原理比翻译系统还简单:

  1. 扫描用户输入,寻找预定义的关键词

  2. 匹配预设的模式(比如“我感到X”)

  3. 用固定模板生成回复(把X填进“你为什么感到X?”)

下面是一个迷你ELIZA的实现:

import re
 
# 迷你版ELIZA聊天机器人
class MiniELIZA:
    def __init__(self):
        # 规则列表:每个元素是 (正则表达式, 回复生成函数)
        self.rules = [
            (re.compile(r'.*我感到(.*)', re.IGNORECASE),
             lambda m: f'你为什么感到{m.group(1)}?'),
            (re.compile(r'.*我(喜欢|讨厌)(.*)', re.IGNORECASE),
             lambda m: f'你为什么{m.group(1)}{m.group(2)}?'),
            (re.compile(r'.*你好.*', re.IGNORECASE),
             lambda m: '你好,今天有什么想聊的吗?'),
        ]
        self.default_response = "嗯,我明白了,请继续说。"
 
    def respond(self, user_input):
        # 遍历所有规则,找到第一个匹配的
        for pattern, response_func in self.rules:
            match = pattern.match(user_input)
            if match:
                return response_func(match)
        return self.default_response
 
# 测试一下
eliza = MiniELIZA()
print(eliza.respond("我感到焦虑"))        # 输出: 你为什么感到焦虑?
print(eliza.respond("我喜欢编程"))        # 输出: 你为什么喜欢编程?
print(eliza.respond("你好"))              # 输出: 你好,今天有什么想聊的吗?
print(eliza.respond("我的猫生病了"))      # 输出: 嗯,我明白了,请继续说。

ELIZA根本不懂“焦虑”是什么,它只是像鹦鹉学舌一样,把你的话里的关键词掏出来再塞回模板。但很多用户居然真的以为它是一位善解人意的心理医生——这说明人类有多容易把简单的模式匹配误解为“智能”。

规则系统的核心缺陷

  • 规则永远写不完。英语光语法规则就有几千条,更别说处理例外了。

  • 无法应对歧义和新词。比如“他吃食堂”里的“吃”其实是“去……吃饭”的意思,但词典里只会写“eat”。

  • 扩展性极差。加一个新功能就要加一堆新规则,最后系统变成一团乱麻。

三、第二阶段:统计方法(1990s–2000s)—— 让数据说话

到了90年代,计算机变强了,互联网也积累了海量文本数据。科学家们换了个思路:不写规则了,让机器自己从数据里统计规律。这就是“数据驱动”的方法。

统计方法的核心思想很简单:一个词出现的概率,只取决于它前面几个词。你不需要告诉计算机“形容词通常放在名词前面”,它只要看过足够多的句子,自己就能算出“红车”出现的次数比“车红”多得多。

3.1 N-gram模型:猜下一个词是什么

N-gram是统计方法中最基础的语言模型。它的假设是:一个词的概率只取决于它前面的 N-1 个词。

  • N=2

     叫 Bigram(只依赖前1个词)

  • N=3

     叫 Trigram(依赖前2个词)

下面用中文例子来演示。假设我们有这样几条训练语料(已经分好词,词之间用空格隔开):

现在我们来统计 Bigram(连续两个词的出现次数):

from collections import defaultdict, Counter
 
# 训练语料(已分词,每个句子是词列表)
corpus = [
    ['我', '爱', '你'],
    ['我', '想', '你'],
    ['我', '爱', '北京'],
    ['我', '想', '吃', '北京', '烤鸭'],
    ['我', '想', '去', '北京'],
    ['北京', '烤鸭', '很', '好吃']
]
 
# 统计Bigram频次
bigram_counts = defaultdict(Counter)
 
for sentence in corpus:
    for i in range(len(sentence) - 1):
        prev_word = sentence[i]
        next_word = sentence[i+1]
        bigram_counts[prev_word][next_word] += 1
 
# 查看“我”后面都跟过哪些词
print(dict(bigram_counts['我']))   # 输出: {'爱': 2, '想': 3}
 
# 根据Bigram模型预测给定前一个词的下一个词(取频次最高的)
def predict_next(prev_word):
    if prev_word not in bigram_counts:
        return None
    return max(bigram_counts[prev_word].items(), key=lambda x: x[1])[0]
 
print(predict_next('我'))          # 输出: '想'(因为“我想”出现3次 > “我爱”2次)

这个简单的模型已经能做“续写”了:给定“我”,它认为后面最可能是“想”。不需要任何语法知识,纯靠统计就能学会词语搭配

但N-gram也有明显缺陷:

  • 如果训练数据里从来没出现过“我 吃 披萨”,模型就认为这个组合的概率是0(尽管它可能是正确的)。

  • 为了解决这个问题,后来引入了平滑技术(比如给没见过的组合分配一个很小的概率,例如加一平滑)。

3.2 隐马尔可夫模型(HMM)做词性标注

词性标注就是把每个词标上名词、动词、形容词等。HMM在这个任务上很成功。它的想法是:我们看到的词是“观测值”,背后隐藏着它们的词性序列。通过统计“词性→词性”的转移概率和“词性→词”的发射概率,就可以反推出最可能的词性序列。

HMM的数学细节比较复杂,这里不展开完整代码。你只需要知道:它是在一张概率图上找最优路径,用的算法叫维特比(Viterbi)。

统计方法的进步

  • 泛化能力大大增强:只要训练数据足够多,模型就能自动学到各种语言规律。

  • 不再需要人工编写语法规则。

  • 但仍然需要人工设计特征——比如词本身、大小写、是否包含数字、前后缀等。特征工程仍然很繁琐。

四、第三阶段:浅层机器学习(2000s–2010s)—— 特征工程的艺术

这个阶段,研究者开始使用更复杂的机器学习模型,比如逻辑回归、支持向量机(SVM)、条件随机场(CRF)。但核心工作依然是特征工程——手工设计各种特征来喂给模型。

4.1 词袋模型 + 逻辑回归做情感分类

词袋模型(Bag-of-Words)是最简单的文本表示方法:忽略词序,只统计每个词出现了几次

from sklearn.feature_extraction.text import CountVectorizer
from sklearn.linear_model import LogisticRegression
 
# 样本评论(1=好评,0=差评)
reviews = [
    "服务很好 环境干净",      # 好评
    "服务很差 环境脏乱",      # 差评
    "服务一般 环境还行",      # 中评(这里标为1仅作演示)
    "太差了 再也不来"         # 差评
]
labels = [1, 0, 1, 0]
 
# 将文本转换为词袋向量(注意:中文需要自己加空格分词,这里已加)
vectorizer = CountVectorizer()
X = vectorizer.fit_transform(reviews)
print("词表:", vectorizer.get_feature_names_out())
# 输出类似: ['一般', '不来', '再也不', '太差', '很好', '很脏', ...]
 
# 训练逻辑回归模型
model = LogisticRegression()
model.fit(X, labels)
 
# 预测一条新评论
new_review = ["味道很好 但服务很差"]
X_new = vectorizer.transform(new_review)
pred = model.predict(X_new)
print("预测结果:", "好评" if pred[0]==1 else "差评")

词袋模型简单好用,但它有一个致命缺陷:完全丢失了词序信息

比如下面两条评论:

  • A: “服务很好 但味道很差” (服务好但味道差)

  • B: “味道很好 但服务很差” (味道好但服务差)

在词袋模型中,两条评论的特征向量完全相同(都包含“服务”“很好”“但”“味道”“很差”)。但它们的实际情感倾向可能完全不同。这显然不合理。

为了解决这个问题,人们引入了n-gram特征:不光统计单个词(1-gram),还统计连续两个词(2-gram)、三个词(3-gram)。这样上面两个句子就能区分开了:

  • A的trigram: ["服务很好", "很好但", "但味道", "味道很差"]

  • B的trigram: ["味道很好", "很好但", "但服务", "服务很差"]

可以看到,两者的trigram集合完全不同。n-gram在一定程度上保留了局部词序信息,算是一种折中方案。

在代码中,只需修改CountVectorizer的参数即可:

# 使用2-gram和3-gram特征
vectorizer = CountVectorizer(ngram_range=(1, 3))

五、第四阶段:深度学习(2010s–至今)—— 自动学习特征,全面超越

深度学习的最大革命是:不再需要人工设计特征。你把原始文本直接扔进神经网络,它自己就能从数据中学习出有用的表示。

5.1 RNN / LSTM / GRU:处理序列数据

文本天然是一个序列(词一个接一个出现)。循环神经网络(RNN) 专门为此设计:它有一个隐藏状态,可以把前一个词的信息传递到下一个词。

但传统RNN有一个问题:长距离依赖。当句子很长时(比如200个词),开头的词对结尾的影响会指数级衰减,模型很难记住。LSTM(长短期记忆网络)和GRU(门控循环单元)通过引入“门”机制解决了这个问题,可以选择性地记住或遗忘信息。

 LSTM(Long Short-Term Memory)

 GRU(Gated Recurrent Unit)

下面是一个用PyTorch实现的简单LSTM情感分类器(仅结构示意,不含训练代码):

import torch
import torch.nn as nn
 
# 简单的LSTM情感分类模型
class SimpleLSTMClassifier(nn.Module):
    def __init__(self, vocab_size, embed_dim, hidden_dim, num_classes):
        super().__init__()
        # 词嵌入层:将单词ID映射为稠密向量
        self.embedding = nn.Embedding(vocab_size, embed_dim)
        # LSTM层
        self.lstm = nn.LSTM(embed_dim, hidden_dim, batch_first=True)
        # 全连接分类层
        self.classifier = nn.Linear(hidden_dim, num_classes)
 
    def forward(self, x):
        # x的形状: (batch_size, seq_len)  每个元素是单词的ID
        emb = self.embedding(x)          # (batch, seq_len, embed_dim)
        # LSTM输出: 所有时刻的隐藏状态 和 最后一个时刻的隐藏状态
        _, (hidden, _) = self.lstm(emb)  # hidden形状: (1, batch, hidden_dim)
        final_repr = hidden.squeeze(0)   # (batch, hidden_dim)
        logits = self.classifier(final_repr)  # (batch, num_classes)
        return logits

关键点:你不需要再手动构造“词是否大写”“是否包含数字”等特征。LSTM会自动学习到哪些信息对分类有用。

5.2 Transformer:彻底改变NLP的架构

2017年,Google发表了论文《Attention Is All You Need》,提出了Transformer架构。它完全抛弃了RNN的循环结构,只使用注意力机制(Attention)

注意力机制可以理解为:在生成每个输出词的时候,模型会“回看”输入句子里的所有词,并给每个词分配一个注意力分数(权重)。分数越高的词对当前输出影响越大。

比如翻译“我爱你”到“I love you”时:

  • 生成“I”的时候,模型重点关注“我”

  • 生成“love”的时候,重点关注“爱”

  • 生成“you”的时候,重点关注“你”

这非常符合直觉。

Transformer由编码器(Encoder) 和解码器(Decoder) 组成:

  • 编码器:把输入句子编码成一系列向量(每个位置包含整个句子的信息)

  • 解码器:基于编码器的输出,一步一步生成目标句子

基于Transformer,研究者们开发出了预训练大模型(BERT、GPT等):

  • BERT

    :擅长理解任务,如情感分析、问答、命名实体识别

  • GPT

    :擅长生成任务,如写文章、对话、代码生成

这些大模型先在超大规模文本上“预训练”,学会通用的语言知识,然后在具体任务上“微调”一小下,效果碾压传统方法。

六、总结与个人看法

我们一路从1950年代走到了2020年代,技术演进可以总结为下面这张表:

阶段

核心方法

优点

缺点

规则系统

手写词典+语法规则

在小任务上可控

扩展性极差,规则写不完

统计方法

N-gram、HMM

自动从数据中学习

需要人工设计特征

浅层机器学习

逻辑回归、SVM、CRF

效果好于纯统计

特征工程费时费力

深度学习

RNN、LSTM、Transformer

自动学习特征,精度高

需要大量数据和算力

大模型时代

预训练+微调

一个模型干所有事

训练成本极高,黑盒难解释

个人观点(供参考):

  1. 不要迷信“端到端”

    。虽然深度学习省去了特征工程,但在某些垂直领域(比如金融、医疗),人工设计的规则仍然非常有用。很多时候,规则 + 统计 + 深度学习混合使用才是最稳妥的方案。

  2. 大模型不是终点

    。GPT-4很强大,但它仍然会犯低级错误,而且成本高、推理慢。未来一定是“大模型做底座 + 小模型/规则处理具体场景”的搭配。

  3. 入门NLP,建议从统计方法开始

    。很多人一上来就学BERT,结果连词袋模型、TF-IDF都不懂。但实际工作中,数据量小的场景下,朴素贝叶斯可能比BERT还好使。把基础打牢,再往上走,你会走得更稳。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容