在深度學(xué)習(xí)中,自編碼器是非常有用的一種無監(jiān)督學(xué)習(xí)模型痊土。自編碼器由encoder和decoder組成肄扎,前者將原始表示編碼成隱層表示,后者將隱層表示解碼成原始表示赁酝,訓(xùn)練目標(biāo)為最小化重構(gòu)誤差犯祠,而且一般而言,隱層的特征維度低于原始特征維度酌呆。
自編碼器只是一種思想衡载,在具體實現(xiàn)中,encoder和decoder可以由多種深度學(xué)習(xí)模型構(gòu)成隙袁,例如全連接層痰娱、卷積層或LSTM等,以下使用Keras來實現(xiàn)用于圖像去噪的卷積自編碼器菩收。
1 結(jié)果##
先看一下最后的結(jié)果梨睁,使用的是手寫數(shù)字MNIST數(shù)據(jù)集,上面一行是添加噪音的圖像娜饵,下面一行是去噪之后的結(jié)果坡贺。
2 代碼##
我使用Keras來實現(xiàn)自編碼器,encoder和decoder使用CNN來實現(xiàn)箱舞。
加載Keras和numpy遍坟。
from keras.datasets import mnist
import numpy as np
獲取數(shù)據(jù)集MNIST,將像素點值轉(zhuǎn)化到0-1區(qū)間褐缠,并且重塑為N×1×28×28的四維tensor政鼠。
(x_train, _), (x_test, _) = mnist.load_data()
x_train = x_train.astype('float32') / 255.
x_test = x_test.astype('float32') / 255.
x_train = np.reshape(x_train, (len(x_train), 1, 28, 28))
x_test = np.reshape(x_test, (len(x_test), 1, 28, 28))
添加噪聲,即疊加一個隨機的高斯白噪聲队魏,并限制加噪之后的值仍處于0-1區(qū)間公般。
noise_factor = 0.5
x_train_noisy = x_train + noise_factor * np.random.normal(loc=0.0, scale=1.0, size=x_train.shape)
x_test_noisy = x_test + noise_factor * np.random.normal(loc=0.0, scale=1.0, size=x_test.shape)
x_train_noisy = np.clip(x_train_noisy, 0., 1.)
x_test_noisy = np.clip(x_test_noisy, 0., 1.)
看一下加噪之后的結(jié)果万搔。
import matplotlib.pyplot as plt
n = 10
plt.figure(figsize=(20, 2))
for i in range(n):
ax = plt.subplot(1, n, i + 1)
plt.imshow(x_test_noisy[i].reshape(28, 28))
plt.gray()
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
plt.show()
畫出來是這個樣子的。
定義模型的輸入官帘。
input_img = Input(shape=(1, 28, 28))
定義encoder部分瞬雹,由兩個32×3×3的卷積層和兩個2×2的最大池化層組成。
x = Convolution2D(32, 3, 3, activation='relu', border_mode='same')(input_img)
x = MaxPooling2D((2, 2), border_mode='same')(x)
x = Convolution2D(32, 3, 3, activation='relu', border_mode='same')(x)
encoded = MaxPooling2D((2, 2), border_mode='same')(x)
定義decoder部分刽虹,由兩個32×3×3的卷積層和兩個2×2的上采樣層組成酗捌。
# at this point the representation is (32, 7, 7)
x = Convolution2D(32, 3, 3, activation='relu', border_mode='same')(encoded)
x = UpSampling2D((2, 2))(x)
x = Convolution2D(32, 3, 3, activation='relu', border_mode='same')(x)
x = UpSampling2D((2, 2))(x)
decoded = Convolution2D(1, 3, 3, activation='sigmoid', border_mode='same')(x)
將輸入和輸出連接起來,構(gòu)成autoencoder并compile涌哲。
autoencoder = Model(input_img, decoded)
autoencoder.compile(optimizer='adadelta', loss='binary_crossentropy')
使用x_train作為輸入和輸出來訓(xùn)練我們的autoencoder胖缤,并使用x_test進(jìn)行validation。
autoencoder.fit(x_train_noisy, x_train,
nb_epoch=100,
batch_size=128,
shuffle=True,
validation_data=(x_test_noisy, x_test))
使用autoencoder對x_test預(yù)測阀圾,并將預(yù)測結(jié)果繪制出來哪廓,和原始加噪圖像進(jìn)行對比。
decoded_imgs = autoencoder.predict(x_test_noisy)
import matplotlib.pyplot as plt
n = 10
plt.figure(figsize=(20, 4))
for i in range(n):
# display original
ax = plt.subplot(2, n, i + 1)
plt.imshow(x_test_noisy[i].reshape(28, 28))
plt.gray()
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
# display reconstruction
ax = plt.subplot(2, n, i + 1 + n)
plt.imshow(decoded_imgs[i].reshape(28, 28))
plt.gray()
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
plt.show()
這樣便可以生成一開始看到的結(jié)果了初烘,完整代碼在這里涡真。
3 其他內(nèi)容##
除了以上用于去噪的卷積自編碼器,這里還有其他幾個代碼:
- 1_simplest_possible_autoencoder:分別僅使用一層Dense作為encoder和decoder構(gòu)成自編碼器肾筐,并對MNIST數(shù)據(jù)集訓(xùn)練和重構(gòu)哆料,50 epoch之后loss為0.1068,val_loss為0.1051吗铐;
- 2_deep_autoencoder:encoder和decoder從一層Dense增加到三層Dense东亦,100 epoch之后loss為0.0974,val_loss為0.0966抓歼;
- 3_convolutional_deep_autoencoder:encoder和decoder分別由CNN實現(xiàn)讥此,100 epoch之后loss為0.0958,val_loss為0.0946谣妻;
- 4_denoising_autoencoder:即以上詳細(xì)討論的去噪卷積自編碼器;
- 5_variational_autoencoder(VAE):隱層添加額外的限制卒稳,即訓(xùn)練目標(biāo)為最小化重構(gòu)誤差蹋半,以及隱層表示分布和原始表示分布的交叉熵(KL Divergence)。
其中最后一個代碼中的VAE將MNIST的原始圖像數(shù)據(jù)映射到了一個二維的隱層充坑,下面是隱層表示中的聚類結(jié)果减江,可以看到在隱層的表示空間中,相同數(shù)字所對應(yīng)的圖像匯聚到了一起捻爷。
如果將隱層中的點解碼到原始的圖像表示空間辈灼,則各個聚類中心會出現(xiàn)對應(yīng)的數(shù)字,而類和類之間的位置則會出現(xiàn)“新的數(shù)字”也榄,即不同數(shù)字之間的過渡形態(tài)巡莹。