大模型如何理解代码的,如何输入、输出的
从工程角度彻底拆解 LLM 的"输入→处理→输出"全过程
全局流程一览
你输入的文字
↓
① Tokenization 文字 → Token ID 数字序列
↓
② Embedding 数字 → 高维向量
↓
③ Transformer 向量 → 深度处理(注意力 × N层)
↓
④ 输出层 最终向量 → 每个Token的概率分布
↓
⑤ 采样策略 概率分布 → 选出下一个Token
↓
⑥ 循环生成 重复④⑤直到结束
↓
⑦ Detokenization Token ID → 文字输出
第一步:Tokenization(分词)
文字不是直接处理的,要先切成 Token
Token 不等于"字"或"词"
import tiktoken
enc = tiktoken.get_encoding("cl100k_base") # GPT-4 的编码器
text = "Hello, 你好,FreeSWITCH!"
tokens = enc.encode(text)
# 结果:
# tokens = [9906, 11, 57668, 53901, 3922, 11, 12Frees, WITCH, 0]
# 注意:英文单词可能1个词=1个token
# 中文通常1-2个字=1个token
# 生僻词/专有名词可能被拆开
Tokenization 可视化
输入: "大模型理解语义"
Tokenization:
"大" → 5927
"模" → 6835
"型" → 578
"理解" → 10951 ← 常见词组合为一个Token
"语义" → 11229
输入: "FreeSWITCH"
Tokenization:
"Free" → 11828
"SW" → 9854
"IT" → 1222
"CH" → 5870 ← 生僻词被拆成多个子词
为什么这样设计?
字符级别:词汇表太大,序列太长
词语级别:未登录词(新词/专业词)无法处理
Token级别(BPE算法):
✅ 词汇表可控(约 32k~100k)
✅ 未知词可拆分子词处理
✅ 平衡了序列长度和表达能力
第二步:Embedding(向量化)
每个 Token ID → 一个高维向量(如 4096 维)
# 概念上的映射(实际是矩阵查表)
embedding_table = {
5927: [0.12, -0.45, 0.78, 0.33, ...], # "大" 的向量
6835: [0.09, -0.41, 0.82, 0.29, ...], # "模" 的向量
10951: [0.31, 0.15, 0.55, 0.71, ...], # "理解" 的向量
# ... 几万个Token各有一个向量
}
位置编码(Position Encoding)
模型本身不知道词的顺序,需要显式加入位置信息:
"狗咬人" ≠ "人咬狗"
向量 = Token向量 + 位置向量
位置0的向量: [sin(0/10000^0), cos(0/10000^0), ...]
位置1的向量: [sin(1/10000^0), cos(1/10000^0), ...]
...
这样模型就能区分同一个词在不同位置的含义
第三步:Transformer 处理(核心)
这是理解发生的地方,多层堆叠的注意力机制
整体结构
输入向量序列 [v1, v2, v3, v4, v5]
↓
Layer 1: Multi-Head Attention → Feed Forward
↓
Layer 2: Multi-Head Attention → Feed Forward
↓
Layer 3: Multi-Head Attention → Feed Forward
↓
...(GPT-4 约 96 层)
↓
输出向量序列 [v1', v2', v3', v4', v5']
(每个向量都融合了全局上下文信息)
核心:Self-Attention 自注意力
每个 Token 向其他所有 Token "询问":你和我有多相关?
句子:"我用 Python 写了一个爬虫"
处理 "爬虫" 这个词时:
对 "我" 的注意力权重: 0.05
对 "用" 的注意力权重: 0.08
对 "Python" 的注意力权重: 0.52 ← 高!Python爬虫强关联
对 "写" 的注意力权重: 0.21
对 "了" 的注意力权重: 0.04
对 "一个" 的注意力权重: 0.06
对 "爬虫" 的注意力权重: 0.04
注意力计算公式:
Attention(Q, K, V) = softmax(QKᵀ / √d_k) × V
Q(Query):我想要什么信息?
K(Key): 我能提供什么信息?
V(Value):我实际的信息内容
通俗理解:
Q × K → 计算相关度得分(点积)
softmax → 得分变成概率(归一化)
× V → 加权融合各Token的信息
Multi-Head:多角度理解
单个注意力头 = 从一个角度看关系
多头注意力 = 同时从多个角度看
"苹果很甜" 这句话:
Head 1(语法关系):苹果 ←主语→ 甜
Head 2(语义关系):苹果 → 水果 → 味道 → 甜
Head 3(指代关系):苹果指的是食物还是品牌?
Head 4(情感极性):甜 → 正面评价
8~96个头的结果拼接 → 更丰富的表示
Feed Forward:知识存储层
注意力层 = 信息"路由"和"融合"
前馈层 = 知识"存储"和"激活"
研究发现:FFN 层的神经元存储了具体知识
某个神经元激活 → "这里在讨论编程"
某个神经元激活 → "这是Python语法"
某个神经元激活 → "需要调用函数知识"
FFN = 一个巨大的模糊键值存储
输入向量 → 激活相关知识神经元 → 输出增强向量
各层学到的东西不同
底层(Layer 1~10):
→ 语法、词法、基本句式结构
→ "这是名词" "这是动词短语"
中层(Layer 11~50):
→ 语义关系、实体识别
→ "这是一个Python函数" "这里有个Bug"
高层(Layer 51~96):
→ 抽象推理、任务理解、意图判断
→ "用户想要我帮他调试代码"
→ "需要解释错误原因并给出修复方案"
第四步:输出层(概率计算)
最后一层向量 → 词汇表上的概率分布
# 最终输出是一个巨大的概率向量
vocab_size = 100000 # GPT-4 词汇表大小
# 经过线性层 + softmax
logits = final_vector @ output_matrix # [vocab_size]
probs = softmax(logits)
# 结果示例(预测下一个Token):
{
"def": 0.312, # 最高概率
"class": 0.218,
"import": 0.156,
"print": 0.089,
"for": 0.043,
"你好": 0.0001,
"苹果": 0.00001,
# ... 10万个Token各有一个概率
}
第五步:采样策略(如何选Token)
有了概率分布,怎么选下一个词?策略很关键!
Temperature(温度)
# Temperature 控制概率分布的"尖锐"程度
原始概率:def(0.31) class(0.22) import(0.16) ...
Temperature = 0.1(接近贪心,确定性强):
def(0.92) class(0.06) import(0.02)
→ 几乎必选 def,输出稳定可预测
→ 适合:代码生成、数学计算
Temperature = 1.0(原始分布):
def(0.31) class(0.22) import(0.16)
→ 正常随机采样
→ 适合:通用对话
Temperature = 1.5(更随机):
def(0.18) class(0.17) import(0.16) ...
→ 更多样化但可能不准确
→ 适合:创意写作、头脑风暴
Top-P(核采样)
# 只从累计概率达到 P 的候选集中采样
Top-P = 0.9:
def(0.31) → 累计 0.31
class(0.22) → 累计 0.53
import(0.16) → 累计 0.69
print(0.09) → 累计 0.78
for(0.07) → 累计 0.85
while(0.05) → 累计 0.90 ← 截止,只在这6个中采样
剩余 99994 个Token被直接排除
Top-K
# 只从概率最高的 K 个候选中采样
Top-K = 50 → 每次只在概率最高的50个Token中选择
第六步:自回归循环生成
每次只生成一个Token,然后把它加回输入,循环往复
初始输入: "用Python写一个快速排序"
第1步:
输入 → [用, Python, 写, 一个, 快速, 排序]
预测 → "def"(概率最高)
输出: def
第2步:
输入 → [用, Python, 写, 一个, 快速, 排序, def]
预测 → "quick"
输出: def quick
第3步:
输入 → [..., def, quick]
预测 → "_"
输出: def quick_
...持续循环...
最终输出:
def quick_sort(arr):
if len(arr) <= 1:
return arr
pivot = arr[len(arr) // 2]
...
这就是为什么LLM:
✅ 生成是流式的(一个词一个词出来)
✅ 不能修改已生成的内容
❌ 不能提前知道自己要说多长
❌ 有时会"说到一半跑偏"
软件开发场景的特殊机制
System Prompt 的作用
┌─────────────────────────────────────────────┐
│ System: 你是一个资深Python工程师, │
│ 代码需要包含类型注解和注释 │ ← 设定角色和约束
├─────────────────────────────────────────────┤
│ User: 写一个二分查找函数 │ ← 用户输入
├─────────────────────────────────────────────┤
│ Assistant: │ ← 模型续写这里
└─────────────────────────────────────────────┘
System Prompt 本质上是:
在高层Transformer层激活特定的"角色知识"神经元
让后续所有生成都在这个上下文约束下进行
代码理解:为什么能读懂代码?
# 训练数据包含 GitHub 上数亿行代码
# 模型学会了代码的语义结构:
def calculate_tax(income: float) -> float:
"""计算个人所得税"""
if income <= 5000:
return 0
...
模型从大量代码中学会了:
✅ 函数名 calculate_tax → 和税收相关
✅ 参数 income → 收入,float类型
✅ 5000 → 可能是起征点(结合上下文)
✅ 整体逻辑 → 阶梯税率模式
为什么能发现 Bug?
输入代码:
for i in range(len(arr)):
for j in range(len(arr)): ← 模型注意力锁定这里
if arr[j] > arr[j+1]: ← j+1 可能越界!
arr[j], arr[j+1] = arr[j+1], arr[j]
模型在高层 Attention 中:
"j+1" 的注意力 → 关联到 "range(len(arr))"
→ 激活"数组越界"相关神经元
→ 生成:"当 j = len(arr)-1 时,j+1 越界"
工程实践:开发者需要知道的
Context Window(上下文窗口)
模型处理的Token总数是有上限的:
GPT-3.5: 4k tokens ≈ 3000 汉字
GPT-4: 128k tokens ≈ 96000 汉字
Claude 3: 200k tokens ≈ 150000 汉字
超出上限:
❌ 早期内容被"遗忘"(滑动窗口)
❌ 或直接报错
实践建议:
代码审查:一次不要贴太多代码
长文档:先摘要再提问
长对话:重要信息要在每轮重申
Prompt 工程的本质
好的Prompt = 激活正确的"知识神经元" + 约束输出空间
对比:
❌ 差的Prompt: "帮我写代码"
→ 激活方向模糊,输出不可控
✅ 好的Prompt:
"用Python3,使用类型注解,
写一个处理CSV文件的函数,
需要处理编码异常,
返回DataFrame,
给出使用示例"
→ 精确激活:Python + 类型注解 + CSV + 异常处理 + Pandas 知识域
Temperature 在开发中的选择
场景 推荐Temperature
─────────────────────────────────────
代码生成/补全 0.0 ~ 0.2
Bug修复 0.0 ~ 0.3
技术文档撰写 0.3 ~ 0.5
代码注释生成 0.2 ~ 0.4
架构方案建议 0.5 ~ 0.7
头脑风暴/创意命名 0.7 ~ 1.0
完整流程总结
开发者输入:
"这段Python代码有什么Bug?
def div(a, b): return a/b"
↓
① Tokenize:
["这", "段", "Python", "代码", "有", "什么",
"Bug", "def", "div", "(", "a", ",", "b", ")",
":", "return", "a", "/", "b"]
↓
② Embedding:每个Token → 4096维向量
↓
③ Transformer(96层):
底层:识别Python语法结构
中层:理解这是一个除法函数
高层:理解用户在问Bug → 激活"代码审查"模式
注意力聚焦到 "a/b" → 激活"除零错误"知识
↓
④ 输出层:计算每个Token的概率
↓
⑤ 采样(Temperature=0.2):确定性输出
↓
⑥ 自回归生成:
"这" → "段" → "代码" → "存在" → "除零" → "错误" ...
↓
最终输出:
"这段代码存在除零错误(ZeroDivisionError)。
当 b=0 时程序会崩溃。
修复方案:
def div(a, b):
if b == 0:
raise ValueError('除数不能为零')
return a / b"
本质的一句话
LLM 的"理解"= 将文字压缩成高维向量,在注意力机制的动态路由下激活相关知识,再通过逐Token采样将知识解压为文字输出。
它不是在"查答案",而是在用数十亿参数构成的巨大函数,将你的输入映射到最可能的输出空间——这个映射过程,就是我们所说的"理解与生成"。