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

在前面章节中,我们一再看到,训练或使用神经网络进行预测时,我们需要把数据转换成张量。例如要把图片输入卷积网络,我们需要把图片转换成二维张量,如果要把句子输入LSTM网络,我们需要把句子中的单词转换成one-hot-encoding向量。

这种数据类型转换往往是由人设计的,我们本节介绍一种神经网络,它能够为输入数据自动找到合适的数据转换方法,它自动把数据转换成某种格式的张量,然后又能把相应张量还原回原有形态,这种网络就叫自动编解码器。

自动编解码器的功能很像加解密系统,对加密而言,当把明文进行加密后,形成的密文是一种随机字符串,再把密文解密后就可以得到明文,解密后的数据必须与加密前的完全一模一样。自动编解码器会把输入的数据,例如是图片转换成给定维度的张量,例如一个含有16个元素的一维向量,解码后它会把对应的含有16个元素的一维向量转换为原有图片,不过转换后的图片与原图片不一定完全一样,但是图片内容绝不会有重大改变。

自动编解码器分为两部分,一部分叫encoder,它负责把数据转换成固定格式,从数学上看,encoder相当于一个函数,被编码的数据相当于输入参数,编码后的张量相当于函数输出: ,其中f对应encoder,x对应要编码的数据,例如图片,z是编码后的结果。

另一部分叫decoder,也就是把编码器编码的结果还原为原有数据,用数学来表达就是: ,函数g相当于解码器,它的输入是编码器输出结果, 是解码器还原结果,它与输入编码器的数据可能有差异,但主要内容会保持不变,如图10-1:

图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:

上面显示图片中,前两行是输入编解码器的手写数字图片,后两行是经过编码然后还原后的图片,如果仔细看我们可以发现两者非常相像,但并不完全一样,我们看第一行最后一个数字0和解码后第三行最后一个数字0,两者有比较明显差异,但都会被解读成数字0.

在代码中需要注意的是,构建解码器时我们使用了一个类叫Conv2DTranspose,它与Conv2D对应,是后者的反操作,如果把Conv2D看做对输入数据的压缩或加密,那么Conv2DTranspose是对数据的解压或解密。 另外还需要注意的是,因为我们网络层较少,因此训练时只需要一次循环就好,如果网络层多的话,我们需要增加循环次数才能使得网络有良好的输出效果。

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

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

图10-3 含有噪音的图片

在信号处理这一学科分支中,有很大一部分就在于研究如何去噪,幸运的是通过编解码网络也能够实现图片噪音去除的效果。本节我们先给手写数字图片增加噪音,使得图片变得很难识别,然后我们再使用编解码网络去除图片噪音,让图片回复原状。

图片噪音本质上是在像素点上添加一些随机值,这里我们使用高斯分布产生随机值,其数学公式如下:

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

#使用高斯分布产生图片噪音
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 网络去噪效果 从上图看,第一行是原图,第二行是加了噪音的图片,第三行是网络去除噪音后的图片。从上图看,网络去噪的效果还是比较完美的。

原文发布于微信公众号 - Coding迪斯尼(gh_c9f933e7765d)

原文发表时间:2018-11-20

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏数据派THU

TensorFlow教程:快速入门深度学习五步法(附Keras实例)

作为一个程序员,我们可以像学习编程一样学习深度学习模型开发。我们以 Keras 为例来说明。我们可以用 5 步 + 4 种基本元素 + 9 种基本层结构,这 5...

2883
来自专栏数据科学与人工智能

【数据分析】异常值检测

什么是异常(outlier)?Hawkins(1980)给出了异常的本质性的定义:异常是在数据集中与众不同的数据,使人怀疑这些数据并非随机偏差,而是产生于完全不...

4466
来自专栏深度学习计算机视觉

数据挖掘之认识数据学习笔记相关术语熟悉

相关术语熟悉 首先认识数据的属性 属性是一个数据字段,表示数据对象的一个特征 标称属性 标称属性的值是一些符号或事物的名称,这一些值可以看做是枚举的 比如,职...

2806
来自专栏数据派THU

教你用TensorFlow和自编码器模型生成手写数字(附代码)

来源:机器之心 本文长度为1876字,建议阅读4分钟 本文介绍了如何使用 TensorFlow 实现变分自编码器(VAE)模型,并通过简单的手写数字生成案例一步...

2728
来自专栏C语言及其他语言

【优秀题解】1175:台球碰撞

题号1174,原题见下图: ? 解题思路: 解题思路: 把台球看做质点(台球坐标不变,球桌坐标各个边界向里收缩R,得到新的球桌); 假设没边界,求出小球沿着直...

2826
来自专栏机器之心

教程 | 如何使用TensorFlow和自编码器模型生成手写数字

40711
来自专栏小樱的经验随笔

DFS中的奇偶剪枝学习笔记

奇偶剪枝学习笔记 描述 现假设起点为(sx,sy),终点为(ex,ey),给定t步恰好走到终点, s | ...

2654
来自专栏xingoo, 一个梦想做发明家的程序员

Spark MLlib 之 大规模数据集的相似度计算原理探索

在spark中RowMatrix提供了一种并行计算相似度的思路,下面就来看看其中的奥妙吧!

3640
来自专栏IT派

教程 | 如何使用TensorFlow和自编码器模型生成手写数字

本文详细介绍了如何使用 TensorFlow 实现变分自编码器(VAE)模型,并通过简单的手写数字生成案例一步步引导读者实现这一强大的生成模型。 全部 VAE ...

36611
来自专栏Petrichor的专栏

leetcode: 84. Largest Rectangle in Histogram

952

扫码关注云+社区