大模型是如何把向量解码成文字输出的

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) 拼接文本 → 重复生成(自回归)

示例代码

"""
大语言模型解码过程详解
===========================
本示例展示了大语言模型如何将隐藏状态向量解码成文本输出
使用GPT-2模型作为演示,展示从输入文本到预测下一个token的完整流程
"""import torch
import numpy as np
import matplotlib.pyplot as plt
from transformers import GPT2LMHeadModel, GPT2Tokenizer# 设置随机种子,确保结果可复现
torch.manual_seed(42)def display_token_probabilities(probabilities, tokens, top_k=5):"""可视化展示token的概率分布(仅展示top_k个)"""# 获取前k个最大概率及其索引top_probs, top_indices = torch.topk(probabilities, top_k)top_probs = top_probs.detach().numpy()top_tokens = [tokens[idx] for idx in top_indices]print(f"\n前{top_k}个最可能的下一个token:")for token, prob in zip(top_tokens, top_probs):print(f"  {token:15s}: {prob:.6f} ({prob * 100:.2f}%)")# 可视化概率分布plt.figure(figsize=(10, 6))plt.bar(top_tokens, top_probs)plt.title(f"Top {top_k} The probability distribution of the next token")plt.ylabel("probability")plt.xlabel("Token")plt.xticks(rotation=45)plt.tight_layout()plt.show()def main():print("Step 1: 加载预训练模型和分词器")# 从Hugging Face加载预训练的GPT-2模型和分词器tokenizer = GPT2Tokenizer.from_pretrained("gpt2")model = GPT2LMHeadModel.from_pretrained("gpt2")model.eval()  # 将模型设置为评估模式print("\nStep 2: 准备输入文本")input_text = "Artificial intelligence is"print(f"输入文本: '{input_text}'")# 将输入文本转换为模型需要的格式inputs = tokenizer(input_text, return_tensors="pt")input_ids = inputs["input_ids"]attention_mask = inputs["attention_mask"]# 展示分词结果tokens = tokenizer.convert_ids_to_tokens(input_ids[0])print(f"分词结果: {tokens}")print(f"Token IDs: {input_ids[0].tolist()}")print("\nStep 3: 运行模型前向传播")# 使用torch.no_grad()避免计算梯度,节省内存with torch.no_grad():# output_hidden_states=True 让模型返回所有层的隐藏状态outputs = model(input_ids=input_ids,attention_mask=attention_mask,output_hidden_states=True)# 获取最后一层的隐藏状态# hidden_states的形状: [层数, batch_size, seq_len, hidden_dim]last_layer_hidden_states = outputs.hidden_states[-1]print(f"隐藏状态形状: {last_layer_hidden_states.shape}")# 获取序列中最后一个token的隐藏状态last_token_hidden_state = last_layer_hidden_states[0, -1, :]print(f"最后一个token的隐藏状态形状: {last_token_hidden_state.shape}")print(f"隐藏状态前5个值: {last_token_hidden_state[:5].tolist()}")print("\nStep 4: 手动计算logits")# 从模型中获取输出嵌入矩阵的权重lm_head_weights = model.get_output_embeddings().weight  # [vocab_size, hidden_dim]print(f"语言模型输出嵌入矩阵形状: {lm_head_weights.shape}")# 通过点积计算logits# logits代表每个词汇表中token的分数logits = torch.matmul(last_token_hidden_state, lm_head_weights.T)  # [vocab_size]print(f"Logits形状: {logits.shape}")print(f"Logits值域: [{logits.min().item():.4f}, {logits.max().item():.4f}]")print("\nStep 5: 应用softmax转换为概率")# 使用softmax将logits转换为概率分布probabilities = torch.softmax(logits, dim=0)print(f"概率总和: {probabilities.sum().item():.4f}")  # 应该接近1# 找出概率最高的tokennext_token_id = torch.argmax(probabilities).item()next_token = tokenizer.decode([next_token_id])print(f"预测的下一个token (ID: {next_token_id}): '{next_token}'")# 展示完整的句子complete_text = input_text + next_tokenprint(f"生成的文本: '{complete_text}'")# 展示top-k的概率分布display_token_probabilities(probabilities, tokenizer.convert_ids_to_tokens(range(len(probabilities))), top_k=10)print("\nStep 6: 比较与模型内置解码结果")# 获取模型内置的logits输出model_outputs = model(input_ids=input_ids, attention_mask=attention_mask)model_logits = model_outputs.logitsprint(f"模型输出的logits形状: {model_logits.shape}")# 获取最后一个token位置的logitslast_token_model_logits = model_logits[0, -1, :]# 验证我们手动计算的logits与模型输出的logits是否一致is_close = torch.allclose(logits, last_token_model_logits, rtol=1e-4)print(f"手动计算的logits与模型输出的logits是否一致: {is_close}")# 如果不一致,计算差异if not is_close:diff = torch.abs(logits - last_token_model_logits)print(f"最大差异: {diff.max().item():.8f}")print(f"平均差异: {diff.mean().item():.8f}")print("\nStep 7: 使用模型进行文本生成")# 使用模型的generate方法生成更多文本# 生成时传递 attention_mask 和 pad_token_idgenerated_ids = model.generate(input_ids,max_length=input_ids.shape[1] + 10,  # 生成10个额外的tokentemperature=1.0,do_sample=True,top_k=50,top_p=0.95,num_return_sequences=1,attention_mask=attention_mask,  # 添加 attention_maskpad_token_id=tokenizer.eos_token_id  # 明确设置 pad_token_id 为 eos_token_id)generated_text = tokenizer.decode(generated_ids[0], skip_special_tokens=True)print(f"模型生成的文本:\n'{generated_text}'")if __name__ == "__main__":main()

Roberta代码案例

import torch
from transformers import RobertaTokenizer, RobertaForMaskedLM
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns# 设置中文字体显示(如果需要显示中文)
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False# 1. 加载预训练的RoBERTa模型和tokenizer
tokenizer = RobertaTokenizer.from_pretrained('roberta-base')
model = RobertaForMaskedLM.from_pretrained('roberta-base')# 2. 定义一个带有[MASK]标记的示例句子
text = f"The capital of France is {tokenizer.mask_token}."
print(f"原始文本: {text}")# 3. 对文本进行编码,转换为模型的输入格式
inputs = tokenizer(text, return_tensors="pt")
print(f"\n标记化后的输入ID: {inputs['input_ids'][0].tolist()}")
print(f"对应的标记: {tokenizer.convert_ids_to_tokens(inputs['input_ids'][0])}")# 4. 找到[MASK]标记的位置
mask_token_index = torch.where(inputs["input_ids"] == tokenizer.mask_token_id)[1]
if mask_token_index.numel() == 0:raise ValueError("没有找到[MASK]标记,请检查输入文本。")
print(f"\n[MASK]标记的位置: {mask_token_index.item()}")# 5. 前向传播,获取预测结果(添加output_hidden_states=True)
with torch.no_grad():outputs = model(**inputs, output_hidden_states=True)# 6. 获取[MASK]位置的预测分数
logits = outputs.logits
mask_token_logits = logits[0, mask_token_index, :]# 7. 找出前5个最可能的标记
top_5_tokens = torch.topk(mask_token_logits, 5, dim=1)
top_5_token_indices = top_5_tokens.indices[0].tolist()
top_5_token_scores = top_5_tokens.values[0].tolist()print("\n预测结果:")
for i, (index, score) in enumerate(zip(top_5_token_indices, top_5_token_scores)):token = tokenizer.decode([index])probability = torch.softmax(mask_token_logits, dim=1)[0, index].item()print(f"  {i + 1}. '{token}' - 分数: {score:.2f}, 概率: {probability:.4f}")# 8. 获取向量表示 - 确保获取到hidden_states
last_hidden_states = outputs.hidden_states[-1]  # 现在这一行应该可以工作了# 9. 可视化[MASK]位置的向量
def visualize_vector(vector, title):plt.figure(figsize=(10, 6))plt.bar(range(len(vector)), vector)plt.title(title)plt.xlabel('维度')plt.ylabel('激活值')plt.tight_layout()plt.show()# 10. 可视化解码过程
def visualize_decoding_process():# 获取模型最终层的权重矩阵decoder_weights = model.lm_head.decoder.weight.detach()# 获取[MASK]位置的隐藏状态向量mask_hidden_state = last_hidden_states[0, mask_token_index].squeeze()# 计算点积得分scores = torch.matmul(mask_hidden_state, decoder_weights.t())# 获取前5个最高分词的索引和分数top_indices = torch.topk(scores, 5).indices.tolist()top_tokens = [tokenizer.decode([idx]) for idx in top_indices]# 可视化注意力/解码过程plt.figure(figsize=(12, 6))# 可视化隐藏状态向量与词表中向量的相似度plt.subplot(1, 2, 1)sns.heatmap(scores.reshape(1, -1)[:, top_indices].detach().numpy(),annot=True, fmt=".2f", cmap="YlGnBu",xticklabels=top_tokens)plt.title("词向量与词表的相似度分数")plt.xlabel("候选词")plt.ylabel("点积分数")# 可视化softmax后的概率分布plt.subplot(1, 2, 2)probabilities = torch.softmax(scores, dim=0)[top_indices].detach().numpy()plt.bar(top_tokens, probabilities)plt.title("解码后的概率分布")plt.xlabel("候选词")plt.ylabel("概率")plt.tight_layout()plt.show()# 可视化[MASK]位置的向量
mask_vector = last_hidden_states[0, mask_token_index].squeeze().detach().numpy()
visualize_vector(mask_vector[:50], "MASK位置的词向量表示(仅显示前50维)")# 显示解码过程
visualize_decoding_process()# 12. 显示最终预测结果
predicted_token_id = top_5_token_indices[0]
predicted_token = tokenizer.decode([predicted_token_id])
print(f"\n最终预测结果: '{predicted_token}'")
print(f"完整句子: {text.replace(tokenizer.mask_token, predicted_token)}")

更详细的代码案例

"""
大语言模型向量解码过程详解 - 使用BERT模型
=============================================
本示例展示了大语言模型如何将隐藏状态向量解码成文本输出
"""
import time
from datetime import datetimeimport torch
import numpy as np
import matplotlib.pyplot as plt
from transformers import BertTokenizer, BertForMaskedLM
from typing import List, Tuple, Dict# 设置随机种子,确保结果可复现
torch.manual_seed(42)class DecodingVisualizer:"""用于可视化大语言模型解码过程的工具类"""def __init__(self, model_name: str = "bert-base-uncased", use_cuda: bool = True):"""初始化模型和分词器"""print(f"正在加载 {model_name} 模型和分词器...")# 添加设备自动检测self.device = torch.device("cuda" if torch.cuda.is_available() and use_cuda else "cpu")# 注意:这里需要根据use_cuda参数决定使用哪个设备,而不是只检查可用性# 添加低内存加载选项self.tokenizer = BertTokenizer.from_pretrained(model_name)print(f"正在加载 {model_name} (设备: {self.device})...")start_time = time.time()self.model = BertForMaskedLM.from_pretrained(model_name,low_cpu_mem_usage=True,torch_dtype=torch.float16 if self.device.type == "cuda" else torch.float32).to(self.device)  # 确保这里没有遗漏.to(self.device)self.model.eval()  # 将模型设置为评估模式# 获取模型配置self.hidden_size = self.model.config.hidden_sizeself.vocab_size = self.model.config.vocab_size# 获取MASK token IDself.mask_token_id = self.tokenizer.mask_token_idself.mask_token = self.tokenizer.mask_tokenload_time = time.time() - start_timeprint(f"加载完成! 耗时: {load_time:.2f}s")print(f"模型加载完成! 隐藏层维度: {self.hidden_size}, 词表大小: {self.vocab_size}")print(f"MASK token: '{self.mask_token}', ID: {self.mask_token_id}")def prepare_masked_input(self, text: str) -> Dict[str, torch.Tensor]:"""掩码输入准备函数"""# 更智能的掩码位置选择words = text.split()if not words:return {"inputs": self.tokenizer(text, return_tensors="pt").to(self.device), # 注意:这里需要将inputs张量移动到设备上"original_text": text,"masked_text": text,"original_word": "","masked_position": 0}# 选择内容词进行掩码(避免掩码停用词)content_pos = []stopwords = {"the", "a", "an", "is", "are", "of", "to"}for i, word in enumerate(words):if word.lower() not in stopwords:content_pos.append(i)# 如果没有内容词,则选择最后一个词masked_pos = content_pos[-1] if content_pos else len(words) - 1original_word = words[masked_pos]words[masked_pos] = self.mask_tokenmasked_text = " ".join(words)inputs = self.tokenizer(masked_text,return_tensors="pt",max_length=512,truncation=True,padding="max_length"  # 固定长度便于批处理).to(self.device)return {"inputs": inputs,"original_text": text,"masked_text": masked_text,"original_word": original_word,"masked_position": masked_pos + 1  # 考虑[CLS] token}def decode_step_by_step(self, text: str) -> None:"""详细展示BERT模型解码过程的每个步骤参数:text: 要处理的输入文本verbose: 是否打印详细过程返回:包含完整解码信息的字典:{"input_text": str,"masked_text": str,"hidden_state": torch.Tensor,"logits": torch.Tensor,"predictions": List[Tuple[str, float]],"top_k_predictions": List[Tuple[str, float]]}"""print("\n" + "="*60)print("BERT模型解码过程演示")print("="*60)# 准备带掩码的输入masked_data = self.prepare_masked_input(text)inputs = masked_data["inputs"]original_text = masked_data["original_text"]masked_text = masked_data["masked_text"]original_word = masked_data["original_word"]print(f"原始文本: '{original_text}'")print(f"掩码文本: '{masked_text}'")print(f"被掩码的词: '{original_word}'")# 分词结果input_ids = inputs["input_ids"]token_type_ids = inputs["token_type_ids"]attention_mask = inputs["attention_mask"]tokens = self.tokenizer.convert_ids_to_tokens(input_ids[0])print(f"\n分词结果: {tokens}")print(f"Token IDs: {input_ids[0].tolist()}")# 查找[MASK]的位置mask_positions = [i for i, id in enumerate(input_ids[0]) if id == self.mask_token_id]if mask_positions:mask_position = mask_positions[0]print(f"[MASK]的位置: {mask_position}, Token: '{tokens[mask_position]}'")else:print("未找到[MASK]标记,使用最后一个token作为示例")mask_position = len(tokens) - 2  # 避免[SEP]标记# Step 1: 运行模型前向传播print("\n【Step 1: 运行模型前向传播】")with torch.no_grad():outputs = self.model(input_ids=input_ids,token_type_ids=token_type_ids,attention_mask=attention_mask,output_hidden_states=True)# 获取最后一层的隐藏状态last_hidden_states = outputs.hidden_states[-1]print(f"隐藏状态形状: {last_hidden_states.shape}")# 获取[MASK]位置的隐藏状态mask_hidden_state = last_hidden_states[0, mask_position, :]print(f"[MASK]位置的隐藏状态形状: {mask_hidden_state.shape}")print(f"隐藏状态前5个值: {mask_hidden_state[:5].tolist()}")# Step 2优化: 添加详细解释和性能优化print("\n【Step 2: 解码向量生成logits(解码过程的核心)】")print("解码过程实质上是将隐藏状态向量映射到词表空间的一个线性变换")print(f"数学表达式: logits = hidden_state × W^T + b")# 使用更高效的矩阵运算with torch.no_grad():cls_weights = self.model.cls.predictions.decoder.weightcls_bias = self.model.cls.predictions.decoder.biasprint(f"解码器权重矩阵形状: {cls_weights.shape}")print(f"解码器偏置向量形状: {cls_bias.shape}")# 使用einsum进行高效矩阵乘法manual_logits = torch.einsum("d,vd->v",mask_hidden_state,cls_weights) + cls_bias# 添加温度系数调节temperature = 1.0  # 可调节参数tempered_logits = manual_logits / temperature# 验证一致性时添加容差说明model_logits = outputs.logits[0, mask_position, :]is_close = torch.allclose(manual_logits,model_logits,rtol=1e-3,atol=1e-5)print(f"\n手动计算的logits与模型输出是否一致: {is_close}")if not is_close:diff = torch.abs(manual_logits - model_logits)print(f"最大差异: {diff.max().item():.8f}")print(f"平均差异: {diff.mean().item():.8f}")print("注: 小的数值差异可能是由于计算精度造成的")# Step 3: 从logits到概率print("\n【Step 3: 将logits转换为概率】")with torch.no_grad():# 使用softmax转换为概率分布probabilities = torch.softmax(manual_logits, dim=0)print(f"概率总和: {probabilities.sum().item():.4f}")  # 应该接近1# 找出概率最高的tokentop_probs, top_indices = torch.topk(probabilities, 5)predicted_token_id = top_indices[0].item()predicted_token = self.tokenizer.convert_ids_to_tokens([predicted_token_id])[0]predicted_word = self.tokenizer.decode([predicted_token_id])print(f"\n预测的token (ID: {predicted_token_id}): '{predicted_token}'")print(f"解码后的单词: '{predicted_word}'")# 原始被遮蔽的词的概率if original_word:original_word_ids = self.tokenizer.encode(original_word, add_special_tokens=False)if original_word_ids:original_id = original_word_ids[0]original_prob = probabilities[original_id].item()print(f"原始单词 '{original_word}' (ID: {original_id}) 的概率: {original_prob:.6f} ({original_prob*100:.2f}%)")# 展示前10个最可能的tokensself._display_token_probabilities(probabilities, top_k=10)def _display_token_probabilities(self, probabilities: torch.Tensor, top_k: int = 5) -> None:# 获取前k个最大概率及其索引top_probs, top_indices = torch.topk(probabilities, top_k)top_tokens = [self.tokenizer.convert_ids_to_tokens([idx.item()])[0] for idx in top_indices]top_words = [self.tokenizer.decode([idx.item()]) for idx in top_indices]# 创建使用更精确比例的图形fig, ax = plt.subplots(figsize=(16, 9), constrained_layout=True)# 使用更适合数据对比的渐变色调colors = plt.cm.Blues(np.linspace(0.6, 0.9, top_k))# 绘制条形图,适当增加条形宽度以提高可读性bars = ax.barh(range(top_k), top_probs.tolist(), color=colors, height=0.6)# 自定义Y轴刻度,同时显示token和对应的实际内容ax.set_yticks(range(top_k))labels = [f"{w} ({t})" if t != w else w for t, w in zip(top_tokens, top_words)]ax.set_yticklabels(labels, fontsize=12)# 添加更突出的标题与标签ax.set_title("Token Prediction Probabilities", fontsize=18, fontweight='bold', pad=20)ax.set_xlabel("Probability", fontsize=15, fontweight='semibold', labelpad=12)# 去掉多余的Y轴标签,因为标签已经在刻度上显示ax.set_ylabel("")# 动态设置X轴范围,确保最高概率条形图占据约80%的宽度max_prob = top_probs[0].item()ax.set_xlim(0, max(max_prob * 1.25, 0.05))# 添加更清晰的数据标签for i, (bar, prob) in enumerate(zip(bars, top_probs)):width = bar.get_width()ax.text(width + 0.005,i,f"{prob:.4f} ({prob:.1%})",ha='left',va='center',fontsize=13,fontweight='bold',color='#333333')# 添加半透明的网格线以便于阅读ax.grid(axis='x', linestyle='--', alpha=0.4, color='gray')# 反转Y轴使最高概率在上方ax.invert_yaxis()# 美化图表边框和背景ax.spines['top'].set_visible(False)ax.spines['right'].set_visible(False)ax.spines['left'].set_linewidth(0.5)ax.spines['bottom'].set_linewidth(0.5)# 设置浅色背景以提高对比度ax.set_facecolor('#f8f8f8')# 添加概率条形图的圆角效果for bar in bars:bar.set_edgecolor('white')bar.set_linewidth(1)plt.show()def visualize_linear_transformation(self, text: str) -> None:"""可视化向量解码的线性变换过程"""print("\n" + "=" * 60)print("可视化向量解码的线性变换过程")print("=" * 60)# 准备带掩码的输入masked_data = self.prepare_masked_input(text)inputs = masked_data["inputs"]# 寻找[MASK]位置input_ids = inputs["input_ids"]mask_positions = [i for i, id in enumerate(input_ids[0]) if id == self.mask_token_id]if mask_positions:mask_position = mask_positions[0]else:mask_position = len(input_ids[0]) - 2  # 避免[SEP]# 运行模型获取隐藏状态with torch.no_grad():outputs = self.model(**inputs,output_hidden_states=True)last_hidden_states = outputs.hidden_states[-1]mask_hidden_state = last_hidden_states[0, mask_position, :]# 获取解码器权重cls_weights = self.model.cls.predictions.decoder.weight# 为了可视化,我们只取前2维隐藏状态和几个样本词reduced_hidden = mask_hidden_state[:2].cpu().numpy()  # 添加cpu()# 选取几个常见词的权重向量common_words = ["the", "is", "and", "of", "to", "a", "in", "for", "with"]word_ids = []for word in common_words:word_ids.extend(self.tokenizer.encode(word, add_special_tokens=False))# 确保我们有不重复的IDsword_ids = list(set(word_ids))[:8]  # 取前8个word_tokens = [self.tokenizer.convert_ids_to_tokens([id])[0] for id in word_ids]# 获取这些词的权重向量word_vectors = cls_weights[word_ids, :2].cpu().numpy()  # 添加cpu()# 可视化plt.figure(figsize=(10, 8))# 绘制隐藏状态向量plt.scatter(reduced_hidden[0], reduced_hidden[1], c='red', s=100, marker='*',label='Hidden state vector')# 绘制词向量plt.scatter(word_vectors[:, 0], word_vectors[:, 1], c='blue', s=50)# 添加词标签for i, token in enumerate(word_tokens):plt.annotate(token, (word_vectors[i, 0], word_vectors[i, 1]),fontsize=10, alpha=0.8)# 计算这些词的logits(向量点积)logits = np.dot(reduced_hidden, word_vectors.T)# 绘制从隐藏状态到各词向量的连线,线宽表示logit值max_logit = np.max(np.abs(logits))for i, token in enumerate(word_tokens):# 归一化logit值作为线宽width = 0.5 + 3.0 * (logits[i] + max_logit) / (2 * max_logit)# 用颜色表示logit的正负color = 'green' if logits[i] > 0 else 'red'alpha = abs(logits[i]) / max_logitplt.plot([reduced_hidden[0], word_vectors[i, 0]],[reduced_hidden[1], word_vectors[i, 1]],linewidth=width, alpha=alpha, color=color)plt.title("The relationship between the hidden state vector and the word vector (2D projection)")plt.xlabel("dimension1")plt.ylabel("dimension2")plt.grid(True, alpha=0.3)plt.legend()plt.tight_layout()plt.show()# 展示与这些词的点积(logits)print("\n隐藏状态与词向量的点积(logits):")for i, token in enumerate(word_tokens):print(f"  与 '{token}' 的点积: {logits[i]:.4f}")# 将logits转换为概率probs = np.exp(logits) / np.sum(np.exp(logits))print("\n转换为概率后:")for i, token in enumerate(word_tokens):print(f"  '{token}' 的概率: {probs[i]:.4f} ({probs[i]*100:.2f}%)")def demonstrate_bert_mlm(self, text: str, positions_to_mask=None) -> None:"""演示BERT掩码语言模型的完整预测过程"""print("\n" + "=" * 60)print("BERT掩码语言模型演示")print("=" * 60)# 分词inputs = self.tokenizer(text,return_tensors="pt",padding=True,truncation=True).to(self.device)  # 添加.to(self.device)将输入移动到正确的设备input_ids = inputs["input_ids"]tokens = self.tokenizer.convert_ids_to_tokens(input_ids[0])print(f"原始文本: '{text}'")print(f"分词结果: {tokens}")# 如果没有指定要掩码的位置,则随机选择if positions_to_mask is None:# 排除[CLS]和[SEP]标记valid_positions = list(range(1, len(tokens) - 1))# 随机选择15%的token进行掩码num_to_mask = max(1, int(len(valid_positions) * 0.15))positions_to_mask = np.random.choice(valid_positions, num_to_mask, replace=False)# 应用掩码masked_input_ids = input_ids.clone()for pos in positions_to_mask:if pos < len(tokens):original_token = tokens[pos]original_id = input_ids[0, pos].item()masked_input_ids[0, pos] = self.mask_token_idprint(f"位置 {pos}: 将 '{original_token}' (ID: {original_id}) 替换为 '{self.mask_token}'")# 运行模型with torch.no_grad():outputs = self.model(input_ids=masked_input_ids,token_type_ids=inputs["token_type_ids"],attention_mask=inputs["attention_mask"])predictions = outputs.logits# 对每个掩码位置进行预测print("\n预测结果:")for pos in positions_to_mask:if pos < len(tokens):# 获取该位置的logitslogits = predictions[0, pos, :]# 应用softmax获取概率probs = torch.softmax(logits, dim=0)# 获取概率最高的tokentop_probs, top_indices = torch.topk(probs, 5)# 显示预测结果original_token = tokens[pos]original_id = input_ids[0, pos].item()print(f"\n位置 {pos} 原始token: '{original_token}' (ID: {original_id})")print("Top 5预测:")for i, (index, prob) in enumerate(zip(top_indices, top_probs)):predicted_token = self.tokenizer.convert_ids_to_tokens([index])[0]print(f"  {i + 1}. '{predicted_token}': {prob:.6f} ({prob * 100:.2f}%)")# 检查原始token的排名和概率original_prob = probs[original_id].item()original_rank = torch.where(torch.argsort(probs, descending=True) == original_id)[0].item() + 1print(f"  原始token '{original_token}' 排名: #{original_rank}, 概率: {original_prob:.6f} ({original_prob * 100:.2f}%)")# 恢复掩码后的文本predicted_ids = torch.argmax(outputs.logits, dim=-1)predicted_tokens = []for i in range(len(tokens)):if i in positions_to_mask:# 使用预测的tokenpredicted_token = self.tokenizer.convert_ids_to_tokens([predicted_ids[0, i].item()])[0]predicted_tokens.append(predicted_token)else:predicted_tokens.append(tokens[i])# 解码回文本predicted_text = self.tokenizer.convert_tokens_to_string(predicted_tokens)print(f"\n恢复后的文本: '{predicted_text}'")def _nucleus_sampling(self, logits: torch.Tensor, p: float = 0.9) -> torch.Tensor:"""实现nucleus sampling (也称为top-p sampling)Args:logits: 模型输出的logitsp: 概率质量阈值(默认0.9)Returns:采样得到的token ID"""# 计算softmax概率probs = torch.softmax(logits, dim=-1)# 按概率从大到小排序sorted_probs, sorted_indices = torch.sort(probs, descending=True)# 计算累积概率cumulative_probs = torch.cumsum(sorted_probs, dim=-1)# 找到累积概率超过p的位置nucleus = cumulative_probs < p# 确保至少选择一个token(如果所有nucleus都是False)if not nucleus.any():nucleus[0] = True# 将概率低于阈值的token概率设为0nucleus_probs = torch.zeros_like(probs)nucleus_probs[sorted_indices[nucleus]] = sorted_probs[nucleus]# 重新归一化概率if nucleus_probs.sum() > 0:nucleus_probs = nucleus_probs / nucleus_probs.sum()else:# 如果所有概率都为0,则使用原始概率的top-1nucleus_probs[sorted_indices[0]] = 1.0# 采样return torch.multinomial(nucleus_probs, num_samples=1)def compare_decoding_strategies(self, text: str = None):"""比较不同解码策略的结果"""if text is None:# 使用更具歧义性的例子,让不同策略有可能生成不同结果text = "The scientist made a [MASK] discovery that changed the field."print(f"使用示例文本: '{text}'")# 扩展策略集合,使用多种参数strategies = {"贪婪解码": lambda logits: torch.argmax(logits, dim=-1),"Top-K=3": lambda logits: torch.multinomial(self._top_k_sampling(logits, k=3),num_samples=1),"Top-K=10": lambda logits: torch.multinomial(self._top_k_sampling(logits, k=10),num_samples=1),"Top-P=0.5": lambda logits: self._nucleus_sampling(logits, 0.5),"Top-P=0.9": lambda logits: self._nucleus_sampling(logits, 0.9)}# Top-K采样函数def _top_k_sampling(self, logits, k=5):values, _ = torch.topk(logits, k)min_value = values[-1]# 创建一个掩码,保留top-k的值mask = logits >= min_valuefiltered_logits = logits.clone()# 将非top-k的logits设为负无穷filtered_logits[~mask] = float('-inf')# 应用softmax获取概率分布probs = torch.softmax(filtered_logits, dim=-1)return probs# 为类添加辅助方法self._top_k_sampling = _top_k_sampling.__get__(self, type(self))masked_data = self.prepare_masked_input(text)inputs = masked_data["inputs"]# 查找[MASK]的位置input_ids = inputs["input_ids"]mask_positions = [i for i, id in enumerate(input_ids[0]) if id == self.mask_token_id]if mask_positions:mask_position = mask_positions[0]else:# 如果没有找到MASK标记,使用默认位置mask_position = masked_data["masked_position"]print(f"\n掩码词: '{self.mask_token}'")print(f"掩码文本: '{masked_data['masked_text']}'")with torch.no_grad():outputs = self.model(**inputs)logits = outputs.logits[0, mask_position]probs = torch.softmax(logits, dim=-1)# 获取原始单词的概率和排名if masked_data["original_word"]:original_word_tokens = self.tokenizer.encode(masked_data["original_word"],add_special_tokens=False)if original_word_tokens:original_id = original_word_tokens[0]original_prob = probs[original_id].item()original_rank = torch.where(torch.argsort(probs, descending=True) == original_id)[0].item() + 1print(f"原始词: '{masked_data['original_word']}', 概率: {original_prob:.4f}, 在词表中排名: #{original_rank}")# 获取总体词汇表预测的Top 10top_probs, top_indices = torch.topk(probs, 10)print("\n模型Top-10预测词:")for i, (index, prob) in enumerate(zip(top_indices, top_probs)):token = self.tokenizer.decode([index.item()])print(f"  {i + 1}. '{token}': {prob:.4f}")print("\n不同解码策略比较结果:")results = {}for name, strategy in strategies.items():# 对每个策略运行5次,查看随机性效果strategy_results = []for i in range(5 if "Top" in name else 1):  # 贪婪解码是确定性的,只需运行一次pred_id = strategy(logits).item()pred_token = self.tokenizer.decode([pred_id])pred_prob = probs[pred_id].item()strategy_results.append((pred_token, pred_prob))results[name] = strategy_results# 显示结果for name, strategy_results in results.items():print(f"\n{name}:")if len(strategy_results) == 1:token, prob = strategy_results[0]print(f"  预测词: '{token}', 概率: {prob:.4f}")else:# 对于随机策略,分析多次运行的结果tokens = [r[0] for r in strategy_results]# 显示唯一词及其出现次数unique_tokens = {}for token in tokens:if token not in unique_tokens:unique_tokens[token] = 0unique_tokens[token] += 1# 显示结果print(f"  5次采样结果:")for token, count in unique_tokens.items():prob = next(p for t, p in strategy_results if t == token)print(f"  '{token}': {count}/5次, 概率: {prob:.4f}")# 额外尝试几个更具歧义的文本if text == "The scientist made a [MASK] discovery that changed the field.":extra_examples = ["The weather forecast for tomorrow is [MASK].","She felt [MASK] after hearing the unexpected news.","The movie was both entertaining and [MASK]."]print("\n\n更多测试示例:")for example in extra_examples:print(f"\n文本: '{example}'")# 使用top-p和贪婪解码做对比masked_data = self.prepare_masked_input(example)inputs = masked_data["inputs"]# 查找[MASK]的位置input_ids = inputs["input_ids"]mask_positions = [i for i, id in enumerate(input_ids[0]) if id == self.mask_token_id]if mask_positions:mask_position = mask_positions[0]else:mask_position = masked_data["masked_position"]with torch.no_grad():outputs = self.model(**inputs)logits = outputs.logits[0, mask_position]# 使用贪婪解码greedy_id = torch.argmax(logits, dim=-1).item()greedy_token = self.tokenizer.decode([greedy_id])# 使用top-p解码topp_results = []for _ in range(5):topp_id = self._nucleus_sampling(logits, 0.7).item()topp_token = self.tokenizer.decode([topp_id])topp_results.append(topp_token)print(f"  贪婪解码: '{greedy_token}'")print(f"  Top-P=0.7 采样 (5次): {', '.join([f'\'{r}\'' for r in topp_results])}")def main():"""主函数"""print("初始化BERT模型解码可视化器...")# 您可以指定不同的预训练模型,如"bert-large-uncased"或"bert-base-chinese"等visualizer = DecodingVisualizer("bert-base-uncased")# 示例1: 基本解码过程# 展示模型如何从隐藏状态向量解码出词汇表中的tokeninput_text = "The neural network transforms vectors into tokens through [MASK]."visualizer.decode_step_by_step(input_text)# 示例2: 可视化线性变换过程# 展示隐藏状态和词向量之间的关系input_text2 = "Language models convert hidden states to vocabulary logits by [MASK]."visualizer.visualize_linear_transformation(input_text2)# 示例3: 完整BERT掩码语言模型演示# 展示BERT如何预测被掩码的多个位置input_text3 = "Deep learning models perform vector transformations to process natural language."# 指定具体位置进行掩码演示,这些索引对应分词后的位置positions_to_mask = [4, 7, 11]  # 对应某些单词位置visualizer.demonstrate_bert_mlm(input_text3, positions_to_mask)# 示例4: 比较不同解码策略# 使用更具歧义性的文本能更好地展示不同解码策略的差异print("\n" + "=" * 60)print("不同解码策略比较")print("=" * 60)# 不传入参数,让函数使用默认的歧义性更强的文本visualizer.compare_decoding_strategies()# 可选:尝试其他歧义性文本来比较解码策略# ambiguous_texts = [#     "The weather forecast for tomorrow is [MASK].",#     "She felt [MASK] after hearing the unexpected news."# ]# for text in ambiguous_texts:#     visualizer.compare_decoding_strategies(text)if __name__ == "__main__":main()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/75453.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++指针(三)

个人主页:PingdiGuo_guo 收录专栏&#xff1a;C干货专栏 文章目录 前言 1.字符指针 1.1字符指针的概念 1.2字符指针的用处 1.3字符指针的操作 1.3.1定义 1.3.2初始化 1.4字符指针使用注意事项 2.数组参数&#xff0c;指针参数 2.1数组参数 2.1.1数组参数的概念 2.1…

生命篇---心肺复苏、AED除颤仪使用、海姆立克急救法、常见情况急救简介

生命篇—心肺复苏、AED除颤仪使用、海姆立克急救法、常见情况急救简介 文章目录 生命篇---心肺复苏、AED除颤仪使用、海姆立克急救法、常见情况急救简介一、前言二、急救1、心肺复苏&#xff08;CPR&#xff09;&#xff08;1&#xff09;适用情况&#xff08;2&#xff09;操作…

基于神经环路的神经调控可增强遗忘型轻度认知障碍患者的延迟回忆能力

简要总结 这篇文章提出了一种名为CcSi-MHAHGEL的框架&#xff0c;用于基于多站点、多图谱fMRI的功能连接网络&#xff08;FCN&#xff09;分析&#xff0c;以辅助自闭症谱系障碍&#xff08;ASD&#xff09;的识别。该框架通过多视图超边感知的超图嵌入学习方法&#xff0c;整合…

[WUSTCTF2020]level1

关键知识点&#xff1a;for汇编 ida64打开&#xff1a; 00400666 55 push rbp .text:0000000000400667 48 89 E5 mov rbp, rsp .text:000000000040066A 48 83 EC 30 sub rsp, 30h .text:000000…

cpp自学 day20(文件操作)

基本概念 程序运行时产生的数据都属于临时数据&#xff0c;程序一旦运行结束都会被释放 通过文件可以将数据持久化 C中对文件操作需要包含头文件 <fstream> 文件类型分为两种&#xff1a; 文本文件 - 文件以文本的ASCII码形式存储在计算机中二进制文件 - 文件以文本的…

Gartner发布软件供应链安全市场指南:软件供应链安全工具的8个强制功能、9个通用功能及全球29家供应商

攻击者的目标是由开源和商业软件依赖项、第三方 API 和 DevOps 工具链组成的软件供应链。软件工程领导者可以使用软件供应链安全工具来保护他们的软件免受这些攻击的连锁影响。 主要发现 越来越多的软件工程团队现在负责解决软件供应链安全 (SSCS) 需求。 软件工件、开发人员身…

备赛蓝桥杯-Python-考前突击

额&#xff0c;&#xff0c;离蓝桥杯开赛还有十个小时&#xff0c;最近因为考研复习节奏的问题&#xff0c;把蓝桥杯的优先级后置了&#xff0c;突然才想起来还有一个蓝桥杯呢。。 到目前为止python基本语法熟练了&#xff0c;再补充一些常用函数供明天考前再背背&#xff0c;算…

榕壹云外卖跑腿系统:基于Spring Boot+MySQL+UniApp的智慧生活服务平台

项目背景与需求分析 随着本地生活服务需求的爆发式增长&#xff0c;外卖、跑腿等即时配送服务成为现代都市的刚性需求。传统平台存在开发成本高、功能定制受限等问题&#xff0c;中小企业及创业团队极需一款轻量级、可快速部署且支持二次开发的外卖跑腿系统。榕壹云外卖跑腿系统…

使用Docker安装Gogs

1、拉取镜像 docker pull gogs/gogs 2、运行容器 # 创建/var/gogs目录 mkdir -p /var/gogs# 运行容器 # -d&#xff0c;后台运行 # -p&#xff0c;端口映射&#xff1a;(宿主机端口:容器端口)->(10022:22)和(10880:3000) # -v&#xff0c;数据卷映射&#xff1a;(宿主机目…

【antd + vue】Modal 对话框:修改弹窗标题样式、Modal.confirm自定义使用

一、标题样式 1、目标样式&#xff1a;修改弹窗标题样式 2、问题&#xff1a; 直接在对应css文件中修改样式不生效。 3、原因分析&#xff1a; 可能原因&#xff1a; 选择器权重不够&#xff0c;把在控制台找到的选择器直接复制下来&#xff0c;如果还不够就再加&#xff…

Streamlit在测试领域中的应用:构建自动化测试报告生成器

引言 Streamlit 在开发大模型AI测试工具方面具有显著的重要性&#xff0c;尤其是在简化开发流程、增强交互性以及促进快速迭代等方面。以下是几个关键点&#xff0c;说明了 Streamlit 对于构建大模型AI测试工具的重要性&#xff1a; 1. 快速原型设计和迭代 对于大模型AI测试…

docker 运行自定义化的服务-后端

docker 运行自定义化的服务-前端-CSDN博客 运行自定义化的后端服务 具体如下&#xff1a; ①打包后端项目&#xff0c;形成jar包 ②编写dockerfile文件&#xff0c;文件内容如下&#xff1a; # 使用官方 OpenJDK 镜像 FROM jdk8:1.8LABEL maintainer"ATB" version&…

解决java使用easyexcel填充模版后,高度不一致问题

自定义工具&#xff0c;可以通过获取上一行行高设置后面所以行的高度 package org.springblade.modules.api.utils;import com.alibaba.excel.write.handler.RowWriteHandler; import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; import com.alibaba.excel.wr…

repo仓库文件清理

1. repo 仓库内文件清理 # 清理所有Git仓库中的项目 repo forall -c git clean -dfx # 重置所有Git 仓库中的项目 repo forall -c git reset --hard 解释&#xff1a; repo forall -c git clean -dfx&#xff1a; repo forall 是一个用于在所有项目中执行命令的工具。-c 后…

结合大语言模型整理叙述并生成思维导图的思路

楔子 我比较喜欢长篇大论。这在代理律师界被视为一种禁忌。 我高中一年级的时候因为入学成绩好&#xff08;所在县榜眼名次&#xff09;&#xff0c;直接被所在班的班主任任命为班长。我其实不喜欢这个岗位。因为老师一来就要提前注意到&#xff0c;要及时喊“起立”、英语课…

spark-core编程2

Key-Value类型&#xff1a; foldByKey 当分区内计算规则和分区间计算规则相同时&#xff0c;aggregateByKey 就可以简化为 foldByKey combineByKey 最通用的对 key-value 型 rdd 进行聚集操作的聚集函数&#xff08;aggregation function&#xff09;。类似于aggregate()&…

原理图设计准备:页面栅格模板应用设置

一、页面大小的设置 &#xff08;1&#xff09;单页原理图页面设置 首先&#xff0c;选中需要更改页面尺寸的那一页原理图&#xff0c;鼠标右键&#xff0c;选择“Schmatic Page Properties”选项&#xff0c;进行页面大小设置。 &#xff08;2&#xff09;对整个原理图页面设…

关于异步消息队列的详细解析,涵盖JMS模式对比、常用组件分析、Spring Boot集成示例及总结

以下是关于异步消息队列的详细解析&#xff0c;涵盖JMS模式对比、常用组件分析、Spring Boot集成示例及总结&#xff1a; 一、异步消息核心概念与JMS模式对比 1. 异步消息核心组件 组件作用生产者发送消息到消息代理&#xff08;如RabbitMQ、Kafka&#xff09;。消息代理中间…

【深度洞察】解码饮料行业破局点:场景革命

当东鹏特饮以 “大瓶装 防尘盖” 精准解决货车司机的场景化需求&#xff0c;当农夫山泉通过 “冷藏版东方树叶” 打开年轻白领的早餐场景 —— 这些现象级案例背后&#xff0c;是饮料行业底层逻辑的深刻变革&#xff1a;真正的市场增量&#xff0c;藏在对消费场景的极致拆解中…

二、TorchRec中的分片

TorchRec中的分片 文章目录 TorchRec中的分片前言一、Planner二、EmbeddingTable 的分片TorchRec 中所有可用的分片类型列表 三、使用 TorchRec 分片模块进行分布式训练TorchRec 在三个主要阶段处理此问题 四、DistributedModelParallel&#xff08;分布式模型并行&#xff09;…