大模型是如何把向量解码成文字输出的
hidden state 向量
当我们把一句话输入模型后,例如 “Hello world”:
token IDs: [15496, 995]
经过 Embedding + Transformer 层后,会得到每个 token 的中间表示,形状为:
hidden_states: (batch_size, seq_len, hidden_dim) 比如: (1, 2, 768)
这是 Transformer 层的输出,即每个 token 的向量表示。
hidden state → logits:映射到词表空间
🔹 使用 输出投影矩阵(通常是 embedding 的转置)
为了从 hidden state 还原出词,我们需要得到它在词表上每个 token 的“分数”,这叫 logits。实现方式如下:
logits = hidden_state @ W_out.T + b
其中:
W_out
是词嵌入矩阵(Embedding matrix),形状为(vocab_size, hidden_dim)
@
是矩阵乘法,hidden_state
形状是(seq_len, hidden_dim)
- 得到的
logits
形状是(seq_len, vocab_size)
所以,每个位置的 hidden state 都被映射成一个 词表大小的分布。
logits → token ID:选出最可能的 token
现在每个位置我们都有了一个 logits 向量,例如:
logits = [2.1, -0.5, 0.3, 6.9, ...] # 长度 = vocab_size
有几种选择方式:
方法 | 说明 |
---|---|
argmax(logits) | 选最大值,对应 greedy decoding |
softmax → sample | 转成概率分布后随机采样 |
top-k sampling | 从 top-k 个中采样,控制多样性 |
top-p (nucleus) | 从累计概率在 p 范围内采样 |
例如:
probs = softmax(logits)
token_id = torch.argmax(probs).item()
token ID → token 字符串片段
token ID 其实对应的是某个词表里的编号,比如:
tokenizer.convert_ids_to_tokens(50256) # 输出: <|endoftext|>
tokenizer.convert_ids_to_tokens(15496) # 输出: "Hello"
如果是多个 token ID,可以:
tokenizer.convert_ids_to_tokens([15496, 995]) # 输出: ["Hello", " world"]
tokens → 拼接成文本(decode)
tokens 是“子词”或“子字符”,例如:
["Hel", "lo", " world", "!"]
通过 tokenizer.decode()
会自动合并它们为字符串:
tokenizer.decode([15496, 995]) # 输出: "Hello world"
它会处理空格、子词连接等细节,恢复为人类可读的句子。
多轮生成:把预测作为输入继续生成
在生成任务(如 GPT)中,模型是逐 token 生成的。
流程如下:
输入: "你好"
↓
tokenize → [token IDs]
↓
送入模型 → 得到下一个 token 的 logits
↓
选出 token ID → decode 成文字
↓
拼接到输入后,继续送入模型 → 下一轮生成
↓
...
直到生成 EOS(终止符)或达到最大长度
总结流程图:
(1) 输入文本 → tokenizer → token IDs
(2) token IDs → Embedding → hidden_states(中间层向量)
(3) hidden_states × W.T → logits(词表得分)
(4) logits → sampling → token ID
(5) token ID → token → decode → 文本
(6) 拼接文本 → 重复生成(自回归)
示例代码
import torch
from transformers import GPT2LMHeadModel, GPT2Tokenizer
tokenizer = GPT2Tokenizer.from_pretrained("gpt2")
model = GPT2LMHeadModel.from_pretrained("gpt2")
# 输入文本
input_text = "Hello"
inputs = tokenizer(input_text, return_tensors="pt")
# 推理
with torch.no_grad():
outputs = model(**inputs, output_hidden_states=True)
hidden_state = outputs.hidden_states[-1] # 最后一层
print("Hidden state shape:", hidden_state.shape) # [1, seq_len, hidden_dim]
# 取最后一个 token 的向量
last_token_hidden = hidden_state[0, -1, :] # shape: [hidden_dim]
# 手动计算 logits(相当于模型内部的最后一层)
embedding_weight = model.get_output_embeddings().weight # shape: [vocab_size, hidden_dim]
logits = torch.matmul(last_token_hidden, embedding_weight.T) # [vocab_size]
# 选出下一个 token
next_token_id = torch.argmax(logits).item()
next_token = tokenizer.decode([next_token_id])
print("Predicted next token:", next_token)