Nlp 使用Pytorch生成LSTM文本

Nlp 使用Pytorch生成LSTM文本,nlp,pytorch,lstm,Nlp,Pytorch,Lstm,我目前正在尝试使用Pytorch使用LSTMs生成报价(字符级)。我目前在理解Pytorch中如何实现隐藏状态方面面临一些问题 一些细节: 我有一个电视连续剧中一个角色的引用列表。我正在使用字典char2idx将它们转换为一个整数序列,每个字符对应一个特定的整数。我还有这个idx2char的倒数,映射是反向的 在那之后,我使用一个滑动窗口,比如说大小window\u size,以及大小step的步骤来准备数据 例如,假设序列是[1,2,3,4,5,0],其中0代表EOS字符。然后使用window

我目前正在尝试使用Pytorch使用LSTMs生成报价(字符级)。我目前在理解Pytorch中如何实现隐藏状态方面面临一些问题

一些细节:

我有一个电视连续剧中一个角色的引用列表。我正在使用字典
char2idx
将它们转换为一个整数序列,每个字符对应一个特定的整数。我还有这个
idx2char
的倒数,映射是反向的

在那之后,我使用一个滑动窗口,比如说大小
window\u size
,以及大小
step
的步骤来准备数据

例如,假设序列是
[1,2,3,4,5,0]
,其中0代表EOS字符。然后使用
window\u size=3
step=2
,我得到x和y的序列为:

x1 = [1, 2, 3], y1 = [2, 3, 4]
x2 = [3, 4, 5], y1 = [4, 5, 0]

x = [x1, x2], y = [y1, y2]
下一步是训练模型。我已经附上了我用来训练模型的代码

注:我没有将隐藏状态从一批传递到另一批,因为(j+1)批的第I个序列可能不是第j批的第I个序列的下一步。(这就是我使用滑动窗口帮助模型记住的原因)。有更好的方法吗

我的主要问题发生在测试期间。我测试的方法有两种

方法1: 我获取初始种子字符串,将其传递到模型中,并获得下一个字符作为预测。现在,我将其添加到起始字符串中,并将整个序列传递到模型中,而不传递隐藏状态。也就是说,我将整个序列输入到模型中,LSTM的初始隐藏状态为0,获取输出,将输出附加到序列中并重复,直到遇到EOS字符

方法2: 我获取初始种子字符串,将其传递到模型中,并获得下一个字符作为预测。现在,我只是将角色和上一个隐藏状态作为下一个输入传递,并继续这样做,直到遇到EOS角色为止

问题

  • 根据我目前的理解,这两种方法的输出应该是相同的,因为在这两种方法中应该发生相同的事情
  • 实际上,两种方法给出的结果完全不同。为什么会这样
  • 对于大多数输入,第二个会卡在无限循环中(例如,它会给出“背对背到…”),而对于某些输入,第一个也会卡在无限循环中。如何预防和避免这种情况
  • 这与培训有什么关系吗
  • 我尝试了多种不同的方法(使用双向LSTM,使用一种热编码(而不是嵌入),更改批大小,而不是使用滑动窗口方法(使用填充并同时填充整个报价)

    我想不出如何解决这个问题。如果有任何帮助,我将不胜感激

    代码

    模型类的代码:

    class RNN(nn.Module):
        def __init__(self, vocab_size, hidden_size, num_layers, dropout=0.15):
            super(RNN, self).__init__()
            self.vocab_size = vocab_size
            self.hidden_size = hidden_size
            self.num_layers = num_layers
            
            self.embedding = nn.Embedding(vocab_size, hidden_size)
            self.lstm = nn.LSTM(hidden_size, hidden_size, num_layers, dropout=dropout, batch_first=True)
            self.dense1 = nn.Linear(hidden_size, hidden_size*4)
            self.dense2 = nn.Linear(hidden_size*4, hidden_size*2)
            self.dense3 = nn.Linear(hidden_size*2, vocab_size)
            self.drop = nn.Dropout(dropout)
            
        def forward(self, X, h=None, c=None):
            if h is None:
                h, c = self.init_hidden(X.size(0))
            out = self.embedding(X)
            out, (h, c) = self.lstm(out, (h, c))
            out = self.drop(out)
            out = self.dense1(out.reshape(-1, self.hidden_size)) # Reshaping it into (batch_size*seq_len, hidden_size)
            out = self.dense2(out)
            out = self.dense3(out)
            return out, h, c
            
        def init_hidden(self, batch_size):
            num_l = self.num_layers
            hidden = torch.zeros(num_l, batch_size, self.hidden_size).to(DEVICE)
            cell = torch.zeros(num_l, batch_size, self.hidden_size).to(DEVICE)
            return hidden, cell
    
    培训代码:

    rnn = RNN(VOCAB_SIZE, HIDDEN_SIZE, NUM_LAYERS).to(DEVICE)
    optimizer = torch.optim.Adam(rnn.parameters(), lr=1e-3)
    criterion = nn.CrossEntropyLoss()
    
    rnn.train()
    history = {}
    best_loss = 100
    
    for epoch in range(EPOCHS): #EPOCH LOOP
        counter = 0
        epoch_loss = 0
        
        for x, y in train_loader: #BATCH LOOP
            optimizer.zero_grad()
            counter += 1
    
            o, h, c = rnn(x)
            loss = criterion(o, y.reshape(-1))   
            epoch_loss += loss.item()
            
            loss.backward()
            nn.utils.clip_grad_norm_(rnn.parameters(), 5) # Clipping Gradients
            optimizer.step()
    
            if counter%print_every == 0:
                print(f"[INFO] EPOCH: {epoch+1}, BATCH: {counter}, TRAINING LOSS: {loss.item()}")
        
        epoch_loss = epoch_loss/counter       
        history["train_loss"] = history.get("train_loss", []) + [epoch_loss]
        print(f"\nEPOCH: {epoch+1} COMPLETED!\nTRAINING LOSS: {epoch_loss}\n")     
    
    
    方法1代码:

    with torch.no_grad():
        w = None
        start_str = "Hey, "
        x1 = quote2seq(start_str)[:-1]
    
        while w != EOS_TOKEN:
            x1 = torch.tensor(x1, device=DEVICE).unsqueeze(0)
            o1, h1, c1 = rnn(x1)
            p1 = F.softmax(o1, dim=1).detach()
            q1 = np.argmax(p1.cpu(), axis=1)[-1].item()
            w = idx2char[q1]
            start_str += w
            x1 = x1.tolist()[0]+ [q1]
        
    quote = start_str.replace("<EOS>", "")
    quote
    
    带火炬的
    。无梯度()
    w=无
    start_str=“嘿,”
    x1=quote2seq(开始)[:-1]
    当w!=EOS_令牌时:
    x1=火炬.张量(x1,设备=设备).反求(0)
    o1,h1,c1=rnn(x1)
    p1=F.softmax(o1,尺寸=1).detach()
    q1=np.argmax(p1.cpu(),轴=1)[-1]。项()
    w=idx2char[q1]
    开始_str+=w
    x1=x1.tolist()[0]+[q1]
    quote=start\u str.replace(“,”)
    引用
    
    方法2代码:

    with torch.no_grad():
        w = None
        start_str = "Are we back"
        x1 = quote2seq(start_str)[:-1]
        h1, c1 = rnn.init_hidden(1)
    
        while w != EOS_TOKEN:
            x1 = torch.tensor(x1, device=DEVICE).unsqueeze(0)
            h1, c1 = h1.data, c1.data
            o1, h1, c1 = rnn(x1, h1, c1)
            p1 = F.softmax(o1, dim=1).detach()
            q1 = np.argmax(p1.cpu(), axis=1)[-1].item()
            w = idx2char[q1]
            start_str += w
            x1 = [q1]
        
    quote = start_str.replace("<EOS>", "")
    quote
    
    带火炬的
    。无梯度()
    w=无
    start\u str=“我们回来了吗”
    x1=quote2seq(开始)[:-1]
    h1,c1=rnn.init_隐藏(1)
    当w!=EOS_令牌时:
    x1=火炬.张量(x1,设备=设备).反求(0)
    h1,c1=h1.数据,c1.数据
    o1,h1,c1=rnn(x1,h1,c1)
    p1=F.softmax(o1,尺寸=1).detach()
    q1=np.argmax(p1.cpu(),轴=1)[-1]。项()
    w=idx2char[q1]
    开始_str+=w
    x1=[q1]
    quote=start\u str.replace(“,”)
    引用