k-max-pooling的keras实现

个人博客地扯:http://xurui.club/2018/06/19/kmaxpooling/

pooling(池化)是卷积神经网络中常用操作,主要目的是减小张量大小,便于计算,可以认为是一步降维操作。前两天项目上需要用到k-max-pooling操作,项目代码是用keras写的,keras没有k-max-pooling层,所以要自己写。

卷积神经网络的最经典的模型是卷积+池化+全连接。
其中卷积操作通过一个滑动的卷积窗口来强化提取特征,可以认为卷积窗是一个权重矩阵,而卷积操作中的每一步,都是一个矩阵的加权求和,随着卷积窗口滑动,每一步的和被记录下来,形成一个新的矩阵。我们有多少种卷积窗,经过一层卷积就留下几个矩阵。然而,如果经过多层卷积,那最后矩阵就越来越大,那我们对资源耗费太大,有没有什么好的操作呢?–pooling

简单理解,pooling就是把一个大矩阵,变成小矩阵。如max-pooling,如size=2,就是在原来的矩阵中,每2*2个块中,取其中最大的一个记录下来,然后下一个块,再下一个十几个,最后记录下来的值就成了一个新矩阵,而这个新矩阵的大小,比以前缩小了4倍。有人会问,那不是损失了信息了么?当然损失了信息,但是我们认为损失的信息并没那么重要,就像一幅超清图,和一张高清图,虽然没那么清晰了,但这张图所描述的信息我们还是一眼能看出来。max-pooling操作,可以这么理解,给你一张图片,你分成了好多块,每块里面,别的地方颜色很浅,就一个地方颜色很深,那你是不是第一眼先看到那个黑点呢?

再说一下mean-pooling.
和max-pooling类似,只不过把每块中取最大值的操作,换成了对每块取均值。这样就会把每个点的值都考虑到,没有max-pooling操作那样粗暴。

还有一种常用的操作是k-max-pooling.这种是在max-pooling上改进来的,因为max-pooling操作太简单粗暴了,k-max-pooling认为每一块不只一个点重要,前几个亮点都比较重要,所以在每一个pooling块中取了前k大的值。

keras中pooling操作好像只有maxpooling和meanpooing.于是自己写了个代码,实现k-max-pooling。
因为项目中用的是LSTM处理文本,我这里就对LSTM层的结果进行处理了,LSTM层输出三维张量(shape=[in_dim1,in_dim2,in_dim3]),其中in_dim1表示样本数量,in_dim2表示一个样本(也就是一段话)的长度(如:这里认为一句话有100个词,那么短于100的句子后面补0,长于100的句子后面截断),in_dim3为lstm结点数量,也就是每个step输出的向量长度。
k-max-pooling操作,就是在dim2中,取出k个最大值,可以想象是取出k个最重要的词。
输入为LSTM层输出的三维张量,输出为一个二维张量,shape=[out_dim1,out_dim2],out_dim1为样本数,out_dim2为in_dim3*k。

代码如下:

def test59():

    from keras.layers import Lambda, Input
    from keras.models import Model
    import tensorflow as tf

    l = [[1, 2, 3, 4, 5, 6, 7, 8, 9], [11, 22, 33, 44, 55, 66, 77, 88, 99]]
    data = np.reshape(l, [2, 3, 3])
    print(data)
###output###
#[[[ 1  2  3]
#  [ 4  5  6]
#  [ 7  8  9]]
# [[11 22 33]
#  [44 55 66]
#  [77 88 99]]]
#数据为2个样本,每个样本是3个step(可以认为是句子有3个词),每个step是一个长为3的向量(可以认为词向量长度为3)
    input = Input(shape=[3, 3], dtype='int32')
    la = Lambda(lambda x: tf.reshape(tf.nn.top_k(tf.transpose(x,[0,2,1]),k=2)[0],shape=[-1,6]))(input)
    model = Model(inputs=input, outputs=la)
    pre = model.predict(data)
    print(pre)
#[[ 7  4  8  5  9  6]
#[77 44 88 55 99 66]]

if __name__=='__main__':
    test59()

这里的k-max-pooling用Lambda写的,tf.reshape(tf.nn.top_k(tf.transpose(x,[0,2,1]),k=2)[0],shape=[-1,6]),分解成三步:
tf.transpose(x,[0,2,1]–>因为tf有个top_k方法,可以对取张量最后一维的前k大的数,所以我们把step所在维度调整到最后。transpose是一个转置操作。
tf.nn.top_k(,k=2)–>取出最后一个维度前k大的数。
tf.reshape(),top_k操作对每个样本取出的结果是一个二维矩阵in_dim3 × k,所以把它转成向量,向量长度为in_dim3*k

当然,也可以写一个自定义层,处理步骤类似

class KMaxPooling(Layer):
    """
    k-max-pooling
    """

    def __init__(self, k=1, **kwargs):
        super().__init__(**kwargs)
        self.input_spec = InputSpec(ndim=3)
        self.k = k

    def compute_output_shape(self, input_shape):
        return (input_shape[0], (input_shape[2] * self.k))

    def call(self, inputs):
        # swap last two dimensions since top_k will be applied along the last dimension
        shifted_input = tf.transpose(inputs, [0, 2, 1])

        # extract top_k, returns two tensors [values, indices]
        top_k = tf.nn.top_k(shifted_input, k=self.k, sorted=True, name=None)[0]

        # return flattened output
        return Flatten()(top_k)
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值