全球人名分类案例 - 搭建 RNN 模型
1 课程概览
本课搭建全球人名分类案例的 RNN 模型。模型结构包含 RNN 层、全连接层和 LogSoftmax 激活函数,将人名字符序列转换为 18 个国家的分类结果。核心参数:input_size=57(字符表大小)、hidden_size=128、output_size=18(国家数量)。
2 核心概念与定义
- 人名分类(Name Classification):根据人名判断其所属国家。
- 字符表(Alphabet):52 个字母 + 5 个符号 = 57 个字符。
- 全连接层(Linear Layer):将隐藏状态转换为输出维度。
- LogSoftmax:将输出类别转换为对数概率分布。
3 模型与算法详解
模型构建流程
- 定义字符串:57 个字符的字符表
- 定义国家:18 个国家
- 加载数据
- 构建数据集
- 构建数据加载器
- 搭建神经网络模型:
- 6.1 搭建 RNN 网络
- 6.2 搭建 LSTM 网络
- 6.3 搭建 GRU 网络
- 测试神经网络模型
RNN 模型结构
输入 → RNN层 → 全连接层 → LogSoftmax → 输出
| 层 | 输入 | 输出 | 说明 |
|---|---|---|---|
| RNN 层 | (seq_len, batch, 57) | (seq_len, batch, 128) | 处理字符序列 |
| 全连接层 | 128 | 18 | 转换为国家分类 |
| LogSoftmax | 18 | 18 | 转换为对数概率 |
模型参数
| 参数 | 值 | 含义 |
|---|---|---|
| input_size | 57 | 字符表大小(词向量维度) |
| hidden_size | 128 | 隐藏层维度(表示能力) |
| output_size | 18 | 国家数量 |
| n_layers | 1 | RNN 层数 |
4 数学原理与推导
RNN 前向传播
$$H_t = \tanh(W_{ih} X_t + W_{hh} H_{t-1} + b_h)$$
全连接层
$$Y = W_{hy} H_{last} + b_y$$
其中 $H_{last}$ 为最后一个时间步的隐藏状态。
LogSoftmax
$$\text{LogSoftmax}(y_i) = \log\left(\frac{e^{y_i}}{\sum_j e^{y_j}}\right)$$
5 代码示例
import torch
import torch.nn as nn
class MyRNN(nn.Module):
def __init__(self, input_size, hidden_size, output_size, n_layers=1):
super(MyRNN, self).__init__()
# 1. 输入特征维度(字符表大小,57)
self.input_size = input_size
# 2. 隐藏层维度(决定模型表示能力,128)
self.hidden_size = hidden_size
# 3. 输出维度(国家数量,18)
self.output_size = output_size
# 4. RNN 层数
self.n_layers = n_layers
# 5. 定义 RNN 层
self.rnn = nn.RNN(
self.input_size, # 57
self.hidden_size, # 128
self.n_layers # 1
)
# 6. 定义全连接层(将隐藏状态转换为输出)
self.linear = nn.Linear(self.hidden_size, self.output_size)
# 7. 定义激活函数(将输出转换为对数概率分布)
self.softmax = nn.LogSoftmax(dim=1)
def forward(self, input, hidden):
# 添加批次维度:input 从 (seq_len, input_size) → (seq_len, batch, input_size)
input = input.unsqueeze(1)
# RNN 计算
output, hn = self.rnn(input, hidden)
# 取最后一个时间步的输出
output = output[-1]
# 全连接层
output = self.linear(output)
# LogSoftmax
output = self.softmax(output)
return output, hn
def initHidden(self):
# 初始化隐藏状态
return torch.zeros(self.n_layers, 1, self.hidden_size)
# 实例化模型
my_rnn = MyRNN(
input_size=57, # 字符表大小
hidden_size=128, # 隐藏层维度
output_size=18, # 国家数量
n_layers=1 # RNN 层数
)
print(my_rnn)
输出示例
MyRNN(
(rnn): RNN(57, 128)
(linear): Linear(in_features=128, out_features=18, bias=True)
(softmax): LogSoftmax(dim=1)
)
6 重难点与易错提醒
- ❗重点:模型结构 = RNN 层 + 全连接层 + LogSoftmax。
- ❗重点:
input_size=57(字符表),hidden_size=128,output_size=18(国家)。 - ⚠️易错:
forward中需要给input添加批次维度(unsqueeze(1))。 - ⚠️易错:取最后一个时间步的输出(
output[-1])。 - 💡深入理解:
hidden_size越大,模型表示能力越强,但计算量也越大。 - 💡深入理解:LogSoftmax 与 NLLLoss 组合使用,等价于 CrossEntropyLoss。
7 课堂问答精选
Q1:RNN 模型的结构是什么?
A:RNN 层 + 全连接层 + LogSoftmax。RNN 层处理字符序列,全连接层将隐藏状态转换为国家分类,LogSoftmax 转换为对数概率分布。
Q2:模型的三个核心参数是什么?
A:input_size=57(字符表大小)、hidden_size=128(隐藏层维度)、output_size=18(国家数量)。
Q3:为什么 forward 中要给 input 添加批次维度?
A:因为外部传入的 input 形状为 (seq_len, input_size),而 RNN 需要的形状为 (seq_len, batch, input_size),所以需要用 unsqueeze(1) 添加批次维度。
Q4:LogSoftmax 和 CrossEntropyLoss 有什么关系?
A:LogSoftmax 与 NLLLoss 组合使用,等价于 CrossEntropyLoss。如果使用 CrossEntropyLoss,则模型不需要加 LogSoftmax。
8 本课小结
- 模型结构:RNN 层 → 全连接层 → LogSoftmax。
- 核心参数:
input_size=57、hidden_size=128、output_size=18。 forward中需添加批次维度(unsqueeze(1))。- 取最后一个时间步的输出(
output[-1])。 - LogSoftmax + NLLLoss = CrossEntropyLoss。
9 延伸思考
- 如何选择合适的
hidden_size? - RNN 层数对模型性能有什么影响?