毕设勉强中…看了《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等,(因为这不是我目前工作任务的重点嘛)我就是这样理解的:针对上述的代码,
一开始我看到这里,哦,我懂啦,so easy 啊,那么我们来看看对应的pytorch有什么解决API吧!
由于这个API的C大写了我们下面为了一目了然,就称之为big_C好了。
这个API的中文文档是这个亚子的:
(u1s1至少我觉得如果我的博客里要是放这种公式会被读者打死的
仿照上面的操作,将一号选手big_C,拆成:
in_channels(int) – 输入信号的通道
out_channels(int) – 卷积产生的通道
kerner_size(int or tuple) - 卷积核的尺寸
这三个输入来定义本来一个list就能解决的事情…怎么说呢,如果就用一个,也不容易晕,几个api下来真晕晕乎乎的
而且最要命的是,因为这里API的接口输入是int,但是将原作者的tf代码改成torch的话,如果要拆原作者的输入,整个代码框架可能都要改。毕业毕竟是个头等大事,所以我又瞄上了二号种子选手:
这个函数感觉就和tf.nn.conv2d比较像了,他的具体用法(仍然是中文文档的图):
诸如(minibatch x in_channels x iH x iW)…是从哪粘过来的吗…
仿照上面的操作,将二号选手small_c,拆成:
所以,这个输入也和原作者的输出顺序很多地方不一致,万般无奈,我使用了输出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)
诺就是这样啦…实属咸鱼操作…