笔者本次选择复现的是汤晓鸥组 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 目前是在进行版本更替,本人看到很多函数有重复和不兼容的,略感迷茫。
(小道消息:版本更替大动作即将现身)