LSTM 句子相似度分析

作者: zqh_zy | 来源:发表于2017-08-10 20:58 被阅读6280次
wordvector

使用句子中出现单词的Vector加权平均进行文本相似度分析虽然简单,但也有比较明显的缺点:没有考虑词序且词向量区别不明确。如下面两个句子:

  • “北京的首都是中国”与“中国的首都是北京”的相似度为1。
  • “学习容易”和“学习困难”的相似度很容易也非常高。

为解决这类问题,需要用其他方法对句子进行表示,LSTM是常用的一种方式,本文简单使用单层LSTM对句子重新表示,并通过若干全连接层对句子相似度进行衡量。

数据准备

训练和测试数据包括两个待比较句子以及其相似度(0-1):


train_pair

测试数据格式相似。

语料编码

自然语言无法直接作为神经网络输入,需进行编码该部分包括以下步骤:

  • 读人训练和测试数据,分词,并给每个词编号。
  • 根据词编号,进一步生成每个句子的编号向量,句子采用固定长度,不足的位置补零。
  • 保存词编号到文件,保存词向量矩阵方便预测使用。

中文分词使用jieba分词工具,词的编号则使用Keras的Tokenizer:

print("Fit tokenizer...")
tokenizer = Tokenizer(num_words=MAX_NB_WORDS, lower=False)
tokenizer.fit_on_texts(texts_1 + texts_2 + test_texts_1 + test_texts_2)
if save:
    print("Save tokenizer...")
    if not os.path.exists(save_path):
        os.makedirs(save_path)
    cPickle.dump(tokenizer, open(os.path.join(save_path, tokenizer_name), "wb"))

其中texts_1 、texts_2 、test_texts_1 、 test_texts_2的元素分别为训练数据和测试数据的分词后的列表,如:

["我", "是", "谁"]

经过上面的过程 tokenizer保存了语料中出现过的词的编号映射。

> print tokenizer.word_index
{"我": 2, "是":1, "谁":3}

利用tokenizer对语料中的句子进行编号

> sequences_1 = tokenizer.texts_to_sequences(texts_1)
> print sequences_1
[[2 1 3], ...]

最终生成固定长度(假设为10)的句子编号列表

> data_1 = pad_sequences(sequences_1, maxlen=MAX_SEQUENCE_LENGTH)
> print data_1
[[0 0 0 0 0 0 0 2 1 3], ...]

data_1即可作为神经网络的输入。

词向量映射

在对句子进行编码后,需要准备句子中词的词向量映射作为LSTM层的输入。这里使用预训练的词向量(这里)参数,生成词向量映射矩阵:

word2vec = Word2Vec.load(EMBEDDING_FILE)
embedding_matrix = np.zeros((nb_words, EMBEDDING_DIM))
for word, i in word_index.items():
    if word in word2vec.wv.vocab:
        embedding_matrix[i] = word2vec.wv.word_vec(word)
np.save(embedding_matrix_path, embedding_matrix)
网络结构

该神经网络采用简单的单层LSTM+全连接层对数据进行训练,网络结构图:


lstm

网络由Keras实现:

def get_model():
    embedding_layer = Embedding(nb_words,
                                EMBEDDING_DIM,
                                weights=[embedding_matrix],
                                input_length=MAX_SEQUENCE_LENGTH,
                                trainable=False)
    lstm_layer = LSTM(num_lstm, dropout=rate_drop_lstm, recurrent_dropout=rate_drop_lstm)

    sequence_1_input = Input(shape=(MAX_SEQUENCE_LENGTH,), dtype='int32')
    embedded_sequences_1 = embedding_layer(sequence_1_input)
    y1 = lstm_layer(embedded_sequences_1)

    sequence_2_input = Input(shape=(MAX_SEQUENCE_LENGTH,), dtype='int32')
    embedded_sequences_2 = embedding_layer(sequence_2_input)
    y2 = lstm_layer(embedded_sequences_2)

    merged = concatenate([y1, y2])
    merged = Dropout(rate_drop_dense)(merged)
    merged = BatchNormalization()(merged)

    merged = Dense(num_dense, activation=act)(merged)
    merged = Dropout(rate_drop_dense)(merged)
    merged = BatchNormalization()(merged)
    preds = Dense(1, activation='sigmoid')(merged)

    model = Model(inputs=[sequence_1_input, sequence_2_input], \
                  outputs=preds)
    model.compile(loss='binary_crossentropy',
                  optimizer='nadam',
                  metrics=['acc'])
    model.summary()
    return model
 

该部分首先定义embedding_layer作为输入层和LSTM层的映射层,将输入的句子编码映射为词向量列表作为LSTM层的输入。两个LSTM的输出拼接后作为全连接层的输入,经过Dropout和BatchNormalization正则化,最终输出结果进行训练。

训练与预测

训练采用nAdam以及EarlyStopping,保存训练过程中验证集上效果最好的参数。最终对测试集进行预测。

model = get_model()
early_stopping = EarlyStopping(monitor='val_loss', patience=3)
bst_model_path = STAMP + '.h5'
model_checkpoint = ModelCheckpoint(bst_model_path, save_best_only=True, save_weights_only=True)

hist = model.fit([data_1, data_2], labels, \
                 validation_data=([val_1, val_2], labels), \
                 epochs=100, batch_size=10, shuffle=True, callbacks=[early_stopping, model_checkpoint])
predicts = model.predict([data_1, data_2], batch_size=10, verbose=1)
for i in range(len(test_ids)):
    print "t1: %s, t2: %s, score: %s" % (test_1[i], test_2[i], predicts[i])
小结

该网络在Kaggle Quora数据集val验证可达到80%左右的准确率,应用于中文,由于数据集有限,产生了较大的过拟合。此外在Tokenizer.fit_on_texts应用于中文时,不支持Unicode编码,可以对其源码方法进行重写,加入Ascii字符和Unicode的转换。

'''
this part is solve keras.preprocessing.text can not process unicode
'''
def text_to_word_sequence(text,
                          filters='!"#$%&()*+,-./:;<=>?@[\\]^_`{|}~\t\n',
                          lower=True, split=" "):
    if lower: text = text.lower()
    if type(text) == unicode:
        translate_table = {ord(c): ord(t) for c, t in zip(filters, split * len(filters))}
    else:
        translate_table = keras.maketrans(filters, split * len(filters))
    text = text.translate(translate_table)
    seq = text.split(split)
    return [i for i in seq if i]
    
keras.preprocessing.text.text_to_word_sequence = text_to_word_sequence

项目源码https://github.com/zqhZY/semanaly/

原创文章,转载注明出处
更多关注公众号:

wechat

相关文章

  • LSTM 句子相似度分析

    使用句子中出现单词的Vector加权平均进行文本相似度分析虽然简单,但也有比较明显的缺点:没有考虑词序且词向量区别...

  • 孪生LSTM模型计算短语和句子相似性

    孪生LSTM(Siamese LSTM)模型可以很容易来表征两个短语或者两个句子的相似性,输入数据是相似或不相似的...

  • 2018-06-23NLP知识体系

    NLP 自然语言处理的大致知识领域如下 语法分析 句法分析 情感分析 句子生成 句子相似度 文本分类/聚类 文本表...

  • 计算句子相似度

    计算句子相似度,①常用方法有基于语义和词序相似度计算方法,②基于关系向量模型基于语义和词序的句子相似度计算方法简介...

  • keras lstm 杂记

    1、例子 情感分析 情感分析(苏剑林) lstm多曲线预测 lstm多曲线预测(原文) 2、lstm参数 lstm...

  • 句子相似度计算

    思路一:先求句向量,然后求余弦相似度 1.求得两个句子的句向量 生成文本词频向量用词频来代替,句子,当然这样做忽略...

  • 中文信息学报关于相似度计算的文献检索

    本文检索了从2010年到2016年《中文信息学报》关于相似度计算的文献,主要是词语相似度,也包括句子相似度。下面对...

  • 【陪你学·生信】七、在数据库中检索相似的序列

    一、相似度Similarity 序列的分析离不开相似度这个指标,相似度比较高的序列往往具有相似的结构、执行相似的功...

  • Quora句子相似度匹配

    预备知识 NLP基础: 词袋模型(Bag-of-words model): TF-IDF算法(term frequ...

  • 夹角余弦or相关系数?(nlp/word2vec之重大发现)

    基础知识:看我的这篇文章向量相关分析基础公式 最近在做nlp方面的工作,计算文字、句子或文章间的相似度,首...

网友评论

  • 优蜜:大佬,这个准确率怎样?
  • ce0ecf06ea42:哈哈哈,这个思路和我的一样
  • 9a560177b215:思路是不错的,不过词序和句子结构这两点简单的lstm我觉得还是不能解决根本问题
  • 35228bdc59e0:请问这个语料方便提供一下么
    zqh_zy:@一笑奈何_590a 您好,文章里的语料信息是例子,真实语料库不太方便分享。但是kaggle的Quora比赛有相同方式标注的英文数据集

本文标题:LSTM 句子相似度分析

本文链接:https://www.haomeiwen.com/subject/falkrxtx.html