我是靠谱客的博主 土豪楼房,最近开发中收集的这篇文章主要介绍CNN在句子相似性建模的应用--tensorflow实现篇2,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

=======================更新===========================
这段时间有很多人反映在跑代码的时候会出现NAN的错误,由于本人最近忙着找实习和实习,所以一直没有时间去找模型里面的错误,一直在拖这个事情。恰好一位网友很认真的在咨询我并且尝试着修改模型代码,所以借着这次机会把模型中的一些问题找了出来并更新了模型代码,最新的代码可以参考这位网友的GitHub:https://github.com/Fengfeng1024/MPCNN

为了方便大家了解代码的问题,以及如何寻找bug,这里贴出来我们邮沟通的细节,希望大家可以通过这个过程学到如何解决代码中的bug,下面邮件内容倒叙排列~~:

https://github.com/Fengfeng1024/MPCNN 感谢博主的分享
—————— 原始邮件 ——————
发件人: “254557889”<254557889@qq.com>;
发送时间: 2018年5月8日(星期二) 晚上9:04
收件人: “输过,败过,不曾怕过”chenfeng_scut@qq.com;
主题: 回复:关于您的MPCNN模型复现代码遗留问题

恭喜恭喜,因为我看这个代码还有文章下面一直有人在咨询NAN的问题,所以如果可以的话你能把终版代码pull到我github的项目上吗,或者说你上传到你自己的GitHub上面,然后我在文章和代码里面给出你实现的连接,好让更多的人能够看到,有所帮助~~
—————— 原始邮件 ——————
发件人: “输过,败过,不曾怕过”chenfeng_scut@qq.com;
发送时间: 2018年5月8日(星期二) 下午4:26
收件人: “刘冲”<254557889@qq.com>;
主题: 回复:关于您的MPCNN模型复现代码遗留问题

今天我用tensorboard可视化了每个层的权重和一些输出,发现计算欧氏距离的时候有些输出是0,我觉得可能因为这个导致最后softmax的输入有些为0,所以loss变为NAN了,既然如此,我于是计算欧式距离最后加了1e-4,没想到loss还是NAN。还有一个想法就是用标准化欧氏距离,不知道可不可行。现在跑模型没用欧式距离,同时对您的代码有一些改进,权重都初始化为mean=0,stddev=1.0,在每一层卷积后加BN层,所有可训练的参数加到L2正则化。附件有acc和loss曲线,增加了epoch,终于收敛了。
—————— 原始邮件 ——————
发件人: “254557889”<254557889@qq.com>;
发送时间: 2018年5月7日(星期一) 中午12:13
收件人: “输过,败过,不曾怕过”chenfeng_scut@qq.com;
主题: 回复:关于您的MPCNN模型复现代码遗留问题

你好,看了你的邮件也给了我一些提示,首先我认为NAN出现的原因可能是,相似度计算处,对每个向量的每个维度的平方差求和,而每个向量都是卷积之后的输出,正如你所说relu激活函数的输出值可能会很大,导致欧氏距离计算平方和时出现NAN的现象,而余弦相似度分母会进行归一化操作,自然没有NAN的问题。上面是我分析的原因,但我不明白的是为什么去掉了sqrt就可以解决NAN??我的想法是把relu改成tanh,或者说不使用欧氏距离。然后至于说训练效果很差这个问题,首先来讲数据集是公共数据集应该不会有问题,肉眼可见的训练上的规律很可疑,因为训练过程中加入了shuffle,不应该能找到像你说的那种规律。这里可以尝试的方案是,将代码改变一下,可以每100个step eval一次,而不是每个epoch eval一次,然后再tensorboard上面查看train和eval两条曲线,看看原因究竟是什么,另外把训练过程中的W和b权重也都可视化一下,往往问题会出现在某个权重的异常上面。再有就是我看训练的acc还算可以,但是eval的acc只有0.3,另外一个就是loss下降的很缓慢,一般使用了tf.reduce_mean之后loss应该降到很低才对,现在维持在40+肯定是有问题的。
—————– 原始邮件 ——————
发件人: “输过,败过,不曾怕过”chenfeng_scut@qq.com;
发送时间: 2018年5月7日(星期一) 中午11:43
收件人: “刘冲”<254557889@qq.com>;
主题: 回复:关于您的MPCNN模型复现代码遗留问题

非常感谢您的回复。根据您提供的思路,首先我采用了梯度截断的方法,将梯度控制在某一范围内,但是这样跑出来的效果并不好;然后我用了tfdbg定位到计算欧式距离的时候出现了NAN,我把tf.sqrt去掉之后就可以了,不过我还是不明白为什么会出现NAN,因为计算余弦相似度的时候也是用了tf.sqrt函数。还有一个原因也会导致NAN,就是relu激活函数,paper中的激活函数都是tanh。训练过程中,我发现到某些数据集的时候训练效果非常不好,而且loss和acc波动有点大,难以收敛。附件有训练日志,lr=1e-3、epoch=10、batch_size=64.
—————— 原始邮件 ——————
发件人: “254557889”<254557889@qq.com>;
发送时间: 2018年5月5日(星期六) 下午3:08
收件人: “输过,败过,不曾怕过”chenfeng_scut@qq.com;
主题: 回复:关于您的MPCNN模型复现代码遗留问题

你好,这个代码是我刚开始使用tf仿真论文的时候做的,当时确实遇到了很多问题,包括代码本身应该也存在很多问题。当时的效果其实也很差,一直想着回头再看看修改一下,但是最近一直比较忙没时间搞,所以希望你在跑程序的同时自己看看程序本身是否存在问题,而不是单纯的跑代码。另外关于你说的W1全部变为NAN这个问题,可以尝试使用RNN训练过程中常用的梯度截断的方法,把梯度限制在10以内,这样应该可以防止NAN现象的出现,另外就是看看代码里面究竟哪一行引起了NAN的出现,这里可以看一下官网上面tfdbg的介绍,里面说了如何找到NAN出现的位置以及简单地分析方法。很哟可能是分母部分为0之类的情况导致的。最后,希望你能把这个代码跳出来改正确吧~~
—————— 原始邮件 ——————
发件人: “输过,败过,不曾怕过”chenfeng_scut@qq.com;
发送时间: 2018年5月5日(星期六) 中午12:45
收件人: “刘冲”<254557889@qq.com>;
主题: 关于您的MPCNN模型复现代码遗留问题

博主,您好,首先非常感谢您对MPCNN模型的思路与代码分享,我在运行您的代码过程中,碰到了loss为nan的情况,网上查了很多方法,调整lr、batch_size,tf.clip_by_value函数调整最后一层输出,同时仔细对比了论文与您的复现代码,没发现任何问题,然后我打印出每一个权重参数,发现第一个batch_size训练过后,权重参数W1[0]全部变为NAN,我还是不知道怎么解决这个问题,无奈之下联系博主,希望博主能解决我的疑问,万分感谢

=======================以下是原文===============================

上节已经介绍了数据预处理部分代码,本节则详细介绍一下模型构建和训练部分。旨在以一个新手的角度出发,详细介绍一下tf中不同类型cnn的使用方法以及其中tensor的shape变化,来感受一下其运行过程的细节。代码可以在我的github上下载

模型初始化

首先我们介绍一下模型构建部分。这部分代码全部写在model.py文件中。为了方便调用,我们写了一个MPCNN_Layer类来实现整个论文模型的构建。首先是init函数,主要用于初始化一些权重参数等,代码入下:

    #初始化一些变量主要包括了各层之间的权重,偏置。
    def __init__(self, num_classes, embedding_size, filter_sizes, num_filters, n_hidden,
                 input_x1, input_x2, input_y, dropout_keep_prob):
        '''

        :param num_classes:  6,代表6种类别。即输出y的维度
        :param embedding_size: 词向量维度
        :param filter_sizes: 卷积窗口大小。此处为列表【1,2,100】100表示对整个句子直接卷积。
        :param num_filters: 卷积核数量,这里为列表【num_filters_A,num_filters_B】分别为20,20.论文中A为300
        :param n_hidden:全连接层的神经元个数
        :param input_x1:输入句子矩阵。shape为【batch_size,sentence_length, embed_size,1】
        :param input_x2:同inpt_x1
        :param input_y:输出6维的array。one-hot编码
        :param dropout_keep_prob:dropout比率
        '''
        self.embedding_size = embedding_size
        self.filter_sizes = filter_sizes
        self .num_filters = num_filters
        self.poolings = [tf.reduce_max, tf.reduce_min, tf.reduce_mean]

        self.input_x1 = input_x1
        self.input_x2 = input_x2
        self.input_y = input_y
        self.dropout_keep_prob = dropout_keep_prob
        #Block_A的参数。因为有三种窗口尺寸,所以初始化三个参数
        self.W1 = [init_weight([filter_sizes[0], embedding_size, 1, num_filters[0]]),
                   init_weight([filter_sizes[1], embedding_size, 1, num_filters[0]]),
                   init_weight([filter_sizes[2], embedding_size, 1, num_filters[0]])]
        self.b1 = [tf.Variable(tf.constant(0.1, shape=[num_filters[0]])),
                   tf.Variable(tf.constant(0.1, shape=[num_filters[0]])),
                   tf.Variable(tf.constant(0.1, shape=[num_filters[0]]))]
        # Block_B的参数。这里只需要两种窗口尺寸(舍弃100的窗口),所以初始化两个参数
        self.W2 = [init_weight([filter_sizes[0], embedding_size, 1, num_filters[1]]),
                   init_weight([filter_sizes[1], embedding_size, 1, num_filters[1]])]
        self.b2 = [tf.Variable(tf.constant(0.1, shape=[num_filters[1], embedding_size])),
                   tf.Variable(tf.constant(0.1, shape=[num_filters[1], embedding_size]))]
        #卷积层经过句子相似计算之后的输出flatten之后的尺寸。用于生成隐藏层的参数。具体会在后面介绍
        self.h = num_filters[0]*len(self.poolings)*2 + 
                 num_filters[1]*(len(self.poolings)-1)*(len(filter_sizes)-1)*3 + 
                 len(self.poolings)*len(filter_sizes)*len(filter_sizes)*3
        #全连接层参数
        self.Wh = tf.Variable(tf.random_normal([self.h, n_hidden], stddev=0.01), name='Wh')
        self.bh = tf.Variable(tf.constant(0.1, shape=[n_hidden]))
        #输出层参数
        self.Wo = tf.Variable(tf.random_normal([n_hidden, num_classes], stddev=0.01), name='Wo')

Block_A和Block_B的构建

接下来进行Block_A的构建,这个比较简单,直接调用conv2d函数即可。如下图所示,代码入下:
这里写图片描述

    def bulit_block_A(self, x):
        #bulid block A and cal the similarity according to algorithm 1
        out = []
        with tf.name_scope("bulid_block_A"):
            #遍历每个pooling方式,max、min、mean
            for pooling in self.poolings:
                pools = []
                #每个pooling都对应几种不同的窗口大小。【1,2,100】
                for i, ws in enumerate(self.filter_sizes):
                    #print x.get_shape(), self.W1[i].get_shape()
                    with tf.name_scope("conv-pool-%s" %ws):
                        #x->[batch_size, sentence_length, embed_size, 1], W1[i]->[ws, embed_size, 1, num_filters]
                        conv = tf.nn.conv2d(x, self.W1[i], strides=[1, 1, 1, 1], padding="VALID")
                        #print conv.get_shape()
                        conv = tf.nn.relu(conv + self.b1[i])  # [batch_size, sentence_length-ws+1, 1, num_filters_A]
                        pool = pooling(conv, axis=1) ## [batch_size, 1, num_filters_A]
                    pools.append(pool)
                out.append(pools)
            return out

所以Block_A的输出out是一个嵌套列表。第一层长度为3,代表三种pooling方式,第二层长度为3,代表三种卷积窗口大小,然后其中每个元素都是[batch_size, 1, num_filters_A]的三维Tensor。

接下来看Block_B部分的构建。由于此处用到了per_dim的卷积核,所以比较麻烦。图和代码如下所示。
这里写图片描述

    def per_dim_conv_layer(self, x, w, b, pooling):
        '''
        实现per_dim卷积,请结合模型介绍篇的图片进行理解
        :param input: [batch_size, sentence_length, embed_size, 1]
        :param w: [ws, embedding_size, 1, num_filters]
        :param b: [num_filters, embedding_size]
        :param pooling:
        :return:
        '''
        # 为了实现per_dim的卷积。所以我们要将输入和权重偏置参数在embed_size维度上进行unstack
        #这样我们就获得了每个维度上的输入、权重、偏置。可以结合模型介绍篇里面的图片进行理解
        input_unstack = tf.unstack(x, axis=2)
        w_unstack = tf.unstack(w, axis=1)
        b_unstack = tf.unstack(b, axis=1)
        convs = []
        #对每个embed_size维度进行卷积操作
        for i in range(x.get_shape()[2]):
            #conv1d要求三维的输入,三维的权重(没有宽度,只有长度。所以称为1d卷积)。具体可以参见官方API。
            conv = tf.nn.relu(tf.nn.conv1d(input_unstack[i], w_unstack[i], stride=1, padding="VALID") + b_unstack[i])
            # [batch_size, sentence_length-ws+1, num_filters_A]
            convs.append(conv)
        #将embed_size个卷积输出在第三个维度上进行进行stack。所以又获得了一个4位的tensor
        conv = tf.stack(convs, axis=2)  # [batch_size, sentence_length-ws+1, embed_size, num_filters_A]
        #池化。即对第二个维度的sentence_length-ws+1个值取最大、最小、平均值等操作、
        pool = pooling(conv, axis=1)  # [batch_size, embed_size, num_filters_A]

        return pool

    def bulid_block_B(self, x):
        out = []
        with tf.name_scope("bulid_block_B"):
            for pooling in self.poolings[:-1]:
                pools = []
                for i, ws in enumerate(self.filter_sizes[:-1]):
                    with tf.name_scope("per_conv-pool-%s" % ws):
                        pool = self.per_dim_conv_layer(x, self.W2[i], self.b2[i], pooling)
                    pools.append(pool)
                out.append(pools)
            return out

其中bulid_block_B函数作用与bulid_block_A相同不做介绍。而 per_dim_conv_layer函数则实现了per_dim卷积方法。注释已经很清楚了。所以Block_B的输出out也是一个嵌套列表。第一层长度为2,代表两种pooling方式,第二层长度为2,代表两种卷积窗口大小,然后其中每个元素都是[batch_size, 50, num_filters_B]的三维Tensor。

相似性测量层的构建

经过上面两个部分的构建,我们就应实现了句子表示部分的代码。接下来就是实现相似测量层的两个算法。如下图所示:
这里写图片描述
这里写图片描述

    def similarity_sentence_layer(self):
        #对输入的两个句子进行构建block_A。
        #sent1,2都是3*3*[batch_size,1, num_filters_A]的嵌套列表
        sent1 = self.bulit_block_A(self.input_x1)
        sent2 = self.bulit_block_A(self.input_x2)
        fea_h = []
        #实现算法1
        with tf.name_scope("cal_dis_with_alg1"):
            for i in range(3):
                #将max,men,mean三个进行连接
                regM1 = tf.concat(sent1[i], 1)
                regM2 = tf.concat(sent2[i], 1)
                #按照每个维度进行计算max,men,mean三个值的相似度。可以参考图中绿色框
                for k in range(self.num_filters[0]):
                    #comU2计算两个tensor的距离,参见上篇博文,得到一个(batch_size,2)的tensor。2表示余弦距离和L2距离
                    fea_h.append(comU2(regM1[:, :, k], regM2[:, :, k]))
        #得到fea_h是一个长度3*20=60的list。其中每个元素都是(batch_size,2)的tensor
        fea_a = []
        #实现算法2的2-9行
        with tf.name_scope("cal_dis_with_alg2_2-9"):
            for i in range(3):
                for j in range(len(self.filter_sizes)):
                    for k in range(len(self.filter_sizes)):
                        # comU1计算两个tensor的距离,参见上篇博文,上图中的红色框。得到一个(batch_size,3)的tensor。3表示余弦距离和L2距离,L1距离
                        fea_a.append(comU1(sent1[i][j][:, 0, :], sent2[i][k][:, 0, :]))
        #得到fea_a是一个长度为3*3*3=27的list。其中每个元素是(batch_size,3)的tensor

        # 对输入的两个句子进行构建block_B。
        # sent1,2都是2*2*[batch_size,50, num_filters_B]的嵌套列表
        sent1 = self.bulid_block_B(self.input_x1)
        sent2 = self.bulid_block_B(self.input_x2)

        fea_b = []
        # 实现算法2的剩余行
        with tf.name_scope("cal_dis_with_alg2_last"):
            for i in range(len(self.poolings)-1):
                for j in range(len(self.filter_sizes)-1):
                    for k in range(self.num_filters[1]):
                        fea_b.append(comU1(sent1[i][j][:, :, k], sent2[i][j][:, :, k]))
        ##得到fea_b是一个长度为2*2*20=80的list。其中每个元素是(batch_size,3)的tensor
        return tf.concat(fea_h + fea_a + fea_b, 1)

经过相似计算函数我们就获得了两个句子的相似性向量fea_h,fea_a,fea_b,通过tf.concat()函数就可以将其进行连接得到一个flatten的相似性输出向量fea。这里需要注意的问题是fea维度的计算,我们之前已经知道了fea_h,fea_a,fea_b的维度,分别是60*[batch_size, 2]、27*[batch_size, 3]、80*[batch_size, 3],那么首先将其连接,然后再第一个维度上进行扩展,最终就可以获得一个shape是[batch_size, 441]的Tensor。

全连接层构建

有了fea,接下来的工作就简单了,我们只需要将其输入全连接层即可完成模型的构建工作。代码入下:

    def similarity_measure_layer(self):
    #调用similarity_sentence_layer函数获得句子的相似性向量
        fea = self.similarity_sentence_layer()
        # fea_h.extend(fea_a)
        # fea_h.extend(fea_b)
        #print len(fea_h), fea_h
        #fea = tf.concat(fea_h+fea_a+fea_b, 1)
        #print fea.get_shape()
        with tf.name_scope("full_connect_layer"):
            h = tf.nn.tanh(tf.matmul(fea, self.Wh) + self.bh)
            h = tf.nn.dropout(h, self.dropout_keep_prob)
            o = tf.matmul(h, self.Wo)
            return o

模型训练

至此我们就完成了模型的搭建工作。接下来就可以进行训练了。这部分代码写在train1.py文件中,主要完成了词向量、训练、测试数据的读取,模型的定义和训练,Summary的记录等工作。是一个比较标准的tf执行流程。这里也推荐大家使用app.flags来定义模型的超参数,这样一来之后修改参数会很方便,修改一个地方就可以了,免得改来改去还总是报错。app定义如下:

tf.app.flags.DEFINE_integer('embedding_dim', 50, 'The dimension of the word embedding')
tf.app.flags.DEFINE_integer('num_filters_A', 20, 'The number of filters in block A')
tf.app.flags.DEFINE_integer('num_filters_B', 20, 'The number of filters in block B')
tf.app.flags.DEFINE_integer('n_hidden', 150, 'number of hidden units in the fully connected layer')
tf.app.flags.DEFINE_integer('sentence_length', 100, 'max size of sentence')
tf.app.flags.DEFINE_integer('num_classes', 6, 'num of the labels')

tf.app.flags.DEFINE_integer('num_epochs', 10, 'Number of epochs to be trained')
tf.app.flags.DEFINE_integer('batch_size', 32, 'size of mini batch')

tf.app.flags.DEFINE_integer("display_step", 100, "Evaluate model on dev set after this many steps (default: 100)")
tf.app.flags.DEFINE_integer("evaluate_every", 100, "Evaluate model on dev set after this many steps (default: 100)")
tf.app.flags.DEFINE_integer("checkpoint_every", 100, "Save model after this many steps (default: 100)")
tf.app.flags.DEFINE_integer("num_checkpoints", 5, "Number of checkpoints to store (default: 5)")

tf.app.flags.DEFINE_float('lr', 1e-3, 'learning rate')
tf.app.flags.DEFINE_float('l2_reg_lambda', 1e-4, 'regularization parameter')

tf.app.flags.DEFINE_boolean("allow_soft_placement", True, "Allow device soft device placement")
tf.app.flags.DEFINE_boolean("log_device_placement", False, "Log placement of ops on devices")

filter_size = [1,2,100]
conf = tf.app.flags.FLAGS
conf._parse_flags()

接下来就是进行数据读入和模型构建工作。都很简单不做过多介绍,代码入下:

#glove是载入的次向量。glove.d是单词索引字典<word, index>,glove.g是词向量矩阵<词个数,300>
glove = emb.GloVe(N=50)
#-------------------------------------Loading data----------------------------------------------#
print ("Loading data...")
Xtrain, ytrain = load_set(glove, path='./sts/semeval-sts/all')
#[22592, 句长]
Xtest, ytest = load_set(glove, path='./sts/semeval-sts/2016')
#[1186, 句长]
#-------------------------------------Loading finished----------------------------------------------#

#-------------------------------------training the network----------------------------------------------#
with tf.Session() as sess:
    #定义输入输出等placeholder
    input_1 = tf.placeholder(tf.int32, [None, conf.sentence_length], name="input_x1")
    input_2 = tf.placeholder(tf.int32, [None, conf.sentence_length], name="input_x2")
    input_3 = tf.placeholder(tf.int32, [None, conf.num_classes], name="input_y")
    dropout_keep_prob = tf.placeholder(tf.float32, name="dropout_keep_prob")

    #通过embedding_lookup进行单词索引-》词向量的转换。glove.g是词向量矩阵
    s0_embed = tf.nn.embedding_lookup(glove.g, input_1)
    s1_embed = tf.nn.embedding_lookup(glove.g, input_2)

    input_x1 = tf.reshape(s0_embed, [-1, conf.sentence_length, conf.embedding_dim, 1])
    input_x2 = tf.reshape(s1_embed, [-1, conf.sentence_length, conf.embedding_dim, 1])
    input_y = tf.reshape(input_3, [-1, conf.num_classes])

    #构建MPCNN模型
    setence_model = MPCNN_Layer(conf.num_classes, conf.embedding_dim, filter_size,
                                [conf.num_filters_A, conf.num_filters_B], conf.n_hidden,
                                input_x1, input_x2, input_y, dropout_keep_prob)
    #调用similarity_measure_layer函数获得模型输出
    out = setence_model.similarity_measure_layer()

    #使用softmax交叉熵函数计算cost
    cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=out, labels=setence_model.input_y))
    train_step = tf.train.AdamOptimizer(conf.lr).minimize(cost)

    #获得预测和精确度
    predict_op = tf.argmax(out, 1)

    acc = tf.reduce_mean(tf.cast(tf.equal(tf.argmax(input_y, 1), tf.argmax(out, 1)), tf.float32))

    timestamp = str(int(time.time()))
    out_dir = os.path.abspath(os.path.join(os.path.curdir, "runs", timestamp))
    print("Writing to {}n".format(out_dir))

    loss_summary = tf.summary.scalar("loss", cost)
    acc_summary = tf.summary.scalar("accuracy", acc)

    train_summary_op = tf.summary.merge([loss_summary, acc_summary])
    train_summary_dir = os.path.join(out_dir, "summaries", "train")
    train_summary_writer = tf.summary.FileWriter(train_summary_dir, sess.graph)

    dev_summary_op = tf.summary.merge([loss_summary, acc_summary])
    dev_summary_dir = os.path.join(out_dir, "summaries", "dev")
    dev_summary_writer = tf.summary.FileWriter(dev_summary_dir, sess.graph)

    checkpoint_dir = os.path.abspath(os.path.join(out_dir, "checkpoints"))
    checkpoint_prefix = os.path.join(checkpoint_dir, "model")
    if not os.path.exists(checkpoint_dir):
        os.makedirs(checkpoint_dir)
    saver = tf.train.Saver(tf.global_variables(), max_to_keep=conf.num_checkpoints)

    init = tf.global_variables_initializer().run()

    for j in range(10):
        for i in range(0, 20000, conf.batch_size):
            x1 = Xtrain[0][i:i + conf.batch_size]
            x2 = Xtrain[1][i:i + conf.batch_size]
            y = ytrain[i:i + conf.batch_size]
            _, summaries, accc, loss = sess.run([train_step, train_summary_op, acc, cost],
                                     feed_dict={input_1: x1, input_2: x2, input_3: y, dropout_keep_prob: 0.5})
            time_str = datetime.datetime.now().isoformat()
            print("{}: loss {:g}, acc {:g}".format(time_str, loss, accc))
            train_summary_writer.add_summary(summaries)
        print("nEvaluation:")
        accc = sess.run(acc, feed_dict={input_1: Xtest[0], input_2: Xtest[1], input_3: ytest, dropout_keep_prob: 1.0})
        print "test accuracy:", accc

一些效果图

执行上面的代码我们就可以去tensorboard查看我们程序运行的效果了。先贴几张模型架构图:
这里写图片描述
下面这张是Block_B的内部架构,其实蓝色方框内还有embed_size个per_dim的卷积核,由于太多了所以不进行展示:
这里写图片描述
再来一张Block_A的内部架构,里面一共9个卷积-pooling层:
这里写图片描述
等等吧,还有很多可以去细看的图,这里就不一一列举了。接下来说一下程序运行不理想的地方==那就是准确度了。这里由于很多参数并未按照论文中所说的那样设置,也有可能是被的一些原因导致准确度上不去,如下图所示。总是震荡,而无法收敛。请教了一些朋友,改了改学习率啊、dropout啊、batch_size啊等等,都收效甚微。但是本着写程序练手的态度就不纠结与这些细节了,也是因为每一次训练成本都比较高。自己懒得去一步步调参了==不过还是贴出来两张训练的结果吧。
这里写图片描述
这里写图片描述

最后的总结

好了,这样我们就算勉强完成了对该论文的方针实现啦(捂脸)。在总结一下几个需要注意的点,也是我在编程序的时候遇到的坑:

  1. 在程序编写过程中一定要对各个变量的shape聊熟于心,刚入门的要多看官方文档,把一些重要的函数用法和作用了解的比较透彻,这样才能在使用的时候知道用哪个函数,怎么用。
  2. 词向量千万不要在读取训练集的时候就转化,否则太消耗内存了。要是用tf提供的embedding_lookup函数在训练的过程中在进行转化
  3. 输入要转化为np数组才行,不然feed的时候会报错。
  4. 变量、每一个比较重要的部分都是用tf.name_scope()函数命名,不然最后你会发现TensorBoard上面的结构图夸张复杂到无法加载。然后就是在TensorBoard架构图页面右侧的模块可以右键添加到左侧,这样才能形成一个完整的流图,不然你看到的可能是一个个分离的模块。
  5. 至于模型调参的问题,我还要继续慢慢研究,感觉还有很长的路要走
  6. 看教程的时候要自己动手写一遍,甚至调试一边,这样会收获更多
  7. 对了,说到调试,跑程序的时候遇到了几个问题,很棘手,比如cost是NAN,当时想着用tfdbg调试呢,但是环境老是报错,最终无奈放弃了。改用调整学习率等参数解决了。但是我觉得tfdbg看起来很好用,决定最近抽时间看一看。配置好。
  8. 还有很多遇到的问题,但是一时想不起来了,以后再说吧。

最后

以上就是土豪楼房为你收集整理的CNN在句子相似性建模的应用--tensorflow实现篇2的全部内容,希望文章能够帮你解决CNN在句子相似性建模的应用--tensorflow实现篇2所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(42)

评论列表共有 0 条评论

立即
投稿
返回
顶部