你可能對不同的神經網絡結構有點熟悉。你可能聽說過前饋神經網絡,CNNs,RNNs,這些神經網絡對於解決諸如迴歸和分類之類的監督學習任務非常有用。
但是,在無監督學習領域,我們面臨著大量的問題,如降維、特徵提取、異常檢測、數據生成、增強以及噪聲抑制等。對於這些任務,我們需要特殊的神經網絡的幫助,這些神經網絡是專門為無監督學習任務而開發的。
因此,他們必須能夠在不需要監督的情況下訓練出來。其中一種特殊的神經網絡結構是自編碼器。
自編碼器
什麼是自編碼器?
自編碼器是一種神經網絡結構,它由兩個子網絡組成,即編碼和解碼網絡,它們之間通過一個潛在空間相互連接。
自編碼器最早由傑弗裡·辛頓(Geoffrey Hinton)和PDP小組在20世紀80年代開發。Hinton和PDP小組的目標是解決“沒有教師的反向傳播”問題,即無監督學習,將輸入作為教師。換句話說,他們只是簡單地將特徵數據用作特徵數據和標籤數據。讓我們仔細看看自編碼器是如何工作的!
自編碼器體系結構
自編碼器由一個編碼器網絡組成,該網絡接收特徵數據並對其進行編碼以適應潛在空間。解碼器使用該編碼數據(即代碼)將其轉換回特徵數據。在編碼器中,模型學習的是如何有效地編碼數據,以便解碼器能夠將其轉換回原始數據。因此,自編碼器訓練的關鍵是生成一個優化的潛在空間。
現在,要知道在大多數情況下,潛在空間中的神經元數量要比輸入層和輸出層小得多,但不一定要這樣。有不同類型的自編碼器,如欠完備、過完備、稀疏、去噪、壓縮和變分自編碼器。在本教程中,我們只關注用於去噪的欠完備自編碼器。
自編碼器中的層
構建自編碼器時的標準做法是設計一個編碼器並創建該網絡的反向版本作為該自編碼器的解碼器。因此,只要編碼器和解碼器網絡之間存在反向關係,你就可以自由地向這些子網絡添加任何層。例如,如果你處理的是圖像數據,你肯定需要卷積和池層。另一方面,如果要處理序列數據,則可能需要LSTM、GRU或RNN單元。這裡重要的一點是,你可以自由地構建任何你想要的東西。
現在,你已經有了可以構建圖像降噪的自編碼器的想法,我們可以繼續學習教程,開始為圖像降噪模型編寫代碼。在本教程中,我們選擇使用TensorFlow的官方教程之一《Autoencoders簡介》[1],我們將使用AI社區成員中非常流行的數據集:Fashion MNIST。
下載Fashion MNIST數據集
Fashion MNIST由德國柏林的歐洲電子商務公司Zalando設計和維護。Fashion MNIST由60000個圖像的訓練集和10000個圖像的測試集組成。每個例子是一個28×28的灰度圖像,與來自10個類的標籤相關聯。
Fashion MNIST包含服裝的圖像(如圖所示),被設計為MNIST數據集的替代數據集,MNIST數據集包含手寫數字。我們選擇Fashion MNIST僅僅是因為MNIST在許多教程中已經被過度使用。
下面的行導入TensorFlow和load Fashion MNIST:
<code>import
tensorflow as tf from tensorflow.keras.datasetsimport
fashion_mnist # 我們不需要y_train和y_test (x_train,_
), (x_test,_
) = fashion_mnist.load_data()Max
valuein
the x_trainis
', x_train[0
].max
())Min
valuein
the x_trainis
', x_train[0
].min
()) /<code>
現在,讓我們使用數據集中的示例生成一個網格,其中包含以下行:
<code>import
matplotlib.pyplot as plt
axs = plt.subplots(5, 10)
=-1)
plt.gray()
a
=0
for
i in range(5):
for
j in range(10):
j].imshow(tf.squeeze(x_test[a]))
j].xaxis.set_visible(False)
j].yaxis.set_visible(False)
a
=a + 1
/<code>
我們的輸出顯示了測試數據集的前50個樣本:
處理Fashion MNIST數據
為了提高計算效率和模型可靠性,我們必須對圖像數據應用Minmax規範化,將值範圍限制在0到1之間。由於我們的數據是RGB格式的,所以最小值為0,最大值為255,我們可以使用以下代碼進行最小最大規格化操作:
<code>x_train
= x_train.astype('float32'
) /255
.x_test
= x_test.astype('float32'
) /255
. /<code>
我們還必須改變NumPy數組維度,因為數據集的當前形狀是(60000,28,28)和(10000,28,28)。我們只需要添加一個單一值的第四個維度(例如,從(60000,28,28)到(60000,28,28,1))。
第四維幾乎可以證明我們的數據是灰度格式的。如果我們有彩色圖像,那麼我們需要在第四維中有三個值。但是我們只需要一個包含單一值的第四維度,因為我們使用灰度圖像。以下幾行代碼可以做到這一點:
<code>x_train
= x_train[…, tf.newaxis]x_test
= x_test[…, tf.newaxis] /<code>
讓我們通過以下幾行來看看NumPy數組的形狀:
<code>
輸出:(60000,28,1)和(10000,28,28,1)
給圖像添加噪聲
記住我們的目標是建立一個模型,它能夠對圖像進行降噪處理。為了做到這一點,我們將使用現有的圖像數據並將它們添加到隨機噪聲中。
然後,我們將原始圖像作為輸入,噪聲圖像作為輸出。我們的自編碼器將學習乾淨的圖像和有噪聲的圖像之間的關係,以及如何清除有噪聲的圖像。
因此,讓我們創建一個有噪聲的版本。
對於這個任務,我們使用tf.random.normal方法。然後,我們用一個噪聲係數乘以隨機值,你可以隨意使用它。以下代碼為圖像添加噪聲:
<code>noise_factor
=0.4
x_train_noisy
= x_train + noise_factor * tf.random.normal(shape=x_train.shape)x_test_noisy
= x_test + noise_factor * tf.random.normal(shape=x_test.shape) /<code>
我們還需要確保數組項的值在0到1的範圍內。為此,我們可以使用 tf.clip_by_value方法。clip_by_value是一種TensorFlow方法,它將“最小值-最大值”範圍之外的值剪裁併替換為指定的“最小值”或“最大值”。以下代碼剪輯超出範圍的值:
<code>x_train_noisy
= tf.clip_by_value(x_train_noisy, clip_value_min=0
., clip_value_max=1
.)x_test_noisy
= tf.clip_by_value(x_test_noisy, clip_value_min=0
., clip_value_max=1
.) /<code>
現在,我們已經創建了數據集的規則化和噪聲版本,我們可以查看它的外觀:
<code>n
=5
=(20, 8))
plt.gray()
for
i in range(n):
ax
=plt.subplot(2, n, i + 1)
size=20)
bx
=plt.subplot(2, n, n+ i + 1)
+ noise", size=20)
plt.show()
/<code>
正如你所見,在嘈雜的圖像中幾乎不可能理解我們所看到的。然而,我們的自編碼器將神奇地學會清潔它。
建立我們的模型
在TensorFlow中,除了順序API和函數API之外,還有第三種方法來構建模型:模型子類化。在模型子類化中,我們可以自由地從零開始實現一切。
模型子類化是完全可定製的,使我們能夠實現自己的定製模型。這是一個非常強大的方法,因為我們可以建立任何類型的模型。但是,它需要基本的面向對象編程知識。我們的自定義類是tf.keras.Model對象。它還需要聲明幾個變量和函數。
另外請注意,由於我們處理的是圖像數據,因此構建一個卷積式自編碼器更為有效,如下所示:
要構建模型,我們只需完成以下任務:
- 創建一個擴展keras.Model的對象
- 創建一個函數來聲明兩個用順序API構建的獨立模型。在它們中,我們需要聲明相互顛倒的層。一個Conv2D層用於編碼器模型,而一個Conv2DTranspose層用於解碼器模型。
- 使用__init__ 方法創建一個call函數,告訴模型如何使用初始化的變量來處理輸入
- 我們需要調用以圖像為輸入的初始化編碼器模型
- 我們還需要調用以編碼器模型(encoded)的輸出作為輸入的初始化解碼器模型
- 返回解碼器的輸出
我們可以通過以下代碼實現所有這些目標:
<code>from tensorflow.keras.layers import Conv2DTranspose, Conv2D, Inputclass
NoiseReducer
(tf
.keras
.Model
):def
__init__
(
self
):super
(NoiseReducer,self
).__init__
()self
.encoder = tf.keras.Sequential([ Input(shape=(28
,28
,1
)), Conv2D(16
, (3
,3
), activation='relu'
, padding='same'
, strides=2
), Conv2D(8
, (3
,3
), activation='relu'
, padding='same'
, strides=2
)])self
.decoder = tf.keras.Sequential([ Conv2DTranspose(8
, kernel_size=3
, strides=2
, activation='relu'
, padding='same'
), Conv2DTranspose(16
, kernel_size=3
, strides=2
, activation='relu'
, padding='same'
), Conv2D(1
, kernel_size=(3
,3
), activation='sigmoid'
, padding='same'
)])def
call
(
self
, x): encoded =self
.encoder(x) decoded =self
.decoder(encoded)return
decoded /<code>
讓我們用一個對象調用來創建模型:
<code>autoencoder
= NoiseReducer() /<code>
配置我們的模型
對於這個任務,我們將使用Adam優化器和模型的均方誤差。我們可以很容易地使用compile函數來配置我們的自編碼器,如下所示:
<code>autoencoder
.compile
(optimizer='adam'
, loss='mse'
) /<code>
最後,我們可以在10個epoch下通過輸入噪聲和乾淨的圖像運行我們的模型,這將需要大約1分鐘的訓練。我們還使用測試數據集進行驗證。以下代碼用於訓練模型:
<code>epochs
=10,
shuffle
=True,
validation_data
=(x_test_noisy, x_test))
/<code>
用我們訓練過的自編碼器降低圖像噪聲
我們現在就可以開始清理噪音圖像了。注意,我們可以訪問編碼器和解碼器網絡,因為我們在NoiseReducer對象下定義了它們。
所以,首先,我們將使用一個編碼器來編碼我們的噪聲測試數據集(x_test_noise)。然後,我們將編碼後的輸出輸入到解碼器,以獲得乾淨的圖像。以下代碼完成這些任務:
<code>encoded_imgs
=autoencoder.encoder(x_test_noisy).numpy()decoded_imgs
=autoencoder.decoder(encoded_imgs) /<code>
讓我們繪製前10個樣本,進行並排比較:
<code>n =10
plt.figure(figsize=(20
,7
)) plt.gray()for
i in range(n): bx = plt.subplot(3
, n, i +1
) plt.title("original + noise"
) plt.imshow(tf.squeeze(x_test_noisy[i])) ax.get_xaxis().set_visible(False
) ax.get_yaxis().set_visible(False
) cx = plt.subplot(3
, n, i + n +1
) plt.title("reconstructed"
) plt.imshow(tf.squeeze(decoded_imgs[i])) bx.get_xaxis().set_visible(False
) bx.get_yaxis().set_visible(False
) ax = plt.subplot(3
, n, i +2
*n +1
) plt.title("original"
) plt.imshow(tf.squeeze(x_test[i])) ax.get_xaxis().set_visible(False
) ax.get_yaxis().set_visible(False
) plt.show() /<code>
第一行用於噪聲圖像,第二行用於清理(重建)圖像,最後,第三行用於原始圖像。查看清理後的圖像與原始圖像的相似性:
結尾
你已經構建了一個自編碼器模型,它可以成功地清除非常嘈雜的圖像,這是它以前從未見過的(我們使用測試數據集)。
顯然有一些未恢復的變形,例如右起第二張圖片中缺少拖鞋的底部。然而,如果考慮到噪聲圖像的變形程度,我們可以說我們的模型在恢復失真圖像方面是相當成功的。
在我的腦子裡,你可以比如說——考慮擴展這個自編碼器,並將其嵌入照片增強應用程序中,這樣可以提高照片的清晰度和清晰度。