Comments 3
Спасибо за статью! Не думали, где может пригодиться подобное сворачивание сверток?
Вот пример где оно нужно https://github.com/ARM-software/sesr. Но обычно хотят наоборот — растащить одну свёртку на несколько ради ускорения инференса (separable convolutions, depthwise separable convolutions)
Итересная статья (и ваша и про SESR). Какой-то когнитивный диссонанс возникает при ее прочтении: обычно все теоретические материалы твердят как один, что надо везде вставлять нелинейности, а не то линейные слои схлопнутся в один (ну и зачем учить 2 слоя если по факту это один). А тут специально сделали "плохо" и получилось хорошо.
Правда кажется что тут все исхищрения с перетасовыванием размерностей можно бы заменить последовательным применением сверток к identity-сверточному ядру. Это следует из ассоциативности свертки:
Где A - изображение, C1,...,CN - операции сверток, I - identity свертка (т.е. такая, которая ничего не делает с изображением).
Вроде должно получиться что-то типа такого (не тестил на работоспособность, это чисто чтобы объяснить, что имею ввиду):
p = (kernel_size - 1) // 2
new_conv = nn.Conv2d(in_channels, in_channels, kernel_size, padding=p)
new_conv_weight = torch.eye(in_channels, in_channels)
new_conv_weight = new_conv_weight.reshape(in_channels, in_channels, 1, 1)
new_conv_weight = torch.nn.functional.pad(new_conv_weight, (p, p, p, p))
new_conv.weight.data = new_conv_weight
new_conv.bias.data = torch.zeros(in_channels)
# new_conv(image) == image
for conv in sequential_convolutions:
new_conv = conv(new_conv)
# new_conv(image) == convN(... conv2(conv1(image))...)
# sequential_convolutions = [conv1, conv2, ..., convN]
Кажется так проще с точки зрения того, что понимание того как устроены веса сверток нужно только на этапе создания identity свертки. А дальше все происходит под капотом pytorch. Собственно так и предлагают делать в самой статье про SESR.
Разбираемся с устройством свёрток на примере объединения двух свёрток в одну в pytorch