自编码器深度分析+定制特征描述子构建初探
1. 關于tailored 特征描述子
? ? ? ? 自從深度學習的繁榮,利用自編碼器、孿生網絡、對抗網絡采用數據驅動的形式設計tailored 特征描述子 成為了計算機視覺領域發展的重要推動力, 這不僅大大削弱了特征工程的壓力,而且降低了相關領域學者對于數學基礎的要求。 本博文重點在于介紹自編碼器在tailored feature方面的潛力。
2. 什么是自編碼器(Autoencoder)初步探索
? ? ? ? 自動編碼器實質是Encode+Decode的過程,實際可以理解為是一種數據壓縮+數據重建算法,其中數據的壓縮和解壓縮函數是數據相關的、有損的、從樣本中自動學習的。在大部分提到自動編碼器的場合,壓縮和解壓縮的函數是通過神經網絡實現的。
- 自動編碼器是數據相關的(data-specific 或 data-dependent),這意味著自動編碼器只能壓縮那些與訓練數據類似的數據,這維學習某一領域的tailored feature提供了可能。比如,使用人臉訓練出來的自動編碼器在壓縮別的圖片,比如樹木時性能很差,因為它學習到的特征是與人臉相關的
- 自動編碼器是有損的,意思是解壓縮的輸出與原來的輸入相比是退化的,這潛在有利于去除數據中不一致、非緊致的噪聲
- 自動編碼器是從數據樣本中自動學習的,這意味著很容易對指定類的輸入訓練出一種特定的編碼器,而不需要完成任何新工作。
? ? ? ? 搭建一個自動編碼器需要完成下面三樣工作:搭建編碼器,搭建解碼器,設定一個損失函數,用以衡量由于壓縮而損失掉的信息。編碼器和解碼器一般都是參數化的方程,并關于損失函數可導,典型情況是使用神經網絡。編碼器和解碼器的參數可以通過最小化損失函數而優化,例如SGD。自編碼器是一個自監督的算法,并不是一個無監督算法。自監督學習是監督學習的一個實例,其標簽產生自輸入數據。要獲得一個自監督的模型,你需要一個靠譜的目標跟一個損失函數,僅僅把目標設定為重構輸入可能不是正確的選項。基本上,要求模型在像素級上精確重構輸入不是機器學習的興趣所在,學習到高級的抽象特征才是。事實上,當主要任務是分類、定位之類的任務時,那些對這類任務而言的最好的特征基本上都是重構輸入時的最差的那種特征。
? ? ? ? 目前自編碼器的應用主要有兩個方面,第一是數據去噪,第二是為進行可視化而降維。配合適當的維度和稀疏約束,自編碼器可以學習到比PCA等技術更有意思的數據投影。對于2D的數據可視化,t-SNE(讀作tee-snee)或許是目前最好的算法,但通常還是需要原數據的維度相對低一些。所以,可視化高維數據的一個好辦法是首先使用自編碼器將維度降低到較低的水平(如32維),然后再使用t-SNE將其投影在2D平面上。
3. 幾種典型自編碼器調研
? ? ? ? 自編碼是神經網絡的一種,經過訓練后能嘗試將輸入復制到輸出。自編碼器內部有一個隱藏層 h,可以產生編碼(code)表示輸入。該網絡可以看作由兩部分組成:一個由函數 h = f(x) 表示的編碼器和一個生成重構的解碼器 r = g(h)。如果一個自編碼器只是簡單地學會將處處設置為 g(f(x)) = x,那么這個自編碼器就沒什么特別的用處。相反,我們不應該將自編碼器設計成輸入到輸出完全相等。這通常需要向自編碼器強加一些約束,使它只能近似地復制,并只能復制與訓練數據相似的輸入。這些約束強制模型考慮輸入數據的哪些部分需要被優先復制,因此它往往能學習到數據的有用特性。
- 欠完備自編碼器
從自編碼器獲得有用特征的一種方法是限制 h的維度比 x 小,這種編碼維度小于輸入維度的自編碼器稱為欠完備undercomplete自編碼器。學習欠完備的表示將強制自編碼器捕捉訓練數據中最顯著的特征。學習過程可以簡單地描述為最小化一個損失函數L(x,g(f(x))),其中 L 是一個損失函數,懲罰g(f(x)) 與 x 的差異,如均方誤差。當解碼器是線性的且 L 是均方誤差,欠完備的自編碼器會學習出與 PCA 相同的生成子空間。這種情況下,自編碼器在訓練來執行復制任務的同時學到了訓據的主元子空間。如果編碼器和解碼器被賦予過大的容量,自編碼器會執行復制任務而捕捉不到任何有關數據分布的有用信息。
- 正則自編碼器
正則自編碼器使用的損失函數可以鼓勵模型學習其他特性(除了將輸入復制到輸出),而不必限制使用淺層的編碼器和解碼器以及小的編碼維數來限制模型的容量。這些特性包括稀疏表示、表示的小導數、以及對噪聲或輸入缺失的魯棒性。即使模型容量大到足以學習一個無意義的恒等函數,非線性且過完備的正則自編碼器仍然能夠從數據中學到一些關于數據分布的有用信息。
- 稀疏自編碼器
稀疏自編碼器簡單地在訓練時結合編碼層的稀疏懲罰 ?(h) 和重構誤差:L(x,g(f(x))) + ?(h),其中 g(h) 是解碼器的輸出,通常 h 是編碼器的輸出,即 h = f(x)。稀疏自編碼器一般用來學習特征,以便用于像分類這樣的任務。稀疏正則化的自編碼器必須反映訓練數據集的獨特統計特征,而不是簡單地充當恒等函數。以這種方式訓練,執行附帶稀疏懲罰的復制任務可以得到能學習有用特征的模型。
- 去噪自編碼器
去噪自編碼器(denoisingautoencoder, DAE)最小化L(x,g(f(? x))),其中 ? x 是被某種噪聲損壞的 x 的副本。因此去噪自編碼器必須撤消這些損壞,而不是簡單地復制輸入。它是一類接受損壞數據作為輸入,并訓練來預測原始未被損壞數據作為輸出的自編碼器。DAE 的訓練準則(條件高斯p(x | h))能讓自編碼器學到能估計數據分布得分的向量場 (g(f(x)) ? x) ,這是 DAE 的一個重要特性。
4. 使用Keras建立簡單的自編碼器
利用全連接層構建簡單的自編碼器如下:
代碼和效果和下圖所示:
from keras.layers import Input, Dense from keras.models import Model from keras.datasets import mnist import numpy as np import matplotlib.pyplot as plt(x_train, _), (x_test, _) = mnist.load_data()x_train = x_train.astype('float32') / 255. x_test = x_test.astype('float32') / 255. x_train = x_train.reshape((len(x_train), np.prod(x_train.shape[1:]))) x_test = x_test.reshape((len(x_test), np.prod(x_test.shape[1:]))) print(x_train.shape) print(x_test.shape)encoding_dim = 32 input_img = Input(shape=(784,))encoded = Dense(encoding_dim, activation='relu')(input_img) decoded = Dense(784, activation='sigmoid')(encoded)autoencoder = Model(inputs=input_img, outputs=decoded) encoder = Model(inputs=input_img, outputs=encoded)encoded_input = Input(shape=(encoding_dim,)) decoder_layer = autoencoder.layers[-1]decoder = Model(inputs=encoded_input, outputs=decoder_layer(encoded_input))autoencoder.compile(optimizer='adadelta', loss='binary_crossentropy')autoencoder.fit(x_train, x_train, epochs=50, batch_size=256, shuffle=True, validation_data=(x_test, x_test))encoded_imgs = encoder.predict(x_test) decoded_imgs = decoder.predict(encoded_imgs)n = 10 # how many digits we will display plt.figure(figsize=(20, 4)) for i in range(n):ax = plt.subplot(2, n, i + 1)plt.imshow(x_test[i].reshape(28, 28))plt.gray()ax.get_xaxis().set_visible(False)ax.get_yaxis().set_visible(False)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()Dense784-Dense32-Dense784結果如下,其損失loss=0.1044:
Dense784-Dense256-Dense64-Dense256-Dense784結果如下,其損失loss=0.0879:
- 稀疏自編碼器、深層自編碼器
如果我們對隱層單元施加稀疏性約束的話,會得到更為緊湊的表達,只有一小部分神經元會被激活。在Keras中,可以通過添加一個activity_regularizer達到對某層激活值進行約束的目的。encoded = Dense( encoding_dim, activation='relu', activity_regularizer = regularizers.activity_l1(10e-5) )(input_img)。深度-稀疏自編碼器如下:
Note:在keras中存在3個正則化技巧,分別是權重正則化。偏置正則化、激活正則化
from keras.layers import Input, Dense from keras import regularizers from keras.models import Model from keras.datasets import mnist import numpy as np import matplotlib.pyplot as plt# only need images, this is self-supervised process (x_train, _), (x_test, _) = mnist.load_data()x_train = x_train.astype('float32') / 255. # normalization x_test = x_test.astype('float32') / 255. x_train = x_train.reshape((len(x_train), np.prod(x_train.shape[1:]))) x_test = x_test.reshape((len(x_test), np.prod(x_test.shape[1:]))) print(x_train.shape) print(x_test.shape)input_img = Input(shape=(784,)) encoded = Dense(128, activation='relu',#activity_regularizer = regularizers.l1(0.000001))(input_img) encoded = Dense(64, activation='relu',#activity_regularizer = regularizers.l1(0.00001))(encoded) decoded_input = Dense(32, activation='relu', #activity_regularizer = regularizers.l1(0.00001))(encoded) decoded = Dense(64, activation='relu')(decoded_input) decoded = Dense(128, activation='relu')(decoded) decoded = Dense(784, activation='sigmoid')(decoded)autoencoder = Model(inputs=input_img, outputs=decoded) encoder = Model(inputs=input_img, outputs=decoded_input)autoencoder.compile(optimizer='adadelta', loss='binary_crossentropy') autoencoder.fit(x_train, x_train, epochs=50, batch_size=256, shuffle=True, validation_data=(x_test, x_test))encoded_imgs = encoder.predict(x_test) decoded_imgs = autoencoder.predict(x_test)n = 10 # how many digits we will display plt.figure(figsize=(20, 6)) for i in range(n):# original imagesax = plt.subplot(3, n, i + 1)plt.imshow(x_test[i].reshape(28, 28))plt.gray()ax.get_xaxis().set_visible(False)ax.get_yaxis().set_visible(False)ax = plt.subplot(3, n, i + 1 + n)plt.imshow(encoded_imgs[i].reshape(8, 4))#plt.gray()plt.set_cmap('jet')ax.get_xaxis().set_visible(False)ax.get_yaxis().set_visible(False)ax = plt.subplot(3, n, i + 1 + n + 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()Dense784-Dense128-Dense64-Dense32-Dense64-Dense128-Dense784 (無正則化)結果顯示,loss=0.1144:
Dense784-Dense128-Dense64-Dense32-Dense64-Dense128-Dense784 (激活正則化1e-6)結果顯示,loss=0.1396:
- 卷積自編碼器:用卷積層構建自編碼器
當輸入是圖像時,使用卷積神經網絡是更好的。卷積自編碼器的編碼器部分由卷積層和MaxPooling層構成,MaxPooling負責空域下采樣。而解碼器由卷積層和上采樣層構成。
from keras.layers import Input, Convolution2D, MaxPooling2D, UpSampling2D from keras.models import Model from keras.datasets import mnist from keras.callbacks import TensorBoard import matplotlib.pyplot as plt# only need images, this is self-supervised process (x_train, _), (x_test, _) = mnist.load_data()x_train = x_train.astype('float32') / 255. # normalization x_test = x_test.astype('float32') / 255. x_train = x_train.reshape((len(x_train), 28, 28, 1)) x_test = x_test.reshape((len(x_test), 28, 28, 1)) print(x_train.shape) print(x_test.shape)input_img = Input(shape=(28, 28, 1))x = Convolution2D(16, (3, 3), activation='relu', padding='same')(input_img) x = MaxPooling2D((2, 2), padding='same')(x) x = Convolution2D(8, (3, 3), activation='relu', padding='same')(x) x = MaxPooling2D((2, 2), padding='same')(x) x = Convolution2D(8, (3, 3), activation='relu', padding='same')(x) encoded = MaxPooling2D((2, 2), padding='same')(x)x = Convolution2D(8, (3, 3), activation='relu', padding='same')(encoded) x = UpSampling2D((2, 2))(x) x = Convolution2D(8, (3, 3), activation='relu', padding='same')(x) x = UpSampling2D((2, 2))(x) x = Convolution2D(16, (3, 3), activation='relu')(x) x = UpSampling2D((2, 2))(x) decoded = Convolution2D(1, (3, 3), activation='sigmoid', padding='same')(x)encoder = Model(inputs=input_img, outputs=encoded) autoencoder = Model(inputs=input_img, outputs=decoded) autoencoder.compile(optimizer='adadelta', loss='binary_crossentropy')autoencoder.fit(x_train, x_train, epochs=50, batch_size=256,shuffle=True, validation_data=(x_test, x_test),callbacks=[TensorBoard(log_dir='autoencoder')])encoded_imgs = encoder.predict(x_test) decoded_imgs = autoencoder.predict(x_test)n = 10 # how many digits we will display plt.figure(figsize=(20, 6)) for i in range(n):# original imagesax = plt.subplot(3, n, i + 1)plt.imshow(x_test[i].reshape(28, 28))plt.gray()ax.get_xaxis().set_visible(False)ax.get_yaxis().set_visible(False)ax = plt.subplot(3, n, i + 1 + n)plt.imshow(encoded_imgs[i].reshape(16, 8))#plt.gray()plt.set_cmap('jet')ax.get_xaxis().set_visible(False)ax.get_yaxis().set_visible(False)ax = plt.subplot(3, n, i + 1 + n + 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()結果顯示:
?
- 使用自動編碼器進行圖像去噪
我們把訓練樣本用噪聲污染,然后使解碼器解碼出干凈的照片,以獲得去噪自動編碼器。首先我們把原圖片加入高斯噪聲,然后把像素值clip到0~1。
from keras.layers import Input, Convolution2D, MaxPooling2D, UpSampling2D from keras.models import Model from keras.datasets import mnist from keras.callbacks import TensorBoard import matplotlib.pyplot as plt# only need images, this is self-supervised process (x_train, _), (x_test, _) = mnist.load_data()x_train = x_train.astype('float32') / 255. # normalization x_test = x_test.astype('float32') / 255. x_train = x_train.reshape((len(x_train), 28, 28, 1)) x_test = x_test.reshape((len(x_test), 28, 28, 1)) print(x_train.shape) print(x_test.shape)input_img = Input(shape=(28, 28, 1))x = Convolution2D(16, (3, 3), activation='relu', padding='same')(input_img) x = MaxPooling2D((2, 2), padding='same')(x) x = Convolution2D(8, (3, 3), activation='relu', padding='same')(x) x = MaxPooling2D((2, 2), padding='same')(x) x = Convolution2D(8, (3, 3), activation='relu', padding='same')(x) encoded = MaxPooling2D((2, 2), padding='same')(x)x = Convolution2D(8, (3, 3), activation='relu', padding='same')(encoded) x = UpSampling2D((2, 2))(x) x = Convolution2D(8, (3, 3), activation='relu', padding='same')(x) x = UpSampling2D((2, 2))(x) x = Convolution2D(16, (3, 3), activation='relu')(x) x = UpSampling2D((2, 2))(x) decoded = Convolution2D(1, (3, 3), activation='sigmoid', padding='same')(x)encoder = Model(inputs=input_img, outputs=encoded) autoencoder = Model(inputs=input_img, outputs=decoded) autoencoder.compile(optimizer='adadelta', loss='binary_crossentropy')autoencoder.fit(x_train, x_train, epochs=50, batch_size=256,shuffle=True, validation_data=(x_test, x_test),callbacks=[TensorBoard(log_dir='autoencoder')])encoded_imgs = encoder.predict(x_test) decoded_imgs = autoencoder.predict(x_test)n = 10 # how many digits we will display plt.figure(figsize=(20, 6)) for i in range(n):# original imagesax = plt.subplot(3, n, i + 1)plt.imshow(x_test[i].reshape(28, 28))plt.gray()ax.get_xaxis().set_visible(False)ax.get_yaxis().set_visible(False)ax = plt.subplot(3, n, i + 1 + n)plt.imshow(encoded_imgs[i].reshape(16, 8))#plt.gray()plt.set_cmap('jet')ax.get_xaxis().set_visible(False)ax.get_yaxis().set_visible(False)ax = plt.subplot(3, n, i + 1 + n + 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()去噪效果顯示,loss=0.1028:
博文參考:
https://www.cnblogs.com/happylion/p/4209570.html
https://blog.csdn.net/marsjhao/article/details/73480859
總結
以上是生活随笔為你收集整理的自编码器深度分析+定制特征描述子构建初探的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python类与对象技巧(2):拓展子类
- 下一篇: Python 中 function(#)