第三章: 神经网络原理详解与Pytorch入门
第二部分:深度学习框架PyTorch入门
第四节:PyTorch 实战
内容:使用 RNN进行人名分类
任务简介
本任务旨在使用 循环神经网络(RNN) 对输入的英文人名进行分类,判断其所属语言(如English、French、Chinese等)。该项目常用于学习字符级别的RNN序列建模。
一、准备数据
PyTorch 官方教程使用的名字数据集来源于 names.tar.gz,每种语言对应一个文本文件,文件中包含若干名字。
1. 加载依赖
import os
import unicodedata
import string
import torch
2. 处理字符集与标准化
all_letters = string.ascii_letters + " .,;'"
n_letters = len(all_letters)
def unicodeToAscii(s):
return ''.join(
c for c in unicodedata.normalize('NFD', s)
if unicodedata.category(c) != 'Mn' and c in all_letters
)
3. 读取每种语言的名字列表
category_lines = {} # dict: language -> [name1, name2, ...]
all_categories = []
def readLines(filename):
with open(filename, encoding='utf-8') as f:
return [unicodeToAscii(line.strip()) for line in f]
data_path = './data/names/'
for filename in os.listdir(data_path):
category = os.path.splitext(filename)[0]
all_categories.append(category)
lines = readLines(os.path.join(data_path, filename))
category_lines[category] = lines
n_categories = len(all_categories)
二、构建输入张量
def letterToTensor(letter):
tensor = torch.zeros(1, n_letters)
tensor[0][all_letters.find(letter)] = 1
return tensor
def lineToTensor(line):
tensor = torch.zeros(len(line), 1, n_letters)
for li, letter in enumerate(line):
tensor[li][0][all_letters.find(letter)] = 1
return tensor
三、构建 RNN 模型
import torch.nn as nn
class RNN(nn.Module):
def __init__(self, input_size, hidden_size, output_size):
super(RNN, self).__init__()
self.hidden_size = hidden_size
self.i2h = nn.Linear(input_size + hidden_size, hidden_size)
self.i2o = nn.Linear(input_size + hidden_size, output_size)
self.softmax = nn.LogSoftmax(dim=1)
def forward(self, input, hidden):
combined = torch.cat((input, hidden), 1)
hidden = self.i2h(combined)
output = self.i2o(combined)
output = self.softmax(output)
return output, hidden
def initHidden(self):
return torch.zeros(1, self.hidden_size)
四、训练过程
criterion = nn.NLLLoss()
learning_rate = 0.005
rnn = RNN(n_letters, 128, n_categories)
def train(category_tensor, line_tensor):
hidden = rnn.initHidden()
rnn.zero_grad()
for i in range(line_tensor.size()[0]):
output, hidden = rnn(line_tensor[i], hidden)
loss = criterion(output, category_tensor)
loss.backward()
for param in rnn.parameters():
param.data -= learning_rate * param.grad.data
return output, loss.item()
五、预测
def predict(input_line):
with torch.no_grad():
line_tensor = lineToTensor(input_line)
hidden = rnn.initHidden()
for i in range(line_tensor.size()[0]):
output, hidden = rnn(line_tensor[i], hidden)
topv, topi = output.topk(1)
category_index = topi[0].item()
return all_categories[category_index]
六、总结与扩展
模块 | 内容 |
---|---|
数据格式 | 文本字符级序列 |
模型 | 单层 RNN(可拓展为 LSTM/GRU) |
输入 | One-hot 字符序列 |
输出 | 各语言类别的 LogSoftmax 概率 |
优化 | 手动梯度下降,可拓展为使用 optimizer.step() |
可视化
-
可视化训练损失下降曲线
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
# 模拟训练损失
epochs = 50
np.random.seed(1)
base_loss = np.linspace(2.0, 0.3, epochs)
noise = np.random.normal(0, 0.05, epochs)
loss_values = np.maximum(base_loss + noise, 0.2)
# 设置图像
fig, ax = plt.subplots(figsize=(8, 4))
ax.set_xlim(1, epochs)
ax.set_ylim(0, 2.2)
line, = ax.plot([], [], lw=2)
point, = ax.plot([], [], 'ro')
title = ax.set_title("Training Loss Animation")
def init():
line.set_data([], [])
point.set_data([], [])
return line, point
def update(frame):
x = np.arange(1, frame + 2)
y = loss_values[:frame + 1]
line.set_data(x, y)
point.set_data([x[-1]], [y[-1]])
title.set_text(f"Epoch: {frame + 1}, Loss: {y[-1]:.3f}")
return line, point, title
ani = animation.FuncAnimation(fig, update, frames=epochs, init_func=init, blit=True, interval=120)
ani.save("training_loss_animation.gif", writer='pillow', fps=10)
-
混淆矩阵评估模型在不同语言上的分类准确率