英译法案例 - 基于 GRU 的无 Attention 的解码器思路分析
1 课程概览
本课分析英译法案例中基于 GRU 的无 Attention 解码器的思路。解码器接收本次输入和上次隐藏状态,经过词嵌入层、GRU、线性层、LogSoftmax,输出概率最高的词。法语词汇表大小为 4345。
2 核心概念与定义
- 解码器输入:本次输入(1 个词)、上次隐藏状态。
- 词嵌入层(Embedding):将词转换为词向量。
- GRU:处理序列数据。
- 线性层(Linear):将隐藏状态映射到词汇表大小。
- LogSoftmax:转概率(取对数)。
- 法语词汇表大小:4345。
3 模型与算法详解
解码器流程
1. input (1×1) → Embedding → (1×1×256)
2. (1×1×256) → GRU → output (1×1×256), hidden (1×1×256)
3. output (1×1×256) → Linear → (1×4345)
4. (1×4345) → LogSoftmax → 概率分布
5. 选择概率最高的词作为输出
形状变化
| 步骤 | 输入形状 | 输出形状 | 说明 |
|---|---|---|---|
| Embedding | [1, 1] | [1, 1, 256] | 词嵌入 |
| GRU | [1, 1, 256] | [1, 1, 256] | GRU 处理 |
| Linear | [1, 256] | [1, 4345] | 线性层 |
| LogSoftmax | [1, 4345] | [1, 4345] | 转概率 |
关键参数
| 参数 | 值 | 说明 |
|---|---|---|
| hidden_size | 256 | 隐藏层维度 |
| output_size | 4345 | 法语词汇表大小 |
无 Attention 解码器结构
input → Embedding → GRU → Linear → LogSoftmax → output
↑
prev_hidden
4 数学原理与推导
词嵌入
$$\text{embedded} = \text{Embedding}(\text{input})$$
GRU
$$\text{output}, \text{hidden} = \text{GRU}(\text{embedded}, \text{prev_hidden})$$
线性层
$$\text{output} = \text{Linear}(\text{output})$$
LogSoftmax
$$\text{output} = \text{LogSoftmax}(\text{output})$$
5 代码示例
import torch
import torch.nn as nn
import torch.nn.functional as F
class DecoderGRU(nn.Module):
"""基于 GRU 的无 Attention 解码器"""
def __init__(self, hidden_size, output_size):
"""
初始化函数
Args:
hidden_size: 隐藏层维度(256)
output_size: 输出维度(4345,法语词汇表大小)
"""
super(DecoderGRU, self).__init__()
self.hidden_size = hidden_size
# 1. 词嵌入层
self.embedding = nn.Embedding(output_size, hidden_size)
# 2. GRU 层(输入 256,输出 256)
self.gru = nn.GRU(hidden_size, hidden_size)
# 3. 线性层(256 → 4345)
self.out = nn.Linear(hidden_size, output_size)
# 4. LogSoftmax
self.softmax = nn.LogSoftmax(dim=1)
def forward(self, input, hidden):
"""
前向传播
Args:
input: 本次输入 [1, 1]
hidden: 上次隐藏状态 [1, 1, 256]
Returns:
output: 概率分布 [1, 4345]
hidden: 本次隐藏状态 [1, 1, 256]
"""
# 1. 词嵌入:[1, 1] → [1, 1, 256]
embedded = self.embedding(input).view(1, 1, -1)
embedded = F.relu(embedded)
# 2. GRU:[1, 1, 256] → [1, 1, 256], [1, 1, 256]
output, hidden = self.gru(embedded, hidden)
# 3. 线性层:[1, 256] → [1, 4345]
output = self.out(output[0])
# 4. LogSoftmax:[1, 4345] → [1, 4345]
output = self.softmax(output)
return output, hidden
def initHidden(self):
"""初始化隐藏状态"""
return torch.zeros(1, 1, self.hidden_size)
# 测试
if __name__ == "__main__":
print("=== 英译法案例 - 基于 GRU 的无 Attention 解码器 ===")
hidden_size = 256
output_size = 4345 # 法语词汇表大小
# 创建解码器
decoder = DecoderGRU(hidden_size, output_size)
# 创建输入
input_tensor = torch.tensor([[1]]) # [1, 1]
hidden = decoder.initHidden() # [1, 1, 256]
print(f"输入形状: {input_tensor.shape}")
print(f"隐藏状态形状: {hidden.shape}")
# 前向传播
output, hidden = decoder(input_tensor, hidden)
print(f"\n输出形状: {output.shape}") # [1, 4345]
print(f"隐藏状态形状: {hidden.shape}") # [1, 1, 256]
print(f"概率最高的词索引: {output.topk(1).indices.item()}")
代码说明
| 代码 | 说明 |
|---|---|
nn.Embedding(output_size, hidden_size) | 词嵌入层 |
nn.GRU(hidden_size, hidden_size) | GRU 层 |
nn.Linear(hidden_size, output_size) | 线性层 |
nn.LogSoftmax(dim=1) | LogSoftmax |
F.relu(embedded) | 激活函数 |
6 重难点与易错提醒
- ❗重点:法语词汇表大小为 4345(不是 28036)。
- ❗重点:使用 LogSoftmax 时,损失函数必须用 NLLLoss。
- ❗重点:Linear 层会自动降维,[1, 1, 256] → [1, 256] → [1, 4345]。
- ⚠️易错:使用 LogSoftmax 后不能再使用 CrossEntropyLoss(会重复取对数)。
7 课堂问答精选
Q1:解码器的输出维度是多少?
A:解码器的输出维度是 4345(法语词汇表大小)。
Q2:为什么使用 LogSoftmax 而不是 Softmax?
A:使用 LogSoftmax 后,损失函数用 NLLLoss。如果使用 CrossEntropyLoss,会重复取对数,导致结果不准。
Q3:Linear 层如何处理三维张量?
A:Linear 层会自动降维,[1, 1, 256] → [1, 256] → [1, 4345]。
8 本课小结
- 解码器流程:input → Embedding → GRU → Linear → LogSoftmax → output。
- 法语词汇表大小为 4345。
- 使用 LogSoftmax 时,损失函数必须用 NLLLoss。
9 延伸思考
- 如何构建带 Attention 的解码器?
- 如何测试无 Attention 的解码器?