孪生网络图像相似度_文本蕴含之孪生网络(Siamese Network)

26c43c7164500e7529e3daa493963230.png

最近一直在分享自然语言推理和文本蕴含的相关论文,并且之前已经分享过三篇,分别是bilateral multi-perspective matching (BiMPM)模型、Enhanced Sequential Inference Model(ESIM)模型和Densely Interactive Inference Network(DIIN)模型。这次分享一下最经典的文本蕴含模型,也就是孪生网络(Siamese Network)。

刘聪NLP:论文阅读笔记:文本蕴含之BiMPM​zhuanlan.zhihu.com
5954ecbfa80e8413add309bbc3a7f660.png
刘聪NLP:论文阅读笔记:文本蕴含之ESIM​zhuanlan.zhihu.com
5954ecbfa80e8413add309bbc3a7f660.png
刘聪NLP:论文阅读笔记:文本蕴含之DIIN​zhuanlan.zhihu.com
5954ecbfa80e8413add309bbc3a7f660.png

一、背景介绍

孪生网络一开始提出是在图像识别领域(例如人脸识别),来求解两张图片(两张人脸图像)相似度,判断两张图片是否相似。如下图所示1,输入两张图片,将两张图片经过同一个卷积神经网络,得到每张图片的向量表示,最后求解两个向量的编辑距离(可以是余弦距离,欧式距离,等等),根据得到的编辑距离判断两张图片是否相似。

孪生网络原文链接​www.cs.utoronto.ca

那么从哪里体现出孪生网络的“孪生”二字呢?从图1中,我们可以看到,其实每张图片是经过了各自的卷积神经网络,但是两个卷积神经网络的所有权值(所有参数)都是共享的,这就像一对双胞胎一样,因此我们管这种权值共享的网络称之为孪生网络。而权值共享的目的有两个:(1)减少参数量,减小模型的复杂度;(2)将两个不同空间维度的向量映射到同一个空间维度上,使其数据分布保持一致,在同一空间维度上对不同向量进行编码。

4293628ffc4f90e75f95c13ae5adfafc.png
图1 基于孪生网络的图像相似度求解图

二、孪生网络在文本蕴含中的使用

在机器学习中,很多算法在图像和自然语言处理两个领域是可以互通的(可以相互借鉴的)。根据上一节的描述,我们可以看出,该任务和我们文本蕴含任务很像;都是输入两个不同的特征向量,最后判断两个特征向量之间的关系。如图2所示,我们将孪生网络应用在文本蕴含领域。

72e06c23226966bf8dcc8a9863cf1aad.png
图2 文本蕴含之孪生网络

主要包含四层:词表示层(Embedding Layer)、编码层(Encoder Layer)、融合层(Aggregated Layer)和预测层(Predict Layer)。

下面通过代码详细介绍:

首先定义模型固定参数和其输入,其中forward()函数包含上文所提到的四层。

class SIAMESE(object):
    def __int__(self, config, word_embedding_matrix=None):
        self.word_embedding_dim = config.word_dim
        self.rnn_hidden_size = config.rnn_hidden_size
        self.fine_tune_embedding = config.fine_tune_embedding
        self.num_sentence_words = config.max_length_content
        self.learning_rate = config.learning_rate
        self.rnn_layers = config.rnn_layers
        
        with tf.variable_scope("input"):
            self.sentence_one_word = tf.placeholder(tf.int32, [None, self.num_sentence_words], name="sentence_one_word")
            self.sentence_two_word = tf.placeholder(tf.int32, [None, self.num_sentence_words], name="sentence_two_word")
            self.y_true = tf.placeholder(tf.int32, [None, 2], name="true_labels")
            self.is_train = tf.placeholder(tf.bool, [], name="is_train")
        
        self.dropout_rate = tf.cond(self.is_train, lambda: config.dropout_rate, lambda: 0.0)
        self.word_mat = tf.get_variable("word_mat", 
                                        initializer=tf.constant(word_embedding_matrix, dtype=tf.float32), 
                                        trainabel=self.fine_tune_embedding)
        self.forward()

1、词表示层(Embedding Layer)

在这一层中,我们将在句子P和句子Q中的每个词映射成一个d维的向量,这d维的向量由word embedding组成,word embedding来自提前预训练好的词向量。

with tf.variable_scope("embedding_layer"):
    word_embedded_sentence_one = tf.nn.embedding_lookup(self.word_mat, self.sentence_one_word)
    word_embedded_sentence_two = tf.nn.embedding_lookup(self.word_mat, self.sentence_two_word)

2、编码层(Encoder Layer)

在这一层中,我们对上一层得到的词表示向量进行上下文编码,使用一个双向LSTM(权值共享)分别编码前提句和假设句的词表示向量,得到其上下文表征向量。

由于使用多层动态双向LSTM,因此首先获取每个句子的实际长度;

with tf.variable_scope("get_length_layer"):
    self.batch_size = tf.shape(self.sentence_one_word)[0]
    self.sentence_one_wordlevel_mask = tf.sign(self.sentence_one_word, name="sentence_one_wordlevel_mask")
    self.sentence_two_wordlevel_mask = tf.sign(self.sentence_two_word, name="sentence_two_wordlevel_mask")
    self.sentence_one_len = tf.reduce_sum(self.sentence_one_wordlevel_mask,1)
    self.sentence_two_len = tf.reduce_sum(self.sentence_two_wordlevel_mask,1)

然后对其编码,得到双向LSTM最后一个time step作为是该句子的句子表征向量;

with tf.variable_scope("sequence_encoder_layer"):
    one_fw_outputs, one_bw_outputs = self.my_bilstm(inputs=word_embedded_sentence_one,
                                                    dropout=self.dropout_rate,
                                                    n_layers=self.rnn_layers,
                                                    scope="bilstm",
                                                    sequence_length=self.sentence_one_len,
                                                    hidden_units=self.rnn_hidden_size)
    two_fw_outputs, two_bw_outputs = self.my_bilstm(inputs=word_embedded_sentence_two,
                                                   dropout=self.dropout_rate,
                                                   n_layers=self.rnn_layers,
                                                   scope="bilstm",
                                                   sequence_length=self.sentence_two_len,
                                                   hidden_units=self.rnn_hidden_size,
                                                   reuse=True)
    last_one_fw_output = self.last_relevant_output(one_fw_outputs, self.sentence_one_len)
    last_one_bw_output = self.last_relevant_output(one_bw_outputs, self.sentence_one_len)
    last_one_output = tf.concat([last_one_fw_output, last_one_bw_output], axis=-1)
    last_two_fw_output = self.get_last_output(two_fw_outputs, self.sentence_two_len)
    last_two_bw_output = self.get_last_output(two_bw_outputs, self.sentence_ontwo_len)
    last_two_output = tf.concat([last_two_fw_output, last_two_bw_output], axis=-1)

多层动态双向LSTM代码如下:

def my_bilstn(self, inputs, dropout, n_layers, scope, sequence_length, hidden_size, reuse=None):
    with tf.variable_scope("fw"+scope, reuse=reuse):
        stacked_rnn_fw = []
        for _ in range(n_layers):
            fw_cell = LSTMCell(hidden_size)
            lstm_fw_cell = DropoutWrapper(fw_cell, output_keep_prob=1-dropout)
            stacked_rnn_fw.append(lstm_fw_cell)
        lstm_fw_cell_m = tf.nn.rnn_cell.MultiRNNCell(cells=stacked_rnn_fw, state_is_tuple=True)
    with tf.variable_scope("bw"+scope, reuse=reuse):
        stacked_rnn_bw = []
        for _ in range(n_layers):
            bw_cell = LSTMCell(hidden_size)
            lstm_bw_cell = DropoutWrapper(bw_cell, output_keep_prob=1-dropout)
            stacked_rnn_bw.append(lstm_bw_cell)
        lstm_bw_cell_m = tf.nn.rnn_cell.MultiRNNCell(cells=stacked_rnn_bw, state_is_tuple=True)    
    with tf.variable_scope(scope, reuse=reuse):
        (fw_outputs, bw_outputs), _ = tf.nn.bidirectional_dynamic_rnn(cell_fw=lstm_fw_cell_m,
                                                                      cell_bw=lstm_bw_cell_m,
                                                                      inputs=inputs,
                                                                      sequence_length=sequence_length,
                                                                      dtype=tf.float32)
    return fw_outputs, bw_outputs

获取动态双向LSTM最后一个time step向量代码如下:

def get_last_output(self, output, sequence_length):
    with tf.name_scope("get_last_output"):
        batch_size = tf.shape(output)[0]
        max_length = tf.shape(output)[-2]
        out_size = int(output.get_shape()[-1])
        index = tf.range(0, batch_size)*max_length + (sequence_length-1)
        flat = tf.reshape(output, [-1, out_size])
        last_output = tf.gather(flat, index)
    return last_output

3、融合层(Aggregated Layer)和预测层(Predict Layer)

在这一层中,我们将上文得到的两个句子的句子表征向量进行拼接([P,H,P*H,P-H]),然后通过两个全连接层去预测,两个句子是否存在蕴含关系。

with tf.variable_scope("output_layer"):
    aggregated_representation = tf.concat([last_one_output, last_two_output, last_one_output*last_two_output, last_one_output-last_two_output], axis=-1)
    predict_one_layer = tf.layers.dense(aggregated_representation,
                                        units=aggregated_representation.get_shape().as_list()[1],
                                        activation=tf.nn.tanh,
                                        name="predict_one_layer")
    d_predict_one_layer = tf.layers.dropout(predict_one_layer, rate=self.dropout_rate, training=self.is_train, name="predict_one_layer_dropout")
    self.predict_two_layer = tf.layers.dense(d_predict_one_layer, units=2,  name="predict_two_layer")
    self.y_pred = tf.nn.softmax(self.predict_two_layer, name="scores")
    self.predictions = tf.arg_max(self.y_pred, axis=1, name="predictions")

三、总结

本人认为,孪生网络的精髓就是两个编码器的参数共享,而孪生网络做文本蕴含应该是文本蕴含的开端;像是BiMPM、DIIN和ESIM,都是孪生网络的改进版,对其编码后的句子向量不是做简单的拼接,而是对其进行注意力提取和句子信息交互,使其结果更好。

以上就是我对孪生网络的理解,如果有不对的地方,请大家见谅并多多指教。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值