ロゴ ロゴ

PGGANを実装してanimefaceを生成したい

前回はSAGANでやったので今回はPGGANを実装したいと思います.
実装したものはここ

環境

  • python 3.8.0
  • pytorch 1.9.0
  • GPU GTX1080

どんな工夫がされているの?

Progressive Growing

生成したい画像のまま学習するのではなく段階的に学習させていきます.
論文中では4×4のサイズから学習を初めており.80k枚の画像を使用してサイズを遷移させ,その後80k枚の画像を使用して遷移後のサイズでの学習を行います.(今回は1サイズの半分のエポックで遷移させ残りのエポックで学習をさせました.).

Pixelwise Normalization

PGGANの論文[1]によるとBatch Normalizationの代わりにPixelwise Normalizationを使用しGeneratorの3×3の畳み込み層の後で使用するとのことです.また式は下記のようになります.
b_{x,y}=\frac{a_{x,y}}{\sqrt{\frac{1}{N}\sum_{j=0}^{N-1}(a_{x,y}^j)^2+\epsilon}} (\epsilon=10^{-8})
x,yは画像ピクセルの座標であり,変数jは各特徴量マップを表します.
式の通りに実装するとこんなかんじ?

class PixelNorm2d(nn.Module):
    def __init__(self,epsilon=1e-8):
        super().__init__()
        self.epsilon = epsilon

    def forward(self,inputs):
        denominator = torch.rsqrt(torch.mean(inputs**2,dim=1,dtype=inputs.dtype,keepdim=True) + self.epsilon)
        output = inputs * denominator
        return output

Equalized Leaning Rate

重みwN(0,1)で初期化その後各層が実行される度にw=\frac{w}{c}の演算を行います

class EqualizedLRConv2d(nn.Conv2d):
    def __init__(self,in_channels,out_channels,kernel_size,stride=1,padding=0,bias=True,**kwargs):
        super().__init__(
            in_channels=in_channels,
            out_channels=out_channels,
            kernel_size=kernel_size,
            stride=stride,
            padding=padding,
            bias=bias,
            **kwargs
            ) 
        nn.init.normal_(self.weight,mean=0,std=1)
        nn.init.constant_(self.bias,val=0.0)
        if(not isinstance(kernel_size,tuple)):
            kernel_size = (kernel_size,kernel_size)
        f_in = torch.prod(torch.tensor([in_channels,*kernel_size],dtype=self.weight.dtype,device=self.weight.device))
        self.scale_factor = torch.sqrt(2/f_in)
    def forward(self,inputs):
        inputs = self.scale_factor *inputs
        output = F.conv2d(inputs,self.weight,self.bias,self.stride,self.padding,self.dilation,self.groups)
        return output

上のself.scale_factorがcに該当します.また畳み込み演算は1次変換であるため,重みではなく入力値との演算に変更してあります.

Minibatch Standard Deviation

ミニバッチ内の特徴量マップすべての標準偏差を計算し平均をとったスカラーを入力値の特徴量に付け加えます.(言葉で説明するより実装を見たほうが早いと思う)

class MiniBatchStddev(nn.Module):
    def __init__(self):
        super().__init__()

    def forward(self,inputs):
        b,_,h,w = inputs.shape
        std = torch.std(inputs,unbiased=False,dim=0)
        v = torch.mean(std)
        output = torch.cat((inputs,torch.full(size=(b,1,h,w),fill_value=v.item(),dtype=inputs.dtype,device=inputs.device)),dim=1)
        return output

Wasserstein loss with gradient penalty

工夫ではないですが一応損失関数の実装も載せておきます.

class WassersteinGP(nn.Module):
    def __init__(self,net,penalty_coef=10):
        super().__init__()
        self.penalty_coef = penalty_coef
        self.net = net

    def forward(self,real,fake,*imgs):
        real_img,fake_img = imgs
        coef = torch.tensor(
            np.random.uniform(size=(real.shape[0],1,1,1)),
            requires_grad=True,
            device=real.device,
            dtype=real.dtype
            )
        penalty_input = fake_img * coef + (1 - coef)*real_img
        penalty_output = self.net(penalty_input)
        gradient = torch.autograd.grad(penalty_output,penalty_input,create_graph=True)[0]
        penalty = torch.square(torch.norm(gradient,dim=1)-1)
        output = fake.mean() - real.mean() + self.penalty_coef * penalty.mean()
        return output

学習結果

今回はSAGANのときと同じデータセットを使用して256×256の画像を生成しました.
バッチサイズ:4~64 16 ,128:8,256:5
画像サイズが小さいので16×16のサイズから載せます.
16×16 エポック0~5

32×32 エポック0~5

64×64 エポック0~5

128×128 エポック0~5

256×256ファイルサイズ大きくてグリットされたやつ貼れなかった(泣)

失敗例

感想

128×128までのサイズはカラーバリエーションもよく比較的に生成された画像を見ていて面白かった.しかし,256×256のサイズはバッチサイズが小さかったためか同じヘアカラーの画像が多く生成されるようになってしまった(原因は定かではないが多分バッチサイズ).それに画像のノイズが他のサイズと比べて目立つ気がする.しかしそれを除けばわりかし大丈夫そう.

終わりに

今回は,PGGANを自分で実装してみました.今回実装するにあたって,クラス設計の大事さを学ぶことができました(GANとは関係ないけど).Githubに上がっているコード(Pytorchの実装とか)を読んでいると自分のクラス設計の汚さに気が付きます(オブジェクト指向勉強しなきゃ…).基本的に人に書いたコードを見せる機会がないので煩雑なコードを書きがちになってしまっている気がする.部内の人にコードレビューしてもらうのもいいかもしれないですね.次は発展型であるStyleGANやStyleGAN2,LightWeightGANを実装していきたいと思います.

参考

[1]Tero Karras, Timo Aila, Samuli Laine, and Jaakko Lehtinen. 2017.
Progressive Growing of GANs for Improved Quality, Stability, and Variation. arXiv preprint arXiv:1710.10196

コメント入力

関連サイト