最近更新:17-5-9 增加Neural style使用Gram矩阵的前提工作
其实这个方向火起来是从2015年Gatys发表的一篇文章 A Neural Algorithm of Artistic Style, 当然了,当时新闻报道还是很标题党的,看的我热血沸腾。。言归正传,虽然只过了短短一年多,但是相关的论文已经有二三十篇了,这些论文在原有的基础上进行了极大的扩展。从最初的Neural Style,到Ulyanov的Texture Networks: Feed-forward Synthesis of Textures and Stylized Images以及李飞飞老师的Perceptual Losses for Real-Time Style Transfer and Super-Resolution。 一般来说主要就是这3篇,后面两篇是差不多的,都是将原来的求解全局最优解问题转换成用前向网络逼近最优解,原版的方法每次要将一幅内容图进行风格转换,就要进行不断的迭代,而后两篇的方法是先将其进行训练,训练得到前向生成网络,以后再来一张内容图,直接输入到生成网络中,即可得到具有预先训练的风格的内容图。 除了这3篇主要的,还有就是将style transfer应用到视频中的,以及一些在改变Gram矩阵的,还有其他很多。这里只写其中某几篇。
下面是最初版本的效果图
Prisma上的一些图片,用的应该是改进版本的方法
额,别跟我提新海诚滤镜,能把人变成二次元?我直接呵呵。你用多了就知道,它的天空就是直接从原著漫画中截取的,就几种,根本不管你现实中有没有云彩。试用“新海诚风”滤镜后 我忍不住戳穿了它。 原理的话,自己百度一下“新海诚滤镜特效的实现解密”就可以了。在此不做评论。
在neural style出来之前,Gatys还做了这个工作Texture Synthesis Using Convolutional Neural Networks,他们发现如果让隐藏层的特征用协方差来进行进行约束,可以得到较好的纹理生成。 他们发现如果用协方差(也就是Gram矩阵)来进行约束隐藏层特征的话,重建出来的特征虽然有些会保持,但是有些可能位置会打散。比如最右侧的一张图,人还是人,但是重建出来相当于“拼图”效果了。这是因为协方差本身就是去除了位置信息。 那么既然协方差可以用于纹理生成,那么如果我们加上 “让生成图的隐藏层特征与原图尽量一样,另一方面让生成图的打散特征与画的打散特征尽量相似”,这就是用神经网络做风格转换的最初想法。这也比较符合“风格”的定义,毕竟风格不应该具有位置信息,一种风格应该是与位置无关的。
注意:Gatys的几篇论文没有解释为什么用Gram矩阵,也别问我为什么要用。原因很简单:第一,你给不了证明。第二:无论怎样解释,你总会觉得你被忽悠。第三:好使,并且大致能解释的通,你自己想想应该知道。 另外,前几个月出了一篇解释用Gram矩阵的论文,以后再写吧。
下面约定p在内容上尽量一致。
l(a,f,p)=α∗lstyle(p,f)+β∗lcontent(a,f) 上式子的 α可由对应的Gram矩阵来计算。Gram矩阵则是计算feature map内部特征的互相关,由于采用的是VGG19较后层的feature map,因此提出的特征具有更多的语义性。
这个论文的方法是采用直接修改随机噪声,使得总体的误差最小。该方法每进行一次前向反向传播,都使得原有噪声得到微调,逐步逼近最终的效果图。主要缺点是速度慢,其次就是如果有大量的图要转换成同一种风格效果,比如我有很多图都想转换成古代文人墨客的山水画效果,我得一张一张的前向反向训练,效率实在太低,针对这一问题,有了下面的论文。
https://github.com/jcjohnson/neural-style
该论文采用的方法就是根据Neural Style的速度慢的问题。他们通过两个网络,前一个是生成网络 G 各自的Gram矩阵,再求MSE。其中Gram矩阵定义如下:
Glij=1Ml∑k=1MlFlikFljk=1Ml⟨Fli:,Flj:⟩ ,文章末尾是对Gram的解释。 其中 Fli:个feature map的竖直向量化表示。可以看出是将每一个feature map相互内积。个人认为,同一层得到的不同个feature map可以认为是“同等非线性复杂程度的feature map”。这种特征对物体的特征表达能力是相近的,可以进行互相关操作,进而综合利用这些特征从而表示图像的风格。该方法的模型一旦训练好,则生成网络 G的,所以图像的概率分布应该类似,所以这样做就是为了加速学习效果。
另一篇与之有异曲同工之妙的是李飞飞的Perceptual Losses for Real-Time Style Transfer and Super-Resolution。只不过后者生成网络没有用多个分辨率的 ,其在style transfer中是先将input进行stride-2的下采样,然后再接上数个残差网络,再用stride为1/2的fractionally convolution进行上采样。而Texture Nets则是采用金字塔模型,其相比Percentual Losses的模型缺少一定程度的自适应性。貌似有一种趋势,普通的Pooling和直接的上/下采样正在逐渐被stride>1的convolution和fractionally convolution所取代。人们解释说这种采样方式可能更加具有自适应性。 图1. Texture Nets的网络
图2. Percentual Losses的G
https://github.com/DmitryUlyanov/texture_nets ,他们不久后又发了一篇论文Instance Normalization: The Missing Ingredient for Fast Stylization,因此代码其实实现了他们自己的2篇,也是李飞飞的非官方Torch7实现。 李飞飞的原版代码是chainer框架的:https://github.com/yusuketomoto/chainer-fast-neuralstyle
该论文解释了Gram矩阵为什么可以用于图像风格提取。其认为feature map经过Gram矩阵后,就是将各个特征进行内积。论文指出:Gram矩阵只能提取静态特征,而且是将整幅图片进行空间平均(spatial averaging)。其对参考图像中的物体的空间排列是完全blind。因此,作者希望能找到一种特征的表达方式,使得物体的空间排列信息保留。论文中采用feature maps Fl具有空间的偏移,进而进行Gram矩阵计算得到互相关,这种互相关包含 的空间偏移,即保留了物体的空间排列信息。
论文中实现了平移和翻转的空间变换,并且通过这些变换计算出来的style loss可以组合,从而得到具有特定对称信息的图片。在某种程度上,可以这样认为该论文通过对Gram的空间变换,从而赋予特征互相关信息额外的空间信息。这种丰富化的互相关信息在可以进行更加有效地风格化表达。
这个论文不太有名。。 https://github.com/guillaumebrg/texture_generation 其实直接用原版代码改改Gram矩阵就可以了。
这篇论文将GAN与Style Transfer结合在一起,实现了一些人脸属性的风格转换。以前的GAN是由噪声直接通过minmax过程生成与数据集具有类似分布的图片,而Style Transfer是将一幅图的属性迁移到另一幅图上面。如果要实现自身属性的变换,假设原图为x的GroundTruth。
论文的关键是目标函数,提出了DIAT和DIAT-A。由于前者效果较后者差,而且方法较为繁琐,因而这里只说DIAT-A。个人认为DIAT-A最关键的一点是将属性损失定义在 D 。
ldynamicid(x)=∑l=45wlldynamicD,l(T(x),x) minTmaxDEa∼patt(a)[logD(a)]+Ex∼pdata(x)[1−logD(T(x))+λldynamicid(x)]更新D是固定的。因此损失是
Ea∼patt(a)[logD(a)]+Ex∼pdata(x)[1−logD(T(x))]更新T固定,可去除,最终损失为:
Ex∼pdata(x)[1−logD(T(x))+λldynamicid(x)]效果图
Gram矩阵其实是一种度量矩阵。矩阵分析中有这样的定义。 设V的度量矩阵,也称为Gram矩阵。 重点解释: 因此:对于三维的向量求Gram矩阵,就是要求gij。所以可以看出,如果将矩阵先进行竖直向量化,然后将其转置,两者进行矩阵相乘,结果就是内积,或是说对应位置点乘,然后相加。
Gram矩阵是计算每个通道i的feature map与每个通道j的feature map的内积。自然就会得到C*C的矩阵。Gram矩阵的每个值可以说是代表i通道的feature map与j通道的feature map的互相关程度。而卷积网络的卷积其实也是互相关,具体情况见CNN基本问题。 值得注意的是:卷积网络的卷积的实现与框架有关。反正torch是直接将将卷积核卷积的,并不先进行旋转卷积核,即互相关是一样的,而不是信号处理中所说的要先将卷积核旋转180再计算。 原因很简单,因为两种方式的最终学到的权值的差别,也就是旋转180°就可以了,完全不影响效果。
require(“torch”) require(‘nn’)local net = nn.Sequential() local module = nn.SpatialConvolution(1,1,2,2):noBias() net:add(module) local wt = torch.Tensor(2,2) wt[{1,1}]=5 wt[{1,2}]=6 wt[{2,1}]=7 wt[{2,2}]=8 print(wt) net:get(1).weight = wt:view(1,1,2,2)
local input = torch.Tensor(2,2) –local wt = torch.Tensor(2,2) input[{1,1}]=1 input[{1,2}]=5 input[{2,1}]=3 input[{2,2}]=4 print(net:forward(input:view(1,1,2,2)))
print(‘xcorr2’) print(torch.xcorr2(input,wt))
print(‘conv2’) print(torch.conv2(input,wt))
– 输出结果 [torch.DoubleTensor of size 2x2]
(1,1,.,.) = 88 [torch.DoubleTensor of size 1x1x1x1]
xcorr2 88 [torch.DoubleTensor of size 1x1]
– 这里确实是卷积核旋转180度,再卷 conv2 81
结论:卷积网络中的卷积就是互相关,等价于torch.xcorr2或torch.xorr3之类的,而信号处理说的卷积等价于torch.conv2之类的
更多的style transfer可以看 超越fast style transfer—-任意风格图和内容图0.1秒出结果