【何之源-21个项目玩转深度学习】——Chapter4-4.2.1 Tensorflow中Deep Dream模型实践(1)

首先引用下该书的原话内如如下,针对Deep Dream的概念的:

Deep Dream 是 Google 公司在 2015 年公布的一顶高趣的技术 。在训练好的卷积神经网络中, 只需要设定几个参数,就可以通过这项技术生成一张图像。生成出的图像不仅令人印象深刻,而且还能帮助我们理解卷积神经网络背后的运行机制 。 本章介绍 Deep Dream 的基本原理 ,并使用 TensorFlow 实现 Deep Dream 生成模型 。

Deep Dream 的技术原理
         在卷积网络中,输入一般是一张图像,中间层是若干卷积运算,输出是图像的类别。在训练阶段,会使用大量的训练图片计算梯度,网络根据梯度不断地调整和学习最佳的参数。对此,通常会有一些疑问,例如. ( I )卷积层究竟学习到了什么内容? ( 2 )卷积层的参数代表的意义是什么? ( 3 )浅层的卷积和深层的卷积学习到的内容有哪些区别?

Deep Dream可以解答上述问题 。
设输入网络的图像为 x ,网络输出的各个类别的概率为 t (如 ImageNet为 1000 种分类,在这种情况下, t 是一个 1000 维的向量 ,代表了 1000 种类别的概率),以香蕉类别为例,假设宫对应的概率输出值为 t[ 100],换句话说 ,t[100] 代表了神经网络认为一张图片是香蕉的概率 。设定 t[100]为优化目标,不断地让神经网络去调整输入图像 x 的像素值,让输出 t[100]尽可能的大。

如想弄清楚神经网络中间的卷积层究竟学到了什么 。  只需要最大化卷积层某一通道的输出就可以 。 同样设输入图像为 x ,中间某个卷积层的输出是 y 。 y 的形状应该是 h*w*c , h 为 y 的高度 , w 为 y 的宽度, c 则代表“通道数” 。 原始图像高 R 、 G 、 B 三个通道 , 而在大多数卷积层中,通道数都远远不止 3 个。卷积的一个通道就可以代表一种学习到的 “信息” 。 以某一个通道的平均值作为优化目标,就可以弄清楚这个通道究竟学习到了什么,这也是 Deep Dream 的基本原理 。 


Step 1:导入模型

下面以导入Inception模型为例,导入的代码如下:

# coding:utf-8
# 导入要用到的基本模块。
from __future__ import print_function
import numpy as np
import tensorflow as tf

# 创建图和Session
graph = tf.Graph()
sess = tf.InteractiveSession(graph=graph)

# tensorflow_inception_graph.pb文件中,既存储了inception的网络结构也存储了对应的数据
# 使用下面的语句将之导入
model_fn = 'tensorflow_inception_graph.pb'
with tf.gfile.FastGFile(model_fn, 'rb') as f:
    graph_def = tf.GraphDef()
    graph_def.ParseFromString(f.read())
# 定义t_input为我们输入的图像
t_input = tf.placeholder(np.float32, name='input')
imagenet_mean = 117.0
# 输入图像需要经过处理才能送入网络中
# expand_dims是加一维,从[height, width, channel]变成[1, height, width, channel]
# t_input - imagenet_mean是减去一个均值
t_preprocessed = tf.expand_dims(t_input - imagenet_mean, 0)
tf.import_graph_def(graph_def, {'input': t_preprocessed})

# 找到所有卷积层
layers = [op.name for op in graph.get_operations() if op.type == 'Conv2D' and 'import/' in op.name]

# 输出卷积层层数
print('Number of layers', len(layers))

# 特别地,输出mixed4d_3x3_bottleneck_pre_relu的形状
name = 'mixed4d_3x3_bottleneck_pre_relu'
print('shape of %s: %s' % (name, str(graph.get_tensor_by_name('import/' + name + ':0').get_shape())))

其中https://storage.googleapis.com/download.tensorflow.org/models/inception5h.zip 下载解压得到模型文件tensorflow_inception_graph.pb 

其中layers是个list,里面包含graph中的刷选出来的操作名,关于graph.get_operations() 这个函数具体使用,请参考这篇文章:get_operations()

那么打印下layers的内容看下具体是些什么:

['import/conv2d0_pre_relu/conv',
 'import/conv2d1_pre_relu/conv',
 'import/conv2d2_pre_relu/conv',
 'import/mixed3a_pool_reduce_pre_relu/conv',
 'import/mixed3a_5x5_bottleneck_pre_relu/conv',
 'import/mixed3a_5x5_pre_relu/conv',
 'import/mixed3a_3x3_bottleneck_pre_relu/conv',
 'import/mixed3a_3x3_pre_relu/conv',
 'import/mixed3a_1x1_pre_relu/conv',
 'import/mixed3b_pool_reduce_pre_relu/conv',
 'import/mixed3b_5x5_bottleneck_pre_relu/conv',
 'import/mixed3b_5x5_pre_relu/conv',
 'import/mixed3b_3x3_bottleneck_pre_relu/conv',
 'import/mixed3b_3x3_pre_relu/conv',
 'import/mixed3b_1x1_pre_relu/conv',
 'import/mixed4a_pool_reduce_pre_relu/conv',
 'import/mixed4a_5x5_bottleneck_pre_relu/conv',
 'import/mixed4a_5x5_pre_relu/conv',
 'import/mixed4a_3x3_bottleneck_pre_relu/conv',
 'import/mixed4a_3x3_pre_relu/conv',
 'import/mixed4a_1x1_pre_relu/conv',
 'import/head0_bottleneck_pre_relu/conv',
 'import/mixed4b_pool_reduce_pre_relu/conv',
 'import/mixed4b_5x5_bottleneck_pre_relu/conv',
 'import/mixed4b_5x5_pre_relu/conv',
 'import/mixed4b_3x3_bottleneck_pre_relu/conv',
 'import/mixed4b_3x3_pre_relu/conv',
 'import/mixed4b_1x1_pre_relu/conv',
 'import/mixed4c_pool_reduce_pre_relu/conv',
 'import/mixed4c_5x5_bottleneck_pre_relu/conv',
 'import/mixed4c_5x5_pre_relu/conv',
 'import/mixed4c_3x3_bottleneck_pre_relu/conv',
 'import/mixed4c_3x3_pre_relu/conv',
 'import/mixed4c_1x1_pre_relu/conv',
 'import/mixed4d_pool_reduce_pre_relu/conv',
 'import/mixed4d_5x5_bottleneck_pre_relu/conv',
 'import/mixed4d_5x5_pre_relu/conv',
 'import/mixed4d_3x3_bottleneck_pre_relu/conv',
 'import/mixed4d_3x3_pre_relu/conv',
 'import/mixed4d_1x1_pre_relu/conv',
 'import/head1_bottleneck_pre_relu/conv',
 'import/mixed4e_pool_reduce_pre_relu/conv',
 'import/mixed4e_5x5_bottleneck_pre_relu/conv',
 'import/mixed4e_5x5_pre_relu/conv',
 'import/mixed4e_3x3_bottleneck_pre_relu/conv',
 'import/mixed4e_3x3_pre_relu/conv',
 'import/mixed4e_1x1_pre_relu/conv',
 'import/mixed5a_pool_reduce_pre_relu/conv',
 'import/mixed5a_5x5_bottleneck_pre_relu/conv',
 'import/mixed5a_5x5_pre_relu/conv',
 'import/mixed5a_3x3_bottleneck_pre_relu/conv',
 'import/mixed5a_3x3_pre_relu/conv',
 'import/mixed5a_1x1_pre_relu/conv',
 'import/mixed5b_pool_reduce_pre_relu/conv',
 'import/mixed5b_5x5_bottleneck_pre_relu/conv',
 'import/mixed5b_5x5_pre_relu/conv',
 'import/mixed5b_3x3_bottleneck_pre_relu/conv',
 'import/mixed5b_3x3_pre_relu/conv',
 'import/mixed5b_1x1_pre_relu/conv']

以上就是用jupyter notebooke执行In[]: layers的结果,总共满足刷选条件的操作名有59个,我们指定一个特定的操作名来观察shape,比如代码中的name = 'mixed4d_3x3_bottleneck_pre_relu'

打印的结果:  shape of mixed4d_3x3_bottleneck_pre_relu: (?, ?, ?, 144)    

以上结果的原因是不知道输入的图像个数,尺寸和通道数,因此前3项是未知的问号。

(一个需要注意的地方是,还需要为图像减去一个像素均值 。这是由于在训练 Inception 模型的时候,已经做了减去均值的预处理,因此应该使用同样的预处理方法,才能保持输入的一致 。 此处使用的 Inception 模型减去的是一个固定的均值 117 ,所以在程序中也定义7 imagenet_mean= 117 ,并用 t_input 减去 imagenet_mean 。)
 


Step2: 生成原始的Deep Dream图

这部分的完整代码如下:

# coding: utf-8
from __future__ import print_function
import os
from io import BytesIO
import numpy as np
from functools import partial
import PIL.Image
import scipy.misc
import tensorflow as tf


graph = tf.Graph()
model_fn = 'tensorflow_inception_graph.pb'
sess = tf.InteractiveSession(graph=graph)
with tf.gfile.FastGFile(model_fn, 'rb') as f:
    graph_def = tf.GraphDef()
    graph_def.ParseFromString(f.read())
t_input = tf.placeholder(np.float32, name='input')
imagenet_mean = 117.0
t_preprocessed = tf.expand_dims(t_input - imagenet_mean, 0)
tf.import_graph_def(graph_def, {'input': t_preprocessed})


def savearray(img_array, img_name):
    scipy.misc.toimage(img_array).save(img_name)
    print('img saved: %s' % img_name)


def render_naive(t_obj, img0, iter_n=20, step=1.0):
    # t_score是优化目标。它是t_obj的平均值
    # 结合调用处看,实际上就是layer_output[:, :, :, channel]的平均值
    t_score = tf.reduce_mean(t_obj)
    # 计算t_score对t_input的梯度
    t_grad = tf.gradients(t_score, t_input)[0]

    # 创建新图
    img = img0.copy()
    for i in range(iter_n):
        # 在sess中计算梯度,以及当前的score
        g, score = sess.run([t_grad, t_score], {t_input: img})
        # 对img应用梯度。step可以看做“学习率”
        g /= g.std() + 1e-8
        img += g * step
        print('score(mean)=%f' % (score))
    # 保存图片
    savearray(img, 'naive.jpg')

# 定义卷积层、通道数,并取出对应的tensor
name = 'mixed4d_3x3_bottleneck_pre_relu'
channel = 139
layer_output = graph.get_tensor_by_name("import/%s:0" % name)

# 定义原始的图像噪声
img_noise = np.random.uniform(size=(224, 224, 3)) + 100.0
# 调用render_naive函数渲染
render_naive(layer_output[:, :, :, channel], img_noise, iter_n=20)

运行结果如下:

score(mean)=-20.088280
score(mean)=-30.066317
score(mean)=12.236075
score(mean)=85.736198
score(mean)=155.088699
score(mean)=213.084106
score(mean)=273.888580
score(mean)=323.319122
score(mean)=378.042328
score(mean)=416.296783
score(mean)=455.985138
score(mean)=500.792450
score(mean)=532.272156
score(mean)=569.163086
score(mean)=596.386108
score(mean)=627.763367
score(mean)=650.017944
score(mean)=684.536133
score(mean)=698.245605
score(mean)=729.858521
img saved: naive.jpg

保存的图像如下显示:

 

 

 

 

 

函数的参数 t_obj 实际上就是 layer_output[:, :, :, channel],也就是说是卷积层某个通道的值。t_score = tf.reduce_mean(t_obj),意即 t_score 是 t_obj 的平均值 。t_score 越大 ,就说明神经网络卷积层对应通道的平均激活越大 。 本节的目标就是通过调整
输入图像 t_input ,来让 t_score 尽可能的大。为此使用梯度下降法,定义梯度 t_grad = tf.gradients(t_score, t_input)[0],在后面的程序中,会把计算得到的梯度应用到输入图像上。
当选择channel=110对应生成的图像如下:

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值