外观
词元id转嵌入向量
约 3315 字大约 11 分钟
设计思路与核心概念
1. 嵌入向量的背景与动机
嵌入向量(Embedding Vector)是深度学习中将离散符号转换为连续向量表示的核心技术,主要解决以下问题:
- 离散到连续:将离散的token ID转换为连续的向量空间,便于神经网络处理
- 语义表示:通过向量空间中的距离和方向表示词汇间的语义关系
- 可学习性:嵌入向量可以通过反向传播学习,自动捕获语义信息
- 位置感知:结合位置嵌入,让模型理解序列中的位置信息
2. 核心设计思想
词元嵌入的核心思想是将每个token ID映射到一个固定维度的稠密向量:
- 查表机制:通过可学习的查找表将整数ID映射为向量
- 语义空间:相似的词汇在向量空间中距离较近
- 位置编码:通过位置嵌入为每个位置分配独特的向量表示
- 向量加法:词嵌入与位置嵌入相加,形成最终的输入表示
3. 技术架构
嵌入系统组成
Token ID → 词嵌入查找 → 词向量
位置索引 → 位置嵌入查找 → 位置向量
词向量 + 位置向量 → 最终嵌入向量
核心组件
- 词嵌入层:将token ID映射为语义向量
- 位置嵌入层:将位置索引映射为位置向量
- 向量加法:组合词义和位置信息
- 参数矩阵:可学习的嵌入权重矩阵
4. 数学原理
词嵌入数学表示
对于词汇表大小为V,嵌入维度为d的词嵌入:
Etoken∈RV×d
给定token ID序列 [t1,t2,...,tn],词嵌入为:
ei=Etoken[ti,:]
位置嵌入数学表示
对于最大序列长度为L的位置嵌入:
Epos∈RL×d
位置i的嵌入为:
pi=Epos[i,:]
最终嵌入表示
hi=ei+pi
执行流程
1. 整体执行流程图
2. 详细计算流程图
处理步骤详解
步骤 | 操作描述 | 核心方法 | 输入示例 | 输出示例 |
---|---|---|---|---|
1 | 初始化词嵌入层 | nn.Embedding() | vocab_size=6, dim=3 | 嵌入层对象 |
2 | 初始化位置嵌入层 | nn.Embedding() | max_len=4, dim=3 | 位置嵌入层对象 |
3 | 生成位置索引 | torch.arange() | max_seq_len=4 | [0, 1, 2, 3] |
4 | 词嵌入查找 | embedding_layer(ids) | [2, 3, 5, 1] | (4, 3)张量 |
5 | 位置嵌入查找 | pos_emb_layer(pos) | [0, 1, 2, 3] | (4, 3)张量 |
6 | 向量加法 | token_emb + pos_emb | 两个(4,3)张量 | (4, 3)组合张量 |
完整代码实现
词元id转嵌入向量.py
import torch
# 创建输入样本:包含4个token索引的张量
input_ids = torch.tensor([2, 3, 5, 1])
# 定义模型参数
vocab_size = 6 # 词汇表大小
output_dim = 3 # 输出维度
max_seq_len = 4 # 最大序列长度
# 固定随机种子
torch.manual_seed(123)
# 初始化词嵌入层(Token Embedding)
embedding_layer = torch.nn.Embedding(vocab_size, output_dim)
# 新增位置嵌入层(Position Embedding)
position_emb_layer = torch.nn.Embedding(
num_embeddings=max_seq_len,
embedding_dim=output_dim
)
# 生成位置索引序列(0~3)
positions = torch.arange(max_seq_len)
# 组合双重嵌入
def embed_with_position(input_ids):
# 词嵌入查询
token_emb = embedding_layer(input_ids) # (4,3)
# 位置嵌入查询
pos_emb = position_emb_layer(positions) # (4,3)
return token_emb + pos_emb
# ==== 打印所有中间结果 ====
# 原始词嵌入矩阵
print("词嵌入矩阵:")
print(embedding_layer.weight)
"""
词嵌入矩阵:
tensor([[ 0.3374, -0.1778, -0.1690],
[ 0.9178, 1.5810, 1.3010],
[ 1.2753, -0.2010, -0.1606],
[-0.4015, 0.9666, -1.1481],
[-1.1589, 0.3255, -0.6315],
[-2.8400, -0.7849, -1.4096]], requires_grad=True)
"""
# 纯词嵌入结果(无位置)
print("\n词元嵌入结果(原始语义向量):")
token_vectors = embedding_layer(input_ids)
print(token_vectors)
"""
词元嵌入结果:
tensor([[ 1.2753, -0.2010, -0.1606],
[-0.4015, 0.9666, -1.1481],
[-2.8400, -0.7849, -1.4096],
[ 0.9178, 1.5810, 1.3010]], grad_fn=<EmbeddingBackward0>)
"""
# 位置嵌入矩阵
print("\n位置嵌入矩阵:")
print(position_emb_layer.weight)
"""
位置嵌入矩阵:
tensor([[-0.6307, 1.2340, 0.3127],
[ 0.6972, -0.9950, -1.1476],
[-0.9178, 0.9045, -2.0975],
[ 1.1558, -1.2157, 0.1295]], requires_grad=True)
"""
# 组合嵌入结果
print("\n组合嵌入(词嵌入 + 位置嵌入):")
print(embed_with_position(input_ids))
"""
组合嵌入结果:
tensor([[ 0.6446, 1.0331, 0.1521], # 词嵌入[2] + 位置0
[ 0.2957, -0.0285, -2.2958], # 词嵌入[3] + 位置1
[-3.7578, 0.1197, -3.5071], # 词嵌入[5] + 位置2
[ 2.0735, 0.3653, 1.4306]], # 词嵌入[1] + 位置3
grad_fn=<AddBackward0>)
"""
代码详细解析
1. 参数配置与初始化
核心参数设计
vocab_size = 6 # 词汇表大小
output_dim = 3 # 输出维度
max_seq_len = 4 # 最大序列长度
参数说明:
vocab_size
:词汇表中不同token的数量,决定词嵌入矩阵的行数output_dim
:每个嵌入向量的维度,决定嵌入矩阵的列数max_seq_len
:支持的最大序列长度,决定位置嵌入矩阵的行数
随机种子设置
torch.manual_seed(123)
作用:
- 可重现性:确保每次运行得到相同的随机初始化结果
- 调试便利:便于验证代码正确性和对比结果
- 实验一致性:保证实验结果的可重复性
2. 嵌入层架构设计
词嵌入层(Token Embedding)
embedding_layer = torch.nn.Embedding(vocab_size, output_dim)
内部结构:
- 权重矩阵:形状为
(vocab_size, output_dim)
=(6, 3)
- 查找机制:通过索引直接访问对应行向量
- 可学习性:权重矩阵参数可通过反向传播更新
位置嵌入层(Position Embedding)
position_emb_layer = torch.nn.Embedding(
num_embeddings=max_seq_len,
embedding_dim=output_dim
)
设计特点:
- 位置感知:为序列中每个位置分配独特向量
- 学习能力:位置表示可以通过训练学习
- 灵活性:相比固定的正弦位置编码更加灵活
3. 嵌入查找机制详解
词嵌入查找过程
input_ids = torch.tensor([2, 3, 5, 1])
token_emb = embedding_layer(input_ids) # (4,3)
查找过程:
- 索引映射:input_ids[0]=2 → 嵌入矩阵第2行
- 向量提取:提取对应的3维向量
- 批量处理:同时处理整个序列的所有token
位置嵌入查找过程
positions = torch.arange(max_seq_len) # [0, 1, 2, 3]
pos_emb = position_emb_layer(positions) # (4,3)
位置生成:
- 自动生成:使用
torch.arange
生成连续位置索引 - 固定模式:位置索引总是从0开始递增
- 长度匹配:位置序列长度与输入序列长度一致
4. 向量组合机制
加法组合策略
def embed_with_position(input_ids):
token_emb = embedding_layer(input_ids) # 词语义
pos_emb = position_emb_layer(positions) # 位置信息
return token_emb + pos_emb # 向量加法
组合原理:
- 线性组合:简单的向量加法,保持维度不变
- 信息融合:词义信息与位置信息线性叠加
- 计算效率:加法运算计算成本低,易于并行
替代组合方式
# 方式1:拼接组合(维度翻倍)
def embed_with_concat(input_ids):
token_emb = embedding_layer(input_ids)
pos_emb = position_emb_layer(positions)
return torch.cat([token_emb, pos_emb], dim=-1) # (4, 6)
# 方式2:加权组合
def embed_with_weighted(input_ids, alpha=0.8):
token_emb = embedding_layer(input_ids)
pos_emb = position_emb_layer(positions)
return alpha * token_emb + (1-alpha) * pos_emb
5. 输出结果分析
词嵌入矩阵解读
# 词嵌入矩阵形状:(6, 3)
embedding_matrix = [
[ 0.3374, -0.1778, -0.1690], # token_id=0
[ 0.9178, 1.5810, 1.3010], # token_id=1
[ 1.2753, -0.2010, -0.1606], # token_id=2
[-0.4015, 0.9666, -1.1481], # token_id=3
[-1.1589, 0.3255, -0.6315], # token_id=4
[-2.8400, -0.7849, -1.4096], # token_id=5
]
矩阵特点:
- 随机初始化:初始值为随机数,通过训练学习语义
- 可学习参数:每个元素都是可训练参数
- 语义潜力:训练后相似词汇的向量会在空间中靠近
位置嵌入矩阵解读
# 位置嵌入矩阵形状:(4, 3)
position_matrix = [
[-0.6307, 1.2340, 0.3127], # position=0
[ 0.6972, -0.9950, -1.1476], # position=1
[-0.9178, 0.9045, -2.0975], # position=2
[ 1.1558, -1.2157, 0.1295], # position=3
]
位置特征:
- 位置唯一性:每个位置有独特的向量表示
- 学习能力:可以学习位置间的相对关系
- 序列感知:帮助模型理解词汇在序列中的位置
组合结果分析
# 组合嵌入结果:词嵌入 + 位置嵌入
combined_embedding = [
[ 0.6446, 1.0331, 0.1521], # token[2] + pos[0]
[ 0.2957, -0.0285, -2.2958], # token[3] + pos[1]
[-3.7578, 0.1197, -3.5071], # token[5] + pos[2]
[ 2.0735, 0.3653, 1.4306], # token[1] + pos[3]
]
组合效果:
- 信息融合:每个向量同时包含词义和位置信息
- 维度保持:输出维度与输入嵌入维度相同
- 可微分性:整个过程可微分,支持梯度反传
高级特性与扩展
1. 嵌入层优化技术
权重初始化策略
import torch.nn.init as init
def initialize_embeddings(embedding_layer, method='xavier'):
"""自定义嵌入层初始化"""
if method == 'xavier':
init.xavier_uniform_(embedding_layer.weight)
elif method == 'normal':
init.normal_(embedding_layer.weight, mean=0, std=0.1)
elif method == 'kaiming':
init.kaiming_uniform_(embedding_layer.weight)
return embedding_layer
# 使用示例
embedding_layer = torch.nn.Embedding(vocab_size, output_dim)
embedding_layer = initialize_embeddings(embedding_layer, 'xavier')
嵌入层冻结技术
def freeze_embeddings(embedding_layer):
"""冻结嵌入层参数,不参与训练"""
for param in embedding_layer.parameters():
param.requires_grad = False
return embedding_layer
def unfreeze_embeddings(embedding_layer):
"""解冻嵌入层参数,恢复训练"""
for param in embedding_layer.parameters():
param.requires_grad = True
return embedding_layer
2. 预训练嵌入集成
加载预训练嵌入
def load_pretrained_embeddings(vocab_size, embedding_dim, pretrained_path):
"""加载预训练的嵌入向量"""
embedding_layer = torch.nn.Embedding(vocab_size, embedding_dim)
# 加载预训练权重
pretrained_weights = torch.load(pretrained_path)
embedding_layer.weight.data.copy_(pretrained_weights)
return embedding_layer
# 部分更新策略
def update_embeddings_partially(embedding_layer, new_tokens_embeddings, start_idx):
"""部分更新嵌入矩阵(如添加新词汇)"""
with torch.no_grad():
embedding_layer.weight[start_idx:start_idx+len(new_tokens_embeddings)] = new_tokens_embeddings
3. 动态嵌入技术
自适应位置嵌入
class AdaptivePositionEmbedding(torch.nn.Module):
"""自适应位置嵌入,支持可变序列长度"""
def __init__(self, max_len, embedding_dim):
super().__init__()
self.max_len = max_len
self.embedding_dim = embedding_dim
self.embeddings = torch.nn.Embedding(max_len, embedding_dim)
def forward(self, seq_len):
"""根据实际序列长度生成位置嵌入"""
positions = torch.arange(seq_len)
return self.embeddings(positions)
# 使用示例
adaptive_pos_emb = AdaptivePositionEmbedding(max_len=100, embedding_dim=3)
pos_emb_short = adaptive_pos_emb(4) # 短序列
pos_emb_long = adaptive_pos_emb(20) # 长序列
相对位置嵌入
class RelativePositionEmbedding(torch.nn.Module):
"""相对位置嵌入,关注位置间的相对关系"""
def __init__(self, max_relative_distance, embedding_dim):
super().__init__()
self.max_relative_distance = max_relative_distance
vocab_size = 2 * max_relative_distance + 1
self.embeddings = torch.nn.Embedding(vocab_size, embedding_dim)
def forward(self, seq_len):
"""生成相对位置嵌入矩阵"""
positions = torch.arange(seq_len)
relative_positions = positions.unsqueeze(0) - positions.unsqueeze(1)
# 截断到最大相对距离
relative_positions = torch.clamp(
relative_positions,
-self.max_relative_distance,
self.max_relative_distance
)
# 转换为正索引
relative_positions += self.max_relative_distance
return self.embeddings(relative_positions)
4. 嵌入分析工具
嵌入相似度分析
def analyze_embedding_similarity(embedding_layer, token_ids):
"""分析嵌入向量间的相似度"""
embeddings = embedding_layer(torch.tensor(token_ids))
# 计算余弦相似度
similarity_matrix = torch.nn.functional.cosine_similarity(
embeddings.unsqueeze(1),
embeddings.unsqueeze(0),
dim=2
)
return similarity_matrix
def find_nearest_neighbors(embedding_layer, target_id, k=5):
"""找到最相似的k个嵌入向量"""
target_emb = embedding_layer.weight[target_id]
# 计算与所有嵌入的相似度
similarities = torch.nn.functional.cosine_similarity(
target_emb.unsqueeze(0),
embedding_layer.weight,
dim=1
)
# 找到最相似的k个(排除自身)
_, indices = similarities.topk(k+1)
return indices[1:] # 排除自身
嵌入可视化
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
import numpy as np
def visualize_embeddings(embedding_layer, token_names=None):
"""可视化嵌入向量(降维到2D)"""
embeddings = embedding_layer.weight.detach().numpy()
# PCA降维
pca = PCA(n_components=2)
embeddings_2d = pca.fit_transform(embeddings)
# 绘制散点图
plt.figure(figsize=(10, 8))
plt.scatter(embeddings_2d[:, 0], embeddings_2d[:, 1])
# 添加标签
if token_names:
for i, name in enumerate(token_names):
plt.annotate(name, (embeddings_2d[i, 0], embeddings_2d[i, 1]))
else:
for i in range(len(embeddings_2d)):
plt.annotate(f'token_{i}', (embeddings_2d[i, 0], embeddings_2d[i, 1]))
plt.title('Token Embeddings Visualization (PCA)')
plt.xlabel(f'PC1 (explained variance: {pca.explained_variance_ratio_[0]:.2%})')
plt.ylabel(f'PC2 (explained variance: {pca.explained_variance_ratio_[1]:.2%})')
plt.grid(True)
plt.show()
5. 性能优化策略
批量处理优化
class BatchEmbedding(torch.nn.Module):
"""支持批量处理的嵌入层"""
def __init__(self, vocab_size, embedding_dim, max_seq_len):
super().__init__()
self.token_embedding = torch.nn.Embedding(vocab_size, embedding_dim)
self.position_embedding = torch.nn.Embedding(max_seq_len, embedding_dim)
def forward(self, input_ids):
"""
input_ids: (batch_size, seq_len)
返回: (batch_size, seq_len, embedding_dim)
"""
batch_size, seq_len = input_ids.shape
# 生成位置索引
positions = torch.arange(seq_len).unsqueeze(0).expand(batch_size, -1)
# 获取嵌入
token_emb = self.token_embedding(input_ids)
pos_emb = self.position_embedding(positions)
return token_emb + pos_emb
# 使用示例
batch_embedding = BatchEmbedding(vocab_size=1000, embedding_dim=128, max_seq_len=512)
batch_input = torch.randint(0, 1000, (32, 128)) # (batch_size=32, seq_len=128)
batch_output = batch_embedding(batch_input) # (32, 128, 128)
内存优化技术
def create_memory_efficient_embedding(vocab_size, embedding_dim, sparse=True):
"""创建内存优化的嵌入层"""
if sparse:
# 使用稀疏嵌入减少内存占用
embedding = torch.nn.Embedding(vocab_size, embedding_dim, sparse=True)
else:
embedding = torch.nn.Embedding(vocab_size, embedding_dim)
return embedding
def gradient_checkpointing_embedding(embedding_layer, input_ids):
"""使用梯度检查点减少内存占用"""
return torch.utils.checkpoint.checkpoint(embedding_layer, input_ids)
实际应用场景
1. 在Transformer中的应用
- 输入层:将token序列转换为向量序列
- 位置编码:为自注意力机制提供位置信息
- 多模态:结合文本、图像等不同模态的嵌入
2. 预训练模型集成
- BERT:使用WordPiece嵌入 + 位置嵌入 + 段嵌入
- GPT:使用BPE嵌入 + 学习的位置嵌入
- T5:使用相对位置嵌入替代绝对位置嵌入
3. 下游任务适配
- 分类任务:添加特殊分类token的嵌入
- 生成任务:支持动态词汇表扩展
- 多语言:使用多语言共享嵌入空间
实践建议
1. 参数设置指南
- 嵌入维度:通常为128、256、512、768等2的幂次
- 词汇表大小:根据数据集和任务需求确定
- 位置编码长度:设置为预期的最大序列长度
2. 训练策略
- 学习率:嵌入层通常使用较小的学习率
- 正则化:可以使用dropout防止过拟合
- 预训练:优先使用预训练的嵌入向量
3. 调试技巧
- 可视化:定期可视化嵌入向量的分布
- 相似度检查:验证相似词汇的嵌入是否接近
- 梯度监控:监控嵌入层的梯度更新情况
更新日志
2025/8/18 00:31
查看所有更新日志
bd1d0
-迁移目录于b0f2a
-docs: 完善大模型学习文档 - 增加设计思路与执行流程于dfb81
-update于dc6b2
-update于
版权所有
版权归属:NateHHX