在caffe中,convolution
图层采用一个底部blob,并将其与学习过滤器(使用权重类型初始化 - “Xavier”,“MSRA”等)进行卷积。但是,我的问题是我们是否可以简单地卷积两个底部blob并产生一个顶部blob。这样做最优雅的方式是什么?这样做的目的是:底部blob中的一个将是data
,另一个将是先前图层生成的动态过滤器(根据data
而变化)(我正在尝试实现{{ 3}})。
我的尝试:
我想到的一种方法是修改filler.hpp
并将底部blob指定为filler
矩阵本身(而不是“Xavier”,“MSRA”等)。然后我认为卷积层会从那里拾取。我们可以设置lr = 0
以指示不应更改由我们的自定义填充程序初始化的权重。但是,在我查看源代码后,我仍然不知道该怎么做。另一方面,我不想打破caffe的工作流程。如果我想要它们,我仍然希望转换层正常运行。
显然,一种更乏味的方法是使用Slice
,tile
和/或Scale
图层的组合来逐字实现卷积。我认为它会起作用,但结果会很混乱。还有其他想法吗?
修改1:
我通过修改caffe的卷积层写了一个新图层。特别是,在第{27}行的src/caffe/layers/conv_layer.cpp
中,它采用filler
定义的权重,并将其与底部blob进行卷积。因此,我没有从filler
填充该blob,而是修改了图层,使其现在需要两个底部。其中一个底部直接分配给填充符。现在我不得不做一些其他改动,例如:
weight
blob对所有样本都具有相同的值。对于不同的样本,它将具有不同的值。所以我改变了第32行:this->forward_cpu_gemm(
bottom_data + n * this->bottom_dim_,
weight,
top_data + n * this->top_dim_);
为:
this->forward_cpu_gemm(
bottom_data + n * bottom[1]->count(1),
bottom[0]->cpu_data() + n * bottom[0]->count(1),
top_data + n * this->top_dim_);
为了让事情变得简单,我假设没有涉及偏见术语,步幅总是1,填充总是0,组总是1等。但是,当我测试前进传球时,它给了我一些奇怪的回答(使用简单的卷积内核= np.ones((1,1,3,3))
。对于这个内核,学习率设置为零,这样它就不会改变。但是,我无法得到正确答案。任何建议都将受到赞赏。< / p>
请不要使用Slice, Eltwise, Crop
等现有图层提出解决方案。我已经实现了 - 它的工作原理 - 但它复杂且内存效率低得令人难以置信。
答案 0 :(得分:8)
我认为你的整体方式是正确的。
对于&#34;奇怪的&#34;卷积结果,我猜这个错误很可能是:
考虑2D卷积
并假设bottom[1]
的形状为(num, channels, height, width)
,
因为caffe
中的卷积是作为2矩阵的乘法执行的,weight
(代表卷积内核)和col_buffer
(从要卷积的数据重新组织),{{1} }是weight
行和num_out
列,channels / this->group_ * kernel_h * kernel_w
是col_buffer
行和channels / this->group_ * kernel_h * kernel_w
列,因此是height_out * width_out
动态卷积的blob图层,weight
的形状最好为bottom[0]
以满足
(num, num_out, channels/group, kernel_h, kernel_w)
,其中bottom[0]->count(1) == num_out * channels / this->group_ * kernel_h * kernel_w
是动态卷积层的输出特征映射的数量。
这意味着,要进行卷积功能
num_out
正常工作,你必须确保
this->forward_cpu_gemm(bottom_data + n * bottom[1]->count(1)
, bottom[0]->cpu_data() + n * bottom[0]->count(1)
, top_data + n * this->top_dim_);
因此,您使用的4维bottom[0]->shape(0) == bottom[1]->shape(0) == num
bottom[0]->count(1) == num_out * channels / this->group_ * kernel_h * kernel_w
的简单卷积内核可能无法满足上述条件并导致错误的卷积结果。
希望它清楚并且会帮助你。
########## 2016年10月10日更新1,北京时间##########
我添加了动态卷积层here但尚未进行单元测试。该层不会破坏caffe的工作流程,只会更改BaseConvolution类的某些私有成员以进行保护。
涉及的文件是:
np.ones((1,1,3,3))
与include/caffe/layers/dyn_conv_layer.hpp,base_conv_layer.hpp
src/caffe/layers/dyn_conv_layer.cpp(cu)
中的卷积层几乎相同,差别主要在于:
caffe
以正确初始化LayerSetUp()
,this->kernel_dim_
等以进行卷积,并忽略初始化卷积层常用的this->weight_offset_
以包含权重和偏差; this->blobs_
以检查作为内核容器的Reshape()
是否具有正确的卷积形状。因为我没时间测试它,可能会有错误,我很高兴看到你的反馈。
########## Update 2,Oct 12th,2016,Beijing time ##########
我刚刚更新了dynamic convolution的测试用例。涉及的文件是bottom[1]
。它似乎工作正常,但可能需要更彻底的测试。
您可以src/caffe/test/test_dyn_convolution_layer.cpp
,cd $CAFFE_ROOT/build && ccmake ..
和cmake -DBUILD_only_tests="dyn_convolution_layer" ..
来构建此咖啡馆。