用於圖像降噪的卷積自編碼器

用於圖像降噪的卷積自編碼器

這篇文章的目的是介紹關於利用自動編碼器實現圖像降噪的內容。

在神經網絡世界中,對圖像數據進行建模需要特殊的方法。其中最著名的是卷積神經網絡(CNN或ConvNet)或稱為卷積自編碼器。並非所有的讀者都瞭解圖像數據,那麼我先簡要介紹圖像數據(如果你對這方面已經很清楚了,可以跳過)。然後,我會介紹標準神經網絡。這個標準神經網絡用於圖像數據,比較簡單。這解釋了處理圖像數據時為什麼首選的是卷積自編碼器。最重要的是,我將演示卷積自編碼器如何減少圖像噪聲。這篇文章將用上Keras模塊和MNIST數據。Keras用Python編寫,並且能夠在TensorFlow上運行,是高級的神經網絡API。

瞭解圖像數據

如圖(A)所示,圖像由“像素”組成。在黑白圖像中,每個像素由0到255之間的數字表示。如今大多數圖像使用24位彩色或更高的顏色。一幅RGB彩色圖像表示一個像素的顏色由紅色、綠色和藍色組成,這三種顏色各自的像素值從0到255。RGB色彩生成器(如下所示)表明,RGB色彩系統利用紅綠藍,組合成各種顏色。因此,一個像素由含三個值的RGB(102、255、102)構成,其色號為#66ff66。

用於圖像降噪的卷積自編碼器

寬800像素,高600像素的圖像具有800 x 600 = 480,000像素,即0.48兆像素(“兆像素”等於100萬像素)。分辨率為1024×768的圖像是一個由1,024列和768行構成的網格,共有1,024×768 = 0.78兆像素。


MNIST

MNIST數據庫是一個大型的手寫數字數據庫,通常用於訓練各種圖像處理系統。Keras的訓練數據集具備60,000條記錄,而測試數據集則包含了10,000條記錄。每條記錄共有28 x 28個像素。

<code>
from keras.layers import Input, Dense
from keras.models import Model
from keras.datasets import mnist
import numpy as np
(x_train, _), (x_test, _) = mnist.load_data()/<code>

它們看起來怎麼樣?我們用繪圖庫及其圖像功能imshow()展示前十條記錄。

<code>
import matplotlib.pyplot as plt

n = 10 # 顯示的記錄數
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)

plt.show()/<code>
用於圖像降噪的卷積自編碼器

圖像數據的堆疊,用於訓練

如果要讓神經網絡框架適用於模型訓練,我們可以在一列中堆疊所有28 x 28 = 784個值。第一條記錄的堆疊列如下所示(使用x_train[1].reshape(1,784)):

用於圖像降噪的卷積自編碼器

然後,我們可以使用標準的神經網絡訓練模型,如圖(B)所示。數值為784的每個值都是輸入層中的一個節點。且慢!堆疊數據會丟失很多信息嗎?答案是肯定的。圖像中的空間關係被忽略了。這使得大量的信息丟失。那麼,我們接著看卷積自編碼器如何保留空間信息。

用於圖像降噪的卷積自編碼器

為什麼圖像數據首選卷積自編碼器?

可以看到,數據切片和數據堆疊會導致信息大量丟失。卷積自編碼器放棄堆疊數據,使圖像數據輸入時保持其空間信息不變,並在卷積層中以溫和的方式提取信息。圖(D)演示了將平面2D圖像先提取到一個厚的正方體(Conv1),再提取到一個長方體(Conv2)和另一個長度更長的長方體(Conv3)。此過程旨在保留數據中的空間關係。這是自動編碼器的編碼過程。中間部分是一個完全連接的自動編碼器,其隱藏層僅由10個神經元組成。然後就是解碼過程。三個立方體將會展平,最後變成2D平面圖像。圖(D)的編碼器和解碼器是對稱的。實際上,編碼器和解碼器不要求對稱。

用於圖像降噪的卷積自編碼器

卷積自編碼器如何工作?

上面的數據析取似乎很神奇。數據析取究竟是如何進行的?這包括以下三層:卷積層,線性整流層和池化層。

用於圖像降噪的卷積自編碼器

1. 卷積層

卷積步驟會生成很多小塊,稱為特徵圖或特徵,如圖(E)的綠色、紅色或深藍色的正方形。這些正方形保留了輸入圖像中像素之間的關係。如圖(F)所示,每個特徵掃描原始圖像。這一產生分值的過程稱為卷積。

用於圖像降噪的卷積自編碼器

掃描完原始圖像後,每個特徵都會生成高分值和低分值的濾波圖像,如圖(G)所示。如果匹配完美,那塊正方形的得分就高。如果匹配度低或不匹配,則得分低或為零。例如,原始圖像有四個區域與紅色方塊完全匹配,那麼這四個區域的得分都很高。

用於圖像降噪的卷積自編碼器

過濾器越多,模型可以提取的特徵就越多。但是,特徵越多,訓練時間也就越長。因此,最好還是選擇最少的過濾器提取特徵。

1.1 填充

特徵如何確定匹配項?一種超參數是填充,有兩種選擇:(i)用零填充原始圖像以符合該特徵,或(ii)刪除原始圖像中不符的部分並保留有效部分。

1.2步長

卷積層的另一個參數:步長。步長是輸入矩陣上移動的像素個數。當步長為1時,過濾器一次移動1個像素。在Keras代碼中,我們將其視為超參數。

2.線性整流步驟

線性整流單位(ReLU)的步驟與典型的神經網絡相同。它將所有的負值校正為零,確保數學運算正確。

3.最大池化層

池化會縮小圖像尺寸。在圖(H)中,一個2 x 2的窗口(稱為池的大小)掃描每個濾波圖像,並將該2 x 2窗口的最大值劃分給新圖像中大小為1 x 1的正方形。如圖(H)所示,第一個2 x 2窗口的最大值分數高(用紅色表示),因此高分劃分給1 x 1正方形。

用於圖像降噪的卷積自編碼器

除了採用最大值之外,其他不常用的池化方法還包括“平均池化”(取平均值)或“總和池化”(總和)。

用於圖像降噪的卷積自編碼器

池化後,會生成新的更小的濾波圖像。現在我們拆分這個濾波圖像,然後堆疊為一列,如圖(J)所示。

Keras模型

以上三層是卷積神經網絡的構建塊。Keras具有以下兩個功能:

• Conv2D(filters, kernelsize, activation = 'reLu', strides=1):核尺寸(kernelsize)是2D卷積窗口的高度和寬度。圖(E)使用的是2×2正方形,所以例子中核尺寸將為(2,2)。步長是輸入矩陣上移動的像素個數。我們一次將濾鏡移動了1個像素,所以步長為1。

• MaxPooling2D(pool_size=(2,2)):在圖(H)中,我們使用2×2窗口作為池的大小。因此,我們將在以下代碼中使用(2,2)。

你可以在卷積自編碼器中構建許多卷積層。在圖(E)中,在編碼部分有三層,分別標記為Conv1,Conv2和Conv3。因此,我們要進行相應的構建。

• 下面的代碼input_img = Input(shape=(28,28,1)表明輸入的2D圖像為28 x 28。

• 然後,它構建了Conv1,Conv2和Conv3。

• 請注意,Conv1在Conv2內部,而Conv2在Conv3內部。

• 要是過濾器無法適應輸入圖像,填充將指定下一步該做什麼。padding='valid'表示過濾器不符合,圖像的一部分將被丟棄;padding='same'用零填充圖片以適應圖片。

<code>
from keras.layers import Input, Dense, Conv2D, MaxPooling2D, UpSampling2D
from keras.models import Model

# 編碼過程
input_img = Input(shape=(28, 28, 1))

############
# 編碼 #

############

# Conv1 #
x = Conv2D(filters = 16, kernel_size = (3, 3), activation='relu', padding='same')(input_img)
x = MaxPooling2D(pool_size = (2, 2), padding='same')(x)

# Conv2 #
x = Conv2D(filters = 8, kernel_size = (3, 3), activation='relu', padding='same')(x)
x = MaxPooling2D(pool_size = (2, 2), padding='same')(x)

# Conv 3 #
x = Conv2D(filters = 8, (3, 3), activation='relu', padding='same')(x)
encoded = MaxPooling2D(pool_size = (2, 2), padding='same')(x)

# 注意:
# padding 是一個超參數,值'valid' or 'same'.
# "valid" 意味不需要填充
# "same" 填充輸入,使輸出具有與原始輸入相同的長度。
/<code>

然後,解碼過程繼續。因此,下面解碼部分已全部完成編碼和解碼過程。

<code>
############
# 解碼 #
############

# DeConv1
x = Conv2D(8, (3, 3), activation='relu', padding='same')(encoded)
x = UpSampling2D((2, 2))(x)

# DeConv2
x = Conv2D(8, (3, 3), activation='relu', padding='same')(x)
x = UpSampling2D((2, 2))(x)

# Deconv3
x = Conv2D(16, (3, 3), activation='relu')(x)
x = UpSampling2D((2, 2))(x)
decoded = Conv2D(1, (3, 3), activation='sigmoid', padding='same')(x)
/<code>

該Keras API需要模型和優化方法的聲明:

•• Model (inputs= inputimg,outputs= decoded):在解碼給定輸入數據inputimg的情況下,模型包括計算輸出所需的所有層。compile(optimizer='adadelta',loss='binary_crossentropy'):優化程序會像漸變梯度一樣執行優化操作。最常見的是隨機梯度下降(SGD),自適應梯度(Adagrad)和Adadelta(Adadelta是Adagrad的擴展)。有關詳細信息,請參見Keras優化器文檔。損失函數可以查找Keras損失文檔。

<code>
# 聲明模型
autoencoder = Model(input_img, decoded)
autoencoder.compile(optimizer='adadelta', loss='binary_crossentropy')
/<code>

下面,我使用xtrain作為輸入和輸出來訓練模型。batchsize是樣本量和epochs是迭代的次數。我指定shuffle=True打亂訓練數據。

<code>
# 訓練模型
autoencoder.fit(x_train, x_train,
epochs=100,
batch_size=128,
shuffle=True,
validation_data=(x_test, x_test)
)
/<code>

我們可以打印出前十張原始圖像和相同十張圖像的預測。

<code>
decoded_imgs = autoencoder.predict(x_test)

n = 10

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()/<code>
用於圖像降噪的卷積自編碼器

如何構建圖像降噪卷積自編碼器?

圖像降噪的想法是訓練一個模型,輸入噪聲數據,並輸出它們各自清晰的數據。這是與上述模型的唯一區別。首先讓我們向數據添加噪音。

<code>
noise_factor = 0.4
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.)
/<code>

前十張噪聲圖像如下所示:

<code>
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()
/<code>
用於圖像降噪的卷積自編碼器

然後,我們訓練模型時將輸入噪聲數據,輸出乾淨的數據。

<code>
autoencoder.fit(x_train_noisy, x_train,
epochs=100,
batch_size=128,
shuffle=True,
validation_data=(x_test_noisy, x_test)
)
/<code>

最後,我們打印出前十個噪點圖像以及相應的降噪圖像。

<code>
decoded_imgs = autoencoder.predict(x_test)

n = 10

plt.figure(figsize=(20, 4))
for i in range(n):
# 顯示原始圖像
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)

# 顯示重構後的圖像
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()
/<code>
用於圖像降噪的卷積自編碼器

是否可以使用任何經過訓練的CNN代碼嗎?

可以的。如果你有興趣學習代碼,Keras提供了幾個經過預訓練的CNN,包括Xception,VGG16,VGG19,ResNet50,InceptionV3,InceptionResNetV2,MobileNet,DenseNet,NASNet和MobileNetV2。值得一提的是,你可以出於研究目的付錢或下載此大型圖像數據庫ImageNet。


分享到:


相關文章: