当前位置: 首页 > 科技观察

使用卷积自动编码器对图片进行去噪

时间:2023-03-15 09:53:54 科技观察

前言这周工作太忙了。本来想多写一些关于Attentiontranslation的,但是一直抽不出时间。以后有时间再写吧。本周一起来看一个简单的autoencoder实战代码。我不会详细介绍自编码器的理论。我在网上搜索了很多。最简单的自动编码器是通过编码器和解码器再现输入。例如,我们将一张图片输入到网络中,自编码器的编码器对图片进行压缩,得到压缩后的信息,然后解码器将这些信息解码,再现出原始图像。自编码器实际上是通过最小化目标和输入之间的差异来优化的,即让输出层尽可能地再现原始信息。由于自编码器的基本形式比较简单,因此它的变体有很多,包括DAE、SDAE、VAE等,有兴趣的可以上网搜索其他相关资料。本文将实现两个demo。第一部分是实现一个简单的输入隐藏输出自动编码器。第二部分将在第一部分的基础上实现一个卷积自编码器,对图像进行降噪。工具说明TensorFlow1.0jupyternotebook资料:MNIST手写数据集完整代码地址:NELSONZHAO/zhihu***部分首先我们实现一个最简单的AutoEncoder,结构如上。加载数据在这里,我们使用MNIST手写数据集进行实验。首先,我们需要导入数据。TensorFlow对这个实验数据集进行了封装,所以我们使用起来非常简单。如果您希望数据显示灰度图像,请使用代码plt.imshow(img.reshape((28,28)),cmap='Greys_r')。我们的数据集可以通过input_data加载。如果你本地已经有MNIST数据集(四个压缩包),你可以把这四个压缩包放在MNIST_data目录下,这样TensorFlow会直接提取数据,不需要重新下载。我们可以通过imshow随意查看一张图片。由于我们加载的数据已经被处理成一个784维的向量,所以在重新显示的时候需要reshape。建立模型我们把数据加载进去之后,就可以进行最简单的建模了。在此之前,我们先获取输入数据的大小。我们加载的图片是一个28x28像素的块,TensorFlow已经帮我们处理成了784维的向量。同时,我们还需要指定隐藏层的大小。这里我指定了64,hidden_??units越小意味着信息损失越多,你也可以尝试其他尺寸看看结果。AutoEncoder包含三层输入、隐藏层和输出层。在隐藏层中,我们使用ReLU作为激活函数。至此,一个简单的AutoEncoder就搭建好了,接下来我们就可以开始TensorFlow的graph进行训练了。训练结果可视化经过以上步骤,我们构建了一个简单的AutoEncoder。接下来,我们将结果可视化以查看其性能。这里,我在测试数据集中选取了5个样本进行可视化。同样,如果要观察灰度图像,请将cmap参数指定为'Greys_r'。上面一行是测试数据集中的原始图片,第二行是AutoEncoder复现的图片。您可以清楚地看到像素信息的丢失。同样,我们也可以将隐藏层压缩后的数据进行可视化,结果如下:这五张图片对应测试中五张图片的压缩隐藏层图像。通过上面这个简单的例子,我们了解了AutoEncoder的基本工作原理。接下来,我们将进一步改进我们的模型,将隐藏层转换为卷积层以进行图像降噪。以上过程省略了部分代码,完整代码请查看我的GitHub。第二部分,在理解上述AutoEncoder工作原理的基础上,我们将在这部分的AutoEncoder中加入多个卷积层,对图像进行去噪。同样,我们仍然使用MNIST数据集进行实验,数据导入的步骤不再赘述,请下载代码查看。在开始之前,我们先通过一张图来看一下我们的整个模型结构:我们给模型输入一张有噪声的图片,在输出端给模型一张没有噪声的图片,让模型经过卷积自编码器。了解降噪过程。输入层这里的输入层和我们前面部分的输入层不同,因为这里需要用到卷积运算,所以输入层应该是一张高x宽x深的图像,一般图像深度是三层RGB格式,这里我们的MNIST数据集的深度只有1。EncoderconvolutionallayerEncoder卷积层设置三层卷积和池化层来处理图像。在第一层卷积中,我们使用了64个过滤器(filter),大小为3x3,strides默认为1,我们的height和width在padding设置为相同后不会改变,所以经过***经过层卷积后,我们得到的数据从原来的28x28x1变成了28x28x64。紧接着,对卷积结果进行最大池化操作(maxpooling)。这里我设置size和stride为2x2,pooling操作不会改变卷积结果的深度,所以pooling后的size为14x14x64。对于其他的卷积层,不再赘述。所有卷积层的激活函数都使用ReLU。经过三层卷积和池化操作,我们得到的conv3其实就相当于上一部分AutoEncoder的隐藏层。这一层的数据被压缩为4x4x32的大小,至此,我们完成了Encoder端的卷积操作,数据维度从最初的28x28x1变成了4x4x32.Decoderconvolutionallayer接下来我们将在Decoder端开始卷积。在此之前,可能有小伙伴要问了,既然Encoder已经把图片滚成了4x4x32,如果我们在Decoder中继续进行卷积,得到的数据大小是不是会越来越小?因此,在Decoder端,我们并没有简单的进行卷积操作,而是结合使用Upsample(中文翻译可以是上采样)+卷积。我们知道,卷积运算就是通过一个滤波器扫描图片中的每一个patch,然后对patch中的像素块进行加权求和,然后再进行非线性处理。比如我们的patch在原图中的大小是3x3(通俗一点就是我们取一张图片中3x3的像素块中的一个),那么我们就用一个3x3的filter来处理patch,补丁经过卷积后变成一个像素块。在反卷积(或转置卷积)中,这个过程是相反的,1个像素块将扩展为3x3像素块。但是Deconvolution也有一些缺点,会造成图片出现棋盘格图案,因为在Deconvolution的过程中,filter会有很多重叠。为了解决这个问题,有人提出用Upsample加卷积层来解决。Upsample常见的有两种方式,一种是最近邻插值,一种是双线性插值。本文还将使用Upsample加卷积来处理Decoder端。Upsample的操作也封装在TensorFlow中。我们使用resize_nearest_neighbor调整Encoder卷积的结果,然后进行卷积处理。经过三次Upsample运算后,我们得到了一个28x28x64的数据大小。***,我们需要对这个结果再进行一次卷积,处理成我们原图的大小。最后一步定义损失和优化器。对于损失函数,我们使用交叉熵进行计算,优化函数的学习率为0.001。构建噪声数据通过以上步骤,我们构建了整个卷积自编码器模型。由于我们要通过这个模型对图片进行去噪处理,所以在训练前还需要根据原始数据构建我们的噪声数据。我们通过上面的简单例子来看看如何添加噪声。我们得到一张图片的数据img(大小为784),在其基础上加上噪声因子乘以随机数的结果,会改变图片上的像素点。.接下来,由于MNIST数据的每个像素数据都被处理成0-1之间的数字,所以我们使用numpy.clip对加了噪声的图片进行裁剪操作,保证每个像素数据仍然在0-1之间。np.random.randn(*img.shape)的操作等于np.random.randn(img.shape[0],img.shape[1])我们来看看加噪声前后的图像对比.训练模型在介绍了模型构建和噪声处理之后,我们接下来可以训练我们的模型。在训练模型的时候,我们的输入已经变成了加入噪声后的数据,输出就是我们没有噪声的原始数据。主要是把原始数据reshape成和inputs_一样的格式。由于深度卷积运算,模型训练时间有点慢,建议使用GPU运行。记得***关闭sess。结果可视化经过上面长时间的训练,我们的模型终于训练完成了。接下来,我们将使用可视化来查看模型的工作原理。可以看到,通过卷积自编码器,我们的降噪效果还是非常不错的,最终生成的图片看起来非常平滑,几乎看不到噪声。可能有的朋友会想,我们也可以使用输入-隐藏-输出结构的AutoEncoder基础版来实现降噪。所以我也实现了一个使用最简单的输入-隐藏-输出结构进行降噪训练的模型版本(代码在我的GitHub中)。我们来看看它的结果:可以看出,与卷积自编码器相比,它的降噪效果更差,reshape后的图像中可以看到一些噪声阴影。至此,我们完成了基础版的AutoEncoder模型,并在其上添加了一个卷积层,对图像进行降噪。相信小伙伴们也对AntoEncoder有了初步的了解。完整的代码已经放在我的GitHub上(NELSONZHAO/zhihu),里面有六个文件:BasicAE,AutoEncoder基础版(包括jupyternotebook和html文件)EasyDAE,去噪AutoEncoder基础版(包括jupyterNotebook和html文件)两个文件)ConvDAE,卷积降噪AutoEncoder(包含jupyternotebook和html两个文件)如果觉得不错,可以给我的GitHub打个star,就更好了!