VAEの作成

66 views
Skip to first unread message

Yoshito Nagaoka

unread,
Jul 20, 2019, 1:17:45 AM7/20/19
to Chainer Japanese User Group
いまVAEを1から実装しようと思い、

d_dim = 256
Z_dim = 2
    
class Encoder(chainer.Chain):
    def __init__(self):
        
        super(Encoder, self).__init__()
        with self.init_scope():
            self.enc1 = L.Linear(None, d_dim)
            self.enc_mu = L.Linear(None, Z_dim)
            self.enc_sigma = L.Linear(None, Z_dim)
        
        
    def forward(self, x):
        x = F.relu(self.enc1(x))
        mu = self.enc_mu(x)
        sigma = self.enc_sigma(x)
        
        self.mu = mu
        self.sigma = sigma
        
        return [mu, sigma]
    
    
class Sampler(chainer.Chain):
    def __init__(self):
        super(Sampler, self).__init__()
        
    def forward(self, x):
        mu, sigma = x
        mb, _ = mu.shape
        epsilon = np.random.normal(0, 1, [mb, Z_dim])
        #epsilon = chainer.distributions.Normal(loc=chainer.Variable(0), scale=chainer.Variable(1)).sample([mb, Z_dim])
        std = F.exp(0.5 * sigma)
        
        if GPU >= 0:
            epsilon = chainer.cuda.to_gpu(epsilon)
            #sigma = chainer.cuda.to_gpu(sigma)

        #sample_z = np.array([mu, epsilon * std], dtype=np.float32)
        #sample_z = F.sum_to(sample_z, shape=[mb, Z_dim])
        sample_z = F.add(mu, epsilon * std)
        
        
        #if GPU >= 0:
        #    sample_z = chainer.cuda.to_gpu(sample_z)
        
        self.sample_z = sample_z
        
        return sample_z


class Decoder(chainer.Chain):
    def __init__(self):
        super(Decoder, self).__init__()
        
        with self.init_scope():
            self.dec1 = L.Linear(None, d_dim)
            self.dec_out = L.Linear(None, img_height * img_width * channel)
        
    def forward(self, x):
        x = F.relu(self.dec1(x))
        x = self.dec_out(x)
        x = F.sigmoid(x)
        return x

こんな風に実装しています。
Encoderは入力が画像、出力はサンプリング時のパラメータ、muとsigmaを出力
Samplerはmuとsigmaから潜在変数sample_zを出力
Decoderはsample_zを入力し、画像を出力する構成です、

これらを
model_encoder = Encoder()
model_sampler = Sampler()
model_decoder = Decoder()
model = chainer.Sequential(model_encoder, model_sampler, model_decoder)

のようにSequentialで繋げて学習させているのですが、どうやら、Encoderにlossが伝搬されていないようでした。
おそらくSamplerからEncoderに伝搬ができていないと思うのですが、どのようにすれば伝搬できるでしょうか?

Masanori YANO

unread,
Jul 25, 2019, 9:59:40 AM7/25/19
to Chainer Japanese User Group
添付の動作確認プログラムを実行すると、GPUでもCPUでも伝播されていました。

ただ「modelをGPUに移す前にoptimizerと紐付ける」と、以下URLの記事の通りになりましたので、確認されるとよいかと思います。
http://buq.hateblo.jp/entry/2016/02/16/222006

2019年7月20日土曜日 14時17分45秒 UTC+9 Yoshito Nagaoka:
vae_test.py

Yoshito Nagaoka

unread,
Jul 25, 2019, 7:56:16 PM7/25/19
to Chainer Japanese User Group
ご対応ありがとうございます。

学習後に異なる画像を入力しても同じ画像が出力されていたので、Decoderのみが学習されていると勘違いしていました。

今はMNISTで学習していて、Adam(lr=0.001)を用いてSigmoid_cross_entropyと
自分で定義したKLDivergence(下式)

def loss_KLDivergence(mu, sigma):    
    return - 0.5 * F.sum(1 + sigma - mu * mu - F.exp(sigma))

を使って学習させているのですが、最適化が間違っているのでしょうか。
恐縮ですが、何かアドバイスをいただけたらと思います。
よろしくお願いします。


2019年7月25日木曜日 22時59分40秒 UTC+9 Masanori YANO:

Masanori YANO

unread,
Jul 26, 2019, 7:58:38 AM7/26/19
to Chainer Japanese User Group
F.sigmoid_cross_entropyは、シグモイド関数を内部で呼び出すことと、ground truth側が0か1前提なことが気にかかります。
https://docs.chainer.org/en/stable/reference/generated/chainer.functions.sigmoid_cross_entropy.html

かわりに、Chainerの過去のVAEサンプルでも使用していた「F.bernoulli_nll」を使用したら改善しないでしょうか。
https://docs.chainer.org/en/stable/reference/generated/chainer.functions.bernoulli_nll.html

2019年7月26日金曜日 8時56分16秒 UTC+9 Yoshito Nagaoka:
Reply all
Reply to author
Forward
0 new messages