核心概念一网打尽
上两篇咱们聊了大模型的基础知识和工作原理。这篇来系统梳理一下大模型开发中会频繁遇到的核心概念。
这些概念不是面试背题用的,而是真的会影响你的开发。比如:
- 不懂 Token,你就不知道为什么 API 有时候返回的内容被截断了
- 不懂上下文窗口,你就不知道为什么模型"忘记"了之前的对话
- 不懂 Temperature,你就不知道为什么同样的问题每次回答都不一样
- 不懂 MoE,你就不知道为什么 DeepSeek 这么便宜还这么厉害
搞清楚这些概念,你才能更好地控制模型的行为,写出更有效的 Prompt,做出更合理的技术选型。
Token:大模型眼中的"文字"
什么是 Token
Token 是大模型处理文本的最小单位。
你可能以为大模型是一个字一个字、或者一个词一个词处理的。实际上不是。大模型处理的是 Token——一种介于字和词之间的单位。
举个例子:
| 原文 | 分词结果(Token) |
|---|---|
| 你好世界 | 你好 / 世界 |
| Hello World | Hello / ␣World |
| programming | program / ming |
| ChatGPT | Chat / G / PT |
| 人工智能 | 人工 / 智能 |
| 今天天气不错 | 今天 / 天气 / 不错 |
| function | function |
| beautiful | beaut / iful |
可以看到:
- 常见的词往往是一个 Token
- 不常见的词可能被拆成多个 Token
- 英文单词可能被拆成词根和后缀
- 空格通常会被算进 Token 里
为什么要用 Token 而不是字/词
为什么不用"字"作为单位?
如果用字符作为单位,英文就是 26 个字母加一些标点。词表太小,模型需要很长的序列才能表达一个词,效率太低。
比如 "programming" 需要 11 个字符,而作为 Token 只需要 2 个(program + ming)。
对于中文,如果用单字作为单位,词表就是几千个汉字。虽然可行,但会丢失很多词语级别的语义信息。"人工智能"被拆成"人"、"工"、"智"、"能"四个字,模型需要花额外的努力去理解这是一个整体。
为什么不用"词"作为单位?
如果用词作为单位,英文可能需要几十万个词(加上各种变体、专有名词),词表太大,而且会遇到大量"未登录词"(词表里没有的词)。
遇到新词怎么办?比如 "ChatGPT" 这个词,2022 年之前根本不存在。如果用词表,就只能当作未知词处理。
Token 是一个折中方案
Token 采用 子词分词(Subword Tokenization) 的方式,把常见词保持原样,把不常见的词拆成更小的单位。
最常用的子词分词算法是 BPE(Byte Pair Encoding,字节对编码)。它的基本思想是:
- 从最小单位(字符或字节)开始
- 统计训练语料中哪两个相邻单位最常一起出现
- 把最常见的组合合并成一个新单位
- 重复步骤 2-3,直到词表达到指定大小
通过这种方式,BPE 能自动学会:
- 高频词保持完整(如 "the"、"is"、"今天")
- 低频词被拆分(如 "programming" → "program" + "ming")
- 新词也能处理(通过组合已有的子词)
BPE 算法详解
为了让你更直观地理解 BPE,咱们用一个简化的例子来走一遍流程。
假设我们的训练语料只有这些词及其出现频率:
low: 5次
lower: 2次
newest: 6次
widest: 3次
第 1 步:初始化
把每个词拆成字符,加上词尾标记 </w>:
l o w </w>: 5
l o w e r </w>: 2
n e w e s t </w>: 6
w i d e s t </w>: 3
初始词表是所有字符:{l, o, w, e, r, n, s, t, i, d, </w>}
第 2 步:统计相邻字符对
统计所有相邻字符对的出现频率:
e s: 出现 6+3=9 次(newest 和 widest 都有)s t: 出现 6+3=9 次lo: 出现 5+2=7 次ow: 出现 5+2=7 次- ...
第 3 步:合并最频繁的对
e s 和 s t 出现次数最多。假设我们先合并 e s → es:
l o w </w>: 5
l o w e r </w>: 2
n e w es t </w>: 6
w i d es t </w>: 3
词表变成:{l, o, w, e, r, n, s, t, i, d, </w>, es}
第 4 步:继续合并
现在 es t 出现 9 次,合并 → est:
l o w </w>: 5
l o w e r </w>: 2
n e w est </w>: 6
w i d est </w>: 3
词表变成:{l, o, w, e, r, n, s, t, i, d, </w>, es, est}
第 5 步:重复直到达到目标词表大小
继续合并高频对:
lo→low→low</w>est </w>→est</w>- ...
最终可能得到这样的词表:
{l, o, w, e, r, n, s, t, i, d, </w>, es, est, est</w>, lo, low, low</w>, ...}
BPE 的精妙之处
通过这种方式:
- "low" 变成了一个完整的 Token
- "newest" 被切成 "new" + "est"
- 即使遇到训练时没见过的词,比如 "lowest",也能切成 "low" + "est"
这就是 BPE 能处理未登录词的原因。
Token 和字符数的关系
一个非常实用的问题:1 个 Token 大约等于多少字/字符?
这取决于语言和具体的分词器,但有一些经验值:
| 语言 | 大约比例 |
|---|---|
| 英文 | 1 Token ≈ 4 个字符 ≈ 0.75 个单词 |
| 中文 | 1 Token ≈ 1.5-2 个汉字 |
| 代码 | 1 Token ≈ 3-4 个字符 |
所以:
- 一篇 1000 字的中文文章,大约是 500-700 Token
- 一篇 1000 词的英文文章,大约是 1300-1500 Token
- 一段 100 行的代码,可能是 500-1000 Token
Token 为什么重要
Token 直接关系到两件事:能力边界和使用成本。
能力边界
大模型有一个上下文窗口(后面会详细讲),限制了一次能处理的 Token 数量。比如 GPT-4 Turbo 的上下文窗口是 128K Token,如果你的输入超过这个限制,要么会报错,要么早期的内容会被"遗忘"。
使用成本
API 调用按 Token 计费。输入 Token 和输出 Token 通常分开计价,输出 Token 往往更贵。
以 GPT-4 为例(价格可能已变化,仅作参考):
- 输入:$0.03 / 1K Token
- 输出:$0.06 / 1K Token
如果你的应用每天处理 100 万 Token,光 API 费用就要几十美元。所以优化 Token 使用量是降低成本的重要手段。
实际开发中的 Token 技巧
技巧 1:估算 Token 数量
在发送请求之前,先估算一下 Token 数量,避免超出限制或产生意外的费用。
import tiktoken
# 使用 GPT-4 的分词器
encoder = tiktoken.encoding_for_model("gpt-4")
text = "你好,请帮我写一段Python代码,实现冒泡排序算法。"
tokens = encoder.encode(text)
print(f"Token 数量: {len(tokens)}") # 输出: Token 数量: 约 20+
print(f"Token 列表: {tokens}")
print(f"解码回文本: {encoder.decode(tokens)}")
对于 Java 开发者,可以使用 jtokkit 库:
import com.knuddels.jtokkit.Encodings;
import com.knuddels.jtokkit.api.Encoding;
import com.knuddels.jtokkit.api.EncodingRegistry;
import com.knuddels.jtokkit.api.ModelType;
import java.util.List;
public class TokenCounter {
public static void main(String[] args) {
EncodingRegistry registry = Encodings.newDefaultEncodingRegistry();
Encoding encoding = registry.getEncodingForModel(ModelType.GPT_4);
String text = "你好,请帮我写一段Python代码,实现冒泡排序算法。";
List<Integer> tokens = encoding.encode(text);
System.out.println("Token 数量: " + tokens.size());
}
}
Maven 依赖:
<dependency>
<groupId>com.knuddels</groupId>
<artifactId>jtokkit</artifactId>
<version>0.6.1</version>
</dependency>
技巧 2:优化 Prompt 减少 Token
Token 越少,成本越低,响应也可能更快。但要注意,不能为了省 Token 而牺牲清晰度。
# 啰嗦版(约 50 Token)
请你帮我分析一下下面这段代码有什么问题,如果有问题的话请指出来,
并且告诉我应该怎么修改才能让代码正确运行。
# 精简版(约 20 Token)
分析以下代码的问题并给出修改建议:
技巧 3:控制输出长度
大多数 API 支持 max_tokens 参数,限制输出的最大 Token 数量。
response = client.chat.completions.create(
model="gpt-4",
messages=[{"role": "user", "content": "讲一个笑话"}],
max_tokens=100 # 限制输出最多 100 Token
)
如果不限制,模型可能会生成很长的回答,既浪费 Token 又可能跑题。
技巧 4:监控和分析 Token 使用
大多数 API 会在响应中返回 Token 使用情况:
{
"usage": {
"prompt_tokens": 156,
"completion_tokens": 423,
"total_tokens": 579
}
}
建立监控体系,分析 Token 消耗的分布,找出优化点。
上下文窗口:模型的"工作记忆"
什么是上下文窗口
上下文窗口(Context Window)是大模型一次能"看到"的最大 Token 数量。
你可以把它理解为模型的"工作记忆"。就像人类的短期记忆有容量限制,你没法同时记住一本书的所有内容。大模型也一样,它能同时处理的信息是有限的。
上下文窗口包括了输入和输出的总和。比如一个 8K 上下文的模型:
- 如果你输入了 6K Token,那最多只能输出 2K Token
- 如果你想要 4K 的输出,输入就只能有 4K
主流模型的上下文窗口
不同模型的上下文窗口差异很大:
| 模型 | 上下文窗口 | 大约相当于 |
|---|---|---|
| GPT-3.5 | 4K / 16K | 约 3000 / 12000 汉字 |
| GPT-4 | 8K / 32K / 128K | 约 6000 / 24000 / 96000 汉字 |
| Claude 2 | 100K | 约 75000 汉字 |
| Claude 3 | 200K | 约 150000 汉字(一本小说) |
| Claude 3.5 | 200K | 约 150000 汉字 |
| Qwen2.5 | 8K / 32K / 128K | 取决于具体版本 |
| DeepSeek-V3 | 128K | 约 96000 汉字 |
| Gemini 1.5 Pro | 1M / 2M | 约 75-150 万汉字(几本书) |
可以看到,上下文窗口在快速增长。从早期的 4K 到现在的百万级别,只用了不到两年。
上下文窗口的深入理解
上下文窗口包含什么?
一次完整的对话请求中,占用上下文窗口的内容包括:
[系统提示词 System Prompt] → 几十到几百 Token
[历史对话记录] → 根据对话长度,可能占大头
- 第1轮:用户问题 + 助手回答
- 第2轮:用户问题 + 助手回答
- ...
[当前用户问题] → 几十到几百 Token
[模型生成的回答] → 这也算在窗口内!
一个常见的误区是以为上下文窗口只包含输入,实际上输出也算。
多轮对话的累积效应
大模型本身是没有记忆的。每次 API 调用都是独立的。所谓的"多轮对话",是在每次请求时把之前的对话历史都带上。
{
"messages": [
{"role": "system", "content": "你是一个编程助手"},
{"role": "user", "content": "什么是冒泡排序?"},
{"role": "assistant", "content": "冒泡排序是一种简单的排序算法...(500字)"},
{"role": "user", "content": "能给我写个例子吗?"},
{"role": "assistant", "content": "当然,这是一个Python实现...(300字)"},
{"role": "user", "content": "能改成Java吗?"}
]
}
看到问题了吗?每次请求都要带上所有历史。对话越长,每次请求消耗的 Token 越多,而且是重复付费的。
假设对话了 20 轮,每轮平均 500 Token,那第 20 轮的请求就要带上 10000 Token 的历史——光是"上下文"就要花不少钱。
超出上下文窗口怎么办
方案 1:截断(Truncation)
最简单的方法,超出部分直接丢掉。但可能丢失重要信息。
def truncate_messages(messages, max_tokens=4000, encoder=None):
"""截断历史消息,保留最新的"""
if encoder is None:
encoder = tiktoken.encoding_for_model("gpt-4")
total_tokens = 0
truncated = []
# 倒序遍历,保留最新的消息
for msg in reversed(messages):
msg_tokens = len(encoder.encode(str(msg)))
if total_tokens + msg_tokens > max_tokens:
break
truncated.insert(0, msg)
total_tokens += msg_tokens
return truncated
方案 2:分块处理(Chunking + Map-Reduce)
把长文本分成多个块,每块单独处理,最后汇总结果。
长文档(50000 字)
↓
分成 10 个块(每块 5000 字)
↓
每块单独让模型处理(提取要点/生成摘要)
↓
把 10 个结果汇总
↓
再让模型对汇总结果做最终处理
这种方法叫 Map-Reduce,适合摘要、提取关键信息、分析长文档等任务。
def summarize_long_document(document, chunk_size=3000):
"""对长文档进行分块摘要"""
# 分块
chunks = split_into_chunks(document, chunk_size)
# Map: 对每个块生成摘要
summaries = []
for chunk in chunks:
summary = call_llm(f"请用100字总结以下内容:\n{chunk}")
summaries.append(summary)
# Reduce: 汇总所有摘要
combined = "\n".join(summaries)
final_summary = call_llm(f"请综合以下摘要,给出最终总结:\n{combined}")
return final_summary
方案 3:滑动窗口 + 摘要
保留最近的完整对话,对早期对话做摘要。
def manage_conversation_history(messages, max_recent=5, max_tokens=6000):
"""管理对话历史:保留最近N轮 + 早期摘要"""
if len(messages) <= max_recent * 2: # 用户+助手算两条
return messages
# 分离早期和最近的消息
recent = messages[-(max_recent * 2):]
early = messages[:-(max_recent * 2)]
# 对早期消息做摘要
early_text = format_messages_as_text(early)
summary = call_llm(f"请简要概括以下对话的主要内容:\n{early_text}")
# 返回:摘要 + 最近消息
return [
{"role": "system", "content": f"之前对话摘要:{summary}"},
*recent
]
方案 4:选择更大上下文的模型
如果你的场景确实需要处理长文本,考虑选择上下文窗口更大的模型。虽然可能更贵,但省去了复杂的工程处理。
注意:大上下文 ≠ 用满了也很准
有一个常见的误解:觉得模型支持 128K 上下文,就能完美处理 128K 的内容。
实际上,上下文越长,模型的表现可能越差。研究表明:
"Lost in the Middle"问题
有研究发现,大模型对上下文中间部分的注意力会下降。如果关键信息藏在一长段文本的中间,模型可能会忽略它。
[开头的内容] → 注意力较高
[中间的内容] → 注意力下降 ← 关键信息可能被忽略
[结尾的内容] → 注意力较高
实验验证
给模型一个 10 万 Token 的上下文,在不同位置放一个关键信息(比如一个密码),然后问模型这个信息是什么。结果发现:
- 放在开头:准确率 90%+
- 放在中间:准确率可能降到 50%
- 放在结尾:准确率 85%+
- 不要因为有大上下文就一股脑塞满。精选最相关的内容,控制在合理的长度
- 把最重要的信息放在开头或结尾
- 如果必须提供很长的上下文,可以在最后重复强调关键点
Temperature:控制回答的"创意度"
什么是 Temperature
Temperature 是一个控制模型输出随机性的参数,取值范围通常是 0 到 2(不同模型可能不同)。
还记得大模型是怎么生成文本的吗?它会计算每个候选词的概率,然后选择一个词输出。Temperature 影响的就是这个选择过程。
- Temperature = 0:总是选择概率最高的词,输出最确定、最"保守"
- Temperature = 1:按照原始概率分布采样,比较均衡
- Temperature > 1:放大低概率词的机会,输出更随机、更"疯狂"
直观理解 Temperature
用一个例子来说明。假设模型在预测"今天天气真____"的下一个词,概率分布是:
| 候选词 | 原始概率 |
|---|---|
| 好 | 60% |
| 不错 | 25% |
| 棒 | 10% |
| 糟糕 | 5% |
Temperature = 0 时
直接选概率最高的"好"。问 100 次,100 次都选"好"。
Temperature = 0.7 时
概率分布会被调整,高概率词更占优势:
| 候选词 | 调整后概率 |
|---|---|
| 好 | ~75% |
| 不错 | ~18% |
| 棒 | ~5% |
| 糟糕 | ~2% |
大多数情况选"好",偶尔选"不错"。
Temperature = 1.5 时
概率分布变得更平均:
| 候选词 | 调整后概率 |
|---|---|
| 好 | ~45% |
| 不错 | ~28% |
| 棒 | ~17% |
| 糟糕 | ~10% |
低概率词也有更多机会被选中。输出更加多样化,但也更可能出现"奇怪"的回答。
Temperature 的数学原理
如果你想了解技术细节,Temperature 是这样工作的。
标准的 Softmax 函数:
P(wi) = exp(zi) / Σexp(zj)
带 Temperature 的 Softmax:
P(wi) = exp(zi/T) / Σexp(zj/T)
其中 T 就是 Temperature。
- 当 T < 1 时,指数函数的差异被放大,高概率词更占优势(分布更"尖锐")
- 当 T > 1 时,指数函数的差异被缩小,概率分布更平均(分布更"平坦")
- 当 T → 0 时,趋近于总是选概率最高的词(贪婪解码,Greedy Decoding)
- 当 T → ∞ 时,趋近于均匀随机选择
举个具体的数学例子
假设两个候选词的原始 logit 是 z1=2, z2=1
T=1 时:
- P1 = exp(2) / (exp(2)+exp(1)) = 7.39 / (7.39+2.72) = 73%
- P2 = exp(1) / (exp(2)+exp(1)) = 2.72 / (7.39+2.72) = 27%
T=0.5 时:
- P1 = exp(4) / (exp(4)+exp(2)) = 54.6 / (54.6+7.39) = 88%
- P2 = 12%
T=2 时:
- P1 = exp(1) / (exp(1)+exp(0.5)) = 2.72 / (2.72+1.65) = 62%
- P2 = 38%
可以看到,T 越小,概率差距越大;T 越大,概率差距越小。
不同场景的 Temperature 建议
| 场景 | 推荐 Temperature | 原因 |
|---|---|---|
| 代码生成 | 0 - 0.3 | 代码需要精确,不能有太多随机性 |
| 数据提取/结构化输出 | 0 - 0.2 | JSON 等结构化输出需要确定性 |
| 翻译 | 0.3 - 0.5 | 需要准确但也要自然流畅 |
| 问答/RAG | 0.3 - 0.7 | 准确性重要,但希望表达有变化 |
| 文章写作 | 0.7 - 1.0 | 需要一定的创意和文采 |
| 头脑风暴 | 1.0 - 1.5 | 需要更多发散思维 |
| 创意故事/诗歌 | 1.2 - 1.8 | 鼓励意想不到的内容 |
实际代码示例
from openai import OpenAI
client = OpenAI()
# 确定性输出(适合代码生成)
response = client.chat.completions.create(
model="gpt-4",
messages=[{"role": "user", "content": "写一个Python冒泡排序函数"}],
temperature=0 # 每次输出一样
)
# 有一定创意(适合文章写作)
response = client.chat.completions.create(
model="gpt-4",
messages=[{"role": "user", "content": "写一篇关于春天的短文,150字左右"}],
temperature=0.8 # 每次可能有不同的表达
)
# 发散思维(适合头脑风暴)
response = client.chat.completions.create(
model="gpt-4",
messages=[{"role": "user", "content": "给我一些用AI改善生活的创意点子"}],
temperature=1.3 # 更加随机,更有可能出现意外的好点子
)
常见误区
误区 1:Temperature 高 = 模型更聪明
很多人以为调高 Temperature 能让模型更"聪明"。其实不是的。
Temperature 只影响选词的随机性,不影响模型的"智商"。调高只是让它更敢冒险选择低概率的词,不是让它更有能力。
如果模型回答的内容本来就不对,调高 Temperature 只会让它更随机地胡说八道。
误区 2:Temperature 总是要调
对于大多数应用场景,默认的 Temperature(通常是 0.7 或 1.0)就够用了。没必要过度调参。
先跑通功能,再根据实际效果微调。
Top-P 和 Top-K:另外两种采样策略
除了 Temperature,还有两个常见的参数也控制采样过程:Top-P 和 Top-K。
Top-K 采样
Top-K 是指只从概率最高的 K 个候选词中选择。
比如设置 K=3,原始概率分布:
| 候选词 | 概率 |
|---|---|
| 好 | 50% |
| 不错 | 25% |
| 棒 | 15% |
| 糟糕 | 5% |
| 坏 | 3% |
| 其他 | 2% |
Top-K=3 后,只考虑前 3 个词,重新归一化:
| 候选词 | 调整后概率 |
|---|---|
| 好 | 55.6% |
| 不错 | 27.8% |
| 棒 | 16.6% |
低概率的"糟糕"、"坏"等词被完全排除,永远不会被选中。
Top-K 的问题:K 是固定的,但不同上下文的概率分布差异很大。
比如在有些上下文中,只有 1-2 个合理选项;在另一些上下文中,可能有 10 个都合理。固定的 K 不够灵活:
- 如果 K 太小,可能排除了合理的选项
- 如果 K 太大,可能包含了太多不合理的选项
Top-P 采样(核采样 / Nucleus Sampling)
Top-P 是指选择累积概率达到 P 的最小候选词集合。
比如设置 P=0.9,按概率从高到低累加:
| 候选词 | 概率 | 累积概率 | 是否保留 |
|---|---|---|---|
| 好 | 50% | 50% | ✓ |
| 不错 | 25% | 75% | ✓ |
| 棒 | 15% | 90% | ✓ |
| 糟糕 | 5% | 95% | ✗ |
| 其他 | 5% | 100% | ✗ |
前 3 个词的累积概率正好达到 90%,所以只从这 3 个词中选择。
Top-P 的优势:它是自适应的。
- 如果概率很集中(一个词就占 95%),候选集可能只有 1 个词
- 如果概率分散(前 10 个词每个都占 9%),候选集可能有 10 个词
这样就能根据实际的概率分布自动调整候选集大小。
Temperature、Top-P、Top-K 的比较
| 参数 | 作用 | 优点 | 缺点 |
|---|---|---|---|
| Temperature | 调整整体概率分布的尖锐程度 | 简单直观 | 可能选到极低概率的词 |
| Top-K | 只保留前 K 个候选 | 明确排除低概率词 | K 值固定,不够灵活 |
| Top-P | 保留累积概率达 P 的候选 | 自适应候选集大小 | 参数含义不如 K 直观 |
如何组合使用
这三个参数可以单独使用,也可以组合使用:
response = client.chat.completions.create(
model="gpt-4",
messages=[{"role": "user", "content": "..."}],
temperature=0.8, # 先调整概率分布
top_p=0.9, # 再从累积概率 90% 的词中选
)
一般建议:
- 如果只调一个,优先用 Temperature(最直观)或 Top-P(更灵活)
- 不建议同时调 Temperature 和 Top-P,容易产生意想不到的效果
- OpenAI 官方建议:调整其中一个时,把另一个设为默认值(Temperature=1 或 Top-P=1)
MoE:让大模型又大又便宜
什么是 MoE
**MoE(Mixture of Experts,混合专家)**是一种模型架构,核心思想是:不是每次推理都使用全部参数,而是动态选择部分"专家"来处理当前输入。
打个比方。传统的大模型像一个综合医院的全员会诊。不管病人来看什么病,所有科室的医生都要参与。感冒患者?内科、外科、骨科、眼科……全部到场。虽然诊断可能很全面,但效率低、成本高。
MoE 模型像一个智能分诊系统。病人来了,前台先判断是什么情况,然后只叫相关科室的专家。感冒?叫呼吸内科就行了。骨折?叫骨科。不需要所有医生都参与。
MoE 的工作原理
MoE 模型的核心组件是:
- 多个专家(Experts):每个专家是一个独立的小型神经网络(通常是前馈网络 FFN)
- 门控网络(Router/Gating Network):决定当前输入应该使用哪些专家
处理流程
输入 Token
↓
门控网络(Router)分析输入
↓
选择 Top-K 个最相关的专家(通常 K=2)
↓
只激活被选中的专家进行计算
↓
合并专家的输出
↓
输出结果
具体例子
假设一个 MoE 模型有 8 个专家,门控网络设置为每次选 2 个:
输入: "写一段Python代码"
↓
Router 分析: 这是编程任务
↓
选择专家: Expert3(代码专家)和 Expert7(Python专家)
↓
只有 Expert3 和 Expert7 参与计算
↓
其他 6 个专家"休息"
再来一个输入:
输入: "翻译这段文字到英文"
↓
Router 分析: 这是翻译任务
↓
选择专家: Expert1(语言理解)和 Expert5(英语生成)
↓
只有 Expert1 和 Expert5 参与计算
MoE 的关键指标
看 MoE 模型时,有两个关键数字:
- 总参数(Total Parameters):所有专家加起来的参数量
- 激活参数(Active Parameters):每次推理实际使用的参数量
以几个典型 MoE 模型为例:
| 模型 | 总参数 | 激活参数 | 专家数 | 每次激活 |
|---|---|---|---|---|
| Mixtral 8x7B | 46.7B | 12.9B | 8 | 2 |
| Mixtral 8x22B | 141B | 39B | 8 | 2 |
| DeepSeek-V2 | 236B | 21B | 160 | 6 |
| DeepSeek-V3 | 671B | 37B | 256 | 8 |
| GPT-4(传闻) | ~1.8T | ~220B | ~16 | ~2 |
DeepSeek-V3 的总参数是 671B,看起来巨大无比。但每次推理只激活 37B,实际的计算成本和一个 37B 的普通模型差不多。
MoE 的优势
优势 1:性价比极高
用较少的计算量获得大模型的效果。
DeepSeek-V3:
- 总参数 671B 的知识容量(学得多、知道得多)
- 实际推理成本约等于 37B 的模型(跑得快、花得少)
- 效果接近甚至超越 GPT-4
这就是为什么 DeepSeek 的 API 价格可以比 GPT-4 便宜 10-20 倍。
优势 2:可扩展性强
传统模型要变大,计算量会等比例增加。参数翻倍,计算量也翻倍。
MoE 可以只增加专家数量,而保持每次激活的专家数量不变。从 8 个专家增加到 256 个,但每次还是只激活 8 个,计算量基本不变。
理论上可以扩展到任意大的参数量,而计算成本增长有限。
优势 3:专家专精(可能)
研究者发现,不同的专家可能会自然地学会处理不同类型的任务:
- 有的专家更擅长代码
- 有的专家更擅长数学
- 有的专家更擅长某种语言
- 有的专家更擅长逻辑推理
门控网络会自动把相关的输入路由到对应的专家,实现某种程度的"专业分工"。
MoE 的局限性
局限 1:显存占用依然很大
虽然每次只激活部分专家,但所有专家的参数都要加载到显存中。
DeepSeek-V3 激活参数只有 37B,但部署时需要加载全部 671B 参数。以 FP16 精度,显存需求超过 1.3TB——需要 16 张 80GB 的 A100 才能装下。
对于本地部署来说,MoE 模型的显存需求比同等激活参数的普通模型大得多。
局限 2:负载均衡问题
如果门控网络总是把输入路由到少数几个专家,其他专家就被"冷落"了,造成:
- 热门专家过载,成为瓶颈
- 冷门专家参数浪费
- 训练不均衡
需要专门的损失函数(如负载均衡损失)来约束,让专家之间的负载尽量均匀。
局限 3:训练复杂度高
MoE 模型的训练比普通模型更复杂:
- 需要处理专家负载均衡
- 分布式训练时的通信开销更大
- 调参空间更大(专家数、激活数、路由策略……)
对开发者意味着什么
如果你通过 API 调用云端模型,MoE 对你来说是透明的。你只需要知道:
- 看激活参数而不是总参数:评估模型的计算成本时,看激活参数更准确
- 性价比考量:MoE 模型往往能以更低的 API 价格提供更好的效果
- 本地部署要注意显存:如果想本地部署 MoE 模型,要按总参数计算显存需求
深度思考与 CoT:让模型"想清楚再说"
什么是深度思考
最近一些模型(如 DeepSeek-R1、OpenAI o1)引入了"深度思考"模式。开启这个模式后,模型会在回答之前先进行一番"内心独白",把问题分析清楚再给出答案。
这种方式能显著提升复杂推理任务的准确率,尤其是数学、逻辑、代码等需要多步推理的任务。
CoT(思维链)的本质
CoT(Chain of Thought,思维链) 是实现深度思考的一种技术。核心思想是:让模型像人一样,一步一步地推理,把中间过程写出来,而不是直接跳到答案。
为什么这样有效?
想象你做一道复杂的数学题。如果直接心算,很容易出错。但如果在草稿纸上一步步写出来,每一步都是简单的计算,最后把结果串起来,正确率就高很多。
大模型也是一样。直接生成最终答案,中间的推理是"隐式"的,容易出错。把推理过程"显式"地写出来,相当于强迫模型一步一步想清楚。
具体例子对比
问题:一个水池有两个进水管,A 管单独注满需要 4 小时,B 管单独注满需要 6 小时。两管同时开,多久能注满?
不用 CoT(直接回答):
答案是 2.4 小时。
答案是对的,但如果题目稍微复杂一点,模型可能就算错了。
用 CoT(思维链回答):
让我一步步分析这个问题。
1. 首先确定每个管的效率:
- A 管每小时注入水池的 1/4
- B 管每小时注入水池的 1/6
2. 两管同时开时,每小时注入量:
1/4 + 1/6 = 3/12 + 2/12 = 5/12
3. 注满整个水池需要的时间:
1 ÷ (5/12) = 12/5 = 2.4 小时
所以两管同时开,需要 2.4 小时注满水池。
通过显式地写出每一步,模型犯错的概率大大降低。
如何触发 CoT
方式 1:在 Prompt 中明确要求
请一步一步地思考这个问题,写出你的推理过程,然后给出最终答案。
问题:xxx
或者:
Let's think step by step.
这是最简单的触发方式,称为 Zero-shot CoT。
方式 2:提供 Few-shot 示例
给模型几个带思维链的例子,它会学着用同样的方式回答:
问题:一辆车以 60 公里/小时的速度行驶,2 小时能走多远?
思考过程:
1. 已知速度是 60 公里/小时
2. 已知时间是 2 小时
3. 根据公式:距离 = 速度 × 时间
4. 距离 = 60 × 2 = 120 公里
答案:120 公里
---
问题:小明有 100 元,买了 3 本书,每本 25 元,还剩多少钱?
思考过程:
模型会自动续写思考过程。
方式 3:使用支持深度思考的模型
一些模型内置了深度思考能力:
- OpenAI o1 / o1-mini:专门优化的推理模型
- DeepSeek-R1:开源的深度思考模型
- Claude 3.5 extended thinking:支持扩展思考模式
这些模型会自动进行内部推理。有的会展示思考过程,有的只展示最终答案。
CoT 的效果数据
研究表明,CoT 在不同任务上的提升幅度:
| 任务类型 | 提升幅度 | 说明 |
|---|---|---|
| 数学推理 | 10-30% | 越复杂的数学题提升越明显 |
| 逻辑推理 | 15-25% | 多步逻辑推理效果显著 |
| 代码生成 | 10-20% | 减少逻辑错误和边界情况遗漏 |
| 常识推理 | 5-15% | 对一些需要推理的常识问题有帮助 |
| 简单问答 | 0 或负 | 简单任务不需要 CoT |
什么时候用 CoT
CoT 不是银弹,要根据任务类型选择:
适合用 CoT 的场景:
- 数学计算和推理
- 逻辑推理和分析
- 复杂的代码生成(需要先设计再编码)
- 多步骤的分析任务
- 需要综合多个因素做决策
不适合用 CoT 的场景:
- 简单的事实问答("中国的首都是哪里?")
- 日常闲聊
- RAG 场景(答案已经在检索结果中)
- 创意写作(思考过程可能反而限制创意)
- 翻译任务(直接翻译通常更好)
CoT 的代价:
- 增加输出 Token(思考过程也要付费)
- 增加延迟(生成思考过程需要时间)
- 有时候"想太多"反而出错
模型量化:让大模型"瘦身"
什么是量化
量化(Quantization)是一种模型压缩技术,通过降低参数的数值精度来减少模型大小和计算量。
大模型的参数通常是 32 位浮点数(FP32)或 16 位浮点数(FP16/BF16)。每个参数用 2-4 个字节存储。
量化就是把这些参数转换成更低精度的表示,比如 8 位整数(INT8)、4 位整数(INT4),甚至 2 位(INT2)。
原始参数 (FP16): 0.0012345678... [16 bits = 2 bytes]
INT8 量化后: 3 [8 bits = 1 byte] → 压缩 2 倍
INT4 量化后: 1 [4 bits = 0.5 byte] → 压缩 4 倍
量化带来的好处
好处 1:显存占用减少
一个 7B 参数的模型:
| 精度 | 每参数字节 | 模型大小 | 运行时显存估算 |
|---|---|---|---|
| FP16 | 2 | 14 GB | ~18-20 GB |
| INT8 | 1 | 7 GB | ~10-12 GB |
| INT4 | 0.5 | 3.5 GB | ~5-6 GB |
量化后,原本需要 24GB 显存才能跑的模型,用 8GB 的消费级显卡也能跑了。
好处 2:推理速度提升
- 低精度计算更快(硬件对 INT8 计算有优化)
- 内存带宽需求降低(数据搬运更快)
- 缓存利用率更高
量化后的模型推理速度可能提升 2-4 倍。
好处 3:部署成本降低
- 可以用更便宜的 GPU
- 或者在同样的 GPU 上部署更大的模型
- 甚至可以用 CPU 运行(GGUF 格式)
常见的量化方法
PTQ(Post-Training Quantization)训练后量化
在模型训练完成后进行量化,不需要重新训练。
代表方法:
- GPTQ:分析权重分布,选择最优的量化策略
- AWQ:考虑激活值分布,保护重要权重
- GGUF:llama.cpp 项目推广的格式
QAT(Quantization-Aware Training)量化感知训练
在训练过程中就考虑量化,让模型"学会"适应低精度表示。效果通常比 PTQ 更好,但需要重新训练。
量化格式详解
GPTQ
- 需要 GPU 运行
- 量化过程需要校准数据集
- 精度损失较小
- 社区支持好,HuggingFace 上有大量预量化模型
# 使用 GPTQ 模型(通过 transformers)
from transformers import AutoModelForCausalLM
model = AutoModelForCausalLM.from_pretrained(
"TheBloke/Llama-2-7B-GPTQ",
device_map="auto"
)
AWQ(Activation-aware Weight Quantization)
- 需要 GPU 运行
- 考虑激活值的分布,保护对输出影响大的权重
- 量化质量可能比 GPTQ 更好
- 推理速度也更快
GGUF(GPT-Generated Unified Format)
- 支持 CPU 运行,不需要显卡
- 由 llama.cpp 项目推广
- 支持多种量化级别
- 通过 Ollama 等工具很容易使用
GGUF 的量化级别命名约定:
| 格式 | 量化位数 | 大小(相对 FP16) | 质量 |
|---|---|---|---|
| Q8_0 | 8 bit | ~50% | 几乎无损 |
| Q6_K | 6 bit | ~42% | 很小的损失 |
| Q5_K_M | 5 bit | ~35% | 小损失,推荐 |
| Q5_K_S | 5 bit | ~33% | 稍大损失 |
| Q4_K_M | 4 bit | ~28% | 明显但可接受 |
| Q4_K_S | 4 bit | ~26% | 更明显损失 |
| Q3_K_M | 3 bit | ~22% | 显著损失 |
| Q2_K | 2 bit | ~15% | 严重损失,不推荐 |
如何选择量化级别
选择建议:
- 显存充足:用 Q8_0 或 Q6_K,质量损失极小
- 显存一般:用 Q5_K_M(最佳平衡点),或 Q4_K_M
- 显存紧张:用 Q4_K_S 或 Q4_K_M
- 极端情况:Q3 及以下不推荐,质量损失太大
经验法则:
- 如果可以接受的话,优先选 Q5_K_M
- Q4 是底线,再低效果会明显下降
- 宁可选稍小的模型用高精度,也不要选大模型用过低精度
量化的代价
量化不是免费的午餐,会有一定的质量损失:
精度下降的表现:
- 复杂推理能力下降
- 数学计算更容易出错
- 知识召回可能不准确
- 小语种能力下降更明显
量化敏感的任务:
- 数学计算
- 代码生成
- 精确的事实问答
- 需要精细区分的分类
量化不敏感的任务:
- 日常对话
- 创意写作
- 情感分析
- 简单的问答
好消息:对于大多数应用场景,Q4 或 Q5 量化的模型和原始模型的差异不容易察觉。只有在对质量要求极高的场景,才需要考虑更高精度。
小结
这篇咱们详细介绍了大模型开发中的核心概念:
1. Token
- 大模型处理文本的最小单位,使用 BPE 等子词分词算法
- 影响能力边界(上下文窗口)和使用成本(API 计费)
- 中文 1 Token ≈ 1.5-2 汉字,英文 1 Token ≈ 0.75 词
2. 上下文窗口
- 模型一次能处理的最大 Token 数
- 包含输入和输出,从 4K 发展到百万级别
- 越长不一定越好,有"Lost in the Middle"问题
3. Temperature
- 控制输出的随机性,值域通常 0-2
- 0 = 确定性,1 = 均衡,>1 = 更随机
- 代码/数据提取用低值,创意任务用高值
4. Top-P/Top-K
- 另外两种采样策略
- Top-K:只从前 K 个候选词选
- Top-P:只从累积概率达 P 的候选词选
- 建议只调一个,不要和 Temperature 同时调
5. MoE(混合专家)
- 动态激活部分参数的架构
- 看激活参数而不是总参数来评估计算成本
- 性价比高,代表:DeepSeek-V3、Mixtral
6. 深度思考与 CoT
- 让模型一步步推理,提高复杂任务准确率
- 可以通过 Prompt 触发,也有专门的模型支持
- 复杂推理任务用 CoT,简单任务直接回答
7. 量化
- 降低参数精度,压缩模型大小
- 常见格式:GPTQ、AWQ、GGUF
- Q5_K_M 是性价比较高的选择
- 让大模型能在消费级硬件上运行
下一篇咱们来看看主流的大模型有哪些,以及如何根据你的需求选择合适的模型。