组卷积在Keras中可用,使用代码如下:https://github.com/tensorflow/tensorflow/issues/34024#issuecomment-552034933
然而,对于我的特定应用,我要求在训练期间,组卷积中的每个组具有相同的权重。例如,如果我有一个形状为8x8x32的张量,并且我想要groups = 2和filter_size=3x3,那么normal group卷积将使用两个3x3x16张量来卷积8x8x32的前半部分和8x8x32的后半部分。我希望确保两个3x3x16张量具有相同的权重,即使在训练期间也是如此。
我可以通过摆脱我的组卷积框架并将我的8x8x32张量拆分为两个8x8x16张量,然后通过单个未分组的3x3x16卷积来运行它们。但是,由于不使用组卷积框架,代码运行速度较慢,因为任务不是并行运行的。
如何在Keras中使用组卷积提供的速度升级,同时限制每个组中的权重相同?
发布于 2020-04-17 22:30:16
显然,组卷积在TensorFlow中是如何工作的(至少在目前,因为它似乎还没有文档记录,所以我猜它可能会改变)是,给定一个带有shape (n, h, w, c)的批处理img和一个带有shape (kh, kw, c1, c2)的filter k,它在结果具有c2通道的g = c / c1组中进行卷积。c必须能被c1整除,并且c2必须是g的倍数。据我所知,这意味着,如果我们将每个组的输出通道数称为a = c2 / g,那么第一组使用过滤器k[:, :, :, :a],第二组使用k[:, :, :, a:2*a],依此类推。如果你想对每个卷积组使用完全相同的过滤器,你只需要为单个组制作过滤器,使用shape (kh, kw, c1, a),然后在最后一个维度上对其进行g次平铺。
在您引用的代码中,您只需进行以下更改。self.kernel的定义将更改为:
# Make sure self.filters is divisible by self.groups
kernel_shape = self.kernel_size + (input_dim // self.groups, self.filters // self.groups)
# Filters for a single group
self.kernel_base = self.add_weight(
name='kernel',
shape=kernel_shape,
initializer=self.kernel_initializer,
regularizer=self.kernel_regularizer,
constraint=self.kernel_constraint,
trainable=True,
dtype=self.dtype)
# Tile filters for the rest of groups
self.kernel = tf.tile(self.kernel_base, [1, 1, 1, self.groups])假设你也想让偏差以同样的方式工作,你也会对它做同样的事情:
if self.use_bias:
# Bias for a single group
self.bias_base = self.add_weight(
name='bias',
shape=(self.filters // self.groups,),
initializer=self.bias_initializer,
regularizer=self.bias_regularizer,
constraint=self.bias_constraint,
trainable=True,
dtype=self.dtype)
# Bias for all groups
self.bias = tf.tile(self.kernel_base, [1, 1, 1, self.groups])
else:
self.bias = None其余的代码将类似地工作,因为tf.nn.conv2d将像以前一样使用self.kernels,并且将以类似的方式添加self.bias。
发布于 2020-04-18 01:20:27
新答案
啊,我以为你想要得到保证,标准分组卷积对两组使用相同的权重。是的,它做到了!
您想要的和tensorflow所做的不同之处在于,您希望“连接”两个组的结果,而tensorflow可能会“求和”这两个组的结果。
在这种情况下,您可以尝试使用TimeDistributed卷积。我不确定性能是否与您想要的简单地将数据一分为二不同,但您将使用单层,这可能会为您带来性能优势:
inputs = Input((8,8,32))
grouped = Reshape((8,8,2,16))(inputs)
grouped = Permute((3,1,2,4))(grouped) #this might be a performance drawback
conv_out = TimeDistributed(Conv2D(half_filters, (3,3), ...))(grouped)
ungrouped = Permute((2,3,1,4))(conv_out)
ungrouped = Reshape((x,y,2*half_filters))老答案
如果您使用源代码中的以下代码计算权重的形状:
kernel_shape = self.kernel_size + (input_dim // self.groups, self.filters)根据您的示例,结果将是:
(3, 3, 32//2, filters)这确实支持了这两组只有一半大小的内核的想法。参数的数量只有一半。
你可以用一个简单的模型来验证:
inputs = Input((x, y, channels))
outputs1 = Conv2D(filters, 3, padding="same")(inputs)
outputs2 = GroupConv2D(filts, 2, groups=2, padding="same")(inputs)
model1 = Model(inputs, outputs1)
model2 = Model(inputs, outputs2)
model1.summary() #count the parameters
model2.summary() #expect half the parameters发布于 2020-04-19 03:43:37
我相信@jdehesa写的tf.tile解决方案是你最好的选择。
但是,如果您碰巧将数据格式化为channel_first/NCHW格式,您还可以尝试在卷积之前从(n,g* c,h,w)重塑为(n * g,c,h,w),然后在卷积之后重塑。
https://stackoverflow.com/questions/61213956
复制相似问题