えんじにあのじゆうちょう

勉強したことを中心にアウトプットしていきます。

autoencoderを理解する

はじめに

E資格対として、今回はautoencoderについて考えてみます。
実装については以下のKeras Blogをそのまま使います。
blog.keras.io

ちなみに前回はこちら。
t.marufeuille.dev


AutoEncoder

ネットワーク構成

AutoEncoderは以下のような構成になります。
f:id:marufeuillex:20200118112055p:plain

入力層のデータを全結合ネットワークで圧縮表現を取り出(エンコード)し、その圧縮表現を復元(デコード)するようなネットワークです。
なので、誤差関数は2乗和誤差で、入力と出力を一致させられれば誤差を小さくすることが目的となります。

試してみる

一番シンプルなもので試してみました。
今回はmnistの手書き文字(28px x 28px, 1ch)を使います。
そのため、入力層の数は784、出力層も同じで784です。
(元のサンプルコードからコメントだけ削除していますが、同じものです)

from keras.layers import Input, Dense
from keras.models import Model

encoding_dim = 32

input_img = Input(shape=(784,))
encoded = Dense(encoding_dim, activation='relu')(input_img)
decoded = Dense(784, activation='sigmoid')(encoded)

autoencoder = Model(input_img, decoded)

中間層の数が悩ましいですが、あまり大きいと圧縮の意味はないですね。サンプルでは32となっているようです。
784/32 = 24.5なので、24.5倍に圧縮しているわけですね。

そのまま手書き文字に対して実行してみました。
f:id:marufeuillex:20200118154108p:plain

上の段が元入力、下の段が得られた画像です。
少しぼやっとしているようですが、まぁ圧縮した結果だと思うとそんなもんでしょうか。

圧縮と復元をじっくり見ていく

エンコード後の要素を取り出し、それをもってデコードするモデルを以下のように定義しておきます。

encoder = Model(input_img, encoded)
encoded_input = Input(shape=(encoding_dim,))
decoder_layer = autoencoder.layers[-1]
decoder = Model(encoded_input, decoder_layer(encoded_input))

では実際にどうなるのか実験してみましょう。
1つ画像を取り出してみます。

plt.imshow(x_test[0].reshape((28,28)))
plt.gray()

f:id:marufeuillex:20200119130424p:plain

それではエンコード(圧縮)してみます。圧縮は以下のようにして行います。

encoded_img = encoder.predict(np.array([x_test[0]]))

結果はこのようになっており、たしかに32個の浮動小数点値になっています。

array([[ 3.5877626 ,  4.2369595 ,  2.9547625 ,  2.500244  ,  9.741833  ,
         0.        ,  6.5683837 ,  7.874444  ,  4.9425797 , 11.080306  ,
        13.041988  ,  3.2718277 , 10.213803  ,  6.008341  ,  4.6966457 ,
         4.366081  ,  1.3713557 ,  2.3973894 ,  5.7510834 , 10.508511  ,
         7.630125  ,  8.372848  , 11.540308  ,  2.3859057 ,  3.8838708 ,
         0.18307471,  3.773189  , 10.607107  ,  5.6901307 ,  5.466326  ,
         1.5997248 ,  2.916244  ]], dtype=float32)

圧縮されているので、このまま表示しても意味はわかりません。
ではデコード(復元)してみます。

decoded_img = decoder.predict(encoded_img)

念の為shapeを見てみます。

decoded_img.shape
(1, 784)

もとの大きさに戻っているので 表示してみます。

plt.imshow(decoded_img.reshape((28,28)))
plt.gray()

f:id:marufeuillex:20200119130918p:plain

元とは違って、少しぼやっとしていますが、ちゃんと7になっていますね!!

まとめ

autoencoderは単純な仕組みながら、以外に正確に特徴を掴んで圧縮できていることがわかります。
また、Keras Blogを読む限りDenseで深くしてもあまり効果はなさそうでしたが、Convolutionを使うことでより良く特徴を捉えられるため、効率の良い圧縮ができるようです。