【大模型教程——第一部分:大语言模型基础】第1章:初识大语言模型

0 阅读17分钟

第1章:初识大语言模型

"The best way to predict the future is to invent it." — Alan Kay, 计算机科学家

本章承诺:带你穿越NLP发展史,理解为什么我们需要大语言模型,以及它们如何从"词袋"进化到"大脑"。


目录


引言:穿越NLP发展史

想象一下,你是一个从未接触过语言的外星人,突然被投放到地球。你看到人类用奇怪的符号(文字)交流,发出各种声音(语言)。你的任务是:理解并使用这些符号和声音

这就是自然语言处理(NLP)面临的核心挑战:如何让计算机理解人类语言

在过去的几十年里,人类尝试了各种方法:

  • 1950-1990年代:基于规则的方法(专家系统)
  • 1990-2010年代:统计方法(词袋模型、n-gram)
  • 2013-2017年:词嵌入时代(Word2Vec、GloVe)
  • 2017-2020年:Transformer革命(BERT、GPT)
  • 2020年至今:大语言模型时代(GPT-3/4、Claude、ChatGPT)

让我们一起回到起点,看看这段激动人心的演化历程。


一、一段简史:从"词袋"到"大脑"

词袋模型的局限

在深度学习出现之前,NLP领域最常用的方法是词袋模型(Bag of Words, BoW)。

核心思想:把文本看作一个"词的袋子",只关心词出现的频率,不关心词的顺序。

举个例子
句子1: "我爱自然语言处理"
句子2: "自然语言处理爱我"

# 词袋表示(词频统计)
句子1: {"我": 1, "爱": 1, "自然": 1, "语言": 1, "处理": 1}
句子2: {"自然": 1, "语言": 1, "处理": 1, "爱": 1, "我": 1}

# 结果:两个句子完全相同!❌

问题显而易见

  1. 丢失了词序信息:"我爱你" vs "你爱我" 完全不同
  2. 无法捕捉语义:"国王" 和 "女王" 的关系无法表示
  3. 维度灾难:词表有10万个词,每个句子都是10万维的稀疏向量

为什么需要更好的表示

问题:如何让计算机理解"国王"和"女王"的关系?

在词袋模型中:

"国王" → [0, 0, 0, ..., 1, ..., 0]  # 第34567个位置是1
"女王" → [0, 0, 0, ..., 1, ..., 0]  # 第78901个位置是1

# 这两个向量完全没有关系!

理想的表示

"国王" → [0.8, 0.2, 0.9, ...]  # 密集向量
"女王" → [0.7, 0.3, 0.9, ...]  # 相似的向量

# 可以计算相似度:
similarity("国王", "女王") = 0.85  # 很相似!

这就是词嵌入要解决的问题。


二、词嵌入:让计算机理解"国王-男人=女王"

分布式假设:物以类聚,词以群分

词嵌入的理论基础是分布式假设(Distributional Hypothesis):

"You shall know a word by the company it keeps." "一个词的含义由它的上下文决定。" — J.R. Firth, 语言学家(1957)

直觉理解

  • "国王"经常和"王冠、宫殿、统治"一起出现
  • "女王"经常和"王冠、宫殿、统治"一起出现
  • "猫"经常和"喵喵、爪子、宠物"一起出现

结论:上下文相似的词,语义也相似。


Word2Vec:神奇的词向量

Word2Vec(2013年,Google)是词嵌入时代的里程碑。

核心思想

训练目标:给定一个词,预测它的上下文词(或反过来)。

两种训练方式:

  1. Skip-gram:用中心词预测上下文
  2. CBOW:用上下文预测中心词
Skip-gram 图示
graph LR
    A[上下文窗口] --> B[中心词: 国王]
    B --> C[预测: 王冠]
    B --> D[预测: 宫殿]
    B --> E[预测: 统治]

    style B fill:#e1f5ff
    style C fill:#d4edda
    style D fill:#d4edda
    style E fill:#d4edda
训练过程示例
# 训练语料
句子: "国王坐在华丽的王座上"

# 窗口大小=2,中心词="国王"
上下文: ["坐在", "华丽"]

# 训练目标
输入: "国王"的向量
输出: 预测"坐在""华丽"的概率要高

# 经过数百万次训练后
"国王"的向量 → [0.25, 0.78, -0.45, 0.12, ...]
"女王"的向量 → [0.27, 0.75, -0.43, 0.15, ...]
# 两个向量非常相似!

神奇的类比能力

经过训练后,Word2Vec能学到惊人的语义关系:

# 向量运算
"国王" - "男人" + "女人""女王"

# 实际数值(简化版)
king = [0.8, 0.2, 0.9, 0.1]
man = [0.5, 0.1, 0.2, 0.1]
woman = [0.5, 0.1, 0.2, 0.8]

king - man + woman = [0.8, 0.2, 0.9, 0.8]
# 最接近的词就是 "queen"!

# 更多例子
"巴黎" - "法国" + "德国""柏林"
"走" - "走了" + "去""去了"

为什么会这样?

因为模型从语料中学到了:

  • "国王"和"女王"的关系 = "男人"和"女人"的关系
  • 这些关系都编码在向量空间的几何结构中
graph TD
    A[国王] -->|性别转换| B[女王]
    C[男人] -->|性别转换| D[女人]

    A -->|去掉性别| C
    B -->|去掉性别| D

    style A fill:#e1f5ff
    style B fill:#d4edda
    style C fill:#fff3cd
    style D fill:#ffe6e6

词嵌入的局限性

尽管Word2Vec非常强大,但它有一个致命缺陷:一词一义

问题示例
句子1: "我去银行存钱"
句子2: "我在河岸边散步"

# Word2Vec中,"银行"只有一个向量
"银行" → [0.3, 0.5, -0.2, ...]

# 问题:
# - 句子1中的"银行"是金融机构
# - 句子2中的"银行"是河岸(英文都是"bank")
# - 但它们的向量表示完全相同!

根本原因:Word2Vec是静态嵌入,每个词只有一个固定的向量,无法根据上下文动态调整。

我们需要什么?

  • 上下文相关的表示:同一个词在不同句子中有不同的向量
  • 这就是Transformer要解决的问题

三、Transformer革命:从"读死书"到"举一反三"

RNN的困境:梯度消失

在Transformer之前,NLP主要使用循环神经网络(RNN)处理序列数据。

RNN的工作方式
graph LR
    A[我] --> B[爱]
    B --> C[自然]
    C --> D[语言]
    D --> E[处理]

    A -.->|隐藏状态| B
    B -.->|隐藏状态| C
    C -.->|隐藏状态| D
    D -.->|隐藏状态| E

    style A fill:#e1f5ff
    style E fill:#d4edda

问题:处理长句子时,早期的信息会逐渐"消失"。

句子: "我昨天去超市买了很多东西,包括牛奶、面包、鸡蛋,
      然后我回家把它们放进冰箱,之后我发现【】忘记带了。"

# RNN处理到【】时:
# - "我"、"昨天"、"超市"的信息已经模糊
# - 很难准确填空

这就是著名的"梯度消失"问题


Self-Attention:理解上下文的艺术

Transformer的核心创新:Self-Attention(自注意力机制)。

直觉理解
句子: "我去银行存钱"

# Self-Attention会计算每个词之间的关联度
"银行" 关注:
  - "存钱" → 高权重(0.8)
  - "我" → 中等权重(0.3)
  - "去" → 低权重(0.1# 结果:"银行"的向量会融合"存钱"的信息
# → 模型理解这里是"金融机构"

---

句子: "我在河岸边散步"

# Self-Attention会重新计算
"银行" 关注:
  - "河" → 高权重(0.7)
  - "散步" → 中等权重(0.4)
  - "在" → 低权重(0.2# 结果:"银行"的向量会融合"河"的信息
# → 模型理解这里是"河岸"

可视化

graph TD
    A[我去银行存钱] --> B{Self-Attention}
    B --> C[银行 + 存钱信息]
    C --> D[理解为金融机构]

    E[我在河岸边散步] --> F{Self-Attention}
    F --> G[银行 + 河的信息]
    G --> H[理解为河岸]

    style C fill:#e1f5ff
    style G fill:#d4edda

Self-Attention的优势

对比RNN

维度RNNTransformer
处理长文本❌ 梯度消失✅ 直接关联任意位置
并行计算❌ 必须顺序处理✅ 可以并行
训练速度快(10倍以上)
长距离依赖

关键公式(简化版):

# 给定查询词 Q(如"银行")
# 和所有键 K(句子中的其他词)

# 1. 计算注意力分数
scores = Q · K  # 点积

# 2. 归一化为概率
attention_weights = softmax(scores)

# 3. 加权求和值 V
output = attention_weights · V

# 结果:output 是融合了上下文的新向量

Encoder vs Decoder:两种思维方式

Transformer有两种架构:

1. Encoder(编码器)

目标:理解输入文本的含义

graph TD
    A[输入: 我爱NLP] --> B[Encoder]
    B --> C[双向Self-Attention]
    C --> D[输出: 上下文向量]

    D --> E[下游任务]
    E --> F[文本分类]
    E --> G[命名实体识别]

    style B fill:#e1f5ff
    style D fill:#d4edda

特点

  • 双向注意力:每个词可以看到前后所有词
  • 擅长理解:适合分类、提取任务

2. Decoder(解码器)

目标:根据已有信息生成下一个词

graph TD
    A[输入: 今天天气] --> B[Decoder]
    B --> C[单向Self-Attention]
    C --> D[预测下一个词]

    D --> E[可能输出]
    E --> F[很好 30%]
    E --> G[不错 25%]
    E --> H[真好 20%]

    style B fill:#e1f5ff
    style F fill:#d4edda

特点

  • 单向注意力(因果注意力):只能看到当前词之前的内容
  • 擅长生成:适合续写、翻译、对话

为什么不能双向?

# 假设使用双向注意力生成文本
输入: "今天天气"
任务: 预测下一个词

# 如果能看到未来:
模型看到: "今天天气【很好】,我们去..."
# 答案已经泄露了!这是作弊!

# 单向注意力(遮挡未来):
模型只看到: "今天天气【?】"
# 必须根据"今天天气"推理下一个词

四、认识两大模型家族

BERT:双向理解的大师

BERT (Bidirectional Encoder Representations from Transformers, 2018)

架构
graph TD
    A[输入文本] --> B[Token Embedding]
    B --> C[Encoder层 x 12]
    C --> D[输出向量]

    D --> E[任务1: 完形填空MLM]
    D --> F[任务2: 句子关系NSP]

    style C fill:#e1f5ff
    style D fill:#d4edda
预训练任务

1. 完形填空(Masked Language Modeling, MLM)

原句: "我爱自然语言处理"
遮挡: "我爱[MASK]语言处理"

# BERT任务:预测[MASK]是什么
模型预测:
  - "自然"85%  ✅
  - "人工"10%
  - "编程"3%

2. 句子关系判断(Next Sentence Prediction, NSP)

句子A: "今天天气很好"
句子B: "我们去公园吧"
标签: 相关 ✅

句子A: "今天天气很好"
句子B: "量子力学很复杂"
标签: 不相关 ❌
适用场景
# 文本分类
输入: "这部电影太棒了!"
BERT: [情感分析] → 正面 ✅

# 命名实体识别
输入: "乔布斯创立了苹果公司"
BERT: [实体提取] → "乔布斯"(人名), "苹果公司"(组织)

# 问答系统
输入: "问题: 谁发明了电灯?上下文: 爱迪生在1879年发明了电灯。"
BERT: [答案提取] → "爱迪生"

GPT:生成式的魔法师

GPT (Generative Pre-trained Transformer, 2018-2023)

架构
graph TD
    A[输入文本] --> B[Token Embedding]
    B --> C[Decoder层 x 96]
    C --> D[预测下一个词]

    D --> E[采样生成]
    E --> F[输出文本]

    style C fill:#e1f5ff
    style F fill:#d4edda
预训练任务

自回归语言模型(Autoregressive Language Modeling)

输入: "我爱自然语言"
任务: 预测下一个词

# GPT学习:
P("处理" | "我爱自然语言") = 0.65  # 高概率
P("规律" | "我爱自然语言") = 0.10
P("科学" | "我爱自然语言") = 0.08
演化历程
模型年份参数量核心能力
GPT-12018117M基础文本生成
GPT-220191.5B零样本学习
GPT-32020175B上下文学习(ICL)
GPT-3.52022175B指令跟随(ChatGPT)
GPT-42023~1.8T多模态、推理增强

两者的区别与适用场景

对比表

维度BERT (Encoder)GPT (Decoder)
注意力方向双向单向(因果)
训练目标完形填空 + 句子关系预测下一个词
擅长任务理解、分类、提取生成、对话、续写
典型应用搜索、问答、NERChatGPT、代码生成
参数量<1B>100B
涌现能力强(思维链、工具调用)

为什么GPT胜出?

  1. 生成任务更通用:续写可以变成翻译、摘要、问答
  2. 扩展性更好:Decoder架构更适合扩展到超大规模
  3. 涌现能力:参数量超过100B后,出现思维链推理、工具调用等"魔法"

五、动手实践:与大模型对话

现在,让我们用DeepSeek(国产大模型)实际体验大模型的能力。

实战一:文本生成(流式输出)

为什么需要流式输出?

# 非流式:用户等待10秒
response = client.chat.completions.create(
    messages=[{"role": "user", "content": "写一篇500字文章"}]
)
print(response.choices[0].message.content)
# 10秒后一次性显示全部 ⏳

# 流式:像ChatGPT一样逐字显示
# 用户体验更好 ✅

代码实现
from openai import OpenAI

# 配置DeepSeek API
client = OpenAI(
    api_key="sk-your-api-key",  # 替换为你的API密钥
    base_url="https://api.deepseek.com"
)

# 流式生成
stream = client.chat.completions.create(
    model="deepseek-chat",
    messages=[
        {"role": "user", "content": "给我讲一个关于Transformer的故事"}
    ],
    stream=True,  # 启用流式输出
    temperature=0.7
)

print("AI回复:", end="", flush=True)

# 逐块接收内容
for chunk in stream:
    if chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="", flush=True)

print()  # 换行

运行效果

AI回复:从前,有一个神经网络叫RNN,它每天辛苦地处理文本...(逐字显示)

高级:带打字机效果
import time

def typewriter_effect(text, delay=0.03):
    """模拟打字机效果"""
    for char in text:
        print(char, end="", flush=True)
        time.sleep(delay)

# 流式 + 打字机
stream = client.chat.completions.create(
    model="deepseek-chat",
    messages=[{"role": "user", "content": "解释Self-Attention"}],
    stream=True
)

for chunk in stream:
    if chunk.choices[0].delta.content:
        typewriter_effect(chunk.choices[0].delta.content)

实战二:文本分类(情感分析)

任务:判断电影评论是正面还是负面。

方法1:零样本分类
def sentiment_analysis(text):
    """情感分析"""
    messages = [
        {
            "role": "system",
            "content": "你是一个情感分析专家。分析文本情感,只回答'正面'或'负面'。"
        },
        {
            "role": "user",
            "content": f"分析以下评论的情感:\n{text}"
        }
    ]

    response = client.chat.completions.create(
        model="deepseek-chat",
        messages=messages,
        temperature=0  # 确定性输出
    )

    return response.choices[0].message.content

# 测试
reviews = [
    "这部电影太棒了!演员演技精湛,剧情引人入胜。",
    "浪费时间,剧情拖沓,演技尴尬。",
    "还可以,有些地方不错,但整体一般。"
]

for review in reviews:
    sentiment = sentiment_analysis(review)
    print(f"评论: {review}")
    print(f"情感: {sentiment}\n")

输出

评论: 这部电影太棒了!演员演技精湛,剧情引人入胜。
情感: 正面

评论: 浪费时间,剧情拖沓,演技尴尬。
情感: 负面

评论: 还可以,有些地方不错,但整体一般。
情感: 中性

方法2:少样本学习(Few-shot)
def few_shot_sentiment(text):
    """少样本情感分析"""
    messages = [
        {
            "role": "system",
            "content": "你是情感分析专家。"
        },
        {
            "role": "user",
            "content": """示例:
评论: "太好看了!强烈推荐。"
情感: 正面

评论: "无聊透顶,不建议观看。"
情感: 负面

评论: "画面精美,但剧情一般。"
情感: 中性

---

现在分析以下评论:
评论: "{}"
情感:""".format(text)
        }
    ]

    response = client.chat.completions.create(
        model="deepseek-chat",
        messages=messages,
        temperature=0
    )

    return response.choices[0].message.content

# 测试
result = few_shot_sentiment("特效炸裂,但故事太弱了。")
print(result)  # 输出: 中性

实战三:Token计数与成本估算

为什么关心Token数?

  1. 计费单位:API按Token收费
  2. 上下文限制:模型有最大Token窗口(如128K)
  3. 速度影响:Token越多,推理越慢

Token计数
import tiktoken

# 加载Tokenizer
encoding = tiktoken.get_encoding("cl100k_base")

# 计算Token数
text = "Transformer彻底改变了NLP领域"
tokens = encoding.encode(text)

print(f"文本: {text}")
print(f"Token IDs: {tokens}")
print(f"Token数: {len(tokens)}")

# 输出:
# Token IDs: [65387, 55040, 104762, 57095, 95885, 51343]
# Token数: 6

成本估算
def estimate_cost(messages, model="deepseek-chat"):
    """估算对话成本"""
    encoding = tiktoken.get_encoding("cl100k_base")

    # 计算输入Token
    input_tokens = 0
    for msg in messages:
        input_tokens += len(encoding.encode(msg["content"]))

    # 假设输出200 tokens
    output_tokens = 200

    # DeepSeek参考定价
    input_price = 0.001 / 1000   # ¥0.001每1K tokens
    output_price = 0.002 / 1000  # ¥0.002每1K tokens

    cost = input_tokens * input_price + output_tokens * output_price

    print(f"输入Token: {input_tokens}")
    print(f"预计输出Token: {output_tokens}")
    print(f"预计成本: ¥{cost:.6f}")

    return cost

# 示例
messages = [
    {"role": "system", "content": "你是Python专家。"},
    {"role": "user", "content": "如何实现快速排序?"}
]

estimate_cost(messages)

# 输出:
# 输入Token: 18
# 预计输出Token: 200
# 预计成本: ¥0.000418

上下文窗口管理
def trim_messages(messages, max_tokens=4000):
    """裁剪消息历史以适应上下文窗口"""
    encoding = tiktoken.get_encoding("cl100k_base")

    # 保留system消息
    system_msg = messages[0] if messages[0]["role"] == "system" else None
    chat_history = messages[1:] if system_msg else messages

    total_tokens = 0
    trimmed_history = []

    # 从最新消息往前计算
    for msg in reversed(chat_history):
        msg_tokens = len(encoding.encode(msg["content"]))
        if total_tokens + msg_tokens > max_tokens:
            break
        trimmed_history.insert(0, msg)
        total_tokens += msg_tokens

    # 重新组合
    result = [system_msg] + trimmed_history if system_msg else trimmed_history

    print(f"原始消息数: {len(messages)}")
    print(f"裁剪后消息数: {len(result)}")
    print(f"总Token数: {total_tokens}")

    return result

六、新手问答

Q1: BERT和GPT到底有什么区别?

简单记忆法

BERT = 完形填空高手(双向理解)
GPT = 作文写作能手(单向生成)

深入对比

维度BERTGPT
训练方式遮挡词,预测被遮挡的根据前文,预测下一个词
注意力双向(能看到前后文)单向(只能看到前文)
擅长理解、分类生成、对话
典型应用搜索引擎、问答系统ChatGPT、代码生成

为什么GPT更火?

  • 生成任务包含了理解任务(续写→翻译、摘要)
  • 扩展到超大规模后,涌现了推理能力

Q2: 为什么Transformer比RNN好?

核心差异

# RNN:顺序处理(串行)
输入: "我 爱 自然 语言 处理"
处理: 我 → 爱 → 自然 → 语言 → 处理
      ↓    ↓     ↓      ↓      ↓
      h1 → h2 → h3 → h4 → h5

# 问题:
# 1. 处理到h5时,h1的信息已经模糊(梯度消失)
# 2. 必须顺序处理,无法并行

---

# Transformer:并行处理
输入: "我 爱 自然 语言 处理"
处理: 所有词同时进入 → Self-Attention
      ↓
      每个词都能直接看到其他所有词

# 优势:
# 1. 没有梯度消失(直接连接)
# 2. 可以并行计算(速度快10倍)

Q3: 什么是"涌现能力"?

定义:模型规模达到某个临界点后,突然出现的新能力。

案例

# 参数量 < 10B
模型: "1 + 1 = ?"
输出: "2"  # 只是记忆

模型: "小明有5个苹果,给了小红2个,还剩几个?"
输出: "3个苹果"  # 错误或不稳定

---

# 参数量 > 100B (如GPT-3)
模型: "1 + 1 = ?"
输出: "2"

模型: "小明有5个苹果,给了小红2个,还剩几个?"
输出: "让我逐步思考:
       初始:5个
       给出:2个
       剩余:5 - 2 = 3个
       答案:还剩3个苹果。"  # 涌现了思维链推理!

涌现能力包括

  • 思维链推理(Chain-of-Thought)
  • 上下文学习(In-Context Learning)
  • 工具调用(Function Calling)

Q4: 为什么需要这么大的模型?

Scaling Law(缩放定律):

模型性能 ∝ 参数量^0.73 × 训练数据量^0.28

数据说明

模型参数量能力
GPT-21.5B基础续写
GPT-3175B零样本学习
GPT-4~1.8T复杂推理

类比

  • 小模型 = 小学生:只能做简单题
  • 大模型 = 博士:能处理复杂推理

但也有代价

  • 训练成本:GPT-3训练费用 ~$5,000,000
  • 推理速度:70B模型比7B慢10倍
  • 部署成本:需要高端GPU

Q5: 我应该学BERT还是GPT?

建议

当前建议:优先学GPT系列

原因

  1. 应用更广:ChatGPT、Claude、文心一言都是GPT架构
  2. 更易上手:只需调用API,无需训练模型
  3. 生态更好:有LangChain、LlamaIndex等丰富工具链

学习路径

graph TD
    A[第1章: 理解Transformer基础] --> B[第2章: 提示工程Prompt]
    B --> C[第3章: 向量与嵌入]
    C --> D[第4章: RAG检索增强]
    D --> E[第5章: Agent应用]

    style A fill:#e1f5ff
    style E fill:#d4edda

七、本章小结

核心收获

  1. NLP演化史

    • 词袋模型 → 只看词频,丢失语序
    • Word2Vec → 学会语义,但一词一义
    • Transformer → 上下文相关,动态表示
  2. Transformer核心

    • Self-Attention → 理解上下文
    • Encoder → 双向理解(BERT)
    • Decoder → 单向生成(GPT)
  3. 两大家族

    • BERT = 理解大师(分类、提取)
    • GPT = 生成魔法师(对话、续写)
  4. 实战能力

    • 流式输出 → 提升用户体验
    • 情感分析 → 零样本/少样本学习
    • Token管理 → 控制成本

思维导图

mindmap
  root((大语言模型))
    历史演进
      词袋模型
        丢失词序
        维度灾难
      Word2Vec
        分布式假设
        一词一义局限
      Transformer
        Self-Attention
        并行计算
    两大家族
      BERT编码器
        双向理解
        完形填空
        理解任务
      GPT解码器
        单向生成
        自回归
        生成任务
    核心技术
      Self-Attention
        上下文相关
        解决歧义
      Token化
        子词单元
        平衡效率
    实战应用
      文本生成
      文本分类
      成本管理

下一章预告

在**第2章《与模型对话:提示工程基础》**中,我们将深入探讨:

  1. 零样本 vs 少样本学习:如何让模型"举一反三"
  2. 思维链(Chain-of-Thought):为什么"逐步思考"能提升准确率
  3. 提示词工程:如何设计高质量Prompt
  4. 温度与采样策略:精确控制模型输出

核心问题

"如何让LLM从'能用'到'好用'?"