利用 TensorFlow 實現卷積自編碼器

coderpai發表於2018-12-12

作者:chen_h
微訊號 & QQ:862251340
微信公眾號:coderpai
我的部落格:請點選這裡

介紹和概念

自動編碼器(Auto-encoders)是神經網路的一種形式,它的輸入資料與輸出資料是相同的。他們通過將輸入資料壓縮到一個潛在表示空間裡面,然後再根據這個表示空間將資料進行重構得到最後的輸出資料。

自編碼器的一個非常受歡迎的使用場景是影像處理。其中使用到的小技巧是用卷積層來替換全連線層。這個轉變方法是將一個非常寬的,非常瘦的(比如 100*100 的畫素點,3 通道,RGB)影像轉換成一個非常窄的,非常厚的影像。這種方法非常有助於幫助我們從影像中提取出視覺特徵,從而得到更準確的潛在表示空間。最後我們的影像重構過程採用上取樣和卷積。

這個自編碼器就稱之為卷積自編碼器(Convolutional Autoencoder,CAE)

使用卷積自編碼器

卷積自編碼器可以用於影像的重構工作。例如,他們可以學習從圖片中去除噪聲,或者重構圖片缺失的部分。

為了實現上述提到的效果,我們一般不使用相同的輸入資料和輸出資料,取而代之的是,使用含有噪聲的圖片作為輸入資料,然後輸出資料是一個乾淨的圖片。卷積自編碼器就會通過學習,去去除圖片中的噪聲,或者去填補圖片中的空缺部分。

接下來,讓我們來看一下 CAE 是如何來填充圖中眼睛上的十字架。我們假設圖片的眼睛上面存在一個十字架黑影,我們需要刪除這個十字架噪聲。首先,我們需要來手動建立這個資料庫,當然,這個動作非常方便。

現在我們的卷積自編碼器就可以開始訓練了,我們可以用它去除我們從未見過的眼睛照片上面的十字線!

利用 TensorFlow 來實現這個卷積自編碼器

看我們利用 MNIST 資料集來看看這個網路是如何實現的,完整的程式碼可以在 Github 上面下載。

網路架構

卷積自編碼器的編碼部分將是一個典型的卷積過程。每一個卷積層之後都會加上一個池化層,主要是為了減少資料的維度。解碼器需要從一個非常窄的資料空間中重構出一個寬的影像。

一般情況下,你會看到我們後面是採用反摺積層來增加我們影像的寬度和高度。它們的工作原理和卷積層的工作原理幾乎完全一樣,但是作用方向相反。比如,你有一個 3

3 的卷積核,那麼在編碼器中我們是將該區域的影像編碼成一個元素點,但是在解碼器中,也就是反摺積中,我們是把一個元素點解碼成 3

3 個元素點。TensorFlow API 為我們提供了這個功能,參考 tf.nn.conv2d_transpose

自動編碼器只需要在噪聲的影像上進行訓練,就可以非常成功的進行圖片去燥。比如,我們可以在訓練圖片中新增入高斯噪聲來建立包含噪聲的影像,然後將這些畫素值裁剪在 0 到 1 之間。我們將噪聲影像作為輸入資料,最原始的感覺影像作為輸出資料,也就是我們的目標值。

模型定義

learning_rate = 0.001
inputs_ = tf.placeholder(tf.float32, (None, 28, 28, 1), name=`inputs`)
targets_ = tf.placeholder(tf.float32, (None, 28, 28, 1), name=`targets`)
### Encoder
conv1 = tf.layers.conv2d(inputs=inputs_, filters=32, kernel_size=(3,3), padding=`same`, activation=tf.nn.relu)
# Now 28x28x32
maxpool1 = tf.layers.max_pooling2d(conv1, pool_size=(2,2), strides=(2,2), padding=`same`)
# Now 14x14x32
conv2 = tf.layers.conv2d(inputs=maxpool1, filters=32, kernel_size=(3,3), padding=`same`, activation=tf.nn.relu)
# Now 14x14x32
maxpool2 = tf.layers.max_pooling2d(conv2, pool_size=(2,2), strides=(2,2), padding=`same`)
# Now 7x7x32
conv3 = tf.layers.conv2d(inputs=maxpool2, filters=16, kernel_size=(3,3), padding=`same`, activation=tf.nn.relu)
# Now 7x7x16
encoded = tf.layers.max_pooling2d(conv3, pool_size=(2,2), strides=(2,2), padding=`same`)
# Now 4x4x16
### Decoder
upsample1 = tf.image.resize_images(encoded, size=(7,7), method=tf.image.ResizeMethod.NEAREST_NEIGHBOR)
# Now 7x7x16
conv4 = tf.layers.conv2d(inputs=upsample1, filters=16, kernel_size=(3,3), padding=`same`, activation=tf.nn.relu)
# Now 7x7x16
upsample2 = tf.image.resize_images(conv4, size=(14,14), method=tf.image.ResizeMethod.NEAREST_NEIGHBOR)
# Now 14x14x16
conv5 = tf.layers.conv2d(inputs=upsample2, filters=32, kernel_size=(3,3), padding=`same`, activation=tf.nn.relu)
# Now 14x14x32
upsample3 = tf.image.resize_images(conv5, size=(28,28), method=tf.image.ResizeMethod.NEAREST_NEIGHBOR)
# Now 28x28x32
conv6 = tf.layers.conv2d(inputs=upsample3, filters=32, kernel_size=(3,3), padding=`same`, activation=tf.nn.relu)
# Now 28x28x32
logits = tf.layers.conv2d(inputs=conv6, filters=1, kernel_size=(3,3), padding=`same`, activation=None)
#Now 28x28x1
# Pass logits through sigmoid to get reconstructed image
decoded = tf.nn.sigmoid(logits)
# Pass logits through sigmoid and calculate the cross-entropy loss
loss = tf.nn.sigmoid_cross_entropy_with_logits(labels=targets_, logits=logits)
# Get cost and define the optimizer
cost = tf.reduce_mean(loss)
opt = tf.train.AdamOptimizer(learning_rate).minimize(cost)複製程式碼

訓練過程:

sess = tf.Session()
epochs = 100
batch_size = 200
# Set`s how much noise we`re adding to the MNIST images
noise_factor = 0.5
sess.run(tf.global_variables_initializer())
for e in range(epochs):
    for ii in range(mnist.train.num_examples//batch_size):
        batch = mnist.train.next_batch(batch_size)
        # Get images from the batch
        imgs = batch[0].reshape((-1, 28, 28, 1))
        
        # Add random noise to the input images
        noisy_imgs = imgs + noise_factor * np.random.randn(*imgs.shape)
        # Clip the images to be between 0 and 1
        noisy_imgs = np.clip(noisy_imgs, 0., 1.)
        
        # Noisy images as inputs, original images as targets
        batch_cost, _ = sess.run([cost, opt], feed_dict={inputs_: noisy_imgs,
                                                         targets_: imgs})
print("Epoch: {}/{}...".format(e+1, epochs),
              "Training loss: {:.4f}".format(batch_cost))複製程式碼

CoderPai 是一個專注於演算法實戰的平臺,從基礎的演算法到人工智慧演算法都有設計。如果你對演算法實戰感興趣,請快快關注我們吧。加入AI實戰微信群,AI實戰QQ群,ACM演算法微信群,ACM演算法QQ群。詳情請關注 “CoderPai” 微訊號(coderpai)。

相關文章