前言html
这周工做太忙,原本想更把 Attention tranlsation 写出来,但一直抽不出时间,等后面有时间再来写。咱们这周来看一个简单的自编码器实战代码,关于自编码器的理论介绍我就不详细介绍了,网上一搜一大把。最简单的自编码器就是经过一个 encoder 和 decoder 来对输入进行复现,例如咱们将一个图片输入到一个网络中,自编码器的 encoder 对图片进行压缩,获得压缩后的信息,进而 decoder 再将这个信息进行解码从而复现原图。git
做图工具:OmniGrafflegithub
自编码器其实是经过去最小化 target 和 input 的差异来进行优化,即让输出层尽量地去复现原来的信息。因为自编码器的基础形式比较简单,对于它的一些变体也很是之多,包括 DAE,SDAE,VAE 等等,若是感兴趣的小伙伴能够去网上搜一下其余相关信息。网络
本篇文章将实现两个 Demo,第一部分即实现一个简单的 input-hidden-output 结的自编码器,第二部分将在第一部分的基础上实现卷积自编码器来对图片进行降噪。dom
工具说明ide
TensorFlow1.0函数
jupyter notebook工具
数据:MNIST 手写数据集学习
完整代码地址:NELSONZHAO/zhihu测试
第一部分
首先咱们将实现一个如上图结构的最简单的 AutoEncoder。
加载数据
在这里,咱们使用 MNIST 手写数据集来进行实验。首先咱们须要导入数据,TensorFlow 已经封装了这个实验数据集,因此咱们使用起来也很是简单。
若是想让数据显示灰度图像,使用代码 plt.imshow(img.reshape((28,28)), cmap='Greys_r') 便可。
经过 input_data 就能够加载咱们的数据集。若是小伙伴本地已经有了 MNIST 数据集(四个压缩包),能够把这四个压缩包放在目录 MNIST_data 下,这样 TensorFlow 就会直接 Extract 数据,而不用再从新下载。咱们能够经过 imshow 来随便查看一个图像。因为咱们加载进来的数据已经被处理成一个 784 维度的向量,所以从新显示的时候须要 reshape 一下。
构建模型
咱们把数据加载进来之后就能够进行最简单的建模。在这以前,咱们首先来获取一下 input 数据的大小,咱们加载进来的图片是 28x28 的像素块,TensorFlow 已经帮咱们处理成了 784 维度的向量。同时咱们还须要指定一下 hidden layer 的大小。
在这里我指定了 64,hidden_units 越小,意味着信息损失的越多,小伙伴们也能够尝试一下其余的大小来看看结果。
AutoEncoder 中包含了 input,hidden 和 output 三层。
在隐层,咱们采用了 ReLU 做为激活函数。
至此,一个简单的 AutoEncoder 就构造完成,接下来咱们能够启动 TensorFlow 的 graph 来进行训练。
训练结果可视化
通过上面的步骤,咱们构造了一个简单的 AutoEncoder,下面咱们将对结果进行可视化看一下它的表现。
这里,我挑选了测试数据集中的 5 个样原本进行可视化,一样的,若是想观察灰度图像,指定 cmap 参数为'Greys_r'便可。上面一行为 test 数据集中原始图片,第二行是通过 AutoEncoder 复现之后的图片,能够很明显的看到像素信息的损失。
一样,咱们也能够把隐层压缩的数据拿出来可视化,结果以下:
这五张图分别对应了 test 中五张图片的隐层压缩后的图像。
经过上面一个简单的例子,咱们了解了 AutoEncoder 的基本工做原理,下面咱们将更进一步改进咱们的模型,将隐层转换为卷积层来进行图像降噪。
上面过程当中省略了一部分代码,完整代码请去个人 GitHub 上查看。
第二部分
在了解了上面 AutoEncoder 工做原理的基础上,咱们在这一部分将对 AutoEncoder 加入多个卷积层来进行图片的降噪处理。
一样的咱们仍是使用 MNIST 数据集来进行实验,关于数据导入的步骤再也不赘述,请下载代码查看。在开始以前,咱们先经过一张图片来看一下咱们的整个模型结构:
做图工具:OmniGraffle
咱们经过向模型输入一个带有噪声的图片,在输出端给模型没有噪声的图片,让模型经过卷积自编码器去学习降噪的过程。
输入层
这里的输入层和咱们上一部分的输入层已经不一样,由于这里咱们要使用卷积操做,所以,输入层应该是一个 height x width x depth 的一个图像,通常的图像 depth 是 RGB 格式三层,这里咱们的 MNIST 数据集的 depth 只有 1。
Encoder 卷积层
Encoder 卷积层设置了三层卷积加池化层,对图像进行处理。
第一层卷积中,咱们使用了 64 个大小为 3 x 3 的滤波器(filter),strides 默认为 1,padding 设置为 same 后咱们的 height 和 width 不会被改变,所以通过第一层卷积之后,咱们获得的数据从最初的 28 x 28 x 1 变为 28 x 28 x 64。
紧接着对卷积结果进行最大池化操做(max pooling),这里我设置了 size 和 stride 都是 2 x 2,池化操做不改变卷积结果的深度,所以池化之后的大小为 14 x 14 x 64。
对于其余卷积层再也不赘述。全部卷积层的激活函数都是用了 ReLU。
通过三层的卷积和池化操做之后,咱们获得的 conv3 实际上就至关于上一部分中 AutoEncoder 的隐层,这一层的数据已经被压缩为 4 x 4 x 32 的大小。
至此,咱们就完成了 Encoder 端的卷积操做,数据维度从开始的 28 x 28 x 1 变成了 4 x 4 x 32。
Decoder 卷积层
接下来咱们就要开始进行 Decoder 端的卷积。在这以前,可能有小伙伴要问了,既然 Encoder 中都已经把图片卷成了 4 x 4 x 32,咱们若是继续在 Decoder 进行卷积的话,那岂不是获得的数据 size 愈来愈小?因此,在 Decoder 端,咱们并非单纯进行卷积操做,而是使用了 Upsample(中文翻译能够为上采样)+ 卷积的组合。
咱们知道卷积操做是经过一个滤波器对图片中的每一个 patch 进行扫描,进而对 patch 中的像素块加权求和后再进行非线性处理。举个例子,原图中咱们的 patch 的大小假如是 3 x 3(说的通俗点就是一张图片中咱们取其中一个 3 x 3 大小的像素块出来),接着咱们使用 3 x 3 的滤波器对这个 patch 进行处理,那么这个 patch 通过卷积之后就变成了 1 个像素块。在 Deconvolution 中(或者叫 transposed convolution)这一过程是反过来的,1 个像素块会被扩展成 3 x 3 的像素块。
可是 Deconvolution 有一些弊端,它会致使图片中出现 checkerboard patterns,这是由于在 Deconvolution 的过程当中,滤波器中会出现不少重叠。为了解决这个问题,有人提出了使用 Upsample 加卷积层来进行解决。
关于 Upsample 有两种常见的方式,一种是 nearest neighbor interpolation,另外一种是 bilinear interpolation。
本文也会使用 Upsample 加卷积的方式来进行 Decoder 端的处理。
在 TensorFlow 中也封装了对 Upsample 的操做,咱们使用 resize_nearest_neighbor 对 Encoder 卷积的结果 resize,进而再进行卷积处理。通过三次 Upsample 的操做,咱们获得了 28 x 28 x 64 的数据大小。最后,咱们要将这个结果再进行一次卷积,处理成咱们原始图像的大小。
最后一步定义 loss 和 optimizer。
loss 函数咱们使用了交叉熵进行计算,优化函数学习率为 0.001。
构造噪声数据
经过上面的步骤咱们就构造完了整个卷积自编码器模型。因为咱们想经过这个模型对图片进行降噪,所以在训练以前咱们还须要在原始数据的基础上构造一下咱们的噪声数据。
咱们经过上面一个简单的例子来看一下如何加入噪声,咱们获取一张图片的数据 img(大小为 784),在它的基础上加入噪声因子乘以随机数的结果,就会改变图片上的像素。接着,因为 MNIST 数据的每一个像素数据都被处理成了 0-1 之间的数,因此咱们经过 numpy.clip 对加入噪声的图片进行 clip 操做,保证每一个像素数据仍是在 0-1 之间。
np.random.randn(*img.shape) 的操做等于 np.random.randn(img.shape[0], img.shape[1])
咱们下来来看一下加入噪声先后的图像对比。
训练模型
介绍完模型构建和噪声处理,咱们接下来就能够训练咱们的模型了。
在训练模型时,咱们的输入已经变成了加入噪声后的数据,而输出是咱们的原始没有噪声的数据,主要要对原始数据进行 reshape 操做,变成与 inputs_相同的格式。因为卷积操做的深度,因此模型训练时候有些慢,建议使用 GPU 跑。
记得最后关闭 sess。
结果可视化
通过上面漫长的训练,咱们的模型终于训练好了,接下来咱们就经过可视化来看一看模型的效果如何。
能够看到经过卷积自编码器,咱们的降噪效果仍是很是好的,最终生成的图片看起来很是顺滑,噪声也几乎看不到了。
有些小伙伴可能就会想,咱们也能够用基础版的 input-hidden-output 结构的 AutoEncoder 来实现降噪。所以我也实现了一版用最简单的 input-hidden-output 结构进行降噪训练的模型(代码在个人 GitHub)。咱们来看看它的结果:
能够看出,跟卷积自编码器相比,它的降噪效果更差一些,在重塑的图像中还能够看到一些噪声的影子。
结尾
至此,咱们完成了基础版本的 AutoEncoder 模型,还在此基础上加入卷积层来进行图片降噪。相信小伙伴对 AntoEncoder 也有了一个初步的认识。
完整的代码已经放在个人 GitHub(NELSONZHAO/zhihu) 上,其中包含了六个文件:
BasicAE,基础版本的 AutoEncoder(包含 jupyter notebook 和 html 两个文件)
EasyDAE,基础版本的降噪 AutoEncoder(包含 jupyter notebook 和 html 两个文件)
ConvDAE,卷积降噪 AutoEncoder(包含 jupyter notebook 和 html 两个文件)
若是以为不错,能够给个人 GitHub 点个 star 就更好啦!