隔了一个季度之后,再次更新。之前停更并非是意志松懈,主要是身体抱恙,疫情期间过度用眼导致眼疲劳,加上疫情稳定后打球崴脚卧床数周,拖着疲劳的眼睛仅能勉强应付网课和日常任务,无力去维持其他的用眼活动。那段时间也反思为什么以前在学校长时间用眼也没有什么不适,细想发现眼睛日常通勤中得到了很好的休息,而在家几乎没有通勤时间。如今每天有意识地调节屏幕时间,眼疲劳得到了一定的缓解。说这么多,总结感悟就是身体是革命的本钱,管理好身体才能更好地做其他事情。
在生成网络中,为了使生成图像与源图像具有相同的特征,最先使用的主要是在像素层面上做正则化,即 ∣ ∣ x s r c − x g ∣ ∣ l ||{\bf x_{src}}-{\bf x_g}||_l ∣∣xsrc−xg∣∣l,用L1或L2范数来对两者的像素层信息进行约束。但像素级约束有很多局限性,比如两张相同的图像,某一张图像的像素进行轻微的整体平移,两者的像素差就会变得很大,而实际内容却是相同的。 感知损失则是在深度特征层面对原图和生成图进行约束,深度特征通过神经网络提取,而深度特征通常随着网络层数的加深而获取到图像更深层次的语义信息,通过惩罚深度特征差异的监督,生成图像可以保留源图像中较高层语义信息。 但是在不同的任务中,研究者们对感知损失或内容损失的使用是有所不同的,下文将对各种使用形式做出相应的梳理,并针对性地对笔者课题相关的使用进行展开式讨论,由于笔者水平有限,如理解有误还望评论指正。
根据笔者的论文阅读存量和搜集情况,目前主要分为以下三种形式。
l f e a t ϕ , j ( y ^ , y ) = 1 C j H j W j ∣ ∣ ϕ j ( y ^ ) − ϕ j ( y ) ∣ ∣ 2 2 l_{feat}^{\phi,j}({\bf \hat y,y})=\frac{1}{C_jH_jW_j}||\phi_j({\bf \hat y})-\phi_j({\bf y})||_2^2 lfeatϕ,j(y^,y)=CjHjWj1∣∣ϕj(y^)−ϕj(y)∣∣22 其中 y ^ , y {\bf \hat y,y} y^,y分别表示生成图像与源图像, ϕ \phi ϕ表示预训练的神经网络, j j j表示该网络的第j层, C j × H j × W j C_j \times H_j \times W_j Cj×Hj×Wj为第j层特征图的形状,这个公式中涉及的feature map有两种选择:
每个卷积模块的激活值[1]每个卷积模块激活值前的特征[2]这种形式的感知损失主要应用于超分辨、人脸属性编辑等任务。下图为文献[1]中的参考图像,风格迁移的内容本文不涉及,详细内容请参考文献[1]。
l f e a t ( x , x ′ ) = ∑ i = 1 N 1 C j H j W j [ ∣ ∣ ϕ j ( x ) − ϕ j ( x ′ ) ∣ ∣ 1 ] l_{feat}({\bf x,x'})=\sum_{i=1}^N\frac{1}{C_jH_jW_j}[||\phi_j({\bf x})-\phi_j({\bf x'})||_1] lfeat(x,x′)=i=1∑NCjHjWj1[∣∣ϕj(x)−ϕj(x′)∣∣1] 其中 x , x ′ {\bf x,x'} x,x′分别表示GT和生成图像, ϕ \phi ϕ表示预训练的神经网络, j j j表示该网络的第j层, C j × H j × W j C_j \times H_j \times W_j Cj×Hj×Wj为第j层特征图的形状,使用的是每个卷积模块的激活值。 这种形式应用于pix2pix问题[3,4],跟形式1有所区别的是,它使用了多层深度特征,并且使用的是L1范数,而与形式1最大的区别是感知损失内的两个对象,这里是GT和生成图像。以pix2pix-HD[4]的实现细节为例,感知损失模型为ImageNet上预训练的VGG19,使用的层数为conv1~5模块的激活值,针对每个模块的特征匹配还做了权重分配,代码摘要如下:
# pytorch实现 # 这种从torchvision加载的方式在训练时不如使用模型参数加载的方式,后者占用的计算资源较少 from torchvision import models class Vgg19(torch.nn.Module): def __init__(self, requires_grad=False): super(Vgg19, self).__init__() vgg_pretrained_features = models.vgg19(pretrained=True).features self.slice1 = torch.nn.Sequential() self.slice2 = torch.nn.Sequential() self.slice3 = torch.nn.Sequential() self.slice4 = torch.nn.Sequential() self.slice5 = torch.nn.Sequential() for x in range(2): self.slice1.add_module(str(x), vgg_pretrained_features[x]) for x in range(2, 7): self.slice2.add_module(str(x), vgg_pretrained_features[x]) for x in range(7, 12): self.slice3.add_module(str(x), vgg_pretrained_features[x]) for x in range(12, 21): self.slice4.add_module(str(x), vgg_pretrained_features[x]) for x in range(21, 30): self.slice5.add_module(str(x), vgg_pretrained_features[x]) if not requires_grad: for param in self.parameters(): param.requires_grad = False def forward(self, X): h_relu1 = self.slice1(X) h_relu2 = self.slice2(h_relu1) h_relu3 = self.slice3(h_relu2) h_relu4 = self.slice4(h_relu3) h_relu5 = self.slice5(h_relu4) out = [h_relu1, h_relu2, h_relu3, h_relu4, h_relu5] return out class VGGLoss(nn.Module): def __init__(self, gpu_ids): super(VGGLoss, self).__init__() self.vgg = Vgg19().cuda() self.criterion = nn.L1Loss() self.weights = [1.0/32, 1.0/16, 1.0/8, 1.0/4, 1.0] def forward(self, x, y): x_vgg, y_vgg = self.vgg(x), self.vgg(y) loss = 0 for i in range(len(x_vgg)): loss += self.weights[i] * self.criterion(x_vgg[i], y_vgg[i].detach()) return losspix2pix-HD[4]的生成器网络框架图如下图,贴图的目的主要是为了对pix2pix有一个感性的认识,论文作者在文献中讨论了感知损失的作用,感知损失可以轻微地提升生成质量,但并不起决定性作用,因为不用感知损失也可以得到较好的效果,效果对比图如下,因此感知损失属于锦上添花的操作。
l f e a t D , l ( x , x ′ ) = 1 2 C l H l W l ∣ ∣ D l ( x ) − D l ( x ′ ) ∣ ∣ F 2 l_{feat}^{D,l}({\bf x,x'})=\frac{1}{2C_lH_lW_l}||D_l({\bf x})-D_l({\bf x'})||_F^2 lfeatD,l(x,x′)=2ClHlWl1∣∣Dl(x)−Dl(x′)∣∣F2 l p e r c e p t i o n ( x , x ′ ) = ∑ l = i j w l l f e a t D , l ( x , x ′ ) l_{perception}({\bf x,x'})=\sum_{l=i}^jw_{_l}l_{feat}^{D,l}({\bf x,x'}) lperception(x,x′)=l=i∑jwllfeatD,l(x,x′) 其中 x , x ′ {\bf x,x'} x,x′分别表示源和生成图像, D D D表示判别器网络, l l l表示判别器的第 l l l层, C j × H j × W j C_j \times H_j \times W_j Cj×Hj×Wj为第j层特征图的形状。文献[5,6]使用类似于这种形式的感知损失。 DIAT[5]是使用GAN的人脸属性迁移任务,和前面两种形式的使用方式不同,这里并不是使用预训练的分类器来提取深度特征,使用的是判别器提取的中间隐层特征。文献[5]中解释,现成的预训练分类器通常是在其他的训练集上训练的,并且不是为当前任务所量身定制的,因此可能不太适合于当前任务下的模型。
从公式上看,感知损失涉及两张图像,根据任务和任务涉及的数据不同,使用感知损失的任务可以分为两种:
匹配Ground Truth和生成图像匹配源输入图像和生成图像第一种形式 存在Ground Truth的任务有超分辨[1,2,8,10]、pix2pix[3,4,6],其中文献[1,2,10]使用的是形式1的公式,即涉及某一层深度特征的匹配,而文献[3,4,6,8]使用多层深度特征的匹配。 这种形式是以Ground Truth作为监督,让生成图像从深度特征层面向Ground Truth靠拢。对于pix2pix问题,由于输入是分割图,而Ground Truth和生成图为实际图像,所以匹配多层深度特征可以给生成模型提供更多的真实域信息;对于超分辨任务,目前搜集到的文献大都采用一层深度特征,而文献[8]则选择了两层,分别为第二层和第五层的池化层特征,涉及低层语义和深层语义,由于笔者对超分辨了解不多,这里就点到为止。
第二种形式 对于不存在Ground Truth的任务,如人脸属性编辑[5]、人脸老化[9],妆容迁移[11],其中文献[5]选择了其判别器模型的3、4层;文献[9,11]使用的公式是形式1。文献[9]主要是使用年龄标签进行跨域学习,使用一层感知损失,使用的是预训练的AlexNet,下图为其对不同层的尝试,由于其模型设计具有一定局限性,只能在人脸内容和纹理风格上做折衷,该方法选择的是conv5,其感知损失在总损失函数的权重极低,为5e-5. 而文献[11]虽然有妆容的参考图像,但不同于Ground Truth,因此其感知损失用于源图和生成图之间,采用的是ImageNet预训练的VGG16,特征匹配层为con4,与文献[9]一样,感知损失的权重压得很低,设置为0.005. 以上这类方法由于没有Ground Truth,所以使用感知损失的目的主要是为了保留源图的某些特征,仅起到重构作用,但由于这类任务主要是为了让源图发生特定的变化,因此感知损失不宜施加较强的约束。
形式1的公式如下: l f e a t ϕ , j ( y ^ , y ) = 1 C j H j W j ∣ ∣ ϕ j ( y ^ ) − ϕ j ( y ) ∣ ∣ 2 2 l_{feat}^{\phi,j}({\bf \hat y,y})=\frac{1}{C_jH_jW_j}||\phi_j({\bf \hat y})-\phi_j({\bf y})||_2^2 lfeatϕ,j(y^,y)=CjHjWj1∣∣ϕj(y^)−ϕj(y)∣∣22 在文献[1]中,作者复现了文献[7]的想法,固定网络参数优化输入,即找到一张图像能够使得上述损失函数最小,效果图如下所示。从这个逆向过程可以反映出深度特征蕴含的信息。 值得一提的是,在文献[1]的风格迁移效果中(如下图),可以看出转换网络是可以感知到图像的语义内容的,例如第二列,湖面和沙滩虽然都按照背景进行处理,但是很好地感知到“人”的语义。作者解释,VGG-16损失网络具有对人和动物进行选择的特征,因为这些对象存在于分类数据集中,并在分类数据集上进行训练。从某种角度来说,提供感知语义的分类器保留了预训练时的任务属性。
文献[1]中使用的是每个卷积模块中的激活值特征,即每个relu层的输出特征。而在文献[2]为超分辨方法,提出使用激活层之前的深度特征,作者列出两个原因:
激活后的特征是非常稀疏的,特别是非常深的网络,如下图,稀疏的激活值将会提供较弱的监督并导致较差的性能。 其中VGG19-54中的“54”指的是特征从第4个卷积层(在第5个最大池化层之前)获取,“22”与之类似。
使用激活后的特征会导致重构亮度不连续,相较于Ground Truth. 该图展示了使用激活层前后特征的亮度值分布,可以看出使用激活层前的特征可以较精确地贴近GT的亮度分布,从右图细节可以看出使用激活层前的特征可以生成更清晰的边角和更丰富的纹理。
在搜集涉及感知损失的文献中,超分辨任务的多篇文献均提到使用感知损失会导致棋盘伪影(checkerboard artifacts),在文献[8]中找到了相关问题的图例,如下图所示,其中ENet-P为仅使用感知损失的上采样网络,可以看出棋盘伪影的效果,但联合其他损失得到的效果则改善了这一问题。由于笔者查阅超分辨的文献较少,且没有遇到这一问题,这里就点到为止。
感知损失的本质就是让两张图片在深度特征层面进行匹配,深度特征由神经网络提取,至于选择什么网络模型提取特征、选取网络中的哪几层特征、让哪两张图片进行匹配则需要根据具体情况具体分析。本文主要涉及了三种形式的感知损失计算公式、两种匹配对象的选择,通过阅读文献,可以初步得出以下结论:
匹配低层特征可以保留几何、纹理等低级语义信息,匹配深层特征可以保留内容、风格等高级语义信息。预训练网络提取的特征附带预训练时的任务属性,会对生成模型产生影响,所以预训练模型尽量选择与生成任务相关的模型参数。选择特征匹配层不必局限于激活层,选择激活层之前的特征进行匹配,可以为生成模型提供更强的监督信息。对于具有Ground Truth的任务,深度特征匹配可以设置多层且较强的约束;对于没有Ground Truth仅使用感知损失做重构的任务,深度特征匹配应设置单层且较弱的约束。