【Pytorch-NLP实战系列】:Seq2Seq训练输出反义词(不到百行代码)

总述:

用RNN编码解码机制训练一个输出反义词的模型,目的在于熟悉pytorch的使用,代码中有新手不太懂的函数都引用了博客,请放心食用。

千言万语皆在代码中:

#coding=utf-8
import numpy as np
import torch
import torch.nn as nn
from torch.autograd import Variable


char_arr = [c for c in 'SEPabcdefghijklmnopqrstuvwxyz']#所有我们需要的字符,S表示输出的开始,P用来填充,E代表输出的结束
num_dic = {n: i for i, n in enumerate(char_arr)}#关于enumerate函数,请见解释1
#此处的输出为{'S':1,'E':2,'P':3,'a':4......'z':29}

seq_data = [['man', 'women'], ['black', 'white'], ['king', 'queen'], ['girl', 'boy'], ['up', 'down'], ['high', 'low'],['good','bad']] #我们所需要的反义词数据

#模型的一些参数
n_hideen = 128  #隐藏层节点的个数
n_class = len(char_arr) #总的字符数,也就是用one-hot编码的话,每一个字符的编码是29维的
batch_size = len(seq_data)#训练数据的多少
max_len = 5 #单词最大长度,不够拿'P'填充


def make_batch(seq_data):
    input_batch,output_batch,target_batch = [],[],[]
    for seq in seq_data:
        seq[0] = seq[0]+(max_len-len(seq[0]))*'P' #不足的长度由P来填充
        seq[1] = seq[1]+(max_len-len(seq[1]))*'P'
        input = [num_dic[s] for s in seq[0]]
        output = [num_dic[s] for s in 'S'+seq[1]]#创造了一个数字列表,每个数字代表在num_dic中对应seq各个字符的下标,可以输出看一看
        target = [num_dic[s] for s in seq[1]+'E']#target不是one-hot编码了
        input_batch.append(np.eye(n_class)[input])#关于np.eye的用法,请看解释二。此处获得了一个len(input)*n_class的矩阵
        output_batch.append(np.eye(n_class)[output])
        target_batch.append(target)

    return  Variable(torch.Tensor(input_batch)),Variable(torch.Tensor(output_batch)),Variable(torch.LongTensor(target_batch))
#返回Tensor类型,为啥有LongTensor?因为计算交叉熵的时候,第二个参数需要是LongTensor类型的。

#构建模型
class Seqtoseq(nn.Module):
    def __init__(self):
        super(Seqtoseq,self).__init__()
        self.encoder = nn.RNN(input_size=n_class,hidden_size=n_hideen,dropout=0.5)#构建两层RNN的模型,一层用来编码,一层用来解码
        self.decoder = nn.RNN(input_size=n_class,hidden_size=n_hideen,dropout=0.5)#如果对pytorchRNN参数有疑惑,请看解释3
        self.fc = nn.Linear(n_hideen,n_class)

    def forward(self, encoder_input,encoder_hidden,decoder_input):#训练前向传播
        # encoder_input: [max_len, batch_size, n_class]
        # decoder_input: [max_len, batch_size, n_class]
        encoder_input = encoder_input.transpose(0,1)#如果不了解为什么此处需要转置,请看解释4
        decoder_input = decoder_input.transpose(0,1)

        # encoder_state : [num_layers(=1) * num_directions(=1), batch_size, n_hidden]
        _,encoder_state = self.encoder(encoder_input,encoder_hidden)#RNN encoder-decoder结构是需要把encoderRNN的最后一次隐式输出hn送给decoder作为输入的。
        decoder_output,_ = self.decoder(decoder_input,encoder_state)
        # outputs : [max_len+1(=6), batch_size, num_directions(=1) * n_hidden(=128)]
        model = self.fc(decoder_output)
        # model : [max_len+1(=6), batch_size, n_class]
        return model

input_batch, output_batch, target_batch = make_batch(seq_data)#得到训练集

#构造模型,交叉熵,优化器,设置学习率为0.01
model = Seqtoseq()
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.01)

#训练
for echo in range(5000):
    hidden = Variable(torch.zeros(1, batch_size, n_hideen))
    optimizer.zero_grad()#在每一次开始训练之前,需要把梯度清零
    # input_batch : [batch_size, max_len, n_class]
    # output_batch : [batch_size, max_len+1 (becase of 'S' or 'E'), n_class]
    # target_batch : [batch_size, max_len+1, not one-hot
    output = model(input_batch, hidden, output_batch)
    output = output.transpose(0, 1)# [batch_size, max_len+1(=6), n_class]
    loss = 0
    for i in range(0,len(target_batch)):
        # output[i] : [max_len+1, n_class, target_batch[i] : max_len+1]
        loss += criterion(output[i], target_batch[i])
    if (echo + 1) % 1000 == 0:
        print("echo ==  ", "%04d" % (echo + 1), "loss ==  ", "{:.6f}".format(loss))
    loss.backward()#反向传播
    optimizer.step()

def translate(str):
    input_batch,output_batch,_ = make_batch([[str,'P'*len(str)]])
    hidden = Variable(torch.zeros(1,1,n_hideen))

    output = model(input_batch,hidden,output_batch)
    output.transpose(0,1)

    predict = output.data.max(2,keepdim = True)[1]#如果对max函数不了解,请见解释5
    decoder = [char_arr[i] for i in predict]

    end = decoder.index('E')
    answer = ''.join(decoder[:end])
    return answer.replace('P','')

print('test')
print('man ->', translate('man'))
print('mans ->', translate('mans'))
print('king ->', translate('king'))
print('black ->', translate('black'))
print('upp ->', translate('upp'))

解释1:

enumerate函数的使用,请见: https://blog.csdn.net/lwgkzl/article/details/88735271

解释2:

np.eye函数的用法,请见:https://blog.csdn.net/chixujohnny/article/details/51011931

解释3:

RNN的用法,请见:https://blog.csdn.net/lwgkzl/article/details/88717678

解释4:

在此处为啥需要转置呢?待分析

解释5:

max函数的使用,请见:https://blog.csdn.net/Z_lbj/article/details/79766690

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
PyTorch中,Seq2Seq模型是一个常用的用于处理序列数据的模型,主要用于将一个序列转换为另一个序列。这种模型在自然语言处理、语音识别等领域都有广泛的应用。 Seq2Seq模型由两个部分组成:编码器和解码器。编码器将输入序列转换为一个固定大小的向量表示,解码器将这个向量表示转换为目标序列。在训练过程中,Seq2Seq模型会将编码器和解码器联合起来进行训练,以最小化输出序列与目标序列之间的差距。 在PyTorch中实现Seq2Seq模型通常需要使用nn.Module类来定义模型结构,同时还需要实现自定义的数据加载和预处理函数。在编码器中,通常使用RNN或CNN等结构对输入序列进行处理,并将最终的状态向量作为解码器的输入。在解码器中,通常使用RNN或Attention等机制来生成目标序列。 以下是一个简单的PyTorch Seq2Seq代码示例: ``` import torch import torch.nn as nn class Encoder(nn.Module): def __init__(self, input_size, hidden_size): super(Encoder, 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): embedded = self.embedding(input) output, hidden = self.gru(embedded) return output, hidden class Decoder(nn.Module): def __init__(self, hidden_size, output_size): super(Decoder, self).__init__() self.hidden_size = hidden_size self.embedding = nn.Embedding(output_size, hidden_size) self.gru = nn.GRU(hidden_size, hidden_size) self.out = nn.Linear(hidden_size, output_size) self.softmax = nn.LogSoftmax(dim=1) def forward(self, input, hidden): output = self.embedding(input).view(1, 1, -1) output, hidden = self.gru(output, hidden) output = self.softmax(self.out(output)) return output, hidden input_size = 100 output_size = 100 hidden_size = 256 encoder = Encoder(input_size, hidden_size) decoder = Decoder(hidden_size, output_size) criterion = nn.NLLLoss() learning_rate = 0.01 encoder_optimizer = torch.optim.SGD(encoder.parameters(), lr=learning_rate) decoder_optimizer = torch.optim.SGD(decoder.parameters(), lr=learning_rate) for epoch in range(num_epochs): for i, (input_seq, target_seq) in enumerate(data_loader): encoder_optimizer.zero_grad() decoder_optimizer.zero_grad() input_len = input_seq.size(0) target_len = target_seq.size(0) encoder_hidden = torch.zeros(1, 1, hidden_size) loss = 0 for j in range(input_len): encoder_output, encoder_hidden = encoder(input_seq[j], encoder_hidden) decoder_input = torch.tensor([[START_TOKEN]]) decoder_hidden = encoder_hidden for j in range(target_len): decoder_output, decoder_hidden = decoder(decoder_input, decoder_hidden) loss += criterion(decoder_output, target_seq[j]) decoder_input = target_seq[j] loss.backward() encoder_optimizer.step() decoder_optimizer.step() ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值