一文看懂PatchGAN

最近看到PatchGAN很是好奇原理是什么,发现网上很多介绍的并不清楚.故墙外墙内来回几次,大概是清楚了.

PatchGAN其实指的是GAN的判别器,将判别器换成了全卷积网络.
这么说并不严谨,PatchGAN和普通GAN判别器是有区别的,普通的GAN判别器是将输入映射成一个实数,即输入样本为真样本的概率.PatchGAN将输入映射为NxN的patch(矩阵)X, X i j X_{ij} 的值代表每个patch为真样本的概率,将 X i j X_{ij} 求均值,即为判别器最终输出, X X 其实就是卷积层输出的特征图.从这个特征图可以追溯到原始图像中的某一个位置,可以看出这个位置对最终输出结果的影响.

对CycleGAN来说,判别器输出大小30x30x1,论文中却指出PatchGAN输入图像处理为70x70patches,就是根据判别器最终输出的特征图进行回溯,最终对应到输入图像70x70的区域.

为了便于理解,看下面的代码,其计算感受域大小

def f(output_size, ksize, stride):
    return (output_size - 1) * stride + ksize

last_layer = f(output_size=1, ksize=4, stride=1)
# Receptive field: 4
fourth_layer = f(output_size=last_layer, ksize=4, stride=1)
# Receptive field: 7
third_layer = f(output_size=fourth_layer, ksize=4, stride=2)
# Receptive field: 16
second_layer = f(output_size=third_layer, ksize=4, stride=2)
# Receptive field: 34
first_layer = f(output_size=second_layer, ksize=4, stride=2)
# Receptive field: 70

print(f'最后一层感受域大小:{last_layer}')
print(f'第一层感受域大小:{first_layer}')
#最后一层感受域大小:4
#第一层感受域大小:70

f即为计算卷积感受域的公式,最后一层的感受域即为卷积核大小4,那么这个卷积核能够感受到原始输入图像多大的范围呢?是70,也就是CycleGAN所说的70x70patches.

综上,PatchGAN并不神秘,其只是一个全卷积网络而已,只是最终输出是一个特征图X,而非一个实数.它就相当于对图像先进行若干次70x70的随机剪裁,将剪裁后图像输入普通的判别器,然后对所有输出的实数值取平均.

啰嗦了这么多,并没有什么感觉,还是给大家show code吧,最后附上一个PatchGAN实现,可以看到,只是几层卷积而已.

class NLayerDiscriminator(nn.Module):
    """Defines a PatchGAN discriminator"""

    def __init__(self, input_nc, ndf=64, n_layers=3, norm_layer=nn.BatchNorm2d):
        """Construct a PatchGAN discriminator
        Parameters:
            input_nc (int)  -- the number of channels in input images
            ndf (int)       -- the number of filters in the last conv layer
            n_layers (int)  -- the number of conv layers in the discriminator
            norm_layer      -- normalization layer
        """
        super(NLayerDiscriminator, self).__init__()
        if type(norm_layer) == functools.partial:  # no need to use bias as BatchNorm2d has affine parameters
            use_bias = norm_layer.func != nn.BatchNorm2d
        else:
            use_bias = norm_layer != nn.BatchNorm2d

        kw = 4
        padw = 1
        sequence = [nn.Conv2d(input_nc, ndf, kernel_size=kw, stride=2, padding=padw), nn.LeakyReLU(0.2, True)]
        nf_mult = 1
        nf_mult_prev = 1
        for n in range(1, n_layers):  # gradually increase the number of filters
            nf_mult_prev = nf_mult
            nf_mult = min(2 ** n, 8)
            sequence += [
                nn.Conv2d(ndf * nf_mult_prev, ndf * nf_mult, kernel_size=kw, stride=1, padding=padw, bias=use_bias),
                norm_layer(ndf * nf_mult),
                nn.LeakyReLU(0.2, True)
            ]

        nf_mult_prev = nf_mult
        nf_mult = min(2 ** n_layers, 8)
        sequence += [
            nn.Conv2d(ndf * nf_mult_prev, ndf * nf_mult, kernel_size=kw, stride=1, padding=padw, bias=use_bias),
            norm_layer(ndf * nf_mult),
            nn.LeakyReLU(0.2, True)
        ]

        sequence += [nn.Conv2d(ndf * nf_mult, 1, kernel_size=kw, stride=1, padding=padw)]  # output 1 channel prediction map
        self.model = nn.Sequential(*sequence)

    def forward(self, input):
        """Standard forward."""
        print(input.shape)
        return self.model(input)

猜你喜欢

转载自blog.csdn.net/weixin_35576881/article/details/88058040