実戦-CycleGANモデル実施


CycleGANはGAN分野で非常に有名なモデルと言える.以前研究した2種類の画像(例えばpix 2 pix)間の変換はpair間で学習する必要があったが,CycleGANはペアリングなしで学習するためにサイクル整合性を導入した.詳細については、論文およびgooglingを参照してください.本稿では,論文中のネットワーク構造からモデルをPyTorchとして実装する.

発電機全体の構造を理解し、実施する


GANは,生成器とDistributorの2つのモデルからなり,各モデルの損失を最適化することによって学習した.まず論文からGeneratorの構造を理解してみましょう.

論文で考慮すべき事項は以下の通りである.

  • まず、使用する作成者は、残りのブロックのサイズが入力画像の解像度に依存することに気づくかもしれない.Generator作成者のパラメータとして指定できます.

  • Residual BlockはRkに設計されている.他のモジュールとして定義します.
  • dkおよびukは、特徴的マッピングを減少または増加させる層である.異なるタイプの構成レイヤを考慮するだけです.
  • これらの要因を考慮して,Generatorクラスをネットワーク構造全体を中心に実施する.
    from torch import nn
    
    class Generator(nn.Module):
        def __init__(self, num_blocks):
            super().__init__()
            model = []
    
            # 1, c7s1-64
            model += [
                nn.ReflectionPad2d(3),
                nn.Conv2d(3, 64, 7),
                nn.InstanceNorm2d(64),
            ]
    
            # 2, dk
            model += [self.__conv_block(64, 128), self.__conv_block(128, 256)]
    
            # 3, Rk
            model += [ResidualBlock()] * num_blocks
    
            # 4, uk
            model += [
                self.__conv_block(256, 128, upsample=True),
                self.__conv_block(128, 64, upsample=True),
            ]
    
            # 5, c7s1-3
            model += [nn.ReflectionPad2d(3), nn.Conv2d(64, 3, 7), nn.Tanh()]
    
            # 6
            self.model = nn.Sequential(*model)
    
        # 7
        def forward(self, x):
            return self.model(x)
  • Convolution-InstanceNorm-ReLU layerをリストとして追加します.本論文では,反射paddingを用い,nn.ReflectionPad2dに予め増やし,次いで輪郭層でのpaddingの使用を回避した.

  • 寸法を2つのdk層に縮小した.
  • Rk、すなわちResidual Blockを複数追加します.ここで、ブロック数は構造関数のパラメータとして使用されます.
  • ukと同じ方法でdKを作成します.しかし、ここでは分数ステップConvolutionを使用しているので、増加した点を示すためにupsampleパラメータとして渡します.

  • 最後のブロックc7s1-3.最後の層は、画像の値を−1から1(を参照)に維持するためにTanh関数を使用することに留意されたい.最後のレイヤーセグメントでは、通常、他の通常のレイヤーは使用されません.

  • リストからnn.Sequentialを順番に通過しましょう.ここで、リストは*を使用して、各値にリストではなくパラメータを含ませる.
  • nn.Sequentialによって作成されたモデル.
  • 類似構造を持つブロックの実装


    構造全体の実装が完了しました.ukdkは、それらの構造が1つの重合層に類似しているため、1つの残りのブロックを実施し、使用を宣言するだけでよい.__conv_blockの方法とResidualBlockのクラスを実施する.
    まずはGenerator内の__conv_blockです.
        def __conv_block(self, in_features, out_features, upsample=False):
            if upsample:
                # 8
                conv = nn.ConvTranspose2d(
                    in_features, out_features, 3, 2, 1, output_padding=1
                )
            else:
                conv = nn.Conv2d(in_features, out_features, 3, 2, 1)
    
            # 9
            return nn.Sequential(
                conv,
                nn.InstanceNorm2d(256),
                nn.ReLU(),
            )

  • feature mapの大きな時間を増やしてnn.Transpose2dを使用します.また、サイズを正確に2倍にするには、縮小時とは異なり、paddingおよびoutput_paddingを使用しなければならない.

  • は、nn.Sequentialと定義されたconvを返します.
  • 次はResidualBlockです.名前に示すように、最後のレイヤの出力と入力.
    class ResidualBlock(nn.Module):
        def __init__(self):
            super().__init__()
            self.conv_block = nn.Sequential(
                nn.ReflectionPad2d(1),
                nn.Conv2d(256, 256, 3),
                nn.InstanceNorm2d(256),
                nn.ReLU(inplace=True),
                nn.ReflectionPad2d(1),
                nn.Conv2d(256, 256, 3),
                nn.InstanceNorm2d(256),
            )
    
        def forward(self, x):
            return x + self.conv_block(x)
    forward関数に入力テンソルを入れることを忘れなければよい.

    Distributorの実装


    以下は区切り記号です.まず構造を見てみましょう.

    論文で述べた区分者はPatchGANの構造である.参考までにここの70x70はTenserの大きさではなく感じ野Ckのクラスを実装するために、重複するDiscriminatorを使用する.
    class Discriminator(nn.Module):
        def __init__(self):
            super().__init__()
    
            # 10
            self.model = nn.Sequential(
                self.__conv_layer(3, 64, norm=False),
                self.__conv_layer(64, 128),
                self.__conv_layer(128, 256),
                self.__conv_layer(256, 512, stride=1),
                nn.Conv2d(512, 1, 4, 1, 1),
            )
    
        # 11
        def __conv_layer(self, in_features, out_features, stride=2, norm=True):
            layer = [nn.Conv2d(in_features, out_features, 4, stride, 1)]
    
            if norm:
                layer.append(nn.InstanceNorm2d(out_features))
    
            layer.append(nn.LeakyReLU(0.2))
    
            layer = nn.Sequential(*layer)
    
            return layer
    
        def forward(self, x):
            return self.model(x)
  • nn.SequentialですCkは、__conv_layerの方法で実施される.C64は正規化されていないので、norm因子に明記してください.論文には書かれていませんが、C512はステップ長が1の点因子で伝達されます.

  • パラメータとして受信したI/Oフィーチャーマッピングの数、ステップ長、正規化の有効性に応じて、モデルの構造を変更できます.まずモジュールをリストに入れ、nn.Sequentialを使用します.
  • 実装モデルの確認


    すべてのモデルが実装されている場合は、正常に動作していることを確認する必要があります.通常、テストを行うには、いずれかのTenserを入れてから結果を確認し、Tenserのshapeがあなたの要求を満たしているかどうかを確認します.
    モデルをテストする方法は大きく2つあります.1つのモデルには複数のモジュールが存在し、各モジュールが実施された後、大きなモジュール、モデルをテストするためのbottom−top法、およびモデル全体を通じて問題が発生した部分を分析するtop−blottom法をテストする.モデルが大きいほどbottom-topメソッドを推奨します.
    では、GeneratorモデルとDiscriminatorモデルの稼働状況を確認してみましょう.
    if __name__ == "__main__":
        x = torch.rand((1, 3, 256, 256))
        generator = Generator(6)
        discriminator = Discriminator()
    
        print("G(x) shape:", generator(x).shape)
        print("D(x) shape:", discriminator(x).shape)
    ここでは、256個のx 256画像を入力すると仮定します.このとき使用される残りのブロック数は6であるため、6はGenerator構造関数の引数とする.次に、これらのコードを実行してみます.
    G(x) shape: torch.Size([1, 3, 256, 256])
    D(x) shape: torch.Size([1, 1, 30, 30])
    したがって,両モデルとも形状の効果を見ることができる.
    リファレンス
    (を参照)
    https://github.com/junyanz/pytorch-CycleGAN-and-pix2pix