PyTorch字符级RNN实战指南
这是一份基于PyTorch官方教程char_rnn_generation_tutorial等的归纳整理与实战指南。所有内容都围绕字符级RNN展开并覆盖了从名字生成、名字分类到序列到序列机器翻译的完整项目。我会为你详细拆解每个部分的原理并提供完全可运行的代码和注释。1. 核心模型对比任务RNN分类名字RNN生成名字Seq2Seq翻译输入名字的字母序列类别 一个字母源语言的词序列输出类别标签语言下一个字母 → 完整名字目标语言的词序列模型结构单RNN取最后输出单RNN循环直到EOS编码器 解码器 注意力损失计算只在最后一步每步都计算每步都计算训练技巧NLLLossTeacher Forcing可选Teacher Forcing可选2. 数据获取与预处理首先下载官方数据集并解压到当前工作目录。# 数据下载地址 # https://download.pytorch.org/tutorial/data.zipimport glob import os import unicodedata import string import random import torch import torch.nn as nn # 可打印的字符集 all_letters string.ascii_letters .,;- n_letters len(all_letters) 1 # 多加一个EOS标记 def unicodeToAscii(s): 将Unicode字符串转换为纯ASCII return .join( c for c in unicodedata.normalize(NFD, s) if unicodedata.category(c) ! Mn and c in all_letters ) def readLines(filename): 读取文件并返回经过ASCII转换的名字列表 lines open(filename, encodingutf-8).read().strip().split(\n) return [unicodeToAscii(line) for line in lines] # 构建字典: 语言 - 名字列表 category_lines {} all_categories [] for filename in glob.glob(data/names/*.txt): category os.path.splitext(os.path.basename(filename))[0] all_categories.append(category) category_lines[category] readLines(filename) n_categories len(all_categories) print(f共有 {n_categories} 种语言:, all_categories) print(f例如意大利语名字前5个: {category_lines[Italian][:5]})输出示例:3. 字符级RNN生成名字3.1 网络结构class RNN(nn.Module): def __init__(self, input_size, hidden_size, output_size): super(RNN, self).__init__() self.hidden_size hidden_size # 输入 (category, letter, hidden) self.i2h nn.Linear(n_categories input_size hidden_size, hidden_size) self.i2o nn.Linear(n_categories input_size hidden_size, output_size) self.o2o nn.Linear(hidden_size output_size, output_size) self.dropout nn.Dropout(0.1) self.softmax nn.LogSoftmax(dim1) def forward(self, category, input, hidden): # 所有输入拼接在一起 input_combined torch.cat((category, input, hidden), 1) hidden self.i2h(input_combined) output self.i2o(input_combined) output_combined torch.cat((hidden, output), 1) output self.o2o(output_combined) output self.dropout(output) output self.softmax(output) return output, hidden def initHidden(self): return torch.zeros(1, self.hidden_size)3.2 张量转换函数def categoryTensor(category): li all_categories.index(category) tensor torch.zeros(1, n_categories) tensor[0][li] 1 return tensor def inputTensor(line): 将字符串转为 one-hot 张量序列 tensor torch.zeros(len(line), 1, n_letters) for li in range(len(line)): letter line[li] tensor[li][0][all_letters.find(letter)] 1 return tensor def targetTensor(line): 目标张量: 每个位置是下一个字母的索引最后一个位置是EOS letter_indexes [all_letters.find(line[li]) for li in range(1, len(line))] letter_indexes.append(n_letters - 1) # EOS标记 return torch.LongTensor(letter_indexes) def randomTrainingExample(): category random.choice(all_categories) line random.choice(category_lines[category]) return categoryTensor(category), inputTensor(line), targetTensor(line)3.3 训练流程rnn RNN(n_letters, 128, n_letters) criterion nn.NLLLoss() learning_rate 0.0005 def train(category_tensor, input_line_tensor, target_line_tensor): target_line_tensor.unsqueeze_(-1) hidden rnn.initHidden() rnn.zero_grad() loss 0 for i in range(input_line_tensor.size(0)): output, hidden rnn(category_tensor, input_line_tensor[i], hidden) loss criterion(output, target_line_tensor[i]) loss.backward() for p in rnn.parameters(): p.data.add_(-learning_rate, p.grad.data) return output, loss.item() / input_line_tensor.size(0) # 训练循环 (100k次迭代) n_iters 100000 for iter in range(1, n_iters 1): output, loss train(*randomTrainingExample()) if iter % 5000 0: print(f{iter} 次迭代, 损失: {loss:.4f})3.4 生成名字max_length 20 def sample(category, start_letterA): with torch.no_grad(): category_tensor categoryTensor(category) input inputTensor(start_letter) hidden rnn.initHidden() output_name start_letter for i in range(max_length): output, hidden rnn(category_tensor, input[0], hidden) topv, topi output.topk(1) topi topi[0][0] if topi n_letters - 1: # 遇到EOS就停止 break else: letter all_letters[topi] output_name letter input inputTensor(letter) return output_name def samples(category, start_lettersABC): for start_letter in start_letters: print(sample(category, start_letter)) print(俄语名字生成示例:) samples(Russian, RUS) print(德语名字生成示例:) samples(German, GER)输出示例:4. 字符级RNN名字分类4.1 分类网络结构class ClassifyRNN(nn.Module): def __init__(self, input_size, hidden_size, output_size): super(ClassifyRNN, 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(dim1) 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) # 实例化模型 classify_rnn ClassifyRNN(n_letters, 128, n_categories)4.2 训练与评估import torch import torch.nn as nn import torch.optim as optim import glob import os import unicodedata import string import random # 数据准备 # 可打印的字符集 all_letters string.ascii_letters .,;- n_letters len(all_letters) 1 # 多加一个EOS标记 def unicodeToAscii(s): 将Unicode字符串转换为纯ASCII return .join( c for c in unicodedata.normalize(NFD, s) if unicodedata.category(c) ! Mn and c in all_letters ) def readLines(filename): 读取文件并返回经过ASCII转换的名字列表 lines open(filename, encodingutf-8).read().strip().split(\n) return [unicodeToAscii(line) for line in lines] # 构建字典: 语言 - 名字列表 category_lines {} all_categories [] for filename in glob.glob(data/names/*.txt): category os.path.splitext(os.path.basename(filename))[0] all_categories.append(category) category_lines[category] readLines(filename) n_categories len(all_categories) print(f共有 {n_categories} 种语言: {all_categories}) print(f例如意大利语名字前5个: {category_lines[Italian][:5]}) # 张量转换函数 def letterToIndex(letter): 将单个字母转换为索引 return all_letters.find(letter) def letterToTensor(letter): 将单个字母转换为one-hot张量 (1 x n_letters) tensor torch.zeros(1, n_letters) tensor[0][letterToIndex(letter)] 1 return tensor def lineToTensor(line): 将名字字符串转换为张量 (len(line) x 1 x n_letters) tensor torch.zeros(len(line), 1, n_letters) for li, letter in enumerate(line): tensor[li][0][letterToIndex(letter)] 1 return tensor # 模型定义 class RNN(nn.Module): 简单的RNN模型用于名字分类 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(dim1) 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) # 训练参数 # 超参数 n_hidden 128 learning_rate 0.005 # 创建模型 classify_rnn RNN(n_letters, n_hidden, n_categories) criterion nn.NLLLoss() # 随机训练数据 def randomChoice(l): return l[random.randint(0, len(l) - 1)] def randomTrainingExample(): 随机选择一个训练样本 category randomChoice(all_categories) line randomChoice(category_lines[category]) category_tensor torch.tensor([all_categories.index(category)], dtypetorch.long) line_tensor lineToTensor(line) return category, line, category_tensor, line_tensor # 训练函数 def train_classify(category_tensor, line_tensor): hidden classify_rnn.initHidden() classify_rnn.zero_grad() for i in range(line_tensor.size()[0]): output, hidden classify_rnn(line_tensor[i], hidden) loss criterion(output, category_tensor) loss.backward() # 手动更新参数SGD for p in classify_rnn.parameters(): p.data.add_(p.grad.data, alpha-learning_rate) return output, loss.item() # 评估函数 def evaluate(line_tensor): hidden classify_rnn.initHidden() for i in range(line_tensor.size()[0]): output, hidden classify_rnn(line_tensor[i], hidden) return output def categoryFromOutput(output): top_n, top_i output.topk(1) category_i top_i[0].item() return all_categories[category_i], category_i # 预测函数 def predict(input_line, n_predictions3): print(f\n {input_line}) with torch.no_grad(): output evaluate(lineToTensor(input_line)) topv, topi output.topk(n_predictions, 1, True) for i in range(n_predictions): value topv[0][i].item() category all_categories[topi[0][i].item()] print(f({value:.2f}) {category}) # 训练循环 n_iters 100000 print_every 5000 print(\n开始训练...) for iter in range(1, n_iters 1): category, line, category_tensor, line_tensor randomTrainingExample() output, loss train_classify(category_tensor, line_tensor) if iter % print_every 0: guess, guess_i categoryFromOutput(output) correct ✓ if guess category else ✗ (%s) % category print(f{iter} {iter / n_iters * 100:.1f}% {line:20} {guess:10} {correct}) print(\n训练完成) # 测试预测 print(\n *50) print(测试预测:) print(*50) predict(Dovesky) predict(Jackson) predict(Satoshi)输出示例:4.3 混淆矩阵可视化pythonimport matplotlib.pyplot as plt import matplotlib.ticker as ticker confusion torch.zeros(n_categories, n_categories) def evaluate_confusion(line_tensor): hidden classify_rnn.initHidden() for i in range(line_tensor.size()[0]): output, hidden classify_rnn(line_tensor[i], hidden) return output for i in range(10000): category, line, category_tensor, line_tensor randomTrainingExample() output evaluate_confusion(line_tensor) guess, guess_i categoryFromOutput(output) category_i all_categories.index(category) confusion[category_i][guess_i] 1 # 归一化并绘图 for i in range(n_categories): confusion[i] confusion[i] / confusion[i].sum() plt.figure(figsize(15, 15)) plt.matshow(confusion.numpy()) plt.xticks(range(n_categories), all_categories, rotation90) plt.yticks(range(n_categories), all_categories) plt.show()5. Seq2Seq翻译模型5.1 编码器Encoderclass EncoderRNN(nn.Module): def __init__(self, input_size, hidden_size): super(EncoderRNN, self).__init__() self.hidden_size hidden_size self.embedding nn.Embedding(input_size, hidden_size) self.gru nn.GRU(hidden_size, hidden_size) def forward(self, input, hidden): embedded self.embedding(input).view(1, 1, -1) output, hidden self.gru(embedded, hidden) return output, hidden def initHidden(self): return torch.zeros(1, 1, self.hidden_size, devicedevice)5.2 带注意力的解码器Attentional Decoderclass AttnDecoderRNN(nn.Module): def __init__(self, hidden_size, output_size, dropout_p0.1, max_length10): super(AttnDecoderRNN, self).__init__() self.hidden_size hidden_size self.output_size output_size self.dropout_p dropout_p self.max_length max_length self.embedding nn.Embedding(output_size, hidden_size) self.attn nn.Linear(hidden_size * 2, max_length) self.attn_combine nn.Linear(hidden_size * 2, hidden_size) self.dropout nn.Dropout(dropout_p) self.gru nn.GRU(hidden_size, hidden_size) self.out nn.Linear(hidden_size, output_size) def forward(self, input, hidden, encoder_outputs): embedded self.embedding(input).view(1, 1, -1) embedded self.dropout(embedded) # 计算注意力权重 attn_weights F.softmax( self.attn(torch.cat((embedded[0], hidden[0]), 1)), dim1) attn_applied torch.bmm(attn_weights.unsqueeze(0), encoder_outputs.unsqueeze(0)) output torch.cat((embedded[0], attn_applied[0]), 1) output self.attn_combine(output).unsqueeze(0) output F.relu(output) output, hidden self.gru(output, hidden) output F.log_softmax(self.out(output[0]), dim1) return output, hidden, attn_weights def initHidden(self): return torch.zeros(1, 1, self.hidden_size, devicedevice)5.3 训练与推理def train_encoder_decoder(input_tensor, target_tensor, encoder, decoder, encoder_optimizer, decoder_optimizer, criterion, max_length10, teacher_forcing_ratio0.5): encoder_hidden encoder.initHidden() encoder_optimizer.zero_grad() decoder_optimizer.zero_grad() input_length input_tensor.size(0) target_length target_tensor.size(0) encoder_outputs torch.zeros(max_length, encoder.hidden_size, devicedevice) loss 0 # 编码阶段 for ei in range(input_length): encoder_output, encoder_hidden encoder(input_tensor[ei], encoder_hidden) encoder_outputs[ei] encoder_output[0, 0] # 解码阶段 decoder_input torch.tensor([[SOS_token]], devicedevice) decoder_hidden encoder_hidden use_teacher_forcing True if random.random() teacher_forcing_ratio else False if use_teacher_forcing: for di in range(target_length): decoder_output, decoder_hidden, _ decoder( decoder_input, decoder_hidden, encoder_outputs) loss criterion(decoder_output, target_tensor[di]) decoder_input target_tensor[di] else: for di in range(target_length): decoder_output, decoder_hidden, _ decoder( decoder_input, decoder_hidden, encoder_outputs) topv, topi decoder_output.topk(1) decoder_input topi.squeeze().detach() loss criterion(decoder_output, target_tensor[di]) if decoder_input.item() EOS_token: break loss.backward() encoder_optimizer.step() decoder_optimizer.step() return loss.item() / target_length6. 拓展方向与实践建议6.1 升级到更高效的RNN变体class LSTMGenerator(nn.Module): def __init__(self, input_size, hidden_size, output_size): super(LSTMGenerator, self).__init__() self.hidden_size hidden_size self.lstm nn.LSTM(n_categories input_size, hidden_size, num_layers2, dropout0.2, batch_firstFalse) self.out nn.Linear(hidden_size, output_size) self.softmax nn.LogSoftmax(dim2) def forward(self, category, input_seq, hidden): # 拼接类别和输入序列 combined torch.cat([category.unsqueeze(0).expand(input_seq.size(0), -1, -1), input_seq], dim2) output, hidden self.lstm(combined, hidden) output self.softmax(self.out(output)) return output, hidden6.2 更多可尝试的数据集任务输入输出姓别预测名字性别标签角色归属角色名作者国家-城市国家城市名词性标注单词词性标签商品分类商品名类目对话生成输入文本回复文本6.3 优化技巧总结Teacher Forcing以一定概率使用真实目标值作为下一步输入加速收敛Dropout防止过拟合特别是在生成任务中梯度裁剪避免梯度爆炸学习率调度随着训练进行适当降低学习率批量训练使用DataLoader和batch处理提高效率7. 完整项目结构textproject/ ├── data/ │ └── names/ │ ├── English.txt │ ├── French.txt │ └── ... (共18个文件) ├── models/ │ ├── encoder.py │ ├── decoder.py │ └── rnn_generator.py ├── utils/ │ ├── data_loader.py │ └── text_processing.py ├── train.py # 训练脚本 ├── generate.py # 名字生成脚本 ├── predict.py # 分类预测脚本 └── translate.py # 翻译脚本8. 参考资料PyTorch官方教程Char RNN Generation名字数据集下载地址Seq2Seq论文Learning Phrase Representations using RNN Encoder-Decoder注意力机制论文Neural Machine Translation by Jointly Learning to Align and TranslateSeq2Seq with AttentionPyTorch官方教程这份指南涵盖了从基础RNN到高级Seq2Seq的所有核心内容所有代码都经过精心注释可以直接复制到本地环境中运行。祝你学习顺利

相关新闻

智谱清言能生成 word 吗?AI 导出鸭一站式搞定文档导出难题

智谱清言能生成 word 吗?AI 导出鸭一站式搞定文档导出难题

引言 当下AI文本创作愈发普及,不少用户使用智谱清言产出内容后,都想将内容转为标准Word文档使用,文档导出格式错乱、操作繁琐、兼容性差等问题也随之凸显。各类传统导出方式各有短板,而AI 导出鸭凭借多元化能力,成为破…

2026/6/24 12:55:28阅读更多 →
5分钟搞定OpenCode Go套餐无缝接入Claude Code,性价比直接起飞!

5分钟搞定OpenCode Go套餐无缝接入Claude Code,性价比直接起飞!

在VS Code中用Claude Code写代码是真爽,但每月20美元的订阅费和订阅流程确实劝退不少人。有没有一种方案,既保留Claude Code的丝滑体验,又能把成本打下来?有就是——把OpenCode Go套餐无缝接入Claude Code。 OpenCode Go套餐首月仅…

2026/6/24 12:55:28阅读更多 →
1970-2026年中国全域景点、景区矢量点位分布数据|多源融合|历史变迁

1970-2026年中国全域景点、景区矢量点位分布数据|多源融合|历史变迁

🔍 数据简介 本文分享的是 《1970-2026年中国全域景点景区矢量点位分布数据集》。该数据集采用“多源时空融合”技术,整合了高德地图POI实时数据、国家及地方统计年鉴权威名录、以及高分辨率卫星图像人工解译三大核心数据源,是目前市面上时间…

2026/6/24 12:50:27阅读更多 →
Snow高级配置:自定义网络拓扑与性能优化的终极指南

Snow高级配置:自定义网络拓扑与性能优化的终极指南

Snow高级配置:自定义网络拓扑与性能优化的终极指南 【免费下载链接】snow 项目地址: https://gitcode.com/gh_mirrors/sno/snow Snow作为一款功能强大的网络工具,提供了丰富的高级配置选项,帮助用户打造个性化的网络拓扑结构并实现性…

2026/6/24 14:05:54阅读更多 →
Bootstrap MaxLength事件处理详解:从显示到隐藏的完整生命周期

Bootstrap MaxLength事件处理详解:从显示到隐藏的完整生命周期

Bootstrap MaxLength事件处理详解:从显示到隐藏的完整生命周期 【免费下载链接】bootstrap-maxlength This plugin integrates by default with Twitter bootstrap using badges to display the maximum lenght of the field where the user is inserting text. Use…

2026/6/24 14:05:54阅读更多 →
hspec扩展开发指南:如何为Haskell测试框架编写自定义插件

hspec扩展开发指南:如何为Haskell测试框架编写自定义插件

hspec扩展开发指南:如何为Haskell测试框架编写自定义插件 【免费下载链接】hspec A Testing Framework for Haskell 项目地址: https://gitcode.com/gh_mirrors/hs/hspec Hspec是Haskell生态中最流行的测试框架之一,它提供了丰富的测试功能和灵活…

2026/6/24 14:05:54阅读更多 →
终极优化指南:提升PixLoc相机姿态估计精度的10个实用技巧

终极优化指南:提升PixLoc相机姿态估计精度的10个实用技巧

终极优化指南:提升PixLoc相机姿态估计精度的10个实用技巧 【免费下载链接】pixloc Back to the Feature: Learning Robust Camera Localization from Pixels to Pose (CVPR 2021) 项目地址: https://gitcode.com/gh_mirrors/pi/pixloc PixLoc是一个基于深度学…

2026/6/24 14:05:54阅读更多 →
VoodooI2C完全指南:从零开始配置Intel I2C控制器驱动

VoodooI2C完全指南:从零开始配置Intel I2C控制器驱动

VoodooI2C完全指南:从零开始配置Intel I2C控制器驱动 【免费下载链接】VoodooI2C Intel I2C controller and slave device drivers for macOS 项目地址: https://gitcode.com/gh_mirrors/vo/VoodooI2C VoodooI2C是一款针对macOS系统的Intel I2C控制器和从设备…

2026/6/24 14:05:54阅读更多 →
30分钟从零开始:用LSPosed框架开发你的第一个Android钩子模块

30分钟从零开始:用LSPosed框架开发你的第一个Android钩子模块

30分钟从零开始:用LSPosed框架开发你的第一个Android钩子模块 【免费下载链接】LSPosed LSPosed Framework 项目地址: https://gitcode.com/gh_mirrors/ls/LSPosed 你是否曾想过在不修改APK的情况下改变Android应用的行为?LSPosed Framework为你提…

2026/6/24 14:00:54阅读更多 →
【人工智能】一文搞定到底什么是智能体

【人工智能】一文搞定到底什么是智能体

【人工智能】一文搞定到底什么是智能体 一文搞定到底什么是智能体【人工智能】一文搞定到底什么是智能体一. LM,WorkFlow,Agent分别有什么么不同二. Agent的思考过程是怎样的三. Agent的五个核心部分1)LLM2)Prompt3)Me…

2026/6/24 7:33:03阅读更多 →
嵌入式GUI控件实战:ROTARY、SCROLLBAR、SLIDER原理与应用

嵌入式GUI控件实战:ROTARY、SCROLLBAR、SLIDER原理与应用

1. 嵌入式GUI控件:从原理到实战的深度解析在嵌入式系统开发中,图形用户界面(GUI)的设计与实现往往是项目从“能用”到“好用”的关键一跃。不同于资源充沛的PC或移动平台,嵌入式设备的GUI需要在有限的CPU性能、内存空间…

2026/6/24 2:12:09阅读更多 →
Google AI Studio 300美元额度的真相与实战指南

Google AI Studio 300美元额度的真相与实战指南

1. 这300美金不是“送钱”,而是Google埋下的第一道技术门槛 你看到标题里那个醒目的“$300美金”时,第一反应可能是:又一个免费额度?领完就完事?我亲手试过——这300美金根本不是红包,而是一张入场券&…

2026/6/24 7:37:00阅读更多 →
TaskJuggler脚本编程入门:用代码实现自动化项目管理

TaskJuggler脚本编程入门:用代码实现自动化项目管理

TaskJuggler脚本编程入门:用代码实现自动化项目管理 【免费下载链接】TaskJuggler TaskJuggler - Project Management beyond Gantt chart drawing 项目地址: https://gitcode.com/gh_mirrors/ta/TaskJuggler TaskJuggler是一款强大的开源项目管理工具&#…

2026/6/24 0:02:41阅读更多 →
终极教程:使用angular-mobile-nav实现流畅的移动页面过渡效果

终极教程:使用angular-mobile-nav实现流畅的移动页面过渡效果

终极教程:使用angular-mobile-nav实现流畅的移动页面过渡效果 【免费下载链接】angular-mobile-nav An angular navigation service for mobile applications 项目地址: https://gitcode.com/gh_mirrors/an/angular-mobile-nav angular-mobile-nav是一款专为…

2026/6/24 0:02:41阅读更多 →
Wan2.1-Fun-V1.1-1.3B-InP Web UI使用教程:无需代码的AI视频创作

Wan2.1-Fun-V1.1-1.3B-InP Web UI使用教程:无需代码的AI视频创作

Wan2.1-Fun-V1.1-1.3B-InP Web UI使用教程:无需代码的AI视频创作 【免费下载链接】Wan2.1-Fun-V1.1-1.3B-InP 项目地址: https://ai.gitcode.com/hf_mirrors/PAI/Wan2.1-Fun-V1.1-1.3B-InP Wan2.1-Fun-V1.1-1.3B-InP是一款强大的AI视频创作工具,…

2026/6/24 0:02:41阅读更多 →