4.keras实现-->生成式深度学习之DeepDream
DeepDream是一种艺术性的图像修改技术,它用到了卷积神经网络学到的表示,DeepDream由Google于2015年发布。这个算法与卷积神经网络过滤器可视化技术几乎相同,都是反向运行一个卷积神经网络:对卷积神经网络的输入做梯度上升,以便将卷积神经网络靠顶部的某一层的某个过滤器激活最大化。但有以下几个简单的区别:
- 使用DeepDream,我们尝试将所有层的激活最大化,而不是将某一层的激活最大化,因此需要同时将大量特征的可视化混合在一起
- 不是从空白的、略微带有噪声的输入开始,而是从现有的图像开始,因此所产生的效果能够抓住已经存在的视觉模式,并以某种艺术性的方式将图像扭曲
- 输入图像是在不同的尺度上【叫作八度(octave)】进行处理的,这可以提高可视化的质量
我们将从一个在ImageNet上预训练的卷积神经网络(Keras内置的Inception V3模型)开始
1 2 3 4 5 6 7 8 9 | #加载预训练的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 ) |
接下来要计算损失loss,即在梯度上升过程中需要最大化的量。在第五章的过滤器可视化中,我们试图将某一层的某个过滤器的值最大化。这里,我们要将多个层的所有过滤器的激活同时最大化。具体来说,就是对一组靠近顶部的层激活的L2范数进行加权求和,然后将其最大化。选择哪些层(以及它们对最终损失的贡献)对生成的可视化结果具有很大影响,所以我们希望让这些参数变得易于配置。更靠近底部的层生成的是几何图案,而更靠近顶部的层生成的则是从中能够看出某些ImageNet类别(比如鸟或狗)的图案。我们将随意选择4层的配置,但以后一定要探索多个不同的配置
1 2 3 4 5 6 7 8 9 | # 设置DeepDream配置 layer_contributions = { 'mixed2' : 0.2 , 'mixed3' : 3. , 'mixed4' : 2 , 'mixed5' : 1.5 , } #这个字典将层的名称映射为一个系数,这个系数定量表示该层激活对你要最大化的损失的贡献大小。 # 注意,层的名称硬编码在内置的inception V3应用中 |
|
<keras.engine.input_layer.InputLayer at 0x281714cc240>,
'conv2d_283':
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | #梯度上升过程 #这个张量用于保存生成的图像,即梦境图像 dream = model. input #计算损失相对于梦境图像的梯度 grads = K.gradients(loss,dream)[ 0 ] #将梯度标准化(重要技巧) grads / = K.maximum(K.mean(K. abs (grads)), 1e - 7 ) #给定一张输出图像,设置一个keras函数来获取损失值和梯度值 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 #这个函数运行itertions次梯度上升 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倍(放大40%),即首先处理小图像,然后逐渐增大图像尺寸。对于每个连续的尺度,从最小到最大,我们都需要在当前尺度运行梯度上升,以便将之前定义的损失最大化。每次运行完梯度上升之后,将得到的图像放大40%。
在每次连续的放大之后(图像会变得模糊或像素化),为避免丢失大量图像细节,我们可以使用一个简单的技巧:每次放大之后,将丢失的细节重新注入到图像中。这种方法是可行的,因为我们知道原始图像放大到这个尺寸应该是什么样子。
给定一个较小的图像尺寸S和一个较大的图像尺寸L,你可以计算将原始图像大小调整为L与将原始图像大小调整为S之间的区别,这个区别可以定量描述从S到L的细节损失。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | #辅助函数 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) #把图片转成array形式 img = np.expand_dims(img,axis = 0 ) #改变图像大小 img = inception_v3.preprocess_input(img) #将图像格式转换为Inception V3模型能够处理的张量 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 )) #对inception_v3.preprocess_input所做的预处理进行反向操作 x / = 2. x + = 0.5 x * = 255. x = np.clip(x, 0 , 255 ).astype( 'uint8' ) return x |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | #在多个连续尺度上运行梯度上升 import numpy as np step = 0.01 #步长 num_octave = 3 #运行梯度上升的尺度个数 octave_scale = 1.4 #两个尺度之间的大小比例 iterations = 20 #在每个尺度上运行梯度上升的步数 max_loss = 10. #如果损失增大到大于10,我们要中断梯度上升的过程,以避免得到丑陋的伪影 base_image_path = 'IU.jpeg' #将这个变量修改为你要使用的图像的路径 img = preprocess_image(base_image_path) #将基础图像加载成一个numpy数组 original_shape = img.shape[ 1 : 3 ] #img.shape = (1, 776, 1200, 3) successive_shapes = [original_shape] #[(776, 1200)] for i in range ( 1 ,num_octave): #准备一个由形状元组组成的列表,它定义了运行梯度上升的不同尺度 shape = tuple ([ int (dim / (octave_scale * * i)) for dim in original_shape]) successive_shapes.append(shape) #[(776, 1200), (554, 857), (395, 612)] successive_shapes = successive_shapes[:: - 1 ] #将形状列表反转,变为升序 original_img = np.copy(img) shrunk_original_img = resize_img(img,successive_shapes[ 0 ]) #将图像numpy数组的大小缩放到最小尺寸 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' ) |
训练损失:
原图 | 效果图 |
![]() |
![]() |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现