阶段一
最初的用神经网络实现的风格转换是需要不断迭代的,不仅速度慢,而且我要在A图加上B图的风格,就需要分别根据这两幅图进行不断前反向传播,更新输入,每次得到一张新的图都需要重新训练一次,效率实在太低。称之为style transfer。
代表文章:2015年Gatys发表的A Neural Algorithm ofArtistic Style。
主要思想:风格转换后的图像应该是取一幅画的style,另一幅画的content,将两者合并创造出新的画。因此生成的图像(gen-image)应该在内容上和content image相似,在风格上跟style image相似。
cnn在不同的特征层可以提取到不同的特征:低层次描述小范围的边角、曲线,中层次描述方块、螺旋,高层次描述内容。
因此要得到一张style-transfer的图像:
- 第一步:首先随机生成一张噪声图像gen-image(当然这张图像可以是content-image,这样训练起来会更快一些)。
- 第二步:将content-image,style-image,gen-image输入预训练好的网络(vgg-19),得到不同层的feature map 的输出。然后计算gen-image的feature map与content image和style image的feature map的差别;
- 第三步:最小化二者之间的loss,不到迭代更新gen-image。(注意:这里训练不再是更新网络的参数,而是将输入作为一个要学习的参数进行更新)
整个网络的结构如下图所示,在计算content loss的时候选取高层特征
(源码上是conv4_3、conv5_3)计算公式为:
其中:l表示第l层,i表示第层第i个feature map,j表示该feature map的第j个位置。P表示content-image,F表示gen-image。(说白了就是该层上对应的featuremap相减,求的是最小均方误差)
在计算style-loss 的时候选取了多个层次的特征(conv1_2、conv2_2、conv3_3、conv4_3、conv5_3),计算loss时首先提出了一个Gram 矩阵,其实就是特征图的内积(理解:Gram矩阵描述的就是全局特征的自相关, 如果输出图与风格图的这种自相关相近, 那么差不多是我们所理解的“风格”)
阶段二:
后来人们就想能不能直接一个前向传播搞定啊~,然后他们想出了,先训练一个GG, 这个GG可以让输入II通过GG后得到的G(I)G(I)是A图加上B图的风格。这个方法还是有很大的问题,那就是我训练的G必须是同一种风格,一般就用一幅风格图B,然后用很多的内容图,不断优化训练G, 最终G有啥用啊,只是“B风格的滤镜”罢了。那我要很多种风格呢?我就要训练很多很多的G,那挺尴尬的。
代表论文是是http://blog.csdn.net/hungryof/article/details/53981959 中提到的 Texture Networks: Feed-forwardSynthesis of Textures and Stylized Images 和 Perceptual Losses for Real-Time Style Transfer and Super-Resolution 这也就是网上经常说的fast style transfer.
Texture Networks: Feed-forward Synthesis of Textures and Stylized Images
网络结构:
一个生成网络,输入一个噪声图像,通过一个生成网络生成一张图片,然后送入预训练好的判别网络(VGG),用和阶段一相同的方法计算loss。训练的过程就是学习生成网络的参数的过程。
生成网络:
这里的生成网络是一个多尺度网络,假设分辨率M是2的幂,输入的噪声z是由K个随机tensor zi组成:
M=256 ,K=6(文章中后来改为了6)输入图像包括了6个尺度的图像,同时将y(content image)也作为输入(经过下采样)concat到输入里面去。将zk进行上采样,上采样的结果与相应尺度的zi进行concat,最后全分辨率的张量最终用一个1x1的滤镜映射到RGB图像上,最后生成图像输入到判别网络计算loss。
Perceptual Losses for Real-Time Style Transfer andSuper-Resolution
网络结构:
一个生成网络,输入一个噪声图像,通过一个生成网络生成一张图片,然后送入预训练好的vgg16,用和阶段一相同的方法计算loss。训练的过程就是学习生成网络的参数的过程。
训练时每个生成网络对应一种style,然后用大量的content-image进行训练,训练好的网络只能生成一种style。
生成网络:
卷积+残差模块+反卷积
def net(image):
#三个参数分别代表:特征图个数,卷积核的大小,strides。
conv1 = _conv_layer(image, 32, 9, 1)
conv2 = _conv_layer(conv1, 64, 3, 2)
conv3 = _conv_layer(conv2, 128, 3, 2)
#残差结构
resid1 = _residual_block(conv3, 3)
resid2 = _residual_block(resid1, 3)
resid3 = _residual_block(resid2, 3)
resid4 = _residual_block(resid3, 3)
resid5 = _residual_block(resid4, 3)
#反卷积
conv_t1 = _conv_tranpose_layer(resid5, 64, 3, 2)
conv_t2 = _conv_tranpose_layer(conv_t1, 32, 3, 2)
conv_t3 = _conv_layer(conv_t2, 3, 9, 1, relu=False)
preds = tf.nn.tanh(conv_t3) * 150 + 255./2
return preds
A LEARNED REPRESENTATION FOR ARTISTIC STYLE
这篇文章在batch normalization 上做了文章。在每一层加入了类似BN的γ和β缩放和平移因子。
在训练每一种风格的时候,只训练对应位置的γ和β,在学习风格化的同时学习多种不同风格的γ和β,并保存起来。这样就可以在每次使用时选择对应风格的γ和β,实现多风格转换。该方法还能实现同一内容图像风格化成多种风格的融合,这只要将多种风格特征的γ和β进行相应的线性融合便可。
在看这篇网站的源码时看了好久才理解了其中的奥妙。主要就是在训练的时候在每一层的BN之后加入了一层,该层引入了两个tensor scale[index]和shift[index]。
训练每一种风格的时候只训练style[i]对应的scale[i]和shift[i]。然后将训练好的模型保存起来,下一次训练的时候在上一次的基础上训练下一种风格对应的scale[i+1]和shift[i+1]。这样所有的风格训练完成之后,不同index的scale和shift就代表了不同的风格。同时还可以使各种风格具有不同的权值,从而进行风格的融合。
阶段四:
任意内容图像任意风格(看不动了,有时间再码)