1

两个图像处理问题的实例以及分析思路

 1 year ago
source link: https://divertingpan.github.io/post/quiz_two_cv_tasks/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

两个图像处理问题的实例以及分析思路

quiz_two_cv_tasks.png

这是老潘曾经做过的某个大学笔试题,代码尽可能干净简练并且能够快速解决问题,比较值得记录并且研究。尤其是第二题,在方法的设计上,对于结果的约束需要一些技巧才行,无脑用绝对误差很难行得通。

这些题目没有标准答案和做法,这里只是抛砖引玉,欢迎进一步交流优化改进。

下面是题目和解决思路以及配套完整代码(链接:https://github.com/divertingPan/utility_room/blob/master/task_2022.ipynb)。

题目:构建一个自编码器,至少包含4个编码层。编码一个至少有500副自然图像的灰度图片,分辨率为600x600

其实这一个做法和很久之前的一篇文章基本一样(复习Pytorch的使用方法,在套路的基础上灵活应变),只不过这里的图像编解码前后,分辨率都比较大(要600x600)。所以在模型的选择上可能要做一些巧妙的处理。我选择使用了Unet结构的模型,虽说这样能够解出题目,但转念一想,这样做的话,如果想要取出编码之后的隐向量,单使用这个编码去还原图像就不太可行。因为在解码器解码过程中需要依赖中间同级的编码层信息。当然也可以把这几个中间层互传的信息也看做隐向量去处理。

做这个用的数据集我拿的是DIV2K,但是这个数据量实际上不大,做做toy example可以,想实用的话还是搞点大的数据集吧。

题目:从CIFAR10中取出1000副图像,之后给定一个图像对x1x_1x1​和x2x_2x2​,构建一个网络模型能够以他们的平均x1+x22\frac{x_1+x_2}{2}2x1​+x2​​作为输入,输出两个原始的图像

最开始用的MAE当损失函数,但是这样是有一点违反直觉的,因为不一定要限制左边分叉只能输出和第一个图接近的结果。换句话说就是,x1x_1x1​和x2x_2x2​输入网络之后,输出可以是(x1,x2)(x_1, x_2)(x1​,x2​),也可以是(x2,x1)(x_2, x_1)(x2​,x1​)。所以说MAE没法做到这种能够交换顺序的衡量。并且,真的试了一下实验结果也不好:

这里img_1和img_2是两个输入,img_merge是融合的图像,pred_1和pred_2是两个预测结果。可以看到,模型仿佛在摆烂,只要我仍然输出均值图像,那这个输出就离两个目标都不算太远。其实这样想一下也是有原因的:如果求平均图像xxx的真实来源,可能是(x1,x2)(x_1, x_2)(x1​,x2​)或者(x2,x1)(x_2, x_1)(x2​,x1​),结果对于同样的xxx,模型一会儿要预测成(x1,x2)(x_1, x_2)(x1​,x2​)一会要(x2,x1)(x_2, x_1)(x2​,x1​),这自然是两个相反的方向,模型摆烂的确是趋于中庸的自然结果。

后来仔细思考了一下,决定施加一些专门的约束。

首先,顺承上面模型摆烂时候发生的情况,一对预测结果的大致位置至少要和真实情况的大致位置相差不大。也就是说,两个输出结果的均值,应该和两个真实图像的均值尽量一样。

反应在图上就是让两个中心点尽量靠近,让pred预测的中心点尽量接近实际的真值xxx。用式子表达的话,可以写成

MAE⁡(x1+x22,pred1+pred22)\operatorname{MAE}\left(\frac{x_1+x_2}{2}, \frac{pred_1+pred_2}{2}\right) MAE(2x1​+x2​​,2pred1​+pred2​​)

就是两个输出的均值,和两个真实值的均值,看均值之间的距离。

第二,进一步,这个位置确定了,但是两个结果之间会有一定的距离,可以想作是,以上面说的那个均值点为中心,各自向两端的距离。这个距离,对于预测和真值也应该是一致的。

1665307322208.PNG

反应在图上就是让实线括号所指的距离,尽量接近虚线括号所指的距离。式子表达是

∣MAE⁡(x1,x2)−MAE⁡(pred1,pred2)∣| \operatorname{MAE}\left(x_1, x_2\right)-\operatorname{MAE}\left(pred_1, pred_2\right) | ∣MAE(x1​,x2​)−MAE(pred1​,pred2​)∣

意思是,两个预测之间的距离,以及两个真实图之间的距离,看这两个距离之间差别多大,只关心绝对值不看正负号。

这时候很自然的会发现,两个样本点在空间上会有一个夹角。对于我们希望的情况应该是,这两个样本点连成的线应该平行。这样刚好对应了两个输出顺序可以不严格一致的情况。

1665308048721.PNG

反应在图上就是让两条线之间最小的夹角尽量小。这个可以用余弦相似度衡量,余弦相似度取值在[−1,1][-1, 1][−1,1]之间,两个向量同向平行是1,反向平行是-1,越垂直越趋向于0。从图上也可以看出来,具体的样本顺序是无所谓的,正好也符合前面所说过的。所以可以这样做损失函数

1−∣cossim⁡(x1−x2,pred1−pred2)∣1-\left|\operatorname{cossim}\left(x_1-x_2, pred_1-pred_2\right)\right| 1−∣cossim(x1​−x2​,pred1​−pred2​)∣

两个样本做差是求出他们之间连线的方向向量。取了余弦相似度的绝对值就是只看平行度,不看方向。用1做减是为了反向他,相当于最小这个式子的时候就是最大化余弦相似度。

这样就可以写代码了,损失函数loss就写成下面这种

loss_func = nn.L1Loss()
parallelism = torch.mean(1 - torch.abs(F.cosine_similarity((img_1-img_2).view(-1, 3*32*32), (pred_1-pred_2).view(-1, 3*32*32), dim=1)))
loss = torch.abs(loss_func(img_1, img_2) - loss_func(pred_1, pred_2)) + loss_func(0.5 * (pred_1 + pred_2), 0.5 * (img_1 + img_2)) + parallelism

在同样的训练次数下,模型显然有了更好的表现:

1665718875014.PNG

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK