人工知性を作りたい

私が日々、挑戦したことや学んだことなどを紹介していく雑記ブログです。 (新しいAI技術HTM, 専門の音声信号処理, 趣味のアニメ等も書いてます。)

【実装 変分オートエンコーダ(VAE)】プリコネキャラ画像を生成! #実験結果 #グレースケール #生成モデル

f:id:hiro-htm877:20200711205637p:plain

f:id:hiro-htm877:20200711205612p:plain

 

変分オートエンコーダ(VAE:Variational Auto Encoder)という生成モデルを用いて画像を生成します。

今回は学習データにアニメキャラ(グレースケール)を使用しました。

 

本記事のテーマ

【自作変分オートエンコーダを使った画像生成】

・3枚のキャラの画像を学習させて画像を生成する。使用する画像:

アニメ「プリンセスコネクト!Re:Dive」から

ペコリーヌ、コッコロ、キャル、ユイ、レイ、ヒヨリ

幅:90

縦:90

VAE Model (変分オートエンコーダのモデル)

潜在変数 :latent_size=2

    class Encoder(nn.Module):
    def __init__(self, latent_size, image_size):
        super(Encoder, self).__init__()
        self.H = int(image_size/2)
        self.W = int(image_size/2)
        self.latent_size = latent_size
        self.conv1 = nn.Conv2d(1, 64, kernel_size=3, stride=1, padding=1)
        self.conv2 = nn.Conv2d(64, 128, kernel_size=3, stride=2, padding=1)
        self.conv3 = nn.Conv2d(128, 128, kernel_size=3, stride=1, padding=1)
        self.conv4 = nn.Conv2d(128, 128, kernel_size=3, stride=1, padding=1)
        self.linear1 = nn.Linear(128*self.H*self.W, 64)
        self.linear2 = nn.Linear(64, latent_size)
        self.linear3 = nn.Linear(64, latent_size)
        self.dropout1 = torch.nn.Dropout2d(p=0.2) 

    def forward(self, x):
        x = F.relu(self.conv1(x))
        x = F.relu(self.conv2(x))
        x = F.relu(self.conv3(x))
        x = F.relu(self.conv4(x))
        x = x.view(x.shape[0],-1)
        x = F.relu(self.linear1(x))
        z_mean = self.linear2(x)
        z_log_var = self.linear3(x)
        return z_mean, z_log_var

    def sampling(self, z_mean, z_log_var):
      epsilon = torch.randn(z_mean.shape, device="cuda")
      return z_mean + epsilon * torch.exp(0.5*z_log_var)

class Decoder(nn.Module):
    def __init__(self, image_size):
        super().__init__()
        self.H = int(image_size/2)
        self.W = int(image_size/2)
        self.to_shape = (128, self.H, self.W)  # (C, H, W)
        self.linear = nn.Linear(2, 2*np.prod(self.to_shape))
        self.deconv1 = nn.ConvTranspose2d(256, 128, kernel_size=4, stride=2, padding=1)
        self.deconv2 = nn.ConvTranspose2d(128, 128, kernel_size=4, stride=2, padding=1)
        self.conv = nn.Conv2d(64, 1, kernel_size=3, stride=1, padding=1)
        self.conv2 = nn.Conv2d(128, 64, kernel_size=3, stride=1, padding=1)
        self.leakyrelu = nn.LeakyReLU(0.2, inplace=True)

    def forward(self, x):
        x = self.leakyrelu(self.linear(x))
        x = x.view(-1, 256, self.H, self.W)  # reshape to (-1, C, H, W)
        x = self.leakyrelu(self.deconv1(x))
        x = self.leakyrelu(self.conv2(x))
        x = self.conv(x)
        x = torch.sigmoid(x)
        return x


class VAE(nn.Module):
    def __init__(self, latent_size, image_size):
        super(VAE, self).__init__()
        self.encoder = Encoder(latent_size, image_size)
        self.decoder = Decoder(image_size)

    def forward(self, x, C=1.0, k=1):
        """Call loss function of VAE.
        The loss value is equal to ELBO (Evidence Lower Bound)
        multiplied by -1.

        Args:
            x (Variable or ndarray): Input variable.
            C (int): Usually this is 1.0. Can be changed to control the
                second term of ELBO bound, which works as regularization.
            k (int): Number of Monte Carlo samples used in encoded vector.
        """
        z_mean, z_log_var = self.encoder(x)

        rec_loss = 0
        for l in range(k):
            z = self.encoder.sampling(z_mean, z_log_var)
            y = self.decoder(z)
            rec_loss += F.binary_cross_entropy(torch.flatten(y, start_dim=1), torch.flatten(x, start_dim=1)) / k

        kl_loss = C * (z_mean ** 2 + torch.exp(z_log_var) - z_log_var - 1) * 0.5
        kl_loss = torch.sum(kl_loss) / len(x)
        return rec_loss + kl_loss

 

実験結果

5%-95%を15等分したパーセント点関数を潜在変数のx,yとして画像を生成する。
ペコリーヌ、コッコロ、キャル

・ペコリーヌ

f:id:hiro-htm877:20200711182726p:plain

・コッコロ

f:id:hiro-htm877:20200711182804p:plain

・キャル

f:id:hiro-htm877:20200711182836p:plain

 

・学習0

f:id:hiro-htm877:20200711182322p:plain

・学習100

f:id:hiro-htm877:20200711182346p:plain

・学習200

f:id:hiro-htm877:20200711182428p:plain

・学習1000

上段、右端辺りはコッコロっぽい。

殆どがキャルっぽい。

ペコリーヌはどこかへ行ってしまったのか?

f:id:hiro-htm877:20200711182446p:plain

・学習5000

平均的な画像がキャルっぽくなった

ただ、目などはキャルではなく、平均っぽい

f:id:hiro-htm877:20200711182509p:plain


 

ペコリーヌ、コッコロ、キャル、ユイ、レイ、ヒヨリ

・ユイ

f:id:hiro-htm877:20200711190825p:plain

・レイ

f:id:hiro-htm877:20200711190857p:plain

・ヒヨリ

f:id:hiro-htm877:20200711190921p:plain

・学習500

f:id:hiro-htm877:20200711183605p:plain

・学習1000

f:id:hiro-htm877:20200711183619p:plain

・学習5000

f:id:hiro-htm877:20200711183634p:plain

 

考察

今回は学習画像を3枚、6枚としたため、過学習となり、潜在変数の値を変えても同じような画像しか生成されていなかった。

データオーギュメンテーションで画像を増やした場合との比較も行いたい。

 

f:id:hiro-htm877:20200711205835p:plain

 

f:id:hiro-htm877:20200711205845p:plain

実装コード(GitHub)

GitHub - hiro877/VAE_Anime-Character-Image

 


 関連記事

 

www.hiro877.com

 

 

www.hiro877.com

 

参考資料

・ゼロから作るDeepLearning3

github.com