跳转至

📖 第1章:NLP基础概念

⚠️ 时效性说明:本章涉及前沿模型/价格/榜单等信息,可能随版本快速变化;请以论文原文、官方发布页和 API 文档为准。

NLP基础概念

学习时间:4小时 难度星级:⭐⭐ 前置知识:Python基础、机器学习基本概念 学习目标:理解NLP的定义与核心任务,了解NLP发展历史及应用场景


📋 目录


1. 什么是自然语言处理

1.1 定义

自然语言处理(Natural Language Processing, NLP) 是人工智能和计算语言学的交叉领域,致力于让计算机理解、解释和生成人类语言。

用更通俗的话说,NLP就是让机器"读懂"人话。

Text Only
人类语言 ←→ 计算机表示
"今天天气真好"  →  机器能理解的数学表示  →  生成"是的,很适合出去走走"

1.2 NLP的学科定位

NLP处于多个学科的交叉点:

Text Only
         计算机科学
  语言学 ──→ NLP ←── 数学/统计
         认知科学
  • 计算机科学:提供算法和工程实现能力
  • 语言学:提供语言规则和理论框架
  • 数学/统计:提供建模工具(概率论、优化理论)
  • 认知科学:理解人类语言处理机制

1.3 自然语言 vs 形式语言

特性 自然语言 形式语言(如Python)
歧义性 大量歧义 无歧义
语法 灵活,可违反 严格,不可违反
上下文依赖 高度依赖 基本独立
隐含信息 大量隐含 显式表达
动态演变 不断变化 版本更新才变

这就是为什么NLP如此困难——自然语言充满了歧义、隐喻、省略和文化背景。

1.4 NLP的层次结构

从语言学角度,NLP可以分为以下层次:

Text Only
┌─────────────────────────────────┐
│         语用层(Pragmatics)      │  说话人的意图是什么?
├─────────────────────────────────┤
│         语义层(Semantics)       │  句子的真正含义是什么?
├─────────────────────────────────┤
│         句法层(Syntax)          │  句子的结构是什么?
├─────────────────────────────────┤
│         词法层(Morphology)      │  词的内部结构是什么?
├─────────────────────────────────┤
│         音韵层(Phonology)       │  语音的规则是什么?
└─────────────────────────────────┘

示例:分析"他把苹果吃了"

Python
# 音韵层:tā bǎ píngguǒ chī le
# 词法层:他/把/苹果/吃/了
# 句法层:主语(他) + 把字句(把苹果) + 谓语(吃了)
# 语义层:动作执行者是"他",动作是"吃",对象是"苹果"
# 语用层:这是一个陈述事实的句子

2. NLP发展历史

2.1 规则与符号主义时代(1950s-1989)

核心思想:用手工编写的规则来处理语言

重要里程碑

年份 事件 意义
1950 图灵提出"图灵测试" 首次定义机器智能标准
1954 Georgetown实验 首次机器翻译实验(俄→英)
1957 Chomsky形式语法 奠定计算语言学理论基础
1966 ELIZA聊天机器人 首个对话系统(模式匹配)
1971 SHRDLU系统 受限域的自然语言理解

ELIZA的基本原理(模式匹配):

Python
import re

def eliza_response(user_input):
    """简单的ELIZA风格对话系统"""
    rules = [
        (r"我觉得(.*)", ["为什么你觉得{0}?", "你能详细说说吗?"]),
        (r"我是(.*)", ["你为什么说你是{0}?", "做{0}是什么感觉?"]),
        (r"(.*)(难过|伤心|不开心)(.*)", ["很抱歉听到你不开心,能说说原因吗?"]),
        (r"(.*)(高兴|开心|快乐)(.*)", ["真好!是什么让你这么高兴?"]),
        (r"为什么(.*)", ["你觉得为什么呢?", "这个问题很好,你怎么想?"]),
        (r"(.*)", ["请继续说", "嗯嗯,然后呢?", "有意思,能详细说说吗?"]),
    ]

    import random
    for pattern, responses in rules:
        match = re.search(pattern, user_input)  # re.search在字符串中搜索匹配模式
        if match:
            response = random.choice(responses)
            # 填充匹配的内容
            for i, group in enumerate(match.groups()):  # enumerate同时获取索引和元素
                response = response.replace(f"{{{i}}}", group if group else "")
            return response

    return "我不太明白,能换个说法吗?"

# 测试
test_inputs = [
    "我觉得今天天气不错",
    "我是一个学生",
    "最近很难过",
    "为什么要学NLP",
]

for inp in test_inputs:
    print(f"用户:{inp}")
    print(f"ELIZA:{eliza_response(inp)}")
    print()

规则方法的局限: - 规则编写耗时耗力 - 难以覆盖所有情况 - 缺乏泛化能力 - 不同语言需要不同规则集

2.2 统计方法时代(1990s-2012)

核心思想:用数据驱动的统计模型取代手Craft规则

标志性事件

年份 技术/模型 创新点
1990 HMM在NLP中广泛应用 概率化的序列建模
1993 IBM翻译模型 统计机器翻译的起点
2001 条件随机场(CRF) 判别式序列标注
2002 BLEU评测指标 标准化翻译评估
2003 LDA主题模型 文档主题发现
2003 神经网络语言模型(Bengio) 连续空间词表示的起点

统计方法的核心公式

\[P(y|x) = \frac{P(x|y) \cdot P(y)}{P(x)}\]

其中,贝叶斯公式是统计NLP的核心。以文本分类为例: - \(x\) = 文本特征 - \(y\) = 类别标签 - \(P(y|x)\) = 给定文本的类别概率

Python
from collections import Counter, defaultdict
import math

class NaiveBayesClassifier:
    """朴素贝叶斯文本分类器 - 统计NLP时代的经典算法"""

    def __init__(self):
        self.class_counts = Counter()  # Counter统计元素出现次数
        self.word_counts = defaultdict(Counter)  # defaultdict访问不存在的键时返回默认值
        self.vocab = set()
        self.total_docs = 0

    def train(self, documents, labels):
        """训练朴素贝叶斯模型"""
        self.total_docs = len(documents)
        for doc, label in zip(documents, labels):  # zip按位置配对
            self.class_counts[label] += 1
            words = doc.split()
            for word in words:
                self.word_counts[label][word] += 1
                self.vocab.add(word)

    def predict(self, document):
        """预测文档类别"""
        words = document.split()
        best_label = None
        best_score = float('-inf')

        for label in self.class_counts:
            # log P(class)
            score = math.log(self.class_counts[label] / self.total_docs)

            # log P(word|class) with Laplace smoothing
            total_words = sum(self.word_counts[label].values())
            for word in words:
                count = self.word_counts[label].get(word, 0)
                score += math.log((count + 1) / (total_words + len(self.vocab)))

            if score > best_score:
                best_score = score
                best_label = label

        return best_label

# 示例:简单情感分类
docs = [
    "这部电影太好看了 强烈推荐",
    "非常喜欢 剧情很精彩",
    "拍得真好 演技在线",
    "太难看了 浪费时间",
    "剧情太差 不推荐",
    "很无聊 看了一半就关了",
]
labels = ["正面", "正面", "正面", "负面", "负面", "负面"]

nb = NaiveBayesClassifier()
nb.train(docs, labels)

test_docs = ["这部电影很好看", "太无聊了 不推荐"]
for doc in test_docs:
    print(f"'{doc}' → {nb.predict(doc)}")

2.3 深度学习时代(2013-2017)

核心思想:用神经网络自动学习特征表示

里程碑事件

年份 技术 论文/团队 影响
2013 Word2Vec Mikolov/Google 开创词向量时代
2014 GloVe Stanford 结合全局统计信息
2014 Seq2Seq Sutskever/Google 序列到序列框架
2014 TextCNN Yoon Kim CNN用于文本分类
2015 Attention Bahdanau 注意力机制
2017 Transformer Vaswani/Google 彻底改变NLP格局
Python
import numpy as np

def softmax(x):
    """Softmax函数 - 注意力机制的核心组件"""
    exp_x = np.exp(x - np.max(x))
    return exp_x / exp_x.sum()

def self_attention(Q, K, V):
    """
    自注意力机制简化实现
    Q, K, V: shape = (seq_len, d_model)
    """
    d_k = K.shape[-1]  # [-1]负索引取最后元素
    # 计算注意力分数
    scores = np.dot(Q, K.T) / np.sqrt(d_k)  # np.dot矩阵/向量点乘
    # 归一化
    attention_weights = np.array([softmax(s) for s in scores])  # np.array创建NumPy数组
    # 加权求和
    output = np.dot(attention_weights, V)
    return output, attention_weights

# 示例:3个词,维度为4的自注意力
np.random.seed(42)
seq_len, d_model = 3, 4
X = np.random.randn(seq_len, d_model)

# 简化版:Q=K=V=X
output, weights = self_attention(X, X, X)
print("输入形状:", X.shape)
print("输出形状:", output.shape)
print("注意力权重:\n", np.round(weights, 3))

2.4 预训练模型时代(2018-2022)

核心思想:大规模预训练 + 下游任务微调

年份 模型 参数量 核心创新
2018.02 ELMo 94M 上下文相关的词向量
2018.06 GPT-1 117M 单向Transformer预训练
2018.10 BERT 340M 双向Transformer + MLM
2019 GPT-2 1.5B 更大的语言模型
2020 GPT-3 175B In-Context Learning
2022 ChatGPT ~175B* RLHF对齐

*注:ChatGPT参数量未公开,估计基于GPT-3.5架构,约175B以上

2.5 大模型时代(2023至今)

核心思想:涌现能力、指令跟随、多模态融合

Text Only
大模型时代的范式转变:

传统范式:预训练 → 微调 → 部署
大模型范式:预训练 → 对齐 → Prompt → 部署

传统NLP:一个任务一个模型
大模型NLP:一个模型解决所有任务

3. NLP核心任务分类

3.1 词法分析

分词(Tokenization / Word Segmentation)

将连续的文本切分为有意义的词语单元。

Python
import jieba

# 中文分词
text = "自然语言处理是人工智能皇冠上的明珠"
words = jieba.lcut(text)
print("分词结果:", "/".join(words))
# 输出: 自然语言/处理/是/人工智能/皇冠/上/的/明珠

# 英文分词(相对简单)
en_text = "Natural language processing is a fascinating field."
en_words = en_text.split()
print("英文分词:", en_words)

词性标注(Part-of-Speech Tagging)

为每个词标注其语法类别(名词、动词、形容词等)。

Python
import jieba.posseg as pseg

text = "小明在北京大学学习计算机科学"
words = pseg.lcut(text)
for word, flag in words:
    print(f"{word}/{flag}", end=" ")
# 输出: 小明/nr 在/p 北京大学/nt 学习/v 计算机/n 科学/n

常见词性标签

标签 含义 示例
n 名词 苹果、电脑
v 动词 吃、学习
a 形容词 漂亮、快速
d 副词 非常、已经
nr 人名 张三、李四
ns 地名 北京、上海
nt 机构名 清华大学

命名实体识别(Named Entity Recognition, NER)

识别文本中的实体(人名、地名、机构名、时间等)。

Python
# 使用简单规则演示NER的基本概念
import re

def simple_ner(text):
    """简单的规则式NER"""
    entities = []

    # 日期识别
    date_pattern = r'\d{4}年\d{1,2}月\d{1,2}日'
    for match in re.finditer(date_pattern, text):
        entities.append(("DATE", match.group(), match.start()))

    # 金额识别
    money_pattern = r'\d+(?:\.\d+)?[万亿]?元'
    for match in re.finditer(money_pattern, text):
        entities.append(("MONEY", match.group(), match.start()))

    return entities

text = "2024年3月15日,阿里巴巴发布了一款价值299元的新产品"
entities = simple_ner(text)
for etype, value, pos in entities:
    print(f"[{etype}] {value} (位置: {pos})")

3.2 句法分析

依存句法分析(Dependency Parsing)

分析句子中词语之间的依存关系。

Text Only
        注册 (ROOT)
       /    \
     用户    了
     /
   新的

"新的用户注册了"
Python
# 使用SpaCy进行句法分析(需安装中文模型)
# pip install spacy
# python -m spacy download zh_core_web_sm

try:  # try/except捕获异常
    import spacy
    nlp = spacy.load("zh_core_web_sm")

    doc = nlp("自然语言处理技术发展迅速")
    for token in doc:
        print(f"{token.text} --{token.dep_}--> {token.head.text}")
except OSError:
    print("请先安装中文SpaCy模型: python -m spacy download zh_core_web_sm")

成分句法分析(Constituency Parsing)

将句子分解为层次化的短语结构。

Text Only
            S
          /   \
        NP     VP
       /  \    / \
     DET   N  V   NP
      |    |  |    |
     The  cat sat  ...

3.3 语义分析

语义角色标注(Semantic Role Labeling)

识别句子中"谁对谁做了什么"。

Text Only
"小明在教室里把作业交给了老师"

动词: 交
施事(Agent): 小明
受事(Patient): 作业
与事(Recipient): 老师
地点(Location): 教室里

文本蕴含(Textual Entailment / NLI)

判断两个句子之间的逻辑关系。

Python
# 文本蕴含示例
examples = [
    {
        "premise": "所有的猫都是动物",
        "hypothesis": "我的猫是动物",
        "label": "蕴含(Entailment)"
    },
    {
        "premise": "今天下雨了",
        "hypothesis": "今天是晴天",
        "label": "矛盾(Contradiction)"
    },
    {
        "premise": "他去了超市",
        "hypothesis": "他买了很多东西",
        "label": "中立(Neutral)"
    },
]

for ex in examples:
    print(f"前提: {ex['premise']}")
    print(f"假设: {ex['hypothesis']}")
    print(f"关系: {ex['label']}")
    print("---")

3.4 文本生成任务

机器翻译(Machine Translation)

Text Only
输入: "自然语言处理是人工智能的核心技术"
输出: "Natural language processing is a core technology of AI"

文本摘要(Text Summarization)

Text Only
输入: 一篇长文档
输出 (抽取式): 从原文中选择关键句子
输出 (生成式): 用新的表述概括原文要点

对话生成(Dialogue Generation)

Text Only
用户: 你好,推荐一本NLP入门书
系统: 推荐《统计自然语言处理》(宗成庆著),
     适合中文NLP入门,理论和实践兼顾。

3.5 信息检索与挖掘

文本分类(Text Classification)

Python
# 文本分类的多种场景

classification_tasks = {
    "情感分析": {
        "输入": "这家餐厅的服务态度太差了",
        "输出": "负面",
        "类别": ["正面", "负面", "中性"]
    },
    "主题分类": {
        "输入": "央行宣布下调存款准备金率0.5个百分点",
        "输出": "财经",
        "类别": ["财经", "科技", "体育", "娱乐", "政治"]
    },
    "意图识别": {
        "输入": "帮我订一张明天去北京的机票",
        "输出": "订票",
        "类别": ["查询", "订票", "退票", "改签", "闲聊"]
    },
    "垃圾检测": {
        "输入": "恭喜你中了100万大奖,点击链接领取",
        "输出": "垃圾信息",
        "类别": ["正常", "垃圾信息"]
    },
}

for task_name, info in classification_tasks.items():
    print(f"📌 {task_name}")
    print(f"   输入: {info['输入']}")
    print(f"   输出: {info['输出']}")
    print(f"   候选类别: {info['类别']}")
    print()

信息抽取(Information Extraction)

Text Only
输入: "苹果公司CEO库克在WWDC 2024上发布了Apple Intelligence"

抽取结果:
- 实体: [苹果公司(ORG), 库克(PER), WWDC 2024(EVENT), Apple Intelligence(PROD)]
- 关系: (库克, CEO_of, 苹果公司), (库克, 发布, Apple Intelligence)
- 事件: 产品发布(时间=WWDC 2024, 发起人=库克, 对象=Apple Intelligence)

4. NLP的技术栈全景

4.1 传统NLP技术栈

Python
传统NLP技术栈 = {
    "特征提取": {
        "词袋模型": "Bag of Words",
        "TF-IDF": "词频-逆文档频率",
        "N-gram": "N元语法模型",
    },
    "经典模型": {
        "朴素贝叶斯": "文本分类",
        "SVM": "文本分类、情感分析",
        "HMM": "序列标注",
        "CRF": "命名实体识别",
        "LDA": "主题模型",
    },
    "工具库": ["NLTK", "jieba", "SnowNLP", "HanLP"],
}

4.2 深度学习NLP技术栈

Python
深度学习NLP技术栈 = {
    "词向量": {
        "静态词向量": ["Word2Vec", "GloVe", "FastText"],
        "上下文词向量": ["ELMo", "BERT embeddings"],
    },
    "基础模型": {
        "CNN": "TextCNN (文本分类)",
        "RNN/LSTM/GRU": "序列建模",
        "Seq2Seq + Attention": "翻译、摘要",
        "Transformer": "通用架构",
    },
    "预训练模型": {
        "编码器": ["BERT", "RoBERTa", "ALBERT", "ERNIE"],
        "解码器": ["GPT", "GPT-2", "GPT-3"],
        "编码器-解码器": ["T5", "BART", "mT5"],
    },
    "工具框架": ["Transformers", "PyTorch", "SpaCy"],
}

4.3 大模型时代技术栈

Python
大模型时代技术栈 = {
    "模型": {
        "闭源": ["GPT-4", "Claude", "Gemini"],
        "开源": ["LLaMA", "Mistral", "ChatGLM", "Qwen"],
    },
    "技术": {
        "Prompt Engineering": "设计高效提示词",
        "RAG": "检索增强生成",
        "Fine-tuning": ["LoRA", "QLoRA", "全参数微调"],
        "RLHF/RLAIF": "人类反馈对齐",
        "Agent": "智能体框架",
    },
    "工具": {
        "模型服务": ["vLLM", "TGI", "Ollama"],
        "应用框架": ["LangChain", "LlamaIndex", "Dify"],
        "向量数据库": ["Milvus", "Chroma", "FAISS"],
    },
}

5. NLP的困难与挑战

5.1 语言的歧义性

歧义是NLP最核心的挑战之一:

词汇歧义(Lexical Ambiguity)

Python
# 同一个词在不同语境下含义不同
ambiguity_examples = {
    "苹果": [
        "我吃了一个苹果",        # 水果
        "苹果公司发布了新产品",   # 公司名
    ],
    "开": [
        "他开了门",              # 打开
        "他开了一家店",          # 创办
        "花儿开了",              # 绽放
        "他开了一个玩笑",        # 讲述
    ],
    "打": [
        "打篮球",    # 进行运动
        "打电话",    # 拨打
        "打酱油",    # 买/不关心
        "打雷了",    # 发出
    ],
}

for word, examples in ambiguity_examples.items():
    print(f"「{word}」的多义:")
    for ex in examples:
        print(f"  - {ex}")
    print()

句法歧义(Syntactic Ambiguity)

Text Only
"用望远镜看到了戴帽子的人"

解读1: [用望远镜] [看到了] [戴帽子的人]
→ 我用望远镜看到了一个戴帽子的人

解读2: [看到了] [用望远镜的] [戴帽子的人]
→ 我看到一个用望远镜且戴帽子的人

"咬死了猎人的狗"

解读1: [咬死了] [猎人的狗]
→ (某物)咬死了属于猎人的那条狗

解读2: [咬死了猎人的] [狗]
→ 那条咬死了猎人的狗

语义歧义

Text Only
"这个人真牛" → 褒义(厉害)
"你把我搞得很烦" → 可以是真的烦,也可以是撒娇的语气

5.2 语言的多样性

Python
# 同一个意思的多种表达
same_meaning = [
    "这部电影很好看",
    "这片子真不错",
    "强烈推荐这个电影",
    "看完觉得值了",
    "难得的好片",
    "这是近几年最好的电影之一",
    "五星好评 yyds",
    "爆好看!!!",
    "🔥🔥🔥",
]

print("以下表达都是正面情感:")
for expr in same_meaning:
    print(f"  ✓ {expr}")

5.3 上下文依赖

Text Only
A: "你吃了吗?"
B: "吃了"           ← 这里省略了"我"和"饭"

A: "明天几点的会?"
B: "十点"           ← 需要理解"会"指什么,"十点"补全了时间

代词消解:
"小明告诉小红他很高兴"  → "他"指谁?取决于上下文

5.4 知识和常识

Text Only
"他端着一杯水走进来,然后把它放在桌上"
→ 机器需要知道"它"指的是水杯(不是桌子)
→ 机器需要知道水杯可以放在桌上(物理常识)

"餐厅里的牛排很老"
→ "老"指口感硬,不是指年龄大
→ 需要领域知识

5.5 中文NLP的特有挑战

Python
中文特有挑战 = {
    "分词问题": "中文没有天然的空格分隔 → '结婚的和尚未结婚的'",
    "字词关系": "字和词的边界模糊 → '研究' vs '研' + '究'",
    "新词发现": "网络新词层出不穷 → '内卷', 'YYDS', '摆烂'",
    "简繁体": "简体和繁体的统一处理",
    "拼音歧义": "同音字多 → shi可以对应几十个字",
    "文言文": "古文和现代文的差异巨大",
    "方言": "不同方言区的语言差异",
}

for challenge, description in 中文特有挑战.items():
    print(f"📌 {challenge}: {description}")

5.6 工程挑战

挑战 描述 应对策略
数据稀缺 特定领域标注数据少 数据增强、迁移学习、Few-shot
标注质量 标注不一致、错误多 多人标注、质量控制
模型效率 大模型推理慢 量化、蒸馏、剪枝
领域迁移 通用模型不适应特定领域 领域预训练、领域微调
冷启动 新任务没有训练数据 Prompt Learning、零样本

6. NLP应用场景

6.1 搜索引擎

Text Only
用户查询: "如何用Python处理文本"
  ┌─────────────────────────┐
  │ 查询理解                 │
  │ - 分词: 如何/用/Python/处理/文本 │
  │ - 意图识别: 编程教程查询   │
  │ - 查询改写/扩展          │
  └─────────────────────────┘
  ┌─────────────────────────┐
  │ 语义匹配                 │
  │ - 文档检索               │
  │ - 语义相似度计算          │
  │ - 排序                   │
  └─────────────────────────┘
  ┌─────────────────────────┐
  │ 结果展示                 │
  │ - 摘要生成               │
  │ - 高亮显示               │
  │ - 相关推荐               │
  └─────────────────────────┘

6.2 智能客服

Python
# 智能客服系统的NLP模块示意
class SmartCustomerService:
    def __init__(self):
        self.intent_classifier = None  # 意图分类
        self.entity_recognizer = None  # 实体识别
        self.dialog_manager = None     # 对话管理
        self.response_generator = None # 回复生成

    def process(self, user_input):
        """处理用户输入"""
        # Step 1: 意图识别
        intent = self.classify_intent(user_input)
        # "我想退货" → intent: "退货申请"

        # Step 2: 实体抽取
        entities = self.extract_entities(user_input)
        # "我昨天买的iPhone 15想退货" → {商品: "iPhone 15", 时间: "昨天"}

        # Step 3: 对话管理
        action = self.manage_dialog(intent, entities)
        # 判断是否需要补充信息

        # Step 4: 生成回复
        response = self.generate_response(action)

        return response

    def classify_intent(self, text):
        """意图分类"""
        intents = {
            "退货": ["退货", "退款", "不想要了"],
            "查询订单": ["订单", "到哪了", "快递"],
            "投诉": ["投诉", "差评", "态度差"],
            "咨询": ["多少钱", "有没有", "推荐"],
        }
        for intent, keywords in intents.items():
            if any(kw in text for kw in keywords):  # any()任一为True则返回True
                return intent
        return "其他"

    def extract_entities(self, text):
        return {}

    def manage_dialog(self, intent, entities):
        return {"intent": intent, "entities": entities}

    def generate_response(self, action):
        return "好的,我来帮您处理。"

# 测试
service = SmartCustomerService()
queries = [
    "我想退货",
    "我的订单到哪了",
    "有什么好的手机推荐吗",
]

for q in queries:
    intent = service.classify_intent(q)
    print(f"用户: {q} → 意图: {intent}")

6.3 更多应用场景

Python
应用场景 = {
    "🔍 搜索推荐": ["查询理解", "语义匹配", "个性化推荐"],
    "💬 智能对话": ["智能客服", "聊天机器人", "语音助手"],
    "📝 内容创作": ["自动写作", "文本摘要", "标题生成"],
    "🌍 机器翻译": ["文档翻译", "实时对话翻译", "字幕翻译"],
    "📊 商业智能": ["舆情分析", "竞品分析", "用户画像"],
    "⚖️ 法律": ["合同审查", "案例检索", "法律问答"],
    "🏥 医疗": ["病历分析", "医学文献挖掘", "辅助诊断"],
    "💰 金融": ["新闻情感分析", "研报摘要", "风控建模"],
    "🎓 教育": ["作文评分", "智能批改", "知识问答"],
    "🛡️ 安全": ["内容审核", "虚假新闻检测", "网络诈骗检测"],
}

for category, tasks in 应用场景.items():
    print(f"{category}: {', '.join(tasks)}")

7. NLP评估指标

7.1 分类任务指标

Python
import numpy as np

def compute_classification_metrics(y_true, y_pred):
    """计算分类指标"""
    # 简化版:假设是二分类
    tp = sum(1 for t, p in zip(y_true, y_pred) if t == 1 and p == 1)
    fp = sum(1 for t, p in zip(y_true, y_pred) if t == 0 and p == 1)
    fn = sum(1 for t, p in zip(y_true, y_pred) if t == 1 and p == 0)
    tn = sum(1 for t, p in zip(y_true, y_pred) if t == 0 and p == 0)

    accuracy = (tp + tn) / (tp + fp + fn + tn)
    precision = tp / (tp + fp) if (tp + fp) > 0 else 0
    recall = tp / (tp + fn) if (tp + fn) > 0 else 0
    f1 = 2 * precision * recall / (precision + recall) if (precision + recall) > 0 else 0

    return {
        "Accuracy": accuracy,
        "Precision": precision,
        "Recall": recall,
        "F1-Score": f1,
    }

# 示例
y_true = [1, 1, 1, 0, 0, 1, 0, 1, 1, 0]
y_pred = [1, 1, 0, 0, 0, 1, 1, 1, 0, 0]

metrics = compute_classification_metrics(y_true, y_pred)
for name, value in metrics.items():
    print(f"{name}: {value:.4f}")

各指标含义

\[\text{Precision} = \frac{TP}{TP + FP} \quad \text{(精确率:预测为正的有多少真的是正)}\]
\[\text{Recall} = \frac{TP}{TP + FN} \quad \text{(召回率:真正的正样本有多少被找出来了)}\]
\[\text{F1} = \frac{2 \cdot Precision \cdot Recall}{Precision + Recall} \quad \text{(精确率和召回率的调和平均)}\]

7.2 序列标注指标

Python
def compute_ner_metrics(true_entities, pred_entities):
    """
    NER评估:基于实体级别的P/R/F1
    true_entities: [("PER", 0, 2), ("LOC", 5, 7), ...]
    pred_entities: [("PER", 0, 2), ("LOC", 5, 8), ...]
    """
    true_set = set(true_entities)
    pred_set = set(pred_entities)

    tp = len(true_set & pred_set)
    precision = tp / len(pred_set) if pred_set else 0
    recall = tp / len(true_set) if true_set else 0
    f1 = 2 * precision * recall / (precision + recall) if (precision + recall) > 0 else 0

    return {"Precision": precision, "Recall": recall, "F1": f1}

# 示例:NER结果 (类型, 起始位置, 结束位置)
true_entities = [("PER", 0, 2), ("LOC", 5, 7), ("ORG", 10, 14)]
pred_entities = [("PER", 0, 2), ("LOC", 5, 8), ("ORG", 10, 14)]

metrics = compute_ner_metrics(true_entities, pred_entities)
print("NER评估结果:")
for name, value in metrics.items():
    print(f"  {name}: {value:.4f}")

7.3 生成任务指标

BLEU分数

Python
from collections import Counter
import math

def compute_bleu(reference, candidate, max_n=4):
    """BLEU分数简化计算"""
    scores = []
    for n in range(1, max_n + 1):
        # 生成n-gram
        ref_ngrams = Counter()
        for i in range(len(reference) - n + 1):
            ref_ngrams[tuple(reference[i:i+n])] += 1

        cand_ngrams = Counter()
        for i in range(len(candidate) - n + 1):
            cand_ngrams[tuple(candidate[i:i+n])] += 1

        # 计算匹配
        match = 0
        for ngram, count in cand_ngrams.items():
            match += min(count, ref_ngrams.get(ngram, 0))

        total = max(len(candidate) - n + 1, 1)
        scores.append(match / total if total > 0 else 0)

    # 几何平均
    if min(scores) > 0:
        log_avg = sum(math.log(s) for s in scores) / len(scores)
        bleu = math.exp(log_avg)
    else:
        bleu = 0

    # 简短惩罚
    bp = min(1, math.exp(1 - len(reference) / max(len(candidate), 1)))

    return bleu * bp

# 示例
reference = "the cat is on the mat".split()
candidate = "the cat is on the mat".split()
print(f"完美匹配 BLEU: {compute_bleu(reference, candidate):.4f}")

candidate2 = "the cat on mat".split()
print(f"部分匹配 BLEU: {compute_bleu(reference, candidate2):.4f}")

ROUGE分数

\[\text{ROUGE-N} = \frac{\text{Count of matching n-grams}}{\text{Count of n-grams in reference}}\]

7.4 常用指标总结

任务类型 主要指标 说明
文本分类 Accuracy, F1 多类别用Macro/Micro F1
NER Entity-level F1 实体边界和类型都要对
机器翻译 BLEU 机器翻译标准指标
文本摘要 ROUGE-L 基于最长公共子序列
阅读理解 EM, F1 EM=完全匹配
文本生成 PPL, Human Eval 困惑度+人工评估

8. 动手实验:初探NLP

8.1 实验一:构建简单的NLP流水线

Python
"""
实验一:构建一个完整的NLP处理流水线
包含分词、词频统计、关键词提取
"""

import re
from collections import Counter

class SimpleNLPPipeline:
    """简单的NLP处理流水线"""

    def __init__(self):
        # 停用词列表(简化版)
        self.stopwords = set([
            "的", "了", "在", "是", "我", "有", "和", "就",
            "不", "人", "都", "一", "一个", "上", "也", "很",
            "到", "说", "要", "去", "你", "会", "着", "没有",
            "看", "好", "自己", "这", "他", "她", "它",
        ])

    def clean_text(self, text):
        """文本清洗"""
        # 去除特殊字符
        text = re.sub(r'[^\u4e00-\u9fa5a-zA-Z0-9\s]', '', text)
        # 去除多余空格
        text = re.sub(r'\s+', ' ', text).strip()
        return text

    def simple_tokenize(self, text):
        """简单的基于字符的分词(实际应用中使用jieba等工具)"""
        # 这里使用简单的逐字分割(演示用)
        # 实际项目中应使用jieba
        tokens = list(text)
        return [t for t in tokens if t.strip()]

    def remove_stopwords(self, tokens):
        """去除停用词"""
        return [t for t in tokens if t not in self.stopwords]

    def word_frequency(self, tokens):
        """词频统计"""
        return Counter(tokens)

    def process(self, text):
        """完整处理流程"""
        print(f"原始文本: {text[:50]}...")  # 切片操作,取前n个元素

        # Step 1: 清洗
        cleaned = self.clean_text(text)
        print(f"清洗后: {cleaned[:50]}...")

        # Step 2: 分词
        tokens = self.simple_tokenize(cleaned)
        print(f"分词结果(前10): {tokens[:10]}")

        # Step 3: 去停用词
        filtered = self.remove_stopwords(tokens)
        print(f"去停用词后(前10): {filtered[:10]}")

        # Step 4: 词频统计
        freq = self.word_frequency(filtered)
        print(f"高频词Top10: {freq.most_common(10)}")

        return {
            "cleaned_text": cleaned,
            "tokens": tokens,
            "filtered_tokens": filtered,
            "word_frequency": freq,
        }

# 测试
pipeline = SimpleNLPPipeline()
text = """自然语言处理是人工智能领域中最重要的研究方向之一。
自然语言处理的目标是让计算机能够理解和生成人类语言。
近年来,随着深度学习技术的发展,自然语言处理取得了巨大进步。
特别是Transformer架构的提出,彻底改变了自然语言处理的研究范式。"""

result = pipeline.process(text)

8.2 实验二:情感分析初体验

Python
"""
实验二:基于关键词的简单情感分析
"""

class SimpleSentimentAnalyzer:
    """基于词典的情感分析(baseline方法)"""

    def __init__(self):
        self.positive_words = set([
            "好", "棒", "美", "喜欢", "开心", "满意", "优秀",
            "推荐", "不错", "赞", "精彩", "完美", "出色",
            "厉害", "高兴", "快乐", "感动", "温暖", "惊喜",
            "有趣", "好看", "好听", "好吃", "舒服", "方便",
        ])

        self.negative_words = set([
            "差", "烂", "讨厌", "失望", "难过", "糟糕", "垃圾",
            "难看", "难吃", "无聊", "坑", "恶心", "后悔",
            "浪费", "差劲", "不好", "难受", "生气", "愤怒",
            "可怕", "丑", "慢", "贵", "假", "骗",
        ])

        self.negation_words = set([
            "不", "没", "无", "别", "莫", "非", "未",
        ])

    def analyze(self, text):
        """分析文本情感"""
        positive_count = 0
        negative_count = 0

        chars = list(text)
        for i, char in enumerate(chars):
            # 检查是否有否定词在前面
            is_negated = False
            if i > 0 and chars[i-1] in self.negation_words:
                is_negated = True

            if char in self.positive_words:
                if is_negated:
                    negative_count += 1
                else:
                    positive_count += 1
            elif char in self.negative_words:
                if is_negated:
                    positive_count += 1
                else:
                    negative_count += 1

        # 也检查双字词
        for i in range(len(chars) - 1):
            word = chars[i] + chars[i+1]
            if word in self.positive_words:
                positive_count += 1
            elif word in self.negative_words:
                negative_count += 1

        # 判断情感
        if positive_count > negative_count:
            sentiment = "正面 😊"
        elif negative_count > positive_count:
            sentiment = "负面 😞"
        else:
            sentiment = "中性 😐"

        return {
            "sentiment": sentiment,
            "positive_count": positive_count,
            "negative_count": negative_count,
            "score": positive_count - negative_count,
        }

# 测试
analyzer = SimpleSentimentAnalyzer()
test_texts = [
    "这部电影太精彩了,强烈推荐!",
    "服务态度太差了,非常失望",
    "产品质量还行,价格有点贵",
    "今天心情不错,天气也很好",
    "这个餐厅不好吃,也不便宜",
]

for text in test_texts:
    result = analyzer.analyze(text)
    print(f"文本: {text}")
    print(f"  情感: {result['sentiment']} (分数: {result['score']})")
    print()

8.3 实验三:文本相似度计算

Python
"""
实验三:基于词袋模型的文本相似度计算
"""
import math
from collections import Counter

def text_to_vector(text):
    """将文本转为词频向量"""
    words = list(text)  # 简单的按字分割
    return Counter(words)

def cosine_similarity(vec1, vec2):
    """计算余弦相似度"""
    # 获取所有词
    all_words = set(vec1.keys()) | set(vec2.keys())

    # 计算点积
    dot_product = sum(vec1.get(w, 0) * vec2.get(w, 0) for w in all_words)

    # 计算模长
    norm1 = math.sqrt(sum(v**2 for v in vec1.values()))
    norm2 = math.sqrt(sum(v**2 for v in vec2.values()))

    if norm1 == 0 or norm2 == 0:
        return 0.0

    return dot_product / (norm1 * norm2)

# 测试文本相似度
texts = [
    "自然语言处理是人工智能的重要分支",
    "自然语言处理属于人工智能领域",
    "深度学习推动了计算机视觉的发展",
    "今天天气很好适合出去玩",
]

print("文本相似度矩阵:")
print("-" * 60)
for i in range(len(texts)):
    for j in range(len(texts)):
        vec_i = text_to_vector(texts[i])
        vec_j = text_to_vector(texts[j])
        sim = cosine_similarity(vec_i, vec_j)
        print(f"{sim:.3f}", end="  ")
    print()

print()
print("文本内容:")
for i, t in enumerate(texts):
    print(f"  [{i}] {t}")

9. 面试要点

🔑 面试高频考点

考点1:NLP的核心挑战是什么?

Text Only
✅ 标准答案要点:
1. 歧义性:词汇歧义、句法歧义、语义歧义
2. 多样性:同一语义可以有多种表达
3. 上下文依赖:代词消解、省略恢复
4. 知识依赖:需要世界知识和常识推理
5. 数据稀缺:低资源语言和领域的数据不足
6. 评估困难:生成任务的质量难以自动评估

考点2:NLP的发展经历了哪些阶段?

Text Only
✅ 标准答案要点:
1. 规则时代(1950s-1989):手写规则和模板
2. 统计方法时代(1990s-2012):HMM、CRF、SVM
3. 深度学习时代(2013-2017):Word2Vec、CNN、RNN、Transformer
4. 预训练模型时代(2018-2022):BERT、GPT、T5
5. 大模型时代(2023-今):ChatGPT、RLHF、Agent

关键转折点:Transformer(2017)和BERT(2018)

考点3:Precision和Recall的区别和trade-off?

Text Only
✅ 标准答案要点:
- Precision(精确率):预测为正的样本中真正为正的比例
- Recall(召回率):所有正样本中被正确预测的比例
- Trade-off:提高Precision通常会降低Recall,反之亦然
- 应用选择:
  - 垃圾邮件过滤:倾向高Precision(不要误伤正常邮件)
  - 疾病诊断:倾向高Recall(不要漏诊)
  - 平衡考虑:使用F1-Score

考点4:BLEU和ROUGE的区别?

Text Only
✅ 标准答案要点:
- BLEU:面向精确率的指标,主要用于机器翻译
  - 衡量候选译文中n-gram在参考译文中出现的比例
  - 注重"生成的有多少是对的"

- ROUGE:面向召回率的指标,主要用于文本摘要
  - 衡量参考摘要中n-gram在候选摘要中出现的比例
  - 注重"参考答案中有多少被覆盖"

考点5:中文NLP和英文NLP的主要区别?

Text Only
✅ 标准答案要点:
1. 分词:中文需要专门的分词步骤
2. 字符集:中文字符集远大于英文(几万 vs 26)
3. 形态学:英文有词形变化(时态、复数),中文没有
4. 句法:语序差异,中文主语可省略
5. 资源:英文NLP资源和工具更丰富
6. 预训练:中文预训练模型(ERNIE、MacBERT)考虑了中文特点

10. 练习题

📝 基础题

  1. 选择题:以下哪项不是NLP的基本任务?
  2. A. 词性标注
  3. B. 图像分割
  4. C. 命名实体识别
  5. D. 机器翻译

答案:B。图像分割属于计算机视觉(CV)任务,不属于NLP的基本任务。词性标注、命名实体识别和机器翻译都是经典的NLP任务。

  1. 简答题:请列举5种NLP的实际应用场景,并简述每种场景使用了哪些NLP技术。

答案:①智能客服/对话系统:使用意图识别、槽填充、对话管理等技术;②搜索引擎:使用查询理解、文本匹配、语义检索等技术;③机器翻译(如Google翻译):使用Seq2Seq、Transformer、注意力机制等技术;④情感分析/舆情监控:使用文本分类、情感词典、深度学习分类模型;⑤智能写作/文本生成(如AI摘要、辅助写作):使用语言模型、Seq2Seq、Prompt Engineering等技术。

  1. 简答题:解释词汇歧义和句法歧义的区别,各举一个中文的例子。

答案词汇歧义指同一个词有多种含义,需根据上下文消歧。例如"苹果"可指水果,也可指苹果公司。句法歧义指同一个句子可以有不同的句法结构和解读。例如"咬死了猎人的狗"可理解为"狗咬死了猎人"或"猎人的狗被咬死了",两种句法分析都合法但语义不同。

💻 编程题

  1. 编程题:修改ELIZA对话系统代码,增加至少5条新的模式匹配规则,使其能够处理更多类型的对话。

  2. 编程题:实现一个简单的文本分类器,使用词频统计作为特征,对以下类别进行分类:体育、科技、娱乐。

  3. 编程题:编写一个函数,计算两段中文文本的Jaccard相似度。

Python
def jaccard_similarity(text1, text2):
    """
    计算两段文本的Jaccard相似度
    提示:Jaccard = |A ∩ B| / |A ∪ B|
    """
    # 你的代码
    pass

# 测试
text1 = "自然语言处理是人工智能的核心"
text2 = "自然语言处理是人工智能的重要分支"
print(f"Jaccard相似度: {jaccard_similarity(text1, text2)}")

🔬 思考题

  1. 思考题:在大模型(如ChatGPT)时代,传统NLP任务(如分词、NER、文本分类)是否还有研究和工程价值?请给出你的分析。

答案:仍有重要价值。①工程价值:大模型推理成本高、延迟大,在高并发/低延迟场景(如搜索引擎分词、实时NER)中,轻量级专用模型更实用;②研究价值:传统任务是理解语言的基础,为大模型提供评测基准和训练数据;③互补关系:大模型擅长通用理解,但在垂直领域、低资源语言、边缘部署等场景,微调的小模型仍是最佳选择;④可解释性:传统模型结构清晰,更容易解释和调试。

  1. 思考题:如果要选择一个NLP方向深入研究(信息抽取、对话系统、文本生成),你会选哪个?为什么?

答案(参考):三个方向各有前景。文本生成是当前最热门的方向,大模型的核心能力就是生成,研究空间广阔(可控生成、幻觉消除、RLHF等)。对话系统应用场景最广(客服、助手、Agent),且与大模型结合最紧密,是产品化最直接的方向。信息抽取在知识图谱、数据治理、行业应用(金融、医疗、法律)中有刚需,且结构化输出是大模型的薄弱环节。建议根据个人兴趣和职业规划选择。


✅ 自我检查清单

Text Only
□ 我能用自己的话解释什么是NLP
□ 我知道NLP的5个发展阶段和关键转折点
□ 我能列举至少8种NLP核心任务
□ 我理解NLP面临的主要挑战(歧义性、多样性等)
□ 我能区分Precision、Recall和F1-Score
□ 我知道BLEU和ROUGE分别用于什么任务
□ 我能运行本章的所有代码示例
□ 我完成了至少4道练习题

📚 延伸阅读

  1. Jurafsky & Martin - Speech and Language Processing (3rd edition)
  2. Stanford CS224N 课程主页
  3. ACL Anthology - NLP顶会论文库
  4. NLP Progress - NLP各任务SOTA追踪
  5. Papers With Code - NLP

下一篇02-文本预处理 — 掌握NLP的第一步:数据清洗与预处理