我们可以选择任意卷积神经网络来实现 DeepDream, 不过卷积神经网络会影响可视化的效果,因为不同的卷积神经网络架构会学到不同的特征。接下来将使用 Keras 内置的 Inception V3模型来够生成漂亮的 DeepDream 图像
步骤如下
加载预训练的 Inception V3 模型
计算损失(loss),即在梯度上升过程中需要最大化的量
将多个层的所有过滤器的激活同时最大化。具体来说,就是对一组靠近顶部的层激活的 L2 范数进行加权求和,然后将其最大化。选择哪些层(以及它们对最终损失的贡献)对生成的可视化结果具有很大影响,所以我们希望让这些参数变得易于配置。更靠近底部的层生成的是几何图案,而更靠近顶部的层生成的则是从中能够看出某些 ImageNet 类别(比如鸟或狗)的图案
(2)设置 DeepDream 配置
(2)定义需要最大化的损失
设置梯度上升过程
在多个连续尺度上运行梯度上升
demo
from keras.applications import inception_v3 from keras import backend as K import numpy as np import scipy from keras.preprocessing import image def resize_img(img, size): img = np.copy(img) factors = (1, float(size[0]) / img.shape[1], float(size[1]) / img.shape[2], 1) return scipy.ndimage.zoom(img, factors, order=1) def save_img(img, fname): pil_img = deprocess_image(np.copy(img)) scipy.misc.imsave(fname, pil_img) def preprocess_image(image_path): img = image.load_img(image_path) img = image.img_to_array(img) img = np.expand_dims(img, axis=0) img = inception_v3.preprocess_input(img) return img def deprocess_image(x): if K.image_data_format() == 'channels_first': x = x.reshape((3, x.shape[2], x.shape[3])) x = x.transpose((1, 2, 0)) else: x = x.reshape((x.shape[1], x.shape[2], 3)) x /= 2. x += 0.5 x *= 255. x = np.clip(x, 0, 255).astype('uint8') return x # 这个命令会禁用所有与训练有关的操作 K.set_learning_phase(0) # 构建不包括全连接层的 Inception V3网络。使用预训练的 ImageNet 权重来加载模型 model = inception_v3.InceptionV3(weights='imagenet', include_top=False) # 将层的名称映射为一个系数,这个系数定量表示该层激活对你要最大化的损失的贡献大小 layer_contributions = { 'mixed2': 0.2, 'mixed3': 3., 'mixed4': 2., 'mixed5': 1.5, } # 定义最大化的损失 layer_dict = dict([(layer.name, layer) for layer in model.layers]) loss = K.variable(0.) for layer_name in layer_contributions: coeff = layer_contributions[layer_name] activation = layer_dict[layer_name].output scaling = K.prod(K.cast(K.shape(activation), 'float32')) # 将该层特征的L2范数添加到loss中。为了避免出现边界伪影,损失中仅包含非边界的像素 loss += coeff * K.sum(K.square(activation[:, 2: -2, 2: -2, :])) / scaling # 梯度上升过程 # 这个张量用于保存生成的图像,即梦境图像 dream = model.input # 计算损失相对于梦境图像的梯度 grads = K.gradients(loss, dream)[0] # 将梯度标准化 grads /= K.maximum(K.mean(K.abs(grads)), 1e-7) outputs = [loss, grads] # 给定一张输出图像,设置一个 Keras 函数来获取损失值和梯度值 fetch_loss_and_grads = K.function([dream], outputs) def eval_loss_and_grads(x): outs = fetch_loss_and_grads([x]) loss_value = outs[0] grad_values = outs[1] return loss_value, grad_values # 运行 iterations次梯度上升 def gradient_ascent(x, iterations, step, max_loss=None): for i in range(iterations): loss_value, grad_values = eval_loss_and_grads(x) if max_loss is not None and loss_value > max_loss: break print('...Loss value at', i, ':', loss_value) x += step * grad_values return x # 在多个连续尺度上运行梯度上升 # 梯度上升的步长 step = 0.01 # 运行梯度上升的尺度个数 num_octave = 3 # 两个尺度之间的大小比例 octave_scale = 1.4 iterations = 20 # 如果损失增大到大于 10,我们要中断梯度上升过程,以避免得到丑陋的伪影 max_loss = 10. base_image_path = 'img_url' img = preprocess_image(base_image_path) original_shape = img.shape[1:3] successive_shapes = [original_shape] for i in range(1, num_octave): # 一个由形状元组组成的列表,它定义了运行梯度上升的不同尺度 shape = tuple([int(dim / (octave_scale ** i)) for dim in original_shape]) successive_shapes.append(shape) # 将形状列表反转,变为升序 successive_shapes = successive_shapes[::-1] original_img = np.copy(img) # 将图像 Numpy 数组的大小缩放到最小尺寸 shrunk_original_img = resize_img(img, successive_shapes[0]) for shape in successive_shapes: print('Processing image shape', shape) # 将梦境图像放大 img = resize_img(img, shape) # 运行梯度上升,改变梦境图像 img = gradient_ascent(img, iterations=iterations, step=step, max_loss=max_loss) # 将原始图像的较小版本放大,它会变得像素化 upscaled_shrunk_original_img = resize_img(shrunk_original_img, shape) # 在这个尺寸上计算原始图像的高质量版本 same_size_original = resize_img(original_img, shape) lost_detail = same_size_original - upscaled_shrunk_original_img # 将丢失的细节重新注入到梦境图像中 img += lost_detail shrunk_original_img = resize_img(original_img, shape) save_img(img, fname='dream_at_scale_' + str(shape) + '.png') save_img(img, fname='final_dream.png')