Molly作者北京航天航空大学学校计算机视觉研究方向

基于深度学习的图像超分辨率重建

笔者本次选择复现的是汤晓鸥组 Chao Dong 的作品,这篇论文也是深度学习应用在超分辨率重构上的开山之作

论文复现代码: 

http://aistudio.baidu.com/#/projectdetail/23978

超分辨率重构

单图像超分辨率重构(SR)可以从一张较小的图像生成一张高分辨率的图像。显然,这种恢复的结果是不唯一的。可以这样直观地理解:远远看到一个模糊的身影,看不清脸,既可以认为对面走来的是个男生,也可以认为这是个女生。那么,当我想象对面人的长相时,会如何脑补呢?

这就依赖于我们的先验知识假如我认为,一个穿着裙子的人肯定是个女生,而对面那个人穿着裙子,所以我认为那是个女生,脑补了一张女神脸。然而,如果我知道穿裙子的人不一定是女生,还可能是女装大佬。迎面走来那个人瘦瘦高高,所以我认为十有八九是个男孩子,就会脑补一个……

也就是说,不同的先验知识,会指向不同的结果。我们的任务,就是学习这些先验知识目前效果最好的办法都是基于样本的(example-based)。

▲ 超分辨率重构的结果。SRCNN所示为论文提出的模型的结果,可以看出,边缘更加清晰。

论文提出一种有趣的视角:CNN 所构造的模型和稀疏编码方法(sparse coding based)是等价的。稀疏编码方法的流程如下: 

1. 从原始图片中切割出一个个小块,并进行预处理(归一化)。这种切割是密集的,也就是块与块之间有重叠;

2. 使用低维词典(low-resolution dictionary)编码,得到一个稀疏参数

3. 使用高维词典(high-resolution dictionary)结合稀疏参数进行重建(换了个密码本);

4. 将多个小块拼接起来,重合部分使用加权和拼接。

上图是卷积神经网络对应于稀疏编码的结构。对于一个低分辨率图像 Y,第一个卷积层提取 feature maps。第二个卷积层将 feature maps 进行非线性变换,变换为高分辨率图像的表示。最后一层恢复出高分辨率图像。 

相比于稀疏编码,论文提出的模型是 end-to-end 的,便于优化。并且,不需要求最小二乘的解,运算速度更快。

模型构造和训练

模型的结构 

这是一个 base-line 模型。如下图,f1=9,f2=1,f3=5,n1=64,n2=32,前两层使用 relu 作为激活函数。输入为图像的 Y 通道。

为了减轻边界带来的影响,论文使用 valid 方式处理卷积的边界。所以模型输出的结果是比输入要小一点点的。 显然,这是一个 FCN 的网络。我们使用图像的一个个 patch 进行训练,在测试时输入为一整张图片。由于没有全连接层,输入图像的大小可以是任意的。 

训练数据 

为了使模型更好地收敛,我们在原始的训练数据集上面切出一系列 33 X 33 大小的图像进行训练,切割的步长为 14。也就是说,我们使用的训练集图像,是有互相重合的部分的。

我们使用的是 timofte 数据集,共 91 张图片。论文中进行对比试验的时候,使用的都是 ImageNet 数据集。相比于 timofte,ImageNet 数据集可以提供更丰富的样本,得到更好的训练结果。但是 91 张图片给出的样本已经很丰富了,并且模型本身参数也不多,还不至于过拟合。所以使用 ImageNet 对结果的提升比较有限。

我们进行论文复现的时候,考虑到计算资源限制,使用 timofte 数据集,可以得到相似的结果。 我们使用 set5 作为验证集,使用 set14 可以得到类似的结论。

    def read_data(self, data_path):
        def data_reader():
            for image in os.listdir(data_path):
                if image.endswith('.bmp'):
                    img = cv2.imread(os.path.join(data_path, image))
                    yuv = cv2.cvtColor(img, cv2.COLOR_BGR2YCrCb)
                    img_y, img_u, img_v = cv2.split(yuv)
                    # 下面是切图的步骤
                    j = 0
                    count = 0
                    while j+33 < len(img_y):
                        i = 0
                        while i+33 < len(img_y[0]):
                            img_patch = img_y[j:j+33, i:i+33]
                            img_gth = img_patch[6:27, 6:27].copy()
                            img_blur = cv2.GaussianBlur(img_patch, (5, 5), 0)
                            img_sumsample = cv2.resize(img_blur, (11, 11))
                            img_input = cv2.resize(img_blur, (33, 33), interpolation=cv2.INTER_CUBIC)
                            yield img_input, img_gth
                            i+=14
                        j+= 14
        return data_reader

▲ 数据读取代码展示

损失函数和模型评估

我们使用 MSE 作为损失函数,即:

其中,是模型中的所有参数。 如上文所述,我们使用 valid 方式处理边界,所以输出的图像比输入图像略小。计算损失值时,只使用输入图像中间和输出图像对应位置的部分进行计算。 

由于超分辨率重建的结果是不唯一的,所以其结果的评估往往比较困难。论文使用峰值信噪比(PSNR)作为模型的评价指标。它和人眼的感受并不完全一致。可能会出现指标很高,但是人眼感受不太好的情况。但是,它仍然是广为接受的指标。 PSNR 的计算公式如下:

其中,n 为每像素的比特数,一般取 8。

模型训练结果

当训练的 backprops 数达到时,模型 PSNR 值达到 32.39dB。而同样的迭代次数,如果使用 ImageNet 数据集,可以达到 32.52dB。也就是说,使用更大的数据集,可以取得更好的结果。

使用更大的模型

论文从卷积核个数、卷积核大小、卷积层数三个方面增大模型的复杂度,在 ImageNet 上面对比可以看出,更加复杂的模型,可以取得更优的结果。

彩色图像上的实验 

在前面的实验中,论文使用图像的 Y 通道进行重建,其它通道使用双三次插值(bicubic)得到。下面进行彩色图像上的探索,包括:baseline(只使用 Y 通道),bicubic,YCbCr 格式的三通道直接输入,预训练 Y 再三个通道一起训练,预训练 CbCr 再三个通道一起训练,RGB 格式的三通道直接输入。对比这些实验,得到了一些非常有意思的结论。

最令人瞩目的结论就是,直接把 YCbCr 格式的三通道图像输入模型,结果竟然连 Bicubic 都不如。作者认为这是由于 Y 和 CbCr 的特征差别较大,导致模型陷入局部最小值。

另外,使用预训练的结果继续训练,结果会比直接训练 YCbCr 三个通道好些,但是还是不如只用 Y 的。最好的结果出现在使用 RGB 的。也比较好理解,因为 RGB 三个通道图像相关性比较高。 需要注意的一点是,加上 CbCr 的结果只比只用 Y 通道的结果好一点点,可见色相和饱和度两个通道对超分辨率重建的帮助不大。

论文总结

论文提出了一种 FCN 模型,并指出它和基于稀疏编码的超分辨率重构方法是等价的,并进行了一些改进,对比结果。 

但是笔者认为,有一些分析是比较牵强的。例如彩色图像的超分辨率重构,其 PSNR 除了直接使用 YCbCr 训练效果很差之外,其它相差都在 0.1 左右,甚至小于 0.1。这么小的差距,在 set5 验证集上面没有太多的说服力(set5 只有 5 张图片,而且这个结果是用 timofte 数据集训练的,也是一个比较小的数据集)。

论文的主要意义其实还是在于开拓了一个新的方法,构造了一个新的超分辨率重构的框架。

论文复现结果

Baseline模型复现结果 

考虑到算力限制,使用 timofte 数据集进行训练。Scale Factor 为 3 。为了加快收敛速度,我们使用 AdamOptimizer,前两层学习率, 最后一层为。相比于 SGD,AdamOptimizer 收敛到一个更好的结果。

这里有一个 trick,就是最后一层的学习率和前两层不同。这个设置是非常重要的,在实际测试中发现,使用这样的设置,模型收敛更加稳定。而所有层使用相同的学习率时,很容易出现 model collapse。

图中横轴是反向传播次数(batchsize * batchnum)。纵轴是 PSNR 值(dB)。可见,收敛速度比论文中快了很多,效果也更好。经过 150 个 epoch 的迭代,最终在 set5 测试集上 PSNR 达到了 35.25dB。 看一下恢复出来的图片是什么样的。Y 通道使用 SRCNN 恢复,Cr,Cb 通道使用 bicubic 插值。

另一张图片:

对比一下高频细节,可见重构的效果还是不错的:上图为输入,下图为输出。

构造更深的网络模型 

论文给出来的结果大同小异,结论是类似的。我们这里复现 filter size 改变的结果。

PSNR 曲线为:

使用 9-3-1 的模型结构,150 个 epoch 之后的 PSNR 值为 35.82。我们做一个对比图片,可以看出,虽然收敛速度、最终收敛结果不同(因为用的是不同的 Optimizor),但是得到的结论是一致的。

论文中的结果为:

三通道 RGB 训练结果 

输入图像为 RGB 三个通道。

模型训练比较困难,很容易出现 model collapse 。使用单通道的参数作为预训练参数,再此基础上进行训练。经过更长的迭代次数(200 个 epoch),PSNR 达到了 35.92。 

看一下 3 个通道训练结果:

对比一下高频细节,左图是输入,右图是输出:

总结

SRCNN 网络是 CNN 应用在超分辨率重建领域的开山之作。虽然论文尝试了更深的网络,但是相比于后来的神经网络,如 DRCN 中的网络,算是很小的模型了。受限于模型的表达能力,最终训练的结果还有很大的提升空间。 

另外,虽然相比于 sparse coding 方法,SRCNN 可以算是 end to end 方法了。但是仍然需要将图片进行 bicubic 差值到同样大小。此后的 ESPCN 使用 sub-pixel convolutional layer,减少了卷积的运算量,大大提高了超分辨率重建的速度。 

在复现的过程中,笔者发现 SGD 收敛速度相当慢,论文中曲线横轴都是数量级。使用 Adam 优化器收敛速度更快,并且几个模型的 PSNR 值更高。说明使用 SGD 训练时候,很容易陷入局部最优了。

关于PaddlePaddle

笔者目前只用到 PaddlePaddle 一些较为基础的功能,看介绍说 program 是特色但是本人在复现过程中并没有用到。

整体使用感受跟 TensorFlow 较为相似,数据读取那个部分较之 TensorFlow 更为方便好用,超赞!另外,可能 PaddlePaddle 目前是在进行版本更替,本人看到很多函数有重复和不兼容的,略感迷茫。

(小道消息:版本更替大动作即将现身)

PaperWeekly
PaperWeekly

推荐、解读、讨论和报道人工智能前沿论文成果的学术平台。

理论深度学习超分辨率重构
7
相关数据
汤晓鸥人物

汤晓鸥,现任香港中文大学信息工程系系主任,兼任中国科学院深圳先进技术研究院副院长。中央组织部“千人计划”入选者,全球人脸识别技术的“开拓者”和“探路者”,商汤科技联合创始人。2014年3月,汤晓鸥团队发布研究成果,基于原创的人脸识别算法,准确率达到98.52%,首次超越人眼识别能力(97.53%)。

深度学习技术

深度学习(deep learning)是机器学习的分支,是一种试图使用包含复杂结构或由多重非线性变换构成的多个处理层对数据进行高层抽象的算法。 深度学习是机器学习中一种基于对数据进行表征学习的算法,至今已有数种深度学习框架,如卷积神经网络和深度置信网络和递归神经网络等已被应用在计算机视觉、语音识别、自然语言处理、音频识别与生物信息学等领域并获取了极好的效果。

激活函数技术

在 计算网络中, 一个节点的激活函数定义了该节点在给定的输入或输入的集合下的输出。标准的计算机芯片电路可以看作是根据输入得到"开"(1)或"关"(0)输出的数字网络激活函数。这与神经网络中的线性感知机的行为类似。 一种函数(例如 ReLU 或 S 型函数),用于对上一层的所有输入求加权和,然后生成一个输出值(通常为非线性值),并将其传递给下一层。

重构技术

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

基准技术

一种简单的模型或启发法,用作比较模型效果时的参考点。基准有助于模型开发者针对特定问题量化最低预期效果。

参数技术

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

收敛技术

在数学,计算机科学和逻辑学中,收敛指的是不同的变换序列在有限的时间内达到一个结论(变换终止),并且得出的结论是独立于达到它的路径(他们是融合的)。 通俗来说,收敛通常是指在训练期间达到的一种状态,即经过一定次数的迭代之后,训练损失和验证损失在每次迭代中的变化都非常小或根本没有变化。也就是说,如果采用当前数据进行额外的训练将无法改进模型,模型即达到收敛状态。在深度学习中,损失值有时会在最终下降之前的多次迭代中保持不变或几乎保持不变,暂时形成收敛的假象。

学习率技术

在使用不同优化器(例如随机梯度下降,Adam)神经网络相关训练中,学习速率作为一个超参数控制了权重更新的幅度,以及训练的速度和精度。学习速率太大容易导致目标(代价)函数波动较大从而难以找到最优,而弱学习速率设置太小,则会导致收敛过慢耗时太长

损失函数技术

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

TensorFlow技术

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

张量技术

张量是一个可用来表示在一些矢量、标量和其他张量之间的线性关系的多线性函数,这些线性关系的基本例子有内积、外积、线性映射以及笛卡儿积。其坐标在 维空间内,有 个分量的一种量,其中每个分量都是坐标的函数,而在坐标变换时,这些分量也依照某些规则作线性变换。称为该张量的秩或阶(与矩阵的秩和阶均无关系)。 在数学里,张量是一种几何实体,或者说广义上的“数量”。张量概念包括标量、矢量和线性算子。张量可以用坐标系统来表达,记作标量的数组,但它是定义为“不依赖于参照系的选择的”。张量在物理和工程学中很重要。例如在扩散张量成像中,表达器官对于水的在各个方向的微分透性的张量可以用来产生大脑的扫描图。工程上最重要的例子可能就是应力张量和应变张量了,它们都是二阶张量,对于一般线性材料他们之间的关系由一个四阶弹性张量来决定。

验证集技术

验证数据集是用于调整分类器超参数(即模型结构)的一组数据集,它有时也被称为开发集(dev set)。

神经网络技术

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

卷积神经网络技术

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

先验知识技术

先验(apriori ;也译作 先天)在拉丁文中指“来自先前的东西”,或稍稍引申指“在经验之前”。近代西方传统中,认为先验指无需经验或先于经验获得的知识。先验知识不依赖于经验,比如,数学式子2+2=4;恒真命题“所有的单身汉一定没有结婚”;以及来自纯粹理性的推断“本体论证明”

过拟合技术

过拟合是指为了得到一致假设而使假设变得过度严格。避免过拟合是分类器设计中的一个核心任务。通常采用增大数据量和测试样本集的方法对分类器性能进行评价。

图网技术

ImageNet 是一个计算机视觉系统识别项目, 是目前世界上图像识别最大的数据库。

优化器技术

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

暂无评论
暂无评论~