GENERATING NAMES WITH A CHARACTER-LEVEL RNN教程解析

RNN生成名字教程NLP FROM SCRATCH: GENERATING NAMES WITH A CHARACTER-LEVEL RNN是一篇不错的RNN入门教程,为了方便理解,这里将里面容易困惑的地方记录下。
这个教程会教你如何从头训练一个RNN模型,这个模型能够根据输入的国家名称和名字首字母来生成对应国家的名字。

python sample.py Russian G
# 输出以G开头的德国名字Gerren
python sample.py Chinese C
# 输出以C开头的中文名字Chan
python sample.py Chinese H
#输出以H开头的中文名字Hang

网络结构:
在这里插入图片描述

数据集结构

训练数据结构如下:

Archive:  data.zip
  inflating: data/eng-fra.txt        
  inflating: data/names/Arabic.txt   
  inflating: data/names/Chinese.txt  
  inflating: data/names/Czech.txt    
  inflating: data/names/Dutch.txt    
  inflating: data/names/English.txt  
  inflating: data/names/French.txt   
  inflating: data/names/German.txt   
  inflating: data/names/Greek.txt    
  inflating: data/names/Irish.txt    
  inflating: data/names/Italian.txt  
  inflating: data/names/Japanese.txt  
  inflating: data/names/Korean.txt   
  inflating: data/names/Polish.txt   
  inflating: data/names/Portuguese.txt  
  inflating: data/names/Russian.txt  
  inflating: data/names/Scottish.txt  
  inflating: data/names/Spanish.txt  
  inflating: data/names/Vietnamese.txt  

每个.txt文件中存储的都是对应国家的人名,总计有18个国家的类别名字,来看下Chinese.txt存了些啥

! cat data/names/Chinese.txt
Chin
Chong
Chou
Chu
Cui
Dai
Deng
Ding
Dong
Dou
Duan
Eng
Fan
Fei
...

在加载了这些字符后,需要将每个ascii字符转换为一个one-hot编码的向量。

import torch
import torch.nn as nn

# 定义一个rnn
class RNN(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        # input_size: n_letters(59), all_letters:abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ .,;'-
        super(RNN, self).__init__()
        self.hidden_size = hidden_size

        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(dim=1)

    def forward(self, category, input, hidden):
        # input: start letter, one hot encoding vector
        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)
        
all_letters = string.ascii_letters + " .,;'-" #总计58个字符
n_letters = len(all_letters) + 1 # Plus EOS marker,n_letters=59

推理

先看下推理是怎么做的,rnn的输入是类别category和首字母,category要转换成18个国家类别的one-hot vector, 首字母start_letter要转换为59个字母表对应的onehot,每次rnn预测出来的一个字符会成为下个迭代中rnn的输入。
需要注意的是,正常我们做模型推理的时候会加上model.eval来避免dropout/bn的随机性,但是下面的sample示例并没有加,原因就是想通过模型中的dropout增加一些随机,同一个起始字符能够输出不同的名字。

def sample(category, start_letter='A'):
    # 将category对18个国家类别做onehot
    category_tensor = categoryTensor(category)
    # 将输入名字对59个字符类别做onehot,
    input = inputTensor(start_letter) # input.shape:[1,1,59], [seq_len, batch_size, n_letters], one hot encoding vector
    hidden = rnn.initHidden()

    output_name = start_letter

    for i in range(max_length):
    # category_tensor, input 都是one-hot
        output, hidden = rnn(category_tensor, input[0], hidden)
        topv, topi = output.topk(1)
        topi = topi[0][0]
        # 如果是EOS则停止预测
        if topi == n_letters - 1:
            break
        else:
            letter = all_letters[topi]
            output_name += letter
        # 将预测出来的字符复制给input参与下个字符的预测。
        input = inputTensor(letter)

    return output_name
sample('Chinese', 'L')
# 打印 Lin, 第一次预测i,第二次预测n
sample('Chinese', 'LW')
#打印 Li和 Wang。

训练

训练与推理不同的是训练要加入额外gt标签target_line_tensor。
以名字yang为例,input_line_tensor为yang的onehot,比如:
[[[0,0,...1,0,0...],[0,0,...1...],...]],shape:(4,1,59)shape 满足(seq_len, batch_size, n_letters),
target_line_tensor为input每个字符的下一个字符,也就是ang<eos>的id列表,注意这里没有对其做onehot,因为这里用的loss是NLLoss,他需要的是类别的index,不需要onehot,target_line_tensor打印可能是下面的结果:[8, 19, 4, 58], shape:(4,)
简化的训练代码:

criterion = nn.NLLLoss()
learning_rate = 0.0005
# train(*randomTrainingExample())
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)
        l = criterion(output, target_line_tensor[i])
        loss += l

    loss.backward()

    for p in rnn.parameters():
        p.data.add_(p.grad.data, alpha=-learning_rate)

    return output, loss.item() / input_line_tensor.size(0)
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
音乐转换器是一种能够生成具有长期结构的音乐的技术。传统上,音乐生成模型主要依赖于自回归模型,即根据前面的音符预测下一个音符。这种方法很难捕捉到音乐的长期结构,因为它只关注于当前音符与前面音符的关系。 然而,音乐转换器采用了一种全新的方法。它将音乐的生成问题转化为基于自注意力机制的序列到序列问题。自注意力机制允许模型在生成每个音符时考虑到整个音乐序列的信息,而不仅仅是前面的音符。 此外,音乐转换器还引入了一种基于位置编码和层归一化的技术,来增强模型对音乐序列的表征能力和泛化能力。位置编码在序列中为每个位置分配一个向量,以提供位置信息。而层归一化则可以确保模型的每一层都保持相似的输出分布,从而提高模型的训练稳定性和生成效果。 通过这些创新技术的运用,音乐转换器能够更好地捕捉到音乐的长期结构。它可以生成具有旋律、和声和节奏等多个音乐要素的音乐片段,并且这些片段之间能够形成完整的结构,如引言、主题、发展和回旋等。 总之,音乐转换器是一种利用自注意力机制、位置编码和层归一化等技术生成具有长期结构的音乐的方法。它的创新之处在于能够全局考虑音乐序列的信息,并能够生成具有完整结构的音乐片段。这使得音乐转换器成为一个有潜力的工具,在音乐创作和生成领域有着广阔的应用前景。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值