zoukankan      html  css  js  c++  java
  • 4.keras实现-->生成式深度学习之DeepDream

    DeepDream是一种艺术性的图像修改技术,它用到了卷积神经网络学到的表示,DeepDream由Google于2015年发布。这个算法与卷积神经网络过滤器可视化技术几乎相同,都是反向运行一个卷积神经网络:对卷积神经网络的输入做梯度上升,以便将卷积神经网络靠顶部的某一层的某个过滤器激活最大化。但有以下几个简单的区别:

    • 使用DeepDream,我们尝试将所有层的激活最大化,而不是将某一层的激活最大化,因此需要同时将大量特征的可视化混合在一起
    • 不是从空白的、略微带有噪声的输入开始,而是从现有的图像开始,因此所产生的效果能够抓住已经存在的视觉模式,并以某种艺术性的方式将图像扭曲
    • 输入图像是在不同的尺度上【叫作八度(octave)】进行处理的,这可以提高可视化的质量

    我们将从一个在ImageNet上预训练的卷积神经网络(Keras内置的Inception V3模型)开始

    #加载预训练的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层的配置,但以后一定要探索多个不同的配置

    # 设置DeepDream配置
    layer_contributions={
        'mixed2':0.2,
        'mixed3':3.,
        'mixed4':2,
        'mixed5':1.5,
    }
    #这个字典将层的名称映射为一个系数,这个系数定量表示该层激活对你要最大化的损失的贡献大小。
    # 注意,层的名称硬编码在内置的inception V3应用中
    

      

    #定义需要最大化的损失
    
    #创建一个字典,将层的名称映射为层的实例
    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,:1])) / scaling
    

      

    
    
    layer_dict ={'input_4':
     <keras.engine.input_layer.InputLayer at 0x281714cc240>,
     'conv2d_283':
    <keras.layers.convolutional.Conv2D at 0x28171545240>,
    ...}

    activation.shape: (?, ?, ?, 288)(?, ?, ?, 768)

    (?, ?, ?, 768)(?, ?, ?, 768)

    loss: Tensor("add_19:0", shape=(),
    dtype=float32)
    
    
    
    
     

      

    #梯度上升过程
    
    #这个张量用于保存生成的图像,即梦境图像
    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的细节损失。

    #辅助函数
    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
    

      

    #在多个连续尺度上运行梯度上升
    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')
    

     训练损失:

     

                           

    原图 效果图
  • 相关阅读:
    Ubuntu中的解压缩文件的方式 分类: Java 2014-07-20 22:33 1264人阅读 评论(0) 收藏
    Ubuntu下安装JDK图文解析 分类: Java 2014-07-20 21:28 4565人阅读 评论(3) 收藏
    数据结构和算法设计专题之---24点游戏(穷举法和递归法) 分类: Java 2014-07-18 21:26 789人阅读 评论(0) 收藏
    数据结构和算法设计专题之---单链表的逆序 分类: Java 2014-07-18 21:24 562人阅读 评论(1) 收藏
    Android中实现静态的默认安装和卸载应用 分类: Android 2014-07-02 22:43 5952人阅读 评论(19) 收藏
    Git的使用方法 分类: Java 2014-06-18 18:34 520人阅读 评论(0) 收藏
    J2EE学习篇之--JQuery技术详解 分类: JavaWeb 2014-06-18 11:35 1958人阅读 评论(1) 收藏
    MyEclipse6.0中使用aptana插件,添加jquery提示功能 分类: JavaWeb 2014-06-04 15:35 648人阅读 评论(0) 收藏
    Android中如何搭建一个WebServer 分类: Android 2014-06-01 14:44 8708人阅读 评论(3) 收藏
    J2EE学习篇之--Spring技术详解 分类: JavaWeb 2014-05-28 10:15 1109人阅读 评论(0) 收藏
  • 原文地址:https://www.cnblogs.com/nxf-rabbit75/p/10012317.html
Copyright © 2011-2022 走看看