CycleGAN代码解析(2)

代码来源:PyTorch-GAN/implementations/cyclegan/models.py at master · eriklindernoren/PyTorch-GAN · GitHub

在此分析文件models.py

二.models.py

def weights_init_normal(m):
    classname = m.__class__.__name__
    if classname.find("Conv") != -1:
        torch.nn.init.normal_(m.weight.data, 0.0, 0.02)
        if hasattr(m, "bias") and m.bias is not None:
            torch.nn.init.constant_(m.bias.data, 0.0)
    elif classname.find("BatchNorm2d") != -1:
        torch.nn.init.normal_(m.weight.data, 1.0, 0.02)
        torch.nn.init.constant_(m.bias.data, 0.0)

这段代码定义了一个名为 `weights_init_normal` 的函数,用于对神经网络中的层进行权重初始化。该函数接受一个神经网络层 `m` 作为参数,并根据层的类型对其权重和偏置进行初始化。下面是详细解释:

1. **函数定义**:定义了一个名为 `weights_init_normal` 的函数,该函数用于对传入的神经网络层进行权重初始化。

2. **获取类名**:通过 `m.__class__.__name__` 获取传入层的类名。

3. **卷积层权重初始化**:
   - 检查类名中是否包含 "Conv"。如果包含,说明该层是一个卷积层。
   - 使用 `torch.nn.init.normal_` 函数将卷积层的权重初始化为均值为0、标准差为0.02的正态分布。
   - 检查该层是否有偏置项(bias),如果存在且不为 `None`,则使用 `torch.nn.init.constant_` 函数将偏置初始化为0。

4. **批归一化层权重初始化**:
   - 检查类名中是否包含 "BatchNorm2d"。如果包含,说明该层是一个2D批归一化层。
   - 使用 `torch.nn.init.normal_` 函数将批归一化层的权重初始化为均值为1、标准差为0.02的正态分布。
   - 使用 `torch.nn.init.constant_` 函数将偏置初始化为0。

这段代码的主要目的是根据层的类型(卷积层或批归一化层)对其权重和偏置进行特定的初始化操作,以便在训练神经网络时有一个良好的初始状态,从而提高训练效果和稳定性。


class ResidualBlock(nn.Module):
    def __init__(self, in_features):
        super(ResidualBlock, self).__init__()

        self.block = nn.Sequential(
            nn.ReflectionPad2d(1),
            nn.Conv2d(in_features, in_features, 3),
            nn.InstanceNorm2d(in_features),
            nn.ReLU(inplace=True),
            nn.ReflectionPad2d(1),
            nn.Conv2d(in_features, in_features, 3),
            nn.InstanceNorm2d(in_features),
        )

    def forward(self, x):
        return x + self.block(x)

这段代码定义了一个名为 `ResidualBlock` 的类,该类继承自 `nn.Module`,用于创建残差块。残差块是深度神经网络(尤其是 ResNet)中常用的一种模块,它通过短连接(skip connection)使得输入可以绕过卷积层直接加到输出上,从而缓解深层网络训练中的梯度消失问题。下面是对代码的详细解释:

1. **定义残差块中的层**:
   使用 `nn.Sequential` 构建一个顺序容器,包含以下层:
   - `nn.ReflectionPad2d(1)`:在输入的边界处进行反射填充,填充1个像素。
   - `nn.Conv2d(in_features, in_features, 3)`:一个卷积层,输入和输出特征数都是 `in_features`,卷积核大小为 3x3。
   - `nn.InstanceNorm2d(in_features)`:实例归一化层,对每个样本独立进行归一化。
   - `nn.ReLU(inplace=True)`:ReLU激活函数,设置 `inplace=True` 以节省内存。
   - `nn.ReflectionPad2d(1)`:再次进行反射填充。
   - `nn.Conv2d(in_features, in_features, 3)`:第二个卷积层。
   - `nn.InstanceNorm2d(in_features)`:第二次实例归一化。

2. **前向传播方法**:
   `forward` 方法定义了前向传播的过程。输入 `x` 通过残差块 `self.block` 进行处理,然后将输出与原输入 `x` 相加。这种结构实现了所谓的 "short connection" 或 "skip connection",即输入可以绕过卷积层直接加到输出上。

这个 `ResidualBlock` 类实现了一个经典的残差块结构,包含两个卷积层和实例归一化层,并通过短连接将输入直接加到输出上。这种设计有助于缓解深层神经网络训练中的梯度消失问题,并且在许多深度学习模型(如 ResNet)中被广泛使用。


class GeneratorResNet(nn.Module):
    def __init__(self, input_shape, num_residual_blocks):
        super(GeneratorResNet, self).__init__()

        channels = input_shape[0]

        # Initial convolution block初始卷积块
        out_features = 64
        model = [
            nn.ReflectionPad2d(channels),
            nn.Conv2d(channels, out_features, 7),
            nn.InstanceNorm2d(out_features),
            nn.ReLU(inplace=True),
        ]
        in_features = out_features

        # Downsampling下采样
        for _ in range(2):
            out_features *= 2
            model += [
                nn.Conv2d(in_features, out_features, 3, stride=2, padding=1),
                nn.InstanceNorm2d(out_features),
                nn.ReLU(inplace=True),
            ]
            in_features = out_features

        # Residual blocks残差块
        for _ in range(num_residual_blocks):
            model += [ResidualBlock(out_features)]

        # Upsampling上采样
        for _ in range(2):
            out_features //= 2
            model += [
                nn.Upsample(scale_factor=2),
                nn.Conv2d(in_features, out_features, 3, stride=1, padding=1),
                nn.InstanceNorm2d(out_features),
                nn.ReLU(inplace=True),
            ]
            in_features = out_features

        # Output layer输出层
        model += [nn.ReflectionPad2d(channels), nn.Conv2d(out_features, channels, 7), nn.Tanh()]
        
        #顺序容器
        self.model = nn.Sequential(*model)
    
    #前向传播
    def forward(self, x):
        return self.model(x)

这段代码定义了一个名为 `GeneratorResNet` 的类,用于生成基于残差网络(ResNet)的生成器模型。这个生成器模型可以用于图像生成任务,例如图像风格转换、图像超分辨率等。下面是对代码的详细解释:

1. **初始卷积块**:
   定义初始卷积块,包括反射填充、卷积层、实例归一化和 ReLU 激活函数。

2. **下采样**:
   进行两次下采样操作,每次将特征数翻倍,使用步幅为2的卷积层来减小空间尺寸。

3. **残差块**:
   添加指定数量的残差块,每个残差块包含两个卷积层和一个短连接。

4. **上采样**:
   进行两次上采样操作,每次将特征数减半,使用上采样层来增大空间尺寸。

5. **输出层**:
   定义输出层,包括反射填充、卷积层和 Tanh 激活函数,将输出映射到 (-1, 1) 范围。

6. **顺序容器**:
   将 `model` 列表中的所有层连接成一个顺序容器。

7. **前向传播方法**:
    定义前向传播过程,输入 `x` 经过模型处理后得到输出。

`GeneratorResNet` 类实现了一个基于残差网络的生成器模型。该模型包括初始卷积块、两次下采样、多层残差块、两次上采样和输出层。通过这种结构,模型可以有效地生成高质量的图像,并且可以用于各种图像生成任务。


class Discriminator(nn.Module):
    def __init__(self, input_shape):
        super(Discriminator, self).__init__()

        channels, height, width = input_shape

        # Calculate output shape of image discriminator (PatchGAN)计算判别器的输出形状
        self.output_shape = (1, height // 2 ** 4, width // 2 ** 4)

        #判别器块
        def discriminator_block(in_filters, out_filters, normalize=True):
            """Returns downsampling layers of each discriminator block"""
            layers = [nn.Conv2d(in_filters, out_filters, 4, stride=2, padding=1)]
            if normalize:
                layers.append(nn.InstanceNorm2d(out_filters))
            layers.append(nn.LeakyReLU(0.2, inplace=True))
            return layers

        #构建模型
        self.model = nn.Sequential(
            *discriminator_block(channels, 64, normalize=False),
            *discriminator_block(64, 128),
            *discriminator_block(128, 256),
            *discriminator_block(256, 512),
            nn.ZeroPad2d((1, 0, 1, 0)),
            nn.Conv2d(512, 1, 4, padding=1)
        )

    #前向传播
    def forward(self, img):
        return self.model(img)

这段代码定义了一个用于图像判别的 `Discriminator` 类。该类用于对输入图像进行判别,以区分真实图像和生成器生成的假图像。这个判别器基于 PatchGAN 结构,通过局部的图像块(patch)进行判别。下面是对代码的详细解释:

1. **计算输出形状**:
   计算判别器的输出形状。由于每次下采样尺寸减半,经过 4 次下采样后,高度和宽度分别除以 \(2^4\)(即 16)。

2. **判别器块**:
   定义一个函数 `discriminator_block`,用于创建判别器的下采样块。每个块包括一个卷积层、可选的实例归一化层和 LeakyReLU 激活函数。

3. **构建模型**:
   使用 `nn.Sequential` 将多个判别器块连接成一个模型。具体包括:
   - 一个 `discriminator_block`,将输入通道数转换为 64(不进行归一化)。
   - 三个 `discriminator_block`,通道数依次从 64 增加到 128、256 和 512。
   - 一个 `nn.ZeroPad2d` 层,进行零填充。
   - 最后一个卷积层,将通道数从 512 转换为 1。

4. **前向传播方法**:
   定义前向传播过程,输入图像 `img` 经过判别器模型处理后得到输出。

  • 28
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值