基于 Transformer 架构的翻译模型实践 - SentencePiece 输出的 token ID 到 Transformer 可处理的词向量

基于 Transformer 架构的翻译模型实践 - SentencePiece 输出的 token ID 到 Transformer 可处理的词向量

flyfish

参考

bash 复制代码
https://github.com/shaoshengsong/ pytorch -transformer-en-zh-translation-demo

本文的完整代码在文末

文本 → Token ID → 词嵌入 → 位置编码 → Transformer 编码器

SentencePiece 输出的是整数类型的 Token ID ,这些 ID 是词嵌入层(Embedding Layer)的索引

Transformer 不直接接收 ID,而是接收连续的低维词向量 ,转换过程就是:通过 ID 查表(Lookup Table)取出对应的向量

  1. 编码器侧:英文句子 → SP英文分词 → Embedding → +位置编码 → 编码器输入
  2. 解码器侧:中文标签 → SP中文分词 → Embedding → +位置编码 → 解码器输入

流程(训练/推理通用)

步骤1:SentencePiece 编码得到 Token ID

输入英文句子 → SentencePiece 分词 → 输出整数 ID 序列

例:"I love translation"[10, 256, 1890, 2](包含开始/结束符)

步骤2:构造模型输入张量

将 ID 序列封装为批量张量,形状:[batch_size, sequence_length]

(Transformer 必须接收批量输入,单条推理也需要加 batch 维度)

步骤3:词嵌入层查表生成词向量

PyTorch 的 Embedding 层内部维护一个权重矩阵

行:对应所有 Token ID(0 ~ 词表大小-1)

列:词向量维度(Transformer 常用 512)

输入 ID → 作为索引 → 直接取出对应行的向量 → 得到词向量

步骤4:添加位置编码

词向量 + 位置编码 是最终输入给编码器/解码器的张量。

维度关系

词嵌入层的输入维度

形状:[batch_size, sequence_length]
和词表大小没有任何关系!

只和「批量大小」「句子最大长度」有关。

词嵌入层的权重矩阵维度

形状:[vocab_size, d_model]
词表大小 = 权重矩阵的行数

词表越大,需要存储的词向量越多,模型参数越大。

输出维度(词向量)

形状:[batch_size, sequence_length, d_model]

这就是 Transformer 编码器/解码器的标准输入。

python 复制代码
import torch
import torch.nn as nn
import math

# ===================== 超参数设置 =====================
d_model = 512    # 模型向量维度
vocab_size = 32000  # 模拟词表大小 (和SentencePiece一致)
max_seq_len = 10    # 句子最大长度
batch_size = 1      # 批量大小(单句推理)

# ===================== 1. 模拟 SentencePiece 分词 =====================
# 原始英文句子
sentence = "Hello, this is machine translation"
# 模拟SP输出的Token ID(替代外部模型,保证代码可直接运行)
token_ids = [1, 8667, 4, 57, 16, 2702, 7962, 2]
print("原始句子:", sentence)
print("Token ID 序列:", token_ids)

# 填充到固定长度 + 构造模型输入张量 [batch_size, seq_len]
padding_length = max_seq_len - len(token_ids)
token_ids += [0] * padding_length  # 用<pad>填充
input_ids = torch.tensor([token_ids])
print("输入张量形状 (input_ids):", input_ids.shape)  # [1, 10]

# ===================== 2. 词嵌入层 (ID → 连续向量) =====================
embedding = nn.Embedding(num_embeddings=vocab_size, embedding_dim=d_model)
word_embeddings = embedding(input_ids)  # 查表生成词向量
print("词向量形状 (word_embeddings):", word_embeddings.shape)  # [1, 10, 512]

# ===================== 3. 标准位置编码 (Transformer 官方实现) =====================
class PositionalEncoding(nn.Module):
    def __init__(self, d_model, max_len=5000):
        super().__init__()
        position = torch.arange(max_len).unsqueeze(1)
        div_term = torch.exp(torch.arange(0, d_model, 2) * (-math.log(10000.0) / d_model))
        pe = torch.zeros(1, max_len, d_model)
        pe[0, :, 0::2] = torch.sin(position * div_term)
        pe[0, :, 1::2] = torch.cos(position * div_term)
        self.register_buffer('pe', pe)

    def forward(self, x):
        return x + self.pe[:, :x.size(1)]

# 初始化位置编码
pos_encoder = PositionalEncoding(d_model)
# 词向量 + 位置编码 → Transformer 最终输入
transformer_input = pos_encoder(word_embeddings)
print("加入位置编码后形状:", transformer_input.shape)  # [1, 10, 512]

# ===================== 4. 输入 Transformer 编码器 =====================
# 定义极简Transformer编码器
encoder_layer = nn.TransformerEncoderLayer(d_model=d_model, nhead=8, batch_first=True)
transformer_encoder = nn.TransformerEncoder(encoder_layer, num_layers=2)

# 前向传播
encoder_output = transformer_encoder(transformer_input)
print("Transformer编码器输出形状:", encoder_output.shape)  # [1, 10, 512]

# ===================== 结束 =====================
print("\n代码执行成功!全流程数据流完成!")

输出

python 复制代码
原始句子: Hello, this is machine translation
Token ID 序列: [1, 8667, 4, 57, 16, 2702, 7962, 2]
输入张量形状 (input_ids): torch.Size([1, 10])
词向量形状 (word_embeddings): torch.Size([1, 10, 512])
加入位置编码后形状: torch.Size([1, 10, 512])
Transformer编码器输出形状: torch.Size([1, 10, 512])

代码执行成功!全流程数据流完成!

手动模拟查表nn.Embedding 查表做对比
Token ID = 行号,直接取矩阵的一行就是词向量
nn.Embedding(input_ids) 等价于 查找表[input_ids]
Token ID 就是行号,没有任何计算,纯索引取值!

python 复制代码
import torch

# ===================== 1. 构造一个「嵌入查找表」(矩阵) =====================
# 词表大小 = 5 (ID 0,1,2,3,4)
# 词向量维度 = 3
# 这就是 nn.Embedding 内部的权重矩阵!
lookup_table = torch.tensor([
    [0.1, 0.2, 0.3],   # ID=0 对应的向量
    [0.4, 0.5, 0.6],   # ID=1 对应的向量
    [0.7, 0.8, 0.9],   # ID=2 对应的向量
    [1.0, 1.1, 1.2],   # ID=3 对应的向量
    [1.3, 1.4, 1.5]    # ID=4 对应的向量
])

# ===================== 2. 输入 Token ID,直接查表取向量 =====================
token_id = 2  # 我们要查 ID=2 的向量

# 直接用 ID 当索引,取矩阵的第 2 行!
vector = lookup_table[token_id]

# ===================== 打印结果 =====================
print("查找表(嵌入矩阵):")
print(lookup_table)
print(f"\nToken ID = {token_id}")
print(f"直接查表取出的向量:{vector.numpy()}")


import torch
import torch.nn as nn

# 1. 定义 Embedding 层
emb = nn.Embedding(num_embeddings=5, embedding_dim=3)
# 2. 把上面的查找表赋值给 Embedding 的权重
emb.weight = torch.nn.Parameter(lookup_table)

# 3. 输入 ID=2
token_id = torch.tensor([2])
# 4. nn.Embedding 内部就是做了 lookup_table[token_id]
vector = emb(token_id)

print("nn.Embedding 取出的向量:", vector.detach().numpy()[0])

输出

python 复制代码
查找表(嵌入矩阵):
tensor([[0.1000, 0.2000, 0.3000],
        [0.4000, 0.5000, 0.6000],
        [0.7000, 0.8000, 0.9000],
        [1.0000, 1.1000, 1.2000],
        [1.3000, 1.4000, 1.5000]])

Token ID = 2
直接查表取出的向量:[0.7 0.8 0.9]
nn.Embedding 取出的向量: [0.7 0.8 0.9]
相关推荐
装不满的克莱因瓶1 小时前
链式法则如何传递参数误差 —— 深入理解神经网络中的梯度传播
人工智能·python·深度学习·神经网络·数学·机器学习·ai
AndrewHZ12 小时前
【LLM技术全景】规模定律与模型演进:为什么模型越大越强?
人工智能·gpt·深度学习·语言模型·llm·openai·规模定律
手写码匠13 小时前
从零实现 Prompt 工程引擎:结构化提示、自动优化与多轮自省体系
人工智能·深度学习·算法·aigc
哈伦201913 小时前
第十二章 深度学习基础 案例:MLP实现银行单据手写数字识别
人工智能·深度学习·图像识别
lqqjuly13 小时前
MLA — 多头潜在注意力深度解析
深度学习·神经网络·算法
Black蜡笔小新13 小时前
企业AI算力工作站DLTM深度学习推理工作站零代码私有化重塑企业AI落地新模式
人工智能·深度学习
啦啦啦_999914 小时前
4. Transformer_4_输出部分
人工智能·深度学习·transformer
CV-deeplearning14 小时前
李沐论文精读合集:67 篇深度学习经典论文逐段精读,从 AlexNet 到 Sora,B 站播放百万级的 AI 自学圣经
gpt·大模型·transformer·李沐·论文精读·ai学习路线
DogDaoDao15 小时前
【GitHub】VoxCPM2 实战全解析:原理、部署与效果对比
深度学习·大模型·github·音频·语音模型·tss·文本生成语音
不考研当牛马16 小时前
Django 框架 深度学习
python·深度学习·django