一次将tensorflow中的tf.nn.conv2d优(re)雅(shape)的转化成torch.nn.F.conv2d的咸鱼操作

写在前面

毕设勉强中…看了《A Capsule Network-based Embedding Model for Knowledge Graph Completion and Search Personalization》后准备写一个pytorch版本的,以便用于后续的相关工作,今天算是被CV领域的各种conv2d洗了个遍…
CapsE的作者是忠实的TFBoys,我们知道,在小学二年级的时候老师教过我们,处理三元组用CNN进行嵌入编码时,通常的操作都是这样的:

    def __call__(self, input, kernel_size=None, stride=None):
        '''
        The parameters 'kernel_size' and 'stride' will be used while 'layer_type' equal 'CONV'
        '''
        if self.layer_type == 'CONV':
            self.kernel_size = kernel_size
            self.stride = stride
            if not self.with_routing:
                if self.useConstantInit == False:   # [3,1,1,50]
                    filter_shape = [self.sequence_length, self.filter_size, 1, self.num_filters] ## [3,1,1,400]
                    # tf.truncated_normal(shape, mean, stddev) :shape表示生成张量的维度,mean是均值,stddev是标准差
                    W = tf.Variable(tf.random.truncated_normal(filter_shape, stddev=0.1, seed=1234), name="W")
                else:
                    init1 = tf.constant([[[[0.1]]], [[[0.1]]], [[[-0.1]]]])
                    weight_init = tf.tile(init1, [1, self.filter_size, 1, self.num_filters])
                    W = tf.get_variable(name="W3", initializer=weight_init)
                b = tf.Variable(tf.constant(0.0, shape=[self.num_filters]), name="b")
                '''input = self.X = tf.expand_dims(self.embedded_chars, -1)  shape=(256,3,100,1)
                具体含义是[训练时一个batch的图片数量 256, 图片高度 3, 图片宽度 100, 图像通道数 1]'''
                conv = tf.nn.conv2d(
                    input,
                    W,# W作为filter卷积核 [卷积核的高度3,卷积核的宽度1,图像通道数1,卷积核个数400]
                    strides=[1, 1, 1, 1],   # strides:卷积时在图像每一维的步长,这是一个一维的向量,长度4
                    padding="VALID",    # 参数padding的值为‘VALID’变为‘SAME’时,表示卷积核可以停留在图像边缘
                    name="conv")
                # Apply nonlinearity
                print(conv.shape)   # (256, 1, 100, 400)
                conv1 = tf.nn.relu(tf.nn.bias_add(conv, b), name="relu")
                conv1 = tf.squeeze(conv1, axis=1)
                capsules =  tf.expand_dims(conv1, -1)
                capsules = squash(capsules)
                return(capsules) #[batch_size, k, num_filters, 1]

当然这种操作是没有任何问题的,在tf.nn.conv2d的文档中 (不得不说TF的文档水平至少在卷积这块是真的甩开pytorch几个秋名山) ,因为我关注的是函数的接口,所以就只谈谈输入和输出,细节方面我一个搞文本的也不太想去深究:

tf.nn.conv2d(input, filter, strides, padding, use_cudnn_on_gpu=None, name=None)

  • input:shape=[batch, in_height, in_width, in_channels]
  • filter:卷积核,shape=[filter_height, filter_width, in_channels, out_channels]
  • 返回结果result:返回一个Tensor,就是我们常说的feature map,其shape=[batch, height, width, out_channels]

不考虑strides等,(因为这不是我目前工作任务的重点嘛)我就是这样理解的:针对上述的代码,

  • 输入就是[256,3,100,1],对应B,入宽,入高,入道
  • 输出就是[256,1,100,1],对应B,出高,出宽,出道
  • 卷积就是[3,1,1,400],对应卷高,卷宽,入道,出道

一开始我看到这里,哦,我懂啦,so easy 啊,那么我们来看看对应的pytorch有什么解决API吧!

一号选手 torch.nn.Conv2d

由于这个API的C大写了我们下面为了一目了然,就称之为big_C好了。
这个API的中文文档是这个亚子的:
一次将tensorflow中的tf.nn.conv2d优(re)雅(shape)的转化成torch.nn.F.conv2d的咸鱼操作_第1张图片
u1s1至少我觉得如果我的博客里要是放这种公式会被读者打死的
仿照上面的操作,将一号选手big_C,拆成:

  • 输入[256,1,3,100],对应B,入道,入高,入宽(amazing!是不是和tf的相差一个transpose(0,3,2,1))
  • 输出[256,400, H o u t H_{out} Hout, W o u t W_{out} Wout],对应B,出道,出高,出宽(wdnmd…顺序又炸了)
  • 卷积…卷积呢?
    big_C的卷积核,你说整一个和tf一样的,多清晰,但是很遗憾,torch要求用

in_channels(int) – 输入信号的通道
out_channels(int) – 卷积产生的通道
kerner_size(int or tuple) - 卷积核的尺寸

这三个输入来定义本来一个list就能解决的事情…怎么说呢,如果就用一个,也不容易晕,几个api下来真晕晕乎乎的

而且最要命的是,因为这里API的接口输入是int,但是将原作者的tf代码改成torch的话,如果要拆原作者的输入,整个代码框架可能都要改。毕业毕竟是个头等大事,所以我又瞄上了二号种子选手:

二号选手torch.nn.functional.conv2d 函数

这个函数感觉就和tf.nn.conv2d比较像了,他的具体用法(仍然是中文文档的图):
一次将tensorflow中的tf.nn.conv2d优(re)雅(shape)的转化成torch.nn.F.conv2d的咸鱼操作_第2张图片

诸如(minibatch x in_channels x iH x iW)…是从哪粘过来的吗…
仿照上面的操作,将二号选手small_c,拆成:

  • 输入[256,1,3,100],对应B,入道,入高,入宽(amazing!是不是和他家的torch.nn.Conv2d的相差一个transpose(0,3,2,1))
  • 输出…没写呢
  • 卷积[400,1,3,1] (这个顺序又和tf的不一样了…唉)

所以,这个输入也和原作者的输出顺序很多地方不一致,万般无奈,我使用了输出reshape的方法(虽然reshape作为一种trick经常用的话会出现米奇不妙的玄学debuff效果…)

       conv = F.conv2d(
            input,
            self.W,# W作为filter卷积核 [卷积核的高度3,卷积核的宽度1,图像通道数1,卷积核个数400]
            stride=1,# strides:卷积时在图像每一维的步长,这是一个一维的向量,长度4
            groups=1,
            padding=0)# 参数padding的值为‘VALID’变为‘SAME’时,表示卷积核可以停留在图像边缘
        # Apply nonlinearity        # N=256,Co=400,Ho=3,Wo=
        print('conv',conv.shape)    # 
        conv1 = F.relu(conv.reshape(256,1,100,400) + b).squeeze(1)

诺就是这样啦…实属咸鱼操作…

你可能感兴趣的:(NLP)