下面编写主内容
if __name__ == '__main__': name = 'mixed4d_3x3_bottleneck_pre_relu' channel = 139 img_noise = np.random.uniform(size=(224, 224, 3)) + 100.0 layer_output = graph.get_tensor_by_name("import/%s:0" % name) render_multiscale(layer_output[:, :, :, channel], img_noise, iter_n=20)
运行代码后,将生成一张大尺寸的图片,如下图:
从图中可以看出,mixed4d_3x3_bottleneck_pre_relu 卷积层的第139个通道实际上就是学到了某种花朵的特征。
4.生成高质量图片
前面两节生成的图片都是分辨率不高的图片,这节将生成高质量的图片。在图像处理算法中,有 高频成分 和 低频成分 之分。所谓高频成分,是指图像中灰度、颜色、明度变化比较剧烈的地方,比如边缘、细节部分。低频成分是指图像变化不大的地方,比如大块色块、整体风格。
上节生成的图片高频成分太多,图片不够柔和。如何解决这个问题呢?一种方法是针对高频成分加入损失,这样图像在生成的时候就会因为新加入损失的作用二发生变化,但是加入损失会导致计算量和收敛步数增大。另一种方法是 放大低频梯度 ,对梯度进行分解,降至分为 高频梯度 和 低频梯度 ,在人为的去放大低频梯度,就可以得到较为柔和的图像。
一般情况下,要使用 拉普拉斯金字塔 对图像进行分解,这种算法可以把图片分解为多层。同时,也可以对梯队进行分解,分解之后,对高频的梯度和低频的梯度都做标准化,可以让梯度的低频成分和高频成分差不多,表现在图像上就会增加图像的低频成分,从而提高生成图像的质量。这种方法称为 拉普拉斯金字塔标准化,具体实现代码如下:
k = np.float32([1, 4, 6, 4, 1]) k = np.outer(k, k) k5x5 = k[:, :, None, None] / k.sum() * np.eye(3, dtype=np.float32) # 这个函数将图像分为低频和高频成分 def lap_split(img): with tf.name_scope('split'): # 做一次卷积相当于一次平滑,因此lo为低频成分 lo = tf.nn.conv2d(img, k5x5, [1, 2, 2, 1], 'SAME') # 低频成分缩放到原始图像大叫就得到lo2,再用原始图像img减去lo2,就得到高频成分hi lo2 = tf.nn.conv2d_transpose(lo, k5x5 * 4, tf.shape(img), [1, 2, 2, 1]) hi = img - lo2 return lo, hi # 这个函数将图像img分成n层拉普拉斯金字塔 def lap_split_n(img, n): levels = [] for i in range(n): # 调用lap_split将图像分为低频和高频部分 # 高频部分保存到levels中 # 低频部分再继续分解 img, hi = lap_split(img) levels.append(hi) levels.append(img) return levels[::-1] # 将拉普拉斯金字塔还原到原始图像 def lap_merge(levels): img = levels[0] for hi in levels[1:]: with tf.name_scope('merge'): img = tf.nn.conv2d_transpose(img, k5x5 * 4, tf.shape(hi), [1, 2, 2, 1]) + hi return img # 对img做标准化 def normalize_std(img, eps=1e-10): with tf.name_scope('normalize'): std = tf.sqrt(tf.reduce_mean(tf.square(img))) return img / tf.maximum(std, eps) # 拉普拉斯金字塔标准化 def lap_normalize(img, scale_n=4): img = tf.expand_dims(img, 0) tlevels = lap_split_n(img, scale_n) # 每一层都做一个normalize_std tlevels = list(map(normalize_std, tlevels)) out = lap_merge(tlevels) return out[0, :, :, :]
编写完拉普拉斯标准化函数后,现在编写生成图像的代码:
# 将一个Tensor函数转换成numpy.ndarray 函数 def tffunc(*argtypes): placeholders = list(map(tf.placeholder, argtypes)) def wrap(f): out = f(*placeholders) def wrapper(*args, **kw): return out.eval(dict(zip(placeholders, args)), session=kw.get('session')) return wrapper return wrap def render_lapnorm(t_obj, img0, iter_n=10, step=1.0, octave_n=3, octave_scale=1.4, lap_n=4): # 同样定义目标和梯度 t_score = tf.reduce_mean(t_obj) t_grad = tf.gradients(t_score, t_input)[0] # 将lap_normalize转换为正常函数 lap_norm_func = tffunc(np.float32)(partial(lap_normalize, scale_n=lap_n)) img = img0.copy() for octave in range(octave_n): if octave > 0: img = resize_ratio(img, octave_scale) for i in range(iter_n): g = calc_grad_tiled(img, t_grad) # 唯一的区别在于我们使用lap_norm_func来标准化g! g = lap_norm_func(g) img += g * step print('.', end=' ') savearray(img, 'lapnorm.jpg') if __name__ == '__main__': name = 'mixed4d_3x3_bottleneck_pre_relu' channel = 139 img_noise = np.random.uniform(size=(224, 224, 3)) + 100.0 layer_output = graph.get_tensor_by_name('import/%s:0' % name) render_lapnorm(layer_output[:, :, :, channel], img_noise, iter_n=20)
运行上面代码后,将生成高质量的图片:
5. 生成最终的图片
前面已经讲解了如何通过极大化卷积层摸个通道的平均值生成图片,并学习了如何生成更大和质量更高的图像。但是最终的Deep Dream 模型还需要对图片添加一个背景。具体代码如下:
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 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 # 同样将图像进行金字塔分解 # 提取高频和低频的方法比较简单,直接缩放 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)) print('.', end=' ') img = img.clip(0, 255) savearray(img, 'deepdream.jpg') if __name__ == '__main__': img0 = PIL.Image.open('test.jpg') img0 = np.float32(img0) # name = 'mixed4d_3x3_bottleneck_pre_relu' name = 'mixed4c' # channel = 139 layer_output = graph.get_tensor_by_name('import/%s:0' % name) # render_deepdream(layer_output[:, :, :, channel], img0, iter_n=150) render_deepdream(tf.square(layer_output), img0)