代码之家  ›  专栏  ›  技术社区  ›  Swamy

如何使用lstm为给定句子分配发生概率的语言模型

  •  5
  • Swamy  · 技术社区  · 6 年前

    目前,我正在使用三角学来做这件事。 它指定给定句子出现的概率。 但仅限于两个词的语境。 但LSTM可以做得更多。 那么,如何建立一个lstm模型来分配给定句子的出现概率呢?

    1 回复  |  直到 6 年前
        1
  •  11
  •   rvinas    5 年前

    我刚刚编写了一个非常简单的示例,演示如何使用lstm模型计算句子出现的概率。完整的代码可以找到 here 是的。

    鹅妈妈童谣集 在伦敦大约1765年):

    # Data
    data = ["Two little dicky birds",
            "Sat on a wall,",
            "One called Peter,",
            "One called Paul.",
            "Fly away, Peter,",
            "Fly away, Paul!",
            "Come back, Peter,",
            "Come back, Paul."]
    

    首先,让我们用 keras.preprocessing.text.Tokenizer 要创建词汇表并标记句子:

    # Preprocess data
    tokenizer = Tokenizer()
    tokenizer.fit_on_texts(data)
    vocab = tokenizer.word_index
    seqs = tokenizer.texts_to_sequences(data)
    

    我们的模型将以一系列单词作为输入(上下文),并在给定上下文的情况下输出每个单词在词汇表中的条件概率分布。为此,我们通过填充序列并在序列上滑动窗口来准备训练数据:

    def prepare_sentence(seq, maxlen):
        # Pads seq and slides windows
        x = []
        y = []
        for i, w in enumerate(seq):
            x_padded = pad_sequences([seq[:i]],
                                     maxlen=maxlen - 1,
                                     padding='pre')[0]  # Pads before each sequence
            x.append(x_padded)
            y.append(w)
        return x, y
    
    # Pad sequences and slide windows
    maxlen = max([len(seq) for seq in seqs])
    x = []
    y = []
    for seq in seqs:
        x_windows, y_windows = prepare_sentence(seq, maxlen)
        x += x_windows
        y += y_windows
    x = np.array(x)
    y = np.array(y) - 1  # The word <PAD> does not constitute a class
    y = np.eye(len(vocab))[y]  # One hot encoding
    

    我决定为每一节分别滑动窗口,但这可以做得不同。

    接下来,我们用keras定义并训练一个简单的lstm模型。该模型由嵌入层、lstm层和具有softmax激活的密集层(使用lstm最后一个时间步的输出生成给定上下文的词汇表中每个单词的概率)组成:

    # Define model
    model = Sequential()
    model.add(Embedding(input_dim=len(vocab) + 1,  # vocabulary size. Adding an
                                                   # extra element for <PAD> word
                        output_dim=5,  # size of embeddings
                        input_length=maxlen - 1))  # length of the padded sequences
    model.add(LSTM(10))
    model.add(Dense(len(vocab), activation='softmax'))
    model.compile('rmsprop', 'categorical_crossentropy')
    
    # Train network
    model.fit(x, y, epochs=1000)
    

    联合概率 P(w_1, ..., w_n) 句子的出现 w_1 ... w_n 可使用条件概率规则计算:

    P(w_1, ..., w_n)=P(w_1)*P(w_2|w_1)*...*P(w_n|w_{n-1}, ..., w_1)

    其中每个条件概率由lstm模型给出。请注意,它们可能非常小,因此在日志空间中工作以避免数值不稳定性问题是明智的。总而言之:

    # Compute probability of occurence of a sentence
    sentence = "One called Peter,"
    tok = tokenizer.texts_to_sequences([sentence])[0]
    x_test, y_test = prepare_sentence(tok, maxlen)
    x_test = np.array(x_test)
    y_test = np.array(y_test) - 1  # The word <PAD> does not constitute a class
    p_pred = model.predict(x_test)  # array of conditional probabilities
    vocab_inv = {v: k for k, v in vocab.items()}
    
    # Compute product
    # Efficient version: np.exp(np.sum(np.log(np.diag(p_pred[:, y_test]))))
    log_p_sentence = 0
    for i, prob in enumerate(p_pred):
        word = vocab_inv[y_test[i]+1]  # Index 0 from vocab is reserved to <PAD>
        history = ' '.join([vocab_inv[w] for w in x_test[i, :] if w != 0])
        prob_word = prob[y_test[i]]
        log_p_sentence += np.log(prob_word)
        print('P(w={}|h={})={}'.format(word, history, prob_word))
    print('Prob. sentence: {}'.format(np.exp(log_p_sentence)))
    

    注意 :这是一个非常小的玩具数据集,我们可能过于适合。