Auto Byte

专注未来出行及智能汽车科技

微信扫一扫获取更多资讯

Science AI

关注人工智能与其他前沿技术、基础学科的交叉研究与融合发展

微信扫一扫获取更多资讯

Dataman作者Analytics Vidhya来源Arno编译

用于图像降噪的卷积自编码器 | 视觉进阶

这篇文章的目的是介绍关于利用自动编码器实现图像降噪的内容。

神经网络世界中,对图像数据进行建模需要特殊的方法。其中最著名的是卷积神经网络(CNN或ConvNet)或称为卷积自编码器。并非所有的读者都了解图像数据,那么我先简要介绍图像数据(如果你对这方面已经很清楚了,可以跳过)。然后,我会介绍标准神经网络。这个标准神经网络用于图像数据,比较简单。这解释了处理图像数据时为什么首选的是卷积自编码器。最重要的是,我将演示卷积自编码器如何减少图像噪声。这篇文章将用上Keras模块和MNIST数据。Keras用Python编写,并且能够在TensorFlow上运行,是高级的神经网络API。

了解图像数据

如图(A)所示,图像由“像素”组成。在黑白图像中,每个像素由0到255之间的数字表示。如今大多数图像使用24位彩色或更高的颜色。一幅RGB彩色图像表示一个像素的颜色由红色、绿色和蓝色组成,这三种颜色各自的像素值从0到255。RGB色彩生成器(如下所示)表明,RGB色彩系统利用红绿蓝,组合成各种颜色。因此,一个像素由含三个值的RGB(102、255、102)构成,其色号为#66ff66。

图 (A)

宽800像素,高600像素的图像具有800 x 600 = 480,000像素,即0.48兆像素(“兆像素”等于100万像素)。分辨率为1024×768的图像是一个由1,024列和768行构成的网格,共有1,024×768 = 0.78兆像素。

MNIST

MNIST数据库是一个大型的手写数字数据库,通常用于训练各种图像处理系统。Keras的训练数据集具备60,000条记录,而测试数据集则包含了10,000条记录。每条记录共有28 x 28个像素。

from keras.layers import Input, Dense
from keras.models import Model
from keras.datasets import mnist
import numpy as np
(x_train, _), (x_test, _) = mnist.load_data()

它们看起来怎么样?我们用绘图库及其图像功能imshow()展示前十条记录。

import matplotlib.pyplot as plt

n = 10 # 显示的记录数
plt.figure(figsize=(20, 4))
for i in range(n):
 # 显示原始图片
 ax = plt.subplot(2, n, i + 1)
 plt.imshow(x_test[i].reshape(28, 28))
 plt.gray()
 ax.get_xaxis().set_visible(False)
 ax.get_yaxis().set_visible(False)

plt.show()

图像数据的堆叠,用于训练

如果要让神经网络框架适用于模型训练,我们可以在一列中堆叠所有28 x 28 = 784个值。第一条记录的堆叠列如下所示(使用x_train[1].reshape(1,784)):

然后,我们可以使用标准的神经网络训练模型,如图(B)所示。数值为784的每个值都是输入层中的一个节点。且慢!堆叠数据会丢失很多信息吗?答案是肯定的。图像中的空间关系被忽略了。这使得大量的信息丢失。那么,我们接着看卷积自编码器如何保留空间信息。

图(B)

为什么图像数据首选卷积自编码器?

可以看到,数据切片和数据堆叠会导致信息大量丢失。卷积自编码器放弃堆叠数据,使图像数据输入时保持其空间信息不变,并在卷积层中以温和的方式提取信息。图(D)演示了将平面2D图像先提取到一个厚的正方体(Conv1),再提取到一个长方体(Conv2)和另一个长度更长的长方体(Conv3)。此过程旨在保留数据中的空间关系。这是自动编码器的编码过程。中间部分是一个完全连接的自动编码器,其隐藏层仅由10个神经元组成。然后就是解码过程。三个立方体将会展平,最后变成2D平面图像。图(D)的编码器和解码器是对称的。实际上,编码器和解码器不要求对称。

图(D)

卷积自编码器如何工作?

上面的数据析取似乎很神奇。数据析取究竟是如何进行的?这包括以下三层:卷积层,线性整流层和池化层。

图 (E): 特征图

1.    卷积层

卷积步骤会生成很多小块,称为特征图或特征,如图(E)的绿色、红色或深蓝色的正方形。这些正方形保留了输入图像中像素之间的关系。如图(F)所示,每个特征扫描原始图像。这一产生分值的过程称为卷积。

图 (F): 过滤过程

扫描完原始图像后,每个特征都会生成高分值和低分值的滤波图像,如图(G)所示。如果匹配完美,那块正方形的得分就高。如果匹配度低或不匹配,则得分低或为零。例如,原始图像有四个区域与红色方块完全匹配,那么这四个区域的得分都很高。

图 (G)

过滤器越多,模型可以提取的特征就越多。但是,特征越多,训练时间也就越长。因此,最好还是选择最少的过滤器提取特征。

1.1填充

特征如何确定匹配项?一种参数是填充,有两种选择:(i)用零填充原始图像以符合该特征,或(ii)删除原始图像中不符的部分并保留有效部分。

1.2步长

卷积层的另一个参数:步长。步长是输入矩阵上移动的像素个数。当步长为1时,过滤器一次移动1个像素。在Keras代码中,我们将其视为参数

2.线性整流步骤

线性整流单位(ReLU)的步骤与典型的神经网络相同。它将所有的负值校正为零,确保数学运算正确。

3.最大池化

池化会缩小图像尺寸。在图(H)中,一个2 x 2的窗口(称为池的大小)扫描每个滤波图像,并将该2 x 2窗口的最大值划分给新图像中大小为1 x 1的正方形。如图(H)所示,第一个2 x 2窗口的最大值分数高(用红色表示),因此高分划分给1 x 1正方形。

图 (H): 最大池化

除了采用最大值之外,其他不常用的池化方法还包括“平均池化”(取平均值)或“总和池化”(总和)。

图 (J)

池化后,会生成新的更小的滤波图像。现在我们拆分这个滤波图像,然后堆叠为一列,如图(J)所示。

Keras模型

以上三层是卷积神经网络的构建块。Keras具有以下两个功能:

•    Conv2D(filters, kernel_size, activation = 'reLu', strides=1):核尺寸(kernel_size)是2D卷积窗口的高度和宽度。图(E)使用的是2×2正方形,所以例子中核尺寸将为(2,2)。步长是输入矩阵上移动的像素个数。我们一次将滤镜移动了1个像素,所以步长为1。

•    MaxPooling2D(pool_size=(2,2)):在图(H)中,我们使用2×2窗口作为池的大小。因此,我们将在以下代码中使用(2,2)。

你可以在卷积自编码器中构建许多卷积层。在图(E)中,在编码部分有三层,分别标记为Conv1,Conv2和Conv3。因此,我们要进行相应的构建。

•    下面的代码input_img = Input(shape=(28,28,1)表明输入的2D图像为28 x 28。

•    然后,它构建了Conv1,Conv2和Conv3。

•    请注意,Conv1在Conv2内部,而Conv2在Conv3内部。

•    要是过滤器无法适应输入图像,填充将指定下一步该做什么。padding='valid'表示过滤器不符合,图像的一部分将被丢弃;padding='same'用零填充图片以适应图片。

from keras.layers import Input, Dense, Conv2D, MaxPooling2D, UpSampling2D
from keras.models import Model

# 编码过程
input_img = Input(shape=(28, 28, 1)) 

############
# 编码 #
############

# Conv1 #
x = Conv2D(filters = 16, kernel_size = (3, 3), activation='relu', padding='same')(input_img)
x = MaxPooling2D(pool_size = (2, 2), padding='same')(x)

# Conv2 #
x = Conv2D(filters = 8, kernel_size = (3, 3), activation='relu', padding='same')(x)
x = MaxPooling2D(pool_size = (2, 2), padding='same')(x) 

# Conv 3 #
x = Conv2D(filters = 8, (3, 3), activation='relu', padding='same')(x)
encoded = MaxPooling2D(pool_size = (2, 2), padding='same')(x)

# 注意:
# padding 是一个参数,值'valid' or 'same'. 
# "valid" 意味不需要填充 
# "same" 填充输入,使输出具有与原始输入相同的长度。 

然后,解码过程继续。因此,下面解码部分已全部完成编码和解码过程。

############
# 解码 #
############

# DeConv1
x = Conv2D(8, (3, 3), activation='relu', padding='same')(encoded)
x = UpSampling2D((2, 2))(x)

# DeConv2
x = Conv2D(8, (3, 3), activation='relu', padding='same')(x)
x = UpSampling2D((2, 2))(x)

# Deconv3
x = Conv2D(16, (3, 3), activation='relu')(x)
x = UpSampling2D((2, 2))(x)
decoded = Conv2D(1, (3, 3), activation='sigmoid', padding='same')(x)

该Keras API需要模型和优化方法的声明:

•  Model (inputs= input_img,outputs= decoded):

在解码给定输入数据input_img的情况下,模型包括计算输出所需的所有层。compile(optimizer='adadelta',loss='binary_crossentropy'):优化程序会像渐变梯度一样执行优化操作。最常见的是随机梯度下降(SGD),自适应梯度(Adagrad)和Adadelta(Adadelta是Adagrad的扩展)。有关详细信息,请参见Keras优化器文档。损失函数可以查找Keras损失文档。

# 声明模型
autoencoder = Model(input_img, decoded)
autoencoder.compile(optimizer='adadelta', loss='binary_crossentropy')

下面,我使用x_train作为输入和输出来训练模型。batch_size是样本量和epochs是迭代的次数。我指定shuffle=True打乱训练数据。

# 训练模型
autoencoder.fit(x_train, x_train,
 epochs=100,
 batch_size=128,
 shuffle=True,
 validation_data=(x_test, x_test)
 )

我们可以打印出前十张原始图像和相同十张图像的预测。

decoded_imgs = autoencoder.predict(x_test)

n = 10

plt.figure(figsize=(20, 4))
for i in range(n):
 # 显示原始图像
 ax = plt.subplot(2, n, i + 1)
 plt.imshow(x_test[i].reshape(28, 28))
 plt.gray()
 ax.get_xaxis().set_visible(False)
 ax.get_yaxis().set_visible(False)

 # 显示重构后的图像
 ax = plt.subplot(2, n, i+1+n)
 plt.imshow(decoded_imgs[i].reshape(28, 28))
 plt.gray()
 ax.get_xaxis().set_visible(False)
 ax.get_yaxis().set_visible(False)
plt.show()

如何构建图像降噪卷积自编码器?

图像降噪的想法是训练一个模型,输入噪声数据,并输出它们各自清晰的数据。这是与上述模型的唯一区别。首先让我们向数据添加噪音

noise_factor = 0.4
x_train_noisy = x_train + noise_factor * np.random.normal(loc=0.0, scale=1.0, size=x_train.shape) 
x_test_noisy = x_test + noise_factor * np.random.normal(loc=0.0, scale=1.0, size=x_test.shape) 

x_train_noisy = np.clip(x_train_noisy, 0., 1.)
x_test_noisy = np.clip(x_test_noisy, 0., 1.)

前十张噪声图像如下所示:

n = 10
plt.figure(figsize=(20, 2))
for i in range(n):
 ax = plt.subplot(1, n, i+1)
 plt.imshow(x_test_noisy[i].reshape(28, 28))
 plt.gray()
 ax.get_xaxis().set_visible(False)
 ax.get_yaxis().set_visible(False)
plt.show()

然后,我们训练模型时将输入噪声数据,输出干净的数据。

autoencoder.fit(x_train_noisy, x_train,
 epochs=100,
 batch_size=128,
 shuffle=True,
 validation_data=(x_test_noisy, x_test)
 )

最后,我们打印出前十个噪点图像以及相应的降噪图像。

decoded_imgs = autoencoder.predict(x_test)

n = 10

plt.figure(figsize=(20, 4))
for i in range(n):
 # 显示原始图像
 ax = plt.subplot(2, n, i + 1)
 plt.imshow(x_test_noisy[i].reshape(28, 28))
 plt.gray()
 ax.get_xaxis().set_visible(False)
 ax.get_yaxis().set_visible(False)

 # 显示重构后的图像
 ax = plt.subplot(2, n, i+1+n)
 plt.imshow(decoded_imgs[i].reshape(28, 28))
 plt.gray()
 ax.get_xaxis().set_visible(False)
 ax.get_yaxis().set_visible(False)
plt.show()

是否可以使用任何经过训练的CNN代码吗?

可以的。如果你有兴趣学习代码,Keras提供了几个经过预训练的CNN,包括Xception,VGG16,VGG19,ResNet50,InceptionV3,InceptionResNetV2,MobileNet,DenseNet,NASNet和MobileNetV2。值得一提的是,你可以出于研究目的付钱或下载此大型图像数据库ImageNet。

磐创AI
磐创AI

AI行业最新动态,机器学习干货文章,深度学习原创博客,深度学习实战项目,Tensorflow中文原创教程,国外最新论文翻译。欢迎喜欢AI、关注深度学习的小伙伴加入我们。

http://www.panchuang.net/
理论降噪自编码器卷积图像处理计算机视觉神经网络KerasMNIST
32
相关数据
自动编码器技术

自动编码器是用于无监督学习高效编码的人工神经网络。 自动编码器的目的是学习一组数据的表示(编码),通常用于降维。 最近,自动编码器已经越来越广泛地用于生成模型的训练。

池化技术

池化(Pooling)是卷积神经网络中的一个重要的概念,它实际上是一种形式的降采样。有多种不同形式的非线性池化函数,而其中“最大池化(Max pooling)”是最为常见的。它是将输入的图像划分为若干个矩形区域,对每个子区域输出最大值。直觉上,这种机制能够有效的原因在于,在发现一个特征之后,它的精确位置远不及它和其他特征的相对位置的关系重要。池化层会不断地减小数据的空间大小,因此参数的数量和计算量也会下降,这在一定程度上也控制了过拟合。通常来说,CNN的卷积层之间都会周期性地插入池化层。

重构技术

代码重构(英语:Code refactoring)指对软件代码做任何更动以增加可读性或者简化结构而不影响输出结果。 软件重构需要借助工具完成,重构工具能够修改代码同时修改所有引用该代码的地方。在极限编程的方法学中,重构需要单元测试来支持。

VGG技术

2014年,牛津大学提出了另一种深度卷积网络VGG-Net,它相比于AlexNet有更小的卷积核和更深的层级。AlexNet前面几层用了11×11和5×5的卷积核以在图像上获取更大的感受野,而VGG采用更小的卷积核与更深的网络提升参数效率。VGG-Net 的泛化性能较好,常用于图像特征的抽取目标检测候选框生成等。VGG最大的问题就在于参数数量,VGG-19基本上是参数量最多的卷积网络架构。VGG-Net的参数主要出现在后面两个全连接层,每一层都有4096个神经元,可想而至这之间的参数会有多么庞大。

参数技术

在数学和统计学裡,参数(英语:parameter)是使用通用变量来建立函数和变量之间关系(当这种关系很难用方程来阐述时)的一个数量。

损失函数技术

在数学优化,统计学,计量经济学,决策理论,机器学习和计算神经科学等领域,损失函数或成本函数是将一或多个变量的一个事件或值映射为可以直观地表示某种与之相关“成本”的实数的函数。

超参数技术

在机器学习中,超参数是在学习过程开始之前设置其值的参数。 相反,其他参数的值是通过训练得出的。 不同的模型训练算法需要不同的超参数,一些简单的算法(如普通最小二乘回归)不需要。 给定这些超参数,训练算法从数据中学习参数。相同种类的机器学习模型可能需要不同的超参数来适应不同的数据模式,并且必须对其进行调整以便模型能够最优地解决机器学习问题。 在实际应用中一般需要对超参数进行优化,以找到一个超参数元组(tuple),由这些超参数元组形成一个最优化模型,该模型可以将在给定的独立数据上预定义的损失函数最小化。

TensorFlow技术

TensorFlow是一个开源软件库,用于各种感知和语言理解任务的机器学习。目前被50个团队用于研究和生产许多Google商业产品,如语音识别、Gmail、Google 相册和搜索,其中许多产品曾使用过其前任软件DistBelief。

数据库技术

数据库,简而言之可视为电子化的文件柜——存储电子文件的处所,用户可以对文件中的数据运行新增、截取、更新、删除等操作。 所谓“数据库”系以一定方式储存在一起、能予多个用户共享、具有尽可能小的冗余度、与应用程序彼此独立的数据集合。

最大池化技术

最大池化(max-pooling)即取局部接受域中值最大的点。

神经网络技术

(人工)神经网络是一种起源于 20 世纪 50 年代的监督式机器学习模型,那时候研究者构想了「感知器(perceptron)」的想法。这一领域的研究者通常被称为「联结主义者(Connectionist)」,因为这种模型模拟了人脑的功能。神经网络模型通常是通过反向传播算法应用梯度下降训练的。目前神经网络有两大主要类型,它们都是前馈神经网络:卷积神经网络(CNN)和循环神经网络(RNN),其中 RNN 又包含长短期记忆(LSTM)、门控循环单元(GRU)等等。深度学习是一种主要应用于神经网络帮助其取得更好结果的技术。尽管神经网络主要用于监督学习,但也有一些为无监督学习设计的变体,比如自动编码器和生成对抗网络(GAN)。

卷积神经网络技术

卷积神经网路(Convolutional Neural Network, CNN)是一种前馈神经网络,它的人工神经元可以响应一部分覆盖范围内的周围单元,对于大型图像处理有出色表现。卷积神经网路由一个或多个卷积层和顶端的全连通层(对应经典的神经网路)组成,同时也包括关联权重和池化层(pooling layer)。这一结构使得卷积神经网路能够利用输入数据的二维结构。与其他深度学习结构相比,卷积神经网路在图像和语音识别方面能够给出更好的结果。这一模型也可以使用反向传播算法进行训练。相比较其他深度、前馈神经网路,卷积神经网路需要考量的参数更少,使之成为一种颇具吸引力的深度学习结构。 卷积网络是一种专门用于处理具有已知的、网格状拓扑的数据的神经网络。例如时间序列数据,它可以被认为是以一定时间间隔采样的一维网格,又如图像数据,其可以被认为是二维像素网格。

随机梯度下降技术

梯度下降(Gradient Descent)是遵循成本函数的梯度来最小化一个函数的过程。这个过程涉及到对成本形式以及其衍生形式的认知,使得我们可以从已知的给定点朝既定方向移动。比如向下朝最小值移动。 在机器学习中,我们可以利用随机梯度下降的方法来最小化训练模型中的误差,即每次迭代时完成一次评估和更新。 这种优化算法的工作原理是模型每看到一个训练实例,就对其作出预测,并重复迭代该过程到一定的次数。这个流程可以用于找出能导致训练数据最小误差的模型的系数。

噪音技术

噪音是一个随机误差或观测变量的方差。在拟合数据的过程中,我们常见的公式$y=f(x)+\epsilon$中$\epsilon$即为噪音。 数据通常包含噪音,错误,例外或不确定性,或者不完整。 错误和噪音可能会混淆数据挖掘过程,从而导致错误模式的衍生。去除噪音是数据挖掘(data mining)或知识发现(Knowledge Discovery in Database,KDD)的一个重要步骤。

神经元技术

(人工)神经元是一个类比于生物神经元的数学计算模型,是神经网络的基本组成单元。 对于生物神经网络,每个神经元与其他神经元相连,当它“兴奋”时会向相连的神经元发送化学物质,从而改变这些神经元的电位;神经元的“兴奋”由其电位决定,当它的电位超过一个“阈值”(threshold)便会被激活,亦即“兴奋”。 目前最常见的神经元模型是基于1943年 Warren McCulloch 和 Walter Pitts提出的“M-P 神经元模型”。 在这个模型中,神经元通过带权重的连接接处理来自n个其他神经元的输入信号,其总输入值将与神经元的阈值进行比较,最后通过“激活函数”(activation function)产生神经元的输出。

图像处理技术

图像处理是指对图像进行分析、加工和处理,使其满足视觉、心理或其他要求的技术。 图像处理是信号处理在图像领域上的一个应用。 目前大多数的图像均是以数字形式存储,因而图像处理很多情况下指数字图像处理。

堆叠技术

堆叠泛化是一种用于最小化一个或多个泛化器的泛化误差率的方法。它通过推导泛化器相对于所提供的学习集的偏差来发挥其作用。这个推导的过程包括:在第二层中将第一层的原始泛化器对部分学习集的猜测进行泛化,以及尝试对学习集的剩余部分进行猜测,并且输出正确的结果。当与多个泛化器一起使用时,堆叠泛化可以被看作是一个交叉验证的复杂版本,利用比交叉验证更为复杂的策略来组合各个泛化器。当与单个泛化器一起使用时,堆叠泛化是一种用于估计(然后纠正)泛化器的错误的方法,该泛化器已经在特定学习集上进行了训练并被询问了特定问题。

优化器技术

优化器基类提供了计算梯度loss的方法,并可以将梯度应用于变量。优化器里包含了实现了经典的优化算法,如梯度下降和Adagrad。 优化器是提供了一个可以使用各种优化算法的接口,可以让用户直接调用一些经典的优化算法,如梯度下降法等等。优化器(optimizers)类的基类。这个类定义了在训练模型的时候添加一个操作的API。用户基本上不会直接使用这个类,但是你会用到他的子类比如GradientDescentOptimizer, AdagradOptimizer, MomentumOptimizer(tensorflow下的优化器包)等等这些算法。

推荐文章
你好,loss和val_loss的值都是负数接近-500.显示重构后的突然都是白色的一块是什么原因呢?
我尝试改变了loss函数与优化器,使他能够降噪。autoencoder.compile(optimizer='adam', loss='categorical_crossentropy')