文章目录
用keras实现DeepDream
我们将在一个ImageNet上一个预训练的卷积神经网络开始。Keras中有许多这样的网络,使用DeepDream有以下几个特点
- 使用DeepDream,我们尝试将所有的层激活最大化,而不是某一层激活的最大化
- 同时将大量特征的可视化混合在一起
- 我们直接从现有的图像开始
- 输入的图像实在不同的尺度上,可以提高可视化的质量
接下来我们将使用InceptionV3来实现这一模型
8-8 加载预训练的 Inception V3模型
from keras.applications import inception_v3
from keras import backend as K
# 我们不需要训练模型,所以这个命令会禁用掉与训练有关的操作
K.set_learning_phase(0)
# 构建不包括全连接层的Inception V3网络,使用预训练的ImageNet权重来加载模型
model = inception_v3.InceptionV3(weights = 'imagenet',
include_top = False)
Downloading data from https://github.com/fchollet/deep-learning-models/releases/download/v0.5/inception_v3_weights_tf_dim_ordering_tf_kernels_notop.h5
87916544/87910968 [==============================] - 38s 0us/step
接下来我们将计算损失,因为我们需要将所有的激活层同时最大化,我们需要给每个激活层加上权重,
然后对一组靠近顶部的L2范数进行加权求和,最后将其最大化。
8-9 设置DeepDream配置
# 将层的名称映射为一个系数,这个系数定量表示该层的激活大小
layer_contributions = {
'mixed2' : 0.2,
'mixed3' : 3.,
'mixed4' : 2.,
'mixed5' : 1.5,
}
接下来我们定义一个包含损失的张量
8-10 定义需要最大化的损失
# 创建一个字典,将层的名称映射为层的实例
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 +
loss = loss + coeff * K.sum(K.square(activation[:, 2: -2, 2: -2, :])) / scaling
8-11 梯度上升过程
# 该张量用于保存生成的图像,即梦境图像
dream = model.input
# 计算损失相对于梦境图像的梯度
grads = K.gradients(loss, dream)[0]
# 将梯度标准化
grads /= K.maximum(K.mean(K.abs(grads)), 1e-7)
# 给定一张输出图像
outputs = [loss, grads]
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
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
最后就是实际的DeepDream算法,首先我们来定义一个列表,里面包含图像的尺度,每个连续的尺度都是前一个的1.4倍
先处理小图像,然后逐步放大尺寸
8-12 在多个连续尺度上运行梯度上升
这里的图像可以是任意的你可以选用一张你自己喜欢的图像进行生成,我这里选用了一张《底特律:变人》
的游戏海报,仅供参考
下面是 8-12 部分的代码
import numpy as np
import scipy.misc
import imageio
# 改变下列超参数,可以得到新的结果
# 梯度上升的步长
step = 0.01
# 运行梯度上升的尺度个数
num_octave = 3
# 两个尺度之间的大小比例
octave_scale = 1.4
# 在每个尺度上运行梯度上升的步数
iterations = 20
# 如果损失增大到10,我们要中断梯度上升过程,以避免得到丑陋的伪影
max_loss = 10
# 将这个变量修改为你要使用的图像路径
base_image_path = r'E:\code\PythonDeep\DataSet\pic\detelv.jpg'
# 将基础图像加载为一个Numpy数组
img = preprocess_image(base_image_path) # 此函数在8-13中定义
orginal_shape = img.shape[1:3]
successive_shapes = [orginal_shape]
# 准备一个由形状元组组成的一个列表
for i in range(1, num_octave):
# 定义梯度上升的不同尺度
shape = tuple([int(dim / (octave_scale ** i)) for dim in orginal_shape])
successive_shapes.append(shape)
# 将列表形状翻转
successive_shapes = successive_shapes[::-1]
# 将Numpy数组的大小缩放到最小尺寸
original_img = np.copy(img)
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')
Processing image shape (135, 241)
...Loss value at 0 : 0.7860751
...Loss value at 1 : 0.94163644
...Loss value at 2 : 1.2914416
...Loss value at 3 : 1.6291509
...Loss value at 4 : 1.9439065
...Loss value at 5 : 2.344722
...Loss value at 6 : 2.716798
...Loss value at 7 : 3.0916216
...Loss value at 8 : 3.5412674
...Loss value at 9 : 3.878184
...Loss value at 10 : 4.213736
...Loss value at 11 : 4.551626
...Loss value at 12 : 4.8698916
...Loss value at 13 : 5.219514
...Loss value at 14 : 5.5585904
...Loss value at 15 : 5.8871818
...Loss value at 16 : 6.192877
...Loss value at 17 : 6.464039
...Loss value at 18 : 6.7419343
...Loss value at 19 : 7.064967
Processing image shape (190, 338)
...Loss value at 0 : 2.2956393
...Loss value at 1 : 3.4630678
...Loss value at 2 : 4.4372005
...Loss value at 3 : 5.344282
...Loss value at 4 : 6.2073174
...Loss value at 5 : 6.987385
...Loss value at 6 : 7.8124037
...Loss value at 7 : 8.535074
...Loss value at 8 : 9.338239
Processing image shape (266, 474)
...Loss value at 0 : 2.955113
...Loss value at 1 : 4.5737886
...Loss value at 2 : 6.358242
...Loss value at 3 : 8.686136
8-13 辅助函数
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)
# 注意,这里版本更新了,原来保存图像的方法用不了了
# 但是可以改成另外一个一样的图像函数
imageio.imsave(fname, pil_img)
def preprocess_image(image_path):
# 通用函数,用于打开图像,改变图像大小
# 以及将图像转为InceptionV3的格式
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
# 显示图像
from matplotlib import pyplot as plt
plt.imshow(deprocess_image(np.copy(img)))
plt.show()
最终效果
写在最后
注:本文代码来自《Python 深度学习》,做成电子笔记的方式上传,仅供学习参考,作者均已运行成功,如有遗漏请练习本文作者
各位看官,都看到这里了,麻烦动动手指头给博主来个点赞8,您的支持作者最大的创作动力哟!
<(^-^)>
才疏学浅,若有纰漏,恳请斧正
本文章仅用于各位同志作为学习交流之用,不作任何商业用途,若涉及版权问题请速与作者联系,望悉知