Модель языка RNN в PyTorch, многократно предсказывающая одни и те же три слова - PullRequest
0 голосов
/ 01 августа 2020

Я пытаюсь создать модель языка на уровне слов, используя RNN в PyTorch. Всякий раз, когда я тренируюсь, потери остаются примерно одинаковыми для всего обучающего набора, и когда я пытаюсь выбрать новое предложение, те же три слова предсказываются в том же порядке. Например, в моей последней попытке RNN предсказывала «то же самое, то то же самое», и эта последовательность просто продолжала повторяться. Я попытался изменить способ настройки RNN, включая использование LSTM, GRU и различных встраиваний, но пока ничего не помогло.

Я обучаю RNN, беря предложение из 50 слов и выбор все большей части предложения со следующим словом, являющимся целью. В конце предложения у меня есть тег EOS. Я использую текст из Republi c Платона в качестве учебного набора и встраиваю его с помощью слоя встраивания pytorch. Затем я загружаю его в LSTM, а затем в линейный слой, чтобы получить правильную форму. Я не уверен, что проблема в RNN, данных, обучении или чем-то еще. Любая помощь будет принята с благодарностью. вы могли бы предложить для этого решение этой проблемы. Моя конечная цель - просто создать предложение. Заранее спасибо!

Вот мой RNN

class LanguageModel(nn.Module):
  """
    Class that defines the reccurent neural network.

    Methods
    -------
    forward(input, h, c)
      Forward propogation through the RNN.
    initHidden()
      Initializes the hidden and cell states.
  """
  def __init__(self, vocabSize, seqLen = 51, embeddingDim = 30, hiddenSize = 32, numLayers = 1, bid = False):
    """
      Initializes the class

      Parameters
      ----------
      seqLen : int, optional
        The length of the input sequence.
      embeddingDim : int, optional
        The dimension that the embedding dimension for the encoder should be.
      vocabSize : int
        The length of the vocab dictionary.
      hiddenSize : int, optional
        The size that the hidden state should be.
      numLayers : int, optional
        The number of LSTM Layers.
      bid : bool, optional
        Whether the RNN should be bidirctional or not.
    """

    super(LanguageModel, self).__init__()
    self.hiddenSize = hiddenSize
    self.numLayers = numLayers

    # Set value of numDirections based on whether or not the RNN is bidirectional.
    if bid == True:
      self.numDirections = 2
    else:
      self.numDirections = 1

    self.encoder = nn.Embedding(vocabSize, embeddingDim)
    self.LSTM = nn.LSTM(input_size = embeddingDim, hidden_size = hiddenSize, num_layers = numLayers, bidirectional = bid)
    self.decoder = nn.Linear(seqLen * self.numDirections * hiddenSize, vocabSize)

  def forward(self, input, h, c):
    """
      Forward propogates through the RNN

      Parameters
      ----------
      input : torch.Tensor
        Input to RNN. Should be formatter using makeInput() and padSeq().
      h : torch.Tensor
        Hidden state.
      c : torch.Tensor
        Cell state.

      Returns
      -------
      torch.Tensor
        Log probabilities for the predicted word from the RNN.
    """

    emb = self.encoder(input)
    emb.unsqueeze_(1) # Add in the batch dimension so the shape is right for the LSTM

    out, (h, c) = self.LSTM(emb, (h, c))
    out = out.view(1, -1) # Reshaping to fit into the loss function.

    out = self.decoder(out)

    logProbs = F.log_softmax(out, dim = 1)

    return logProbs

  def initHidden(self):
    """
      Initializes the hidden and cell states.

      Returns
      -------
      torch.Tensor
        Tensor containing the initial hidden state.
      torch.Tensor
        Tensor containing the intial cell state.
    """
    h = torch.zeros(self.numLayers * self.numDirections, 1, self.hiddenSize)
    c = torch.zeros(self.numLayers * self.numDirections, 1, self.hiddenSize)
    
    return h, c

вот как я создаю свои входные данные и цели

def makeInput(sentence):
  """
    Prepares a sentence for input to the RNN.

    Parameters
    ----------
    sentence : list
      The sentence to be converted into input. Should be of form: [str] 

    Returns
    -------
    torch.Tensor
      Tensor of the indices for each word in the input sentence.
  """

  sen = sentence[0].split() # Split the list into individual words
  sen.insert(0, 'START')

  input = [word2Idx[word] for word in sen] # Iterate over the words in sentence and convert to indices

  return torch.tensor(input)

def makeTarget(sentence):
  """
    Prepares a sentence to be a target.

    Parameters
    ----------
    sentence : str
      The sentence to be made into a target. Should be of form: [str]

    Returns
    -------
    torch.Tensor
      Tensor of the indices for the target phrase including the <EOS> tag.
  """

  sen = sentence[0].split() # Split the list into individual words
  sen.append('EOS')
  
  target = [word2Idx[word] for word in sen]
  target = torch.tensor(target, dtype = torch.long)

  return target.unsqueeze_(-1) # Removing dimension for loss function 

def padSeq(seq, refSeq):
  """
    Pads a sequence to be the same shape as another sequence.

    Parameters
    ----------
    seq : torch.Tensor
      The sequence to pad.
    refSeq : torch.Tensor
      The reference sequence. seq will be padded to be the same shape as refSeq.

    Returns
    -------
    torch.Tensor
      Tensor containing the padded sequence.
  """

  padded = pad_sequence([refSeq, seq])
  tmp = torch.t(padded) # Transpose the padded sequence for easier indexing on return

  return tmp[1] # Return only the padded seq not both sequences

и вот мои тренировки l oop

def train():
  """
    Trains the model.
  """

  start = time.time()
  for i, data in enumerate(trainLoader):
    inputTensor = makeInput(data)
    targetTensor = makeTarget(data)

    targetTensor = targetTensor.to(device)

    h, c = model.initHidden()
    h = h.to(device)
    c = c.to(device)
    
    optimizer.zero_grad()
    loss = 0

    for x in range(inputTensor.size(0)): # Iterate over all of the words in the input sentence
      """ Preparing input for the rnn """
      input = inputTensor[: x + 1] # We only want part of the input so the RNN can learn on predicting the next words
      input = padSeq(input, inputTensor)
      input = input.to(device)

      out = model(input, h, c)
      l = criterion(out, targetTensor[x])
      loss += l

    loss.backward()
    optimizer.step()
  
    if i % 250 == 0: # Print updates to the models loss every 10 iters.
      print('[{}] Epoch: {} -> {}'.format(timeSince(start), i, loss / inputTensor.size(0)))
...