笔者本次选择复现的是 汤晓鸥 组 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 。为了加快 收敛 速度,我们使用 Adam Optimizer ,前两层 学习率 为 , 最后一层为 。相比于 SGD,Adam Optimizer 收敛 到一个更好的结果。
这里有一个 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 是特色但是本人在复现过程中并没有用到。
整体使用感受跟 Tensor Flow 较为相似,数据读取那个部分较之 Tensor Flow 更为方便好用,超赞!另外,可能 PaddlePaddle 目前是在进行版本更替,本人看到很多函数有重复和不兼容的,略感迷茫。
(小道消息:版本更替大动作即将现身)