質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Chainer

Chainerは、国産の深層学習フレームワークです。あらゆるニューラルネットワークをPythonで柔軟に書くことができ、学習させることが可能。GPUをサポートしており、複数のGPUを用いた学習も直感的に記述できます。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

1回答

4238閲覧

VAEの誤差関数にMSEを使うと学習がうまく進まない。

physics303

総合スコア89

Chainer

Chainerは、国産の深層学習フレームワークです。あらゆるニューラルネットワークをPythonで柔軟に書くことができ、学習させることが可能。GPUをサポートしており、複数のGPUを用いた学習も直感的に記述できます。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2019/06/03 06:33

編集2019/06/07 00:59

やったこと

Chainerユーザーです。Chainerを使ってVAEを実装しました。参考にしたURLは

Variational Autoencoder徹底解説
AutoEncoder, VAE, CVAEの比較

PyTorch+Google ColabでVariational Auto Encoderをやってみた

などです。実装したコードのコアになる部分は以下の通りです。

python3

1class VAE(chainer.Chain): 2 3 def __init__(self, n_in, n_latent, n_h, act_func=F.tanh): 4 super(VAE, self).__init__() 5 self.act_func = act_func 6 with self.init_scope(): 7 # encoder 8 self.le1 = L.Linear(n_in, n_h) 9 self.le2 = L.Linear(n_h, n_h) 10 self.le3_mu = L.Linear(n_h, n_latent) 11 self.le3_ln_var = L.Linear(n_h, n_latent) 12 13 # decoder 14 self.ld1 = L.Linear(n_latent, n_h) 15 self.ld2 = L.Linear(n_h, n_h) 16 self.ld3 = L.Linear(n_h, n_in) 17 18 def __call__(self, x, sigmoid=True): 19 return self.decode(self.encode(x)[0], sigmoid) 20 21 def encode(self, x): 22 h1 = self.act_func(self.le1(x)) 23 h2 = self.act_func(self.le2(h1)) 24 mu = self.le3_mu(h2) 25 ln_var = self.le3_ln_var(h2) 26 return mu, ln_var 27 28 def decode(self, z, sigmoid=True): 29 h1 = self.act_func(self.ld1(z)) 30 h2 = self.act_func(self.ld2(h1)) 31 h3 = self.ld3(h2) 32 if sigmoid: 33 return F.sigmoid(h3) 34 else: 35 return h3 36 37 def get_loss_func(self, C=1.0, k=1): 38 def lf(x): 39 mu, ln_var = self.encode(x) 40 batchsize = len(mu.data) 41 # reconstruction error 42 rec_loss = 0 43 for l in six.moves.range(k): 44 z = F.gaussian(mu, ln_var) 45 z.name = "z" 46 rec_loss += F.bernoulli_nll(x, self.decode(z, sigmoid=False)) / (k * batchsize) 47 self.rec_loss = rec_loss 48 self.rec_loss.name = "reconstruction error" 49 self.latent_loss = C * gaussian_kl_divergence(mu, ln_var) / batchsize 50 self..name = "latent loss" 51 self.loss = self.rec_loss + self.latent_loss 52 self.loss.name = "loss" 53 return self.loss 54 return lf

rec_lossは再構成誤差、すなわち入力と出力がどの程度等しいかを表していて、latent_lossの方は特徴量空間における分布が正規分布からどれくらいことなるを表す誤差だと認識しています。

MNISTで実験してみた結果、
1.lossが減少していく
2.再構成がちゃんと行われる(input画像が3なら、output画像も3になっている)
3.特徴量空間でランダムサンプリングを行った結果、ちゃんと数字が出力される。
などが確かめられました。

疑問

ところで、疑問なのですが、rec_lossは再構成誤差なので、素朴には平均二乗誤差をつかうのが自然だと思われます。そこでrec_lossの部分を

python3

1rec_loss += F.mean_squared_error(x, self.decode(z)) / k

と書き換え、ほかの条件は全部そのままで(他の部分は一切書き換えずに)、実験すると

  1. rec_lossが2epoch目以降、全く減少しない。
  2. 再構成が行われない。適当なinputを与えても、意味のないoutput画像が得られる。(ちなみにinput画像の種類によらずoutput画像は一定のようです)

などの結果が得られて、学習がうまくいっていっていないようです。

これはなぜでしょうか。

追記1

bernoulli_nllはデフォルトではすべて合計する一方で、mean_squared_errorは二乗誤差をバッチとピクセルの両方で平均するので、再構成項が過小評価されてしまっているかもしれないと考えました。

MNSITは28×28の画像なので、MSEを用いる際には28×28×F.mean_squared_error(x, decode(z))とすれば良いと思い、試してみましたが結果は変わりませんでした。

追記2

chainerでMSEを使ったVAEの実装を行っているコードを見つけました。
https://github.com/maguro27/VAE-CIFAR10_chainer/blob/master/VAE_CIFAR10.ipynb)
なぜ、このコードでは動いて、私の上のコードでは学習がうまくいかないのでしょうか。

追記3

optimizerの問題ではないかとの指摘を受けて、Adam,AdaDelta,SGDで試しましたが結果は変わらず…

解決策?

passerbyさんに言われた通り、

python3

1rec_loss += F.mean_squared_error(x, self.decode(z)) / k

python3

1rec_loss += F.mean(F.sum((x - self.decode(z)) ** 2, axis=1))

に変えるとうまくいきます。でもなぜだ…?

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tiitoi

2019/06/03 10:46 編集

回答依頼をいただき申し訳ないですが、Chainer は使ったことがないので、実際に動かして検証することができず、回答はできないです。 ただ理論的には、reconstruction error は MSE でも問題ないはずです。 実際、Keras の VAE のサンプルコードでは、MSE が使われています。 https://github.com/keras-team/keras/blob/master/examples/variational_autoencoder.py 損失関数の形状が変わると適切な最適化手法というのも変わるので、いくつか最適化手法を変更してみたりはどうでしょうか (adam、SGD、adadelta など)
physics303

2019/06/04 00:23

ありがとうございます。 私もpytorchでMSEを採用したVAEを確認しました。 https://gist.github.com/koshian2/2098e2261d673c818f6bdc51fa485e86 なので、特に何も工夫することなくMSEを使えるかと私も思っているのですが… Adam, AdaDelta, SGDで試してみましたが結果に変化はありませんでした。
passerby

2019/06/06 13:14

rec_loss += F.mean(F.sum((x - self.decode(z)) ** 2, axis=1))はどうですか?
physics303

2019/06/06 23:55

コメントありがとうございます。やってみます。
physics303

2019/06/07 00:57

MNISTの場合についてですが、うまくいきました!! rec_loss += F.mean_squared_error(x, self.decode(z)) / k がうまくいかずに、 rec_loss += F.mean(F.sum((x - self.decode(z)) ** 2, axis=1)) でうまくいくのはどういう理屈なのでしょう。どちらも同じことをやっている印象なのですが…
guest

回答1

0

自己解決

passerbyさんに言われた通り、

python3

1rec_loss += F.mean_squared_error(x, self.decode(z)) / k

python3

1rec_loss += F.mean(F.sum((x - self.decode(z)) ** 2, axis=1))

に変えるとうまくいきます。でもなぜだ…?

投稿2020/01/15 07:47

physics303

総合スコア89

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問