このブログ記事では、ディープラーニングにおけるオートエンコーダについて紹介します。

オートエンコーダ
オートエンコーダは、2つのニューラルネットワークを組み合わせたものです。エンコーダは次元を潜在空間に圧縮し、 デコーダは次元を元のサイズに拡張します。エンコーダの出力はデコーダの入力として接続され、オートエンコーダモデルは逆伝播を 使用して元の画像を再現するように(平均二乗誤差を最小化するように)訓練されます。以下は、次元を784から32に圧縮し、 32から784に拡張するオートエンコーダのアーキテクチャの例です。

上のような構造やニューラルネットワークの使用により、エンコーダは特徴間の、非線形な関係も含む、重要な関係を保持しながら 次元を削減すことでデコーダが元の画像を復元できるように訓練できます。訓練後、デコーダは破棄でき、エンコーダは次元削減に使用できます。PCA(主成分分析)は 線形関係のみを捉えるため(共分散行列の固有ベクトルを使用するため)、オートエンコーダが次元削減のための優れた技術であると 言えるでしょう。(PCAについて知りたい方は、MLエンジニアへの道 #8 - PCA の記事をチェックすることをお勧めします。)
コードの実装
オートエンコーダの構築は一見複雑に見えるかもしれませんが、PyTorchやTensorFlowのような機械学習フレームワークを 使用すれば比較的簡単に作成できます。
ステップ 1 & 2. データ探索と前処理
オートエンコーダを構築する前に、以下のようにデータを適切に前処理する必要があります。
(X_train, y_train), (X_test, y_test) = keras.datasets.mnist.load_data()
# Flatten Images
X_train = X_train.reshape(X_train.shape[0], X_train.shape[1]*X_train.shape[2])
X_test = X_test.reshape(X_test.shape[0], X_test.shape[1]*X_test.shape[2])
# Normalize
def zscore(X, axis = None):
X_mean = X.mean(axis=axis, keepdims=True)
X_std = np.std(X, axis=axis, keepdims=True)
zscore = (X-X_mean)/X_std
return zscore
X_train = zscore(X_train)
X_test = zscore(X_test)
# Validation Dataset
X_train, X_val, y_train, y_val = train_test_split(X_train, y_train, test_size=10000, random_state=101)
次に、PyTorch用にDataset
とDataLoader
を作成します。オートエンコーダの出力はクラスラベルではなく、
画像そのものであることを忘れないでください。
train_dataset = torch.utils.data.TensorDataset(X_train, X_train)
val_dataset = torch.utils.data.TensorDataset(X_val, X_val)
test_dataset = torch.utils.data.TensorDataset(X_test, X_test)
train_loader = torch.utils.data.DataLoader(dataset=train_dataset, batch_size=32, shuffle=True)
val_loader = torch.utils.data.DataLoader(dataset=val_dataset, batch_size=32, shuffle=True)
test_loader = torch.utils.data.DataLoader(dataset=test_dataset, batch_size=10000, shuffle=True)
ステップ 3. モデル
以下は、PyTorchとTensorFlowを使用した例のオートエンコーダの実装です。
ステップ 4. モデル評価
モデルを訓練した後は、テストデータセットを使用してデコードされた画像をプロットし、元の画像と比較して、 オートエンコーダが次元削減を効果的に学習したかどうかを確認します。そのために、モデルから予測を取得し、 予測とテストデータセットの両方を784から(28, 28)に再形成します。
# TensorFlow predictions
preds = autoencoder.predict(X_test)
# PyTorch predictions
for X, y in test_loader:
X_test = X
preds = autoencoder(X)
X_test = X_test.numpy()
preds = preds.detach().numpy()
# Reshape them for visualization
X_test = X_test.reshape(X_test.shape[0], 28, 28)
preds = preds.reshape(preds.shape[0], 28, 28)
その後、以下の関数を使用して10サンプルを視覚化できます。
def plotImgs (X):
plt.figure(figsize=(10, 4))
for i in range(10):
plt.subplot(2, 5, i + 1)
plt.imshow(X[i], cmap='gray')
plt.axis('off')
plt.tight_layout()
plt.show()
plotImgs(X_test)
plotImgs(preds)
以下は、オートエンコーダ(TensorFlowを使用)を3エポック訓練した後の結果です。

オートエンコーダが小さな潜在表現から画像を適切に再構築していることがすでに確認できます。エンコーダにアクセスして、 潜在空間内の画像ベクトルを取得できます。
# TensorFlow Encoding
encoded_train = encoder.predict(X_train)
encoded_test = encoder.predict(X_test)
# PyTorch Encoding
for X, y in train_loader:
encoded_train = autoencoder.encoder(X)
for X, y in test_loader:
encoded_train = autoencoder.encoder(X)
その後、これらの潜在表現を小さな分類器モデルの入力として使用し、数字を分類できます。
デコーダ
潜在空間内のベクトルに少し変化を加えたものをデコーダに渡すことで、新しい画像を生成できるかどうか気になる方 もいるかもしれません。では、実際にデコーダを画像生成に使用できるか見てみましょう。(ここではTensorFlowを使用します。)
encoded = encoder.predict(X_test)
latent = np.random.normal(encoded, 1) # 少し変化を加える
decoded = decoder.predict(latent)
decoded = decoded.reshape(decoded.shape[0], 28, 28)
plotImgs(decoded)

画像の中にはわずかに識別可能なものもありますが(変化の程度によるものと思われます)、多くの画像はそうではありません。 これは、エンコーダが潜在空間内で画像をどのように整理するかに関するルールがなく、デコーダが未知の潜在空間を画像に戻す ように特訓されているためです。
どのようにエンコーダの生み出す潜在空間にルールを適用して、デコーダが新しい画像を生成するためのベクトルを潜在空間から 選択できるようにできるでしょうか。次回の記事で取り上げるまでに考えてみてください。