【AIプログラミング】GAN(生成的敵対ネットワーク)について勉強をする、PyTorchのチュートリアル4

1_プログラミング

今日はDCGANのディスクリミネータについて色々勉強していました。

新しい概念ばかりだね。

DCGANはニューラルネットワークの進化系という感覚で軽く勉強をはじめましたが、CNNや全結合のニューラルネットワークとは全然違うものであることがわかりました。また、ニューラルネットワーク自体の構成も厳密に研究されているみたいで、論文が基準になっていたりと、かなり基礎研究的な部分の技術で、本当に理解しようとすると、結構な難しさを感じています。

ということで、今回はPyTorchのDCGANのチュートリアルのディスクリミネータから勉強を進めていきます。

前回の記事はこちらです。

スポンサーリンク

ディスクリミネータ

ディスクリミネータは、ジェネレータが生成した画像を偽物と見破るためのニューラルネットワークです。ディスクリミネータは本物の画像を入れた時は本物と判定し、ディスクリミネータの画像を入れた時に偽物と判定するような2クラス分類のためのニューラルネットワークです。出力として「本物である確率」を出力するように設計してあります。

PyTorchのDCGANでは具体的に、「3チャンネルの64×64」の画像を入力としてとります。

そして、畳み込み層(今度は転置ではない)を行い、バッチノーマライゼーション層を通り、「LealyReLU」という活性化関数で畳み込みを行います。この畳み込みを4回行い、最後にシグモイド関数を通すことで2クラス分類の確率の数値となります。

class Discriminator(nn.Module):
    def __init__(self, ngpu):
        super(Discriminator, self).__init__()
        self.ngpu = ngpu
        self.main = nn.Sequential(
            #入力は64×64、3チャンネルの画像
            nn.Conv2d(nc, ndf, 4, 2, 1, bias=False),
            nn.LeakyReLU(0.2, inplace=True),
            # state size. (ndf) x 32 x 32
            nn.Conv2d(ndf, ndf * 2, 4, 2, 1, bias=False),
            nn.BatchNorm2d(ndf * 2),
            nn.LeakyReLU(0.2, inplace=True),
            # state size. (ndf*2) x 16 x 16
            nn.Conv2d(ndf * 2, ndf * 4, 4, 2, 1, bias=False),
            nn.BatchNorm2d(ndf * 4),
            nn.LeakyReLU(0.2, inplace=True),
            # state size. (ndf*4) x 8 x 8
            nn.Conv2d(ndf * 4, ndf * 8, 4, 2, 1, bias=False),
            nn.BatchNorm2d(ndf * 8),
            nn.LeakyReLU(0.2, inplace=True),
            # state size. (ndf*8) x 4 x 4
            nn.Conv2d(ndf * 8, 1, 4, 1, 0, bias=False),
            nn.Sigmoid()
        )

    def forward(self, input):
        return self.main(input)

そして、次のプログラムでディスクリミネータのネットワークの重みを初期化します。

最初のいくつかのプログラムは、GPUを利用するかどうかという条件式になっています。

重みの初期化は、「平均0、標準偏差0.02」の正規分布から数値を取得します。

# Create the Discriminator
netD = Discriminator(ngpu).to(device)

# Handle multi-gpu if desired
if (device.type == 'cuda') and (ngpu > 1):
    netD = nn.DataParallel(netD, list(range(ngpu)))

# Apply the weights_init function to randomly initialize all weights
#  to mean=0, stdev=0.2.
netD.apply(weights_init)

# Print the model
print(netD)

次のように出力されました。

Discriminator(
  (main): Sequential(
    (0): Conv2d(3, 64, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1), bias=False)
    (1): LeakyReLU(negative_slope=0.2, inplace=True)
    (2): Conv2d(64, 128, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1), bias=False)
    (3): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (4): LeakyReLU(negative_slope=0.2, inplace=True)
    (5): Conv2d(128, 256, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1), bias=False)
    (6): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (7): LeakyReLU(negative_slope=0.2, inplace=True)
    (8): Conv2d(256, 512, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1), bias=False)
    (9): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (10): LeakyReLU(negative_slope=0.2, inplace=True)
    (11): Conv2d(512, 1, kernel_size=(4, 4), stride=(1, 1), bias=False)
    (12): Sigmoid()
  )
)

損失関数と最適化手法

BCELossという2クラス問題に利用される損失関数を利用します。これはBinaryCrossEntoropyの略称です。

また、ジェネレータに入力するための潜在ベクトルzもここで準備しています。「fixed_noise」として準備されます。

その次に「real_label」「fake_label」とあります。これは、ディスクリミネータを訓練するときの答えとなります。GANは教師なし学習という分野に属しています。しかし、ディスクリミネータについては、本物、実際に準備できたデータに「1」という答えをつけ、ジェネレータが生成したデータに「0」という答えを定義しておくことで、2クラス問題を訓練させていきます。

最後に、ディスクリミネータ、ジェネレータ共に最適化手法の「Adam」で重みを更新していホームくように設定しています。

#誤差をバイナリクロスエントロピー関数とします。
criterion = nn.BCELoss()

#潜在ベクトル
fixed_noise = torch.randn(64, nz, 1, 1, device=device)

# Establish convention for real and fake labels during training
real_label = 1
fake_label = 0

# Setup Adam optimizers for both G and D
optimizerD = optim.Adam(netD.parameters(), lr=lr, betas=(beta1, 0.999))
optimizerG = optim.Adam(netG.parameters(), lr=lr, betas=(beta1, 0.999))

ちょっとまだzのデータのサイズ感とかは理解できていないのですが、まずは訓練してみたいです。

訓練に向けてデータを準備

今回の訓練データは、データ量が1.4Gと多いので、パソコンにデータをダウンロードして訓練を行い、色々やっていましたが、GPUを積んだWindowsが一時的に利用できない状態にある中で、mac miniのローカル環境で行なっていました。しかし、CUDAが利用できないので、かなり訓練に時間がかかるようで・・・GoogleDriveにデータを保存することにしました。

手作業でアップロードだと時間がかかると感じ、PyTorchのリンク先のGoogleDriveから自分のDriveにコピーして、「ZipExtractor」で直接解凍を行なっています。

これが、データの解凍に40時間ほどかかっています 笑

手作業でアップロードの方が早かったのかもしれません・・・が、もうすぐでGoogleColaboで検証ができると信じています。

続きの記事はこちらです。

タイトルとURLをコピーしました