DeepDream是利用指定标签,通过方向传播调整输入图像来让固有的CNN网络能够生成出目标标签图像。它与CNN的不同在于,CNN是利用真实标签对训练图片集的误差来修正神经网络,让神经网络能够识别图像。而DeepDream则是利用一个固定的网络,让一个随机图像能够逼近网络的识别图像。
!
下图就是deep_dream生成的海星图
下面的代码是经过优化的,可生成高质量deep_dream图像的代码,可以直接运行测试。使用的模型是训练好的GoogleNet的Inception。
#导入库与inception模型
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()
#从inception读入模型,该模型文件存放在当前目录下
model_fn = 'tensorflow_inception_graph.pb'
sess = tf.InteractiveSession(graph=graph)
with tf.gfile.GFile(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)
#将图像放大ratio倍
def resize_ratio(img,ratio):
min = img.min()
max = img.max()
img = (img-min)/(max-min)*255
img = np.float32(scipy.misc.imresize(img,ratio))
img = img/255*(max-min)+min
return img
#调整图像尺寸
def resize(img,hw):
min = img.min()
max = img.max()
img = (img-min)/(max-min)*255
img = np.float32(scipy.misc.imresize(img,hw))
img = img/255*(max-min) +min
return img
#每次只对tile_sieze*tile_size大小的图像计算梯度,避免内存问题
def calc_grad_tiled(img,t_grad,tile_size=512):
sz = tile_size
h,w=img.shape[:2]
sx,sy = np.random.randint(sz,size=2)
#先在行上做整体移动,再在列上做整体移动
img_shift = np.roll(np.roll(img,sx,1),sy,0)
grad = np.zeros_like(img)
for y in range(0,max(h-sz//2,sz),sz):
for x in range(0,max(w-sz//2,sz),sz):
sub = img_shift[y:y+sz,x:x+sz]
g= sess.run(t_grad,{t_input:sub})
grad[y:y+sz,x:x+sz] = g
return np.roll(np.roll(grad,-sx,1),-sy,0)
#改进的渲染函数,octave_n是金字塔的层数,octive_scale是层与层之间的倍数
def render_deepdream(t_obj,img0,iter_n=10,step=1.5,octave_n =4,octave_scale=1.4):
t_score = tf.reduce_mean(t_obj)
t_grad = tf.gradients(t_score,t_input)[0]
img = img0.copy()
#将图像进行金字塔分解
#分成高频和低频部分
octaves = []
for i in range(octave_n -1):
#获取图像尺寸的前两位 高和宽
hw=img.shape[:2]
lo = resize(img,np.int32(np.float32(hw)/octave_scale))
hi = img - resize(lo,hw)
img = lo
octaves.append(hi)
#首先生成低频的图像,再以此放大并加上高频
for octave in range(octave_n):
if octave>0:
hi = octaves[-octave]
img = resize(img,hi.shape[:2])+hi
for i in range(iter_n):
g = calc_grad_tiled(img,t_grad)
img += g*(step/(np.abs(g).mean()+1e-7))
img = img.clip(0,255)
savearray(img,'test_deepdream.jpg')
im = PIL.Image.open("test_deepdream.jpg").show()
#生成以背景图像作为起点的DeepDream图像
#从incption模型中取出某层卷积张量
name = 'mixed4c'
layer_output = graph.get_tensor_by_name("import/%s:0"%name)
img0 = PIL.Image.open('test.jpg')
img0 = np.float32(img0)
render_deepdream(tf.square(layer_output),img0,iter_n=100)
从代码可以看到,从训练好了的网络模型中取出mixed4c这一层的卷积张量。利用该卷积张量对预置图像做训练,训练后可以看到这一层卷积对图像的作用效果。
训练前的原图:
训练后的deep_dream图片
能够直观地观察到某一层的卷积提取的是图像的什么信息。总体概括来说,卷积层越深,提取的特征就越抽象,卷积核的通道越多,提取的特征就越抽象。
PS:本例的模型tensorflow_inception_graph.pb从https://storage.googleapis.com/download.tensorflow.org/models/inception5h.zip下载
PS:本例的图像预处理是根据tensorflow_inception_graph模型做的预处理,目的就是保证输入图像与模型训练输入图像的维度保持一致