[机器学习笔记#5] Neural Style 分析及实验
Cirno2016/12/16软件综合 IP:美国

[机器学习笔记#4] Neural Style 算法分析及caffe实验
在之前的帖子中,楼主简要介绍了 Neural Style 算法的原理和实现思路,该方法2015年由三位德国科学家以一份tech report的形式提出,今年他们发表了一篇CVPR 论文

在楼主随后的进一步实验中,发现当初好死不死选caffe是给自己挖了个大坑,caffe作为曾经占据科研界支配地位的工具,用来跑各种pre-trained的model 是相当顺手,但用来实现自定义运算的layer,尤其是用Python,其过程相当之蛋疼,而且也缺乏运算效率。于是推翻重来,改用tensorflow实现。

实现neural style的代码,首先需要一个 pre-trained VGG16 模型,tensorflow 中今年新加入的轻量级图像识别库TF-Slim提供了官方的pre-trained的VGG网络,非常方便。
VGG16 网络结构如下,其特征是大量使用两层级联的具有3x3小卷积核(能分辨上下左右概念的最小尺寸)的卷积层来代替类似Alexnet结构中的单层大卷积核卷积层,而最后的等效 receptive field 是接近的(5x5)。这样的好处一是减少了待训练weight的数量,二是小卷积核之间引入了额外的ReLu层,增加了非线性程度,使得整体的representation power提高(简单理解成用两层的MLP代替单层inear classifier)。

['vgg_16/conv1/conv1_1',
 'vgg_16/conv1/conv1_2',
 'vgg_16/pool1',
 'vgg_16/conv2/conv2_1',
 'vgg_16/conv2/conv2_2',
 'vgg_16/pool2',
 'vgg_16/conv3/conv3_1',
 'vgg_16/conv3/conv3_2',
 'vgg_16/conv3/conv3_3',
 'vgg_16/pool3',
 'vgg_16/conv4/conv4_1',
 'vgg_16/conv4/conv4_2',
 'vgg_16/conv4/conv4_3',
 'vgg_16/pool4',
 'vgg_16/conv5/conv5_1',
 'vgg_16/conv5/conv5_2',
 'vgg_16/conv5/conv5_3',
 'vgg_16/pool5',
 'vgg_16/fc6',
 'vgg_16/fc7',
 'vgg_16/fc8']

在实现中,原paper用conv4_2层输出的activation作为表征原图content的feature,原理是CNN中high level卷积层的neuron主要是被输入图片中物理的轮廓、形状激活,而忽略细节纹理(理解为假如我有两双同款篮球鞋,分别是不同的花纹、颜色,二者的同角度照片在该层的activation相同)。

而对于原图片的style,该paper提出使用卷积层不同filter输出的activation之间的corelation来表征,至于为什么,则出自作者的另一篇paper。这种corelation可以用inner product来计算,得到的feature称为Gram matrix。最终原图的style由 conv1_1,conv2_1,conv3_1,conv4_1,conv5_1的输出共同计算得到。

整个结构如图,出自原paper配图: Screenshot from 2016-12-15 21:42:28.png

生成过程中需要提供两种图片,一张提供content,计算得到content feature;一张提供style,计算得到 style feature (gram matrix)。然后我们的目标图片会被初始化为一张target白噪声图片(随机初始化),该图输出的 content feature 和style feature 于content image 和style image的输出分别进行比较,计算其L2 distance,即得到一个L2 loss function。通过反向传播可以计算该L2 loss 在target图片像素上的gradient,然后对target图片像素进行gradient based optimization,通过迭代循环使其逐渐呈现我们想要的面貌(loss function 最小)。这里很有趣的一点是,该方法中的优化过程,并非针对CNN网络本身,反而是输入图片,虽然有些反直觉,但该方法在各种图片相关的generative model中并不少见,包括效果骇人的deepdream。

[修改于 7年11个月前 - 2017/02/16 23:58:57]

来自:计算机科学 / 软件综合
11
已屏蔽 原因:{{ notice.reason }}已屏蔽
{{notice.noticeContent}}
~~空空如也
Cirno 作者
8年1个月前 修改于 8年1个月前 IP:美国
828501

实现的实验代码是这两天下班后顺着思路往下写的,比较丑陋,回头整理封装一下。有几个值得一提的细节: 1 )虽然标准的VGG16图像识别应用中输入图片尺寸被限制在了224x224,但在这里你可以想用多大用多大(因为forward pass不会传到FC layer,btw无法想象生成224x224的艺术图有卵用?);2)原文使用的L-BFGS在tensorflow中并没有加入,所以使用AdamOptimizer取代;3)由于要对输入图片求导,所以不要用XXXXXnstant作为输入。。4)这玩意儿调参很麻烦

计算content feature的代码:

## compute content feature
with tf.Graph().as_default():
    image=tf.constant(content,dtype=tf.float32)
    with slim.arg_scope(vgg.vgg_arg_scope()):
        _, end_points=vgg.vgg_16(image,spatial_squeeze=False)
    ## load vgg16 model    
    init_fn = slim.assign_from_checkpoint_fn(
    os.path.join('./model/', 'vgg_16.ckpt'),
    slim.get_model_variables())
    ## compute content Feature
    content_feature=tf.reshape(end_points['vgg_16/conv4/conv4_2'],shape=[-1])
    with tf.Session() as sess:
        init_fn(sess)
        content_ft_val=sess.run(content_feature)    

计算style feature的代码(用了许多hard-code):

style_list=['vgg_16/conv1/conv1_1',
            'vgg_16/conv2/conv2_1',
            'vgg_16/conv3/conv3_1',
            'vgg_16/conv4/conv4_1',
            'vgg_16/conv5/conv5_1']
## compute style feature
def style_gram_compute(idx):
    dim=end_points[style_list[idx]].get_shape().as_list()
    style_feature=tf.reshape(end_points[style_list[idx]],shape=[-1,dim[-1]])
    style_gram=tf.reshape(
tf.matmul(style_feature,style_feature,transpose_a=True),
shape=[-1])/(dim[1]*dim[2])
    return style_gram,dim

## compute style feature
with tf.Graph().as_default():
    image=tf.constant(style,dtype=tf.float32)
    with slim.arg_scope(vgg.vgg_arg_scope()):
        _, end_points=vgg.vgg_16(image,spatial_squeeze=False)
    ## load vgg16 model    
    init_fn = slim.assign_from_checkpoint_fn(
    os.path.join('./model/', 'vgg_16.ckpt'),
    slim.get_model_variables())
    ## compute content Feature
    style_gram_1,dim1=style_gram_compute(0)
    style_gram_2,dim2=style_gram_compute(1)
    style_gram_3,dim3=style_gram_compute(2)
    style_gram_4,dim4=style_gram_compute(3)
    style_gram_5,dim5=style_gram_compute(4)
    with tf.Session() as sess:
        init_fn(sess)
        style_gram_val_1=sess.run(style_gram_1)    
        style_gram_val_2=sess.run(style_gram_2)
        style_gram_val_3=sess.run(style_gram_3)
        style_gram_val_4=sess.run(style_gram_4)
        style_gram_val_5=sess.run(style_gram_5)

主优化程序:

def style_loss_compute(style_gram_val,target_style_gram):
    style_gram_const=tf.constant(style_gram_val)
    style_loss=tf.reduce_mean(tf.squared_difference(target_style_gram,style_gram_const))
    return style_loss

with tf.Graph().as_default():
    ### generate target image ####
    image=tf.Variable(tf.random_normal(shape=(1,h,w,3),stddev=np.std(content)*0.1,dtype=tf.float32))
    with slim.arg_scope(vgg.vgg_arg_scope()):
        _, end_points=vgg.vgg_16(image,spatial_squeeze=False)
    ## load vgg16 model    
    init_fn = slim.assign_from_checkpoint_fn(
    os.path.join('./model/', 'vgg_16.ckpt'),
    slim.get_model_variables())
    ## compute target content Feature
    target_content_feature=tf.reshape(end_points['vgg_16/conv4/conv4_2'],shape=[-1])
    ## compute target style Feature
    target_style_gram_1,_=style_gram_compute(0)
    target_style_gram_2,_=style_gram_compute(1)
    target_style_gram_3,_=style_gram_compute(2)
    target_style_gram_4,_=style_gram_compute(3)
    target_style_gram_5,_=style_gram_compute(4)
    ## define content loss
    content_loss=tf.reduce_sum(tf.squared_difference(target_content_feature,content_ft_val))
    ## define style loss
    style_loss_1=style_loss_compute(style_gram_val_1,target_style_gram_1)
    style_loss_2=style_loss_compute(style_gram_val_2,target_style_gram_2)
    style_loss_3=style_loss_compute(style_gram_val_3,target_style_gram_3)
    style_loss_4=style_loss_compute(style_gram_val_4,target_style_gram_4)
    style_loss_5=style_loss_compute(style_gram_val_5,target_style_gram_5)
    w0=1.0/5
    style_loss=w0*style_loss_1+w0*style_loss_2+w0*style_loss_3+w0*style_loss_4+w0*style_loss_5
    ## define total loss
    total_loss=1e-3*content_loss+style_loss
    ## define train step
    global_step = tf.Variable(0, trainable=False)
    starter_learning_rate = 1.
    learning_rate = tf.train.exponential_decay(starter_learning_rate, global_step,
                                           100, 0.95, staircase=True)
    train_op=tf.train.AdamOptimizer(learning_rate).minimize(loss=total_loss,
                                                            var_list=[image],
                                                            global_step=global_step)
    with tf.Session() as sess:
        sess.run(tf.global_variables_initializer())
        init_fn(sess)
        max_iter=1000
        for iter in range(max_iter):
            _,loss_content_val,loss_style_val=sess.run([train_op,content_loss,style_loss]) 
            sys.stdout.write('\r'+'iteration:%4d-----Loss_content: %0f6-----Loss_style: %0f6-----learning_rate: %0f5'
                             %(iter,loss_content_val,loss_style_val,learning_rate.eval()))
            output=image.eval()
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
Cirno作者
8年1个月前 修改于 8年1个月前 IP:美国
828502
调试阶段使用不同参数下的结果:
cat_style_5e-3.png
cat_style.png
cat5e-4.png
car1e-3.png
content图片:
cat.jpg
style图片:
1-style.jpg

调参数主要是针对 1)content 与 style 的权重比值; 2)Learning rate;3)迭代次数。

目前看来效果要好还要多试几个
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
Cirno作者
8年1个月前 IP:美国
828504
car_style_1e-2.png
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
Cirno作者
8年1个月前 IP:美国
828509

output.mp4  点击下载
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
Cirno作者
8年1个月前 IP:美国
828582
补充最新结果:
cat_5e-4.jpg
cat_1e-3.jpg
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
Cirno作者
8年1个月前 IP:美国
828583
nyc1.jpg
0390_frame.jpg
0299_frame.jpg
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
Cirno作者
8年1个月前 IP:美国
828584
主要改进了初始化方法
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
云中子3529
7年7个月前 IP:安徽
835065
XXXXXXXXXXXXXXXXXX/jcjohnson/fast-neural-style
或许你应该看看这个……
快到没朋友的
如果不是他的程序整天1提示Buy a new RAM的话……
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
Cirno作者
7年4个月前 IP:美国
839249
引用 云中子3529:
XXXXXXXXXXXXXXXXXX/jcjohnson/fast-neural-style
或许你应该看看这个……
快到没朋友的
如果不是他的程序整天1提示Buy a new RAM的话……
写之前我就看过了
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
novakon
7年0个月前 IP:广东
842370

我在这里贴一下我做的style transfer,技巧和楼主是一样的。当时是发在个人博客上的:XXXXXXXXXXXXXXXXXXXXXXXXX/site/on_learning/image/style_XXXXXXXXXXXml

image.png

具体的过程和解释也写在那边。

注意到图中噪声很大,这是因为VGG用了MaxPool,导致Adversarial Sample的机会出奇地多。这种噪声是有解决方案的,参见 https://distill.pub/2017/feature-visualization/

引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论

想参与大家的讨论?现在就 登录 或者 注册

所属专业
所属分类
上级专业
同级专业
Cirno
专家 进士 老干部 学者 机友 笔友
文章
34
回复
359
学术分
2
2012/09/03注册,5个月17天前活动

Machine Learning, computer vision enthusiast

Google

主体类型:个人
所属领域:无
认证方式:手机号
IP归属地:未同步
插入公式
评论控制
加载中...
文号:{{pid}}
投诉或举报
加载中...
{{tip}}
请选择违规类型:
{{reason.type}}

空空如也

加载中...
详情
详情
推送到专栏从专栏移除
设为匿名取消匿名
查看作者
回复
只看作者
加入收藏取消收藏
收藏
取消收藏
折叠回复
置顶取消置顶
评学术分
鼓励
设为精选取消精选
管理提醒
编辑
通过审核
评论控制
退修或删除
历史版本
违规记录
投诉或举报
加入黑名单移除黑名单
查看IP
{{format('YYYY/MM/DD HH:mm:ss', toc)}}