zoukankan      html  css  js  c++  java
  • TensorFlow中的卷积函数

    前言

    最近尝试看TensorFlow中Slim模块的代码,看的比较郁闷,所以试着写点小的代码,动手验证相关的操作,以增加直观性。

    卷积函数

    slim模块的conv2d函数,是二维卷积接口,顺着源代码可以看到最终调的TensorFlow接口是convolution,这个地方就进入C++层面了,暂时不涉及。先来看看这个convolution函数,官方定义是这样的:

    tf.nn.convolution(
        input,
        filter,
        padding,
        strides=None,
        dilation_rate=None,
        name=None,
        data_format=None
    )

    其中在默认情况下,也就是data_format=None的时候,input的要求格式是[batch_size] + input_spatial_shape + [in_channels],  也就是要求第一维是batch,最后一维是channel,中间是真正的卷积维度。所以这个接口不仅只支持2维卷积,猜测2维卷积tf.nn.conv2d是对此接口的封装。[batch, height, weight, channel]就是conv2d的input参数格式,batch就是样本数,或者更狭隘一点,图片数量,height是图片高,weight是图片的宽,Slim的分类网络都是height=weight的,以实现方阵运算,所有slim模块中的原始图片都需要经过预处理过程,这里不展开。

    filter参数是卷积核的定义,spatial_filter_shape + [in_channels, out_channels],对于2维卷积同样是4维参数[weight, height, channel, out_channel]。

    明明是2维卷积,输入都是4维,已经有点抽象了,所以进入下一个阶段,写段代码,验证一下吧。

    实践一下

    这个例子先定义一个3X3的图片,再定义一个2X2的卷积核,代码如下:

    import tensorflow as tf
    
    input = tf.constant(
    [
            [
                    [
                            [100., 100., 100.],
                            [100., 100., 100.],
                            [100., 100., 100.]
                    ],
                    [
                            [100., 100., 100.],
                            [100., 100., 100.],
                            [100., 100., 100.]
                    ],
                    [
                            [100., 100., 100.],
                            [100., 100., 100.],
                            [100., 100., 100.],
                    ]
            ]
    ]
    );
    
    
    filter = tf.constant(
    [
            [
                    [
                            [0.5],
                            [0.5],
                            [0.5]
                    ],
                    [
                            [0.5],
                            [0.5],
                            [0.5]
                    ]
            ],
            [
                    [
                            [0.5],
                            [0.5],
                            [0.5]
                    ],
                    [
                            [0.5],
                            [0.5],
                            [0.5]
                    ]
            ],
    ]
    );
    
    result = tf.nn.convolution(input, filter, padding='VALID');
    
    with tf.Session() as sess:
            print sess.run(result)

     从上述代码可以看到,input的shape是[1, 3, 3, 3],filter的shape是[2, 2, 3, 1 ],卷积的过程在方阵[3, 3] 和 核[2, 2]上展开,并且由于有三个通道,每个通道分别卷积后求和。

    代码的执行结果:

    [

      [

        [

          [600.]
          [600.]

        ]

        [

          [600.]

          [600.]

        ]

      ]

    ]

    由于我们填的padding参数是VALID,所以最后的结果矩阵面积会缩小,满足(3-2)+1,即 (iw - kw) + 1。

    以上例子,我们可以将它称为单张图片二维3通道卷积,所以计算过程应该是每个通道进行卷积后最后三个通道的数值累加。

    如果是从单个通道看,input就是:

    [

      [100., 100., 100,]

      [100., 100., 100,]

      [100., 100., 100,]

    ]

    卷积核:

    [

      [0.5, 0.5]

      [0.5, 0.5]

    ]

    那么单层卷积结果:

    [

      [200., 200.]

      [200., 200.]

    ]

    将三层结果叠加就是程序输出结果。

    增加输出通道

    slim.conv2d函数的第二参数就是输出通道的数量,就是对应convolution接口filter的第4维,我们把程序改一下,增加一个输出通道:

    filter = tf.constant(
    [
            [
                    [
                            [0.5, 0.1],
                            [0.5, 0.1],
                            [0.5, 0.1]
                    ],
                    [
                            [0.5, 0.1],
                            [0.5, 0.1],
                            [0.5, 0.1]
                    ]
            ],
            [
                    [
                            [0.5, 0.1],
                            [0.5, 0.1],
                            [0.5, 0.1]
                    ],
                    [
                            [0.5, 0.1],
                            [0.5, 0.1],
                            [0.5, 0.1]
                    ]
            ],
    ]
    );

    最后的输出结果:

    [

      [

        [

          [600. 120.]
          [600. 120.]

        ]
        [

          [600. 120.]
          [600. 120.]

        ]

      ]

    ]

    其中 120 = 3 * (100 * 0.1 + 100 * 0.1 + 100 * 0.1 + 100 * 0.1)

    从结果可以看到,输出结果满足 [batch_size] + output_spatial_shape + [out_channels]的格式。

    padding=SAME更常用

    上面的例子中使用了padding=VALID,是指不填充的情况下进行的有效卷积结果矩阵面积会收缩。而我们在阅卷几个经典网络时,都是使用padding=SAME的方式,这种方式下,结果输出矩阵形状不变,这样就便于对不同分支结果进行连接等操作。

    将第一个例子中的padding改为SAME,输出结果为:

    [

      [

        [

          [600.]
          [600.]
          [300.]

        ]
        [

          [600.]
          [600.]
          [300.]

        ]

        [

          [300.]
          [300.]
          [150.]

        ]

      ]

    ]

    在SAME模式下,为了保证输出结果输入输入形状一致,实时上在原矩阵的的右侧和底部扩展了行、列 0

    暂时性结束

    作为新手,一旦碰到多维就蒙了,所有以上的实践,都是只是为了增加理解。



  • 相关阅读:
    kickstart自动安装部署RHEL7
    物流即使查询API
    快递单号查询快递鸟API接口-100家快递单轨迹推送
    物流跟踪API-快递单订阅
    如何最快实现物流即使查询功能-物流轨迹查询API
    提供一个不错的物流物流接口给大家,本人亲测,真的不错
    给idea添加类注释和方法注释模板
    分享一个生成反遗忘复习计划的java程序
    用TreeSet和Comparator给list集合元素去重
    对poi-excel导出的浅层理解
  • 原文地址:https://www.cnblogs.com/billux/p/9072173.html
Copyright © 2011-2022 走看看