使用自动编解码器网络实现图片噪音去除

在前面章节中,我们一再看到,训练或使用神经网络进行预测时,我们需要把数据转换成张量。例如要把图片输入卷积网络,我们需要把图片转换成二维张量,如果要把句子输入LSTM网络,我们需要把句子中的单词转换成one-hot-encoding向量。
这种数据类型转换往往是由人设计的,我们本节介绍一种神经网络,它能够为输入数据自动找到合适的数据转换方法,它自动把数据转换成某种格式的张量,然后又能把相应张量还原回原有形态,这种网络就叫自动编解码器。

自动编解码器的功能很像加解密系统,对加密而言,当把明文进行加密后,形成的密文是一种随机字符串,再把密文解密后就可以得到明文,解密后的数据必须与加密前的完全一模一样。自动编解码器会把输入的数据,例如是图片转换成给定维度的张量,例如一个含有16个元素的一维向量,解码后它会把对应的含有16个元素的一维向量转换为原有图片,不过转换后的图片与原图片不一定完全一样,但是图片内容绝不会有重大改变。
自动编解码器分为两部分,一部分叫encoder,它负责把数据转换成固定格式,从数学上看,encoder相当于一个函数,被编码的数据相当于输入参数,编码后的张量相当于函数输出: ,其中f对应encoder,x对应要编码的数据,例如图片,z是编码后的结果。
另一部分叫decoder,也就是把编码器编码的结果还原为原有数据,用数学来表达就是: ,函数g相当于解码器,它的输入是编码器输出结果, 是解码器还原结果,它与输入编码器的数据可能有差异,但主要内容会保持不变,如图10-1:

《使用自动编解码器网络实现图片噪音去除》 10-1.png

图10-1 编解码器运行示意图

如上图,手写数字图片7经过编码器后,转换成给定维度的张量,例如含有16个元素的一维张量,然后经过解码器处理后还原成一张手写数字图片7,还原的图片与输入的图片图像显示上有些差异,但是他们都能表达手写数字7这一含义。
代码是对原理最好的解释,我们看看实现过程:

from keras.layers import Dense, Input
from keras.layers import Conv2D, Flatten
from keras.layers import Reshape, Conv2DTranspose
from keras.models import Model
from keras.datasets import mnist
from keras.utils import plot_model
from keras import backend as K

import numpy as np
import matplotlib.pyplot as plt

#加载手写数字图片数据
(x_train, _), (x_test, _) = mnist.load_data()
image_size = x_train.shape[1]
#把图片大小统一转换成28*28,并把像素点值都转换为[0,1]之间
x_train = np.reshape(x_train, [-1, image_size, image_size, 1])
x_test = np.reshape(x_test, [-1, image_size, image_size, 1])
x_train = x_train.astype('float32') / 255
x_test = x_test.astype('float32') / 255
接下来我们构建自动编解码器网络:
#构建解码器
latent_inputs = Input(shape=(latent_dim, ), name='decoder_input')
x = Dense(shape[1] * shape[2] * shape[3])(latent_inputs)
x = Reshape((shape[1], shape[2], shape[3]))(x)

'''
使用Conv2DTranspose做卷积操作的逆操作。相应的Conv2D做怎样的计算操作,该网络层就逆着来
'''
for filters in layer_filters[::-1]:
  x = Conv2DTranspose(filters = filters, kernel_size = kernel_size, 
                     activation='relu', strides = 2, padding='same')(x)

#还原输入
outputs = Conv2DTranspose(filters = 1, kernel_size = kernel_size, 
                         activation='sigmoid', padding='same', 
                          name='decoder_output')(x)

decoder = Model(latent_inputs, outputs, name='decoder')
decoder.summary()

我们把编码器和解码器前后相连,于是数据从编码器输入,编码器将数据进行计算编号后所得的输出直接传给解码器,解码器进行相对于编码器的逆运算最后得到类似于输入编码器的数据,相应代码如下:


'''
将编码器和解码器前后相连,数据从编码器输入,编码器运算后把结果直接传递给解码器,
解码器进行编码器的逆运算,最后输出与数据输入时相似的结果
'''
autoencoder = Model(inputs, decoder(encoder(inputs)),
                   name='autoencoder')

autoencoder.compile(loss='mse', optimizer='adam')
'''

在训练网络时,输入数据是x_train,对应标签也是x_train,这意味着我们希望网络将输出尽可能的调整成与输入一致

'''
autoencoder.fit(x_train, x_train, validation_data=(x_test, x_test), epochs = 1, 
                batch_size = batch_size)

网络训练好后,我们把图片输入网络,编码器把图片转换为含有16个元素的一维向量,然后向量输入解码器,解码器把向量还原为一张二维图片,相应代码如下:

'''
把手写数字图片输入编码器然后再通过解码器,检验输出后的图像与输出时的图像是否相似
'''
x_decoded = autoencoder.predict(x_test)
imgs = np.concatenate([x_test[:8], x_decoded[:8]])
imgs = imgs.reshape((4, 4, image_size, image_size))
imgs = np.vstack([np.hstack(i) for i in imgs])
plt.figure()
plt.axis('off')
plt.title('Input image: first and second rows, Decoded: third and forth rows')
plt.imshow(imgs, interpolation='none', cmap = 'gray')
plt.savefig('input_and_decoded.png')
plt.show()

上面代码运行后结果如图10-2:

《使用自动编解码器网络实现图片噪音去除》 10-2.png

图10-2 代码运行结果
上面显示图片中,前两行是输入编解码器的手写数字图片,后两行是经过编码然后还原后的图片,如果仔细看我们可以发现两者非常相像,但并不完全一样,我们看第一行最后一个数字0和解码后第三行最后一个数字0,两者有比较明显差异,但都会被解读成数字0.
在代码中需要注意的是,构建解码器时我们使用了一个类叫Conv2DTranspose,它与Conv2D对应,是后者的反操作,如果把Conv2D看做对输入数据的压缩或加密,那么Conv2DTranspose是对数据的解压或解密。
另外还需要注意的是,因为我们网络层较少,因此训练时只需要一次循环就好,如果网络层多的话,我们需要增加循环次数才能使得网络有良好的输出效果。

2.使用编解码器去除图片噪音

在八零年代,改革开放不久后,一种‘稀有’的家电悄悄潜入很多家庭,那就是录像机。你把一盘录像带推入机器,在电视上就可以把内容播放出来,有一些录像带它的磁带遭到破坏的话,播放时画面会飘散一系列‘雪花’,我们将那称之为画面‘噪音’。
当图片含有‘噪音’时,图片表现为含有很多花点,如图10-3所示:

《使用自动编解码器网络实现图片噪音去除》 10-3.jpg

图10-3 含有噪音的图片

在信号处理这一学科分支中,有很大一部分就在于研究如何去噪,幸运的是通过编解码网络也能够实现图片噪音去除的效果。本节我们先给手写数字图片增加噪音,使得图片变得很难识别,然后我们再使用编解码网络去除图片噪音,让图片回复原状。
图片噪音本质上是在像素点上添加一些随机值,这里我们使用高斯分布产生随机值,其数学公式如下:

《使用自动编解码器网络实现图片噪音去除》 屏幕快照 2018-11-20 上午11.42.04.png

它有两个决定性参数,分别是μ 和 σ,只要使得这两个参数取不同的值,我们就可以得到相应分布的随机数,其中μ 称之为均值, σ称之为方差,我们看看如何使用代码实现图片加噪,然后构建编解码网络去噪音:

#使用高斯分布产生图片噪音
np.random.seed(1337)
#使用高斯分布函数生成随机数,均值0.5,方差0.5
noise = np.random.normal(loc=0.5, scale=0.5, size=x_train.shape)
x_train_noisy = x_train + noise

noise = np.random.normal(loc=0.5, scale=0.5, size=x_test.shape)
x_test_noisy = x_test + noise
#把像素点取值范围转换到[0,1]间
x_train_noisy = np.clip(x_train_noisy, 0., 1.)
x_test_noisy = np.clip(x_test_noisy, 0., 1.)
上面的代码先使用高斯函数产生随机数,然后加到像素点上从而形成图片噪音。接着我们看如何构建编解码器实现图片去噪:
#构造编解码网络,以下代码与上一小节代码大部分相同
input_shape = (image_size, image_size, 1)
batch_size = 32
kernel_size = 3
latent_dim = 16
layer_filters = [32, 64]
#构造编码器
inputs = Input(shape=input_shape, name='encoder_input')
x = inputs
for filters in layer_filters:
  x = Conv2D(filters = filters, kernel_size = kernel_size, strides = 2,
            activation='relu', padding='same')(x)

shape = K.int_shape(x)
x = Flatten()(x)
latent = Dense(latent_dim, name='latent_vector')(x)
encoder = Model(inputs, latent, name='encoder')

#构造解码器
latent_inputs = Input(shape=(latent_dim, ), name='decoder_input')
x = Dense(shape[1] * shape[2] * shape[3])(latent_inputs)
x = Reshape((shape[1], shape[2], shape[3]))(x)

for filters in layer_filters[::-1]:
  x = Conv2DTranspose(filters = filters, kernel_size = kernel_size, strides = 2,
                     activation='relu', padding='same')(x)
  
outputs = Conv2DTranspose(filters=1, kernel_size=kernel_size, padding='same',
                         activation='sigmoid', name='decoder_output')(x)
decoder = Model(latent_inputs, outputs, name='decoder')


#将编码器和解码器前后相连
autoencoder = Model(inputs, decoder(encoder(inputs)), name='autoencoder')
autoencoder.compile(loss='mse', optimizer='adam')
#输入数据是有噪音图片,对应结果是无噪音图片
autoencoder.fit(x_train_noisy, x_train, validation_data=(x_test_noisy, x_test),
               epochs = 10, batch_size = batch_size)

代码中值得注意的是,训练网络时,训练数据时含有噪音的图片,对应结果是没有噪音的图片,也就是我们希望网络能通过学习自动掌握去噪功能,训练完成后,我们把测试图片输入网络,看看噪音去除效果:

x_decoded = autoencoder.predict(x_test_noisy)

rows, cols = 3, 9
num = rows * cols
imgs = np.concatenate([x_test[:num], x_test_noisy[:num],
                      x_decoded[:num]])
imgs = imgs.reshape((rows * 3, cols, image_size, image_size))
imgs = np.vstack(np.split(imgs, rows, axis=1))
imgs = imgs.reshape((rows * 3, -1, image_size, image_size))
imgs = np.vstack([np.hstack(i) for i in imgs])
imgs = (imgs * 255).astype(np.uint8)
plt.figure()
plt.axis('off')
plt.title('Original images: top rows'
          'Corrupted images: middle rows'
         'Denoised Input: third rows')
plt.imshow(imgs, interpolation='none', cmap='gray')
plt.show()

上面代码运行后如图10-4所示:

《使用自动编解码器网络实现图片噪音去除》 10-4.png

图10-4 网络去噪效果
从上图看,第一行是原图,第二行是加了噪音的图片,第三行是网络去除噪音后的图片。从上图看,网络去噪的效果还是比较完美的。

更详细的讲解和代码调试演示过程,请点击链接

更多技术信息,包括操作系统,编译器,面试算法,机器学习,人工智能,请关照我的公众号:

《使用自动编解码器网络实现图片噪音去除》 这里写图片描述

    原文作者:望月从良
    原文地址: https://www.jianshu.com/p/d9a04b15e7e7
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞