vieyahn2017 / iBlog

44 stars 0 forks source link

1.3 深度学习之生成式对抗网络GAN #346

Closed vieyahn2017 closed 3 months ago

vieyahn2017 commented 4 years ago

深度学习之生成式对抗网络GAN https://blog.csdn.net/liuy9803/article/details/82597934

一、GAN介绍

生成式对抗网络GAN(Generative Adversarial Networks)是一种深度学习模型,是近年来复杂分布上无监督学习最具前景的方法之一。模型通过框架中(至少)两个模块:生成模型(Generative model)和判别模型(Discriminative model)的相互博弈学习产生相当好的输出。原始GAN理论中并不要求G和D都是神经网络,只需要是能够拟合相应生成和判别的函数即可。但实际应用中一般均使用深度神经网络DNN/MLP作为G和D。一个优秀的GAN应用需要有良好的的训练方法,否则可能由于神经网络模型的自由性而导致输出不理想。

GAN将机器学习中的两大模型紧密结合在了一起,在这个框架中将会有两个模型被同时训练:G用来捕获数据分布,D用来估计样本来自训练数据而不是G的概率,G的训练目的是最大化D产生错误的概率。这个框架相当于一个极小化极大的双方博弈。在任意函数G和D的空间中存在唯一解,此时G恢复训练数据分布,且D处处都等于1/2。在G和D由DNN构成的情况下,可以使用反向传播进行训练,在训练或生成样本时不需要任何马尔可夫链或展开的近似推理网络。

GAN总结

1、优点

(1)生成模型G不需要直接用样本更新,避免了损失函数设计的困难;

(2)GAN是一个非常灵活的框架,各种类型的损失函数都可以整合到GAN模型中,可以产生更加清晰、真实的样本,广泛用于无监督学习和半监督学习领域;

(3)相比于变分自编码器,GAN没有引入任何决定性偏置(deterministic bias),由于变分方法引入了决定性偏置,是优化对数似然的下界,而不是似然本身,导致VAE生成的实例比GAN模糊。GAN没有变分下界,如果训练良好,GAN是渐进一致的。

(4)GAN相比其他的生成模型(受限玻尔兹曼机RBM、DBM、生成随机网络GSN等)只用到了反向传播,不需要复杂的Markov链;GAN的表达能力更强,而基于Markov链的模型需要分布比较模糊才能在不同的模式间进行混合。

vieyahn2017 commented 4 years ago

生成对抗网络(GAN)的5个最有趣的应用

https://blog.csdn.net/weixin_42137700/article/details/93988738 转载喜欢打酱油的老鸟 发布于2019-06-29 10:24:43 阅读数 845 收藏 展开 https://www.toutiao.com/a6706480830081925646/

“GAN”这个词是由Ian Goodfellow在2014年引入的,但这个概念早在1990年就开始存在(由JürgenSchmidhuber开创)。 事实上,GAN现在无处不在。 数据科学家和深度学习研究人员使用这种技术生成逼真的图像,改变面部表情,创建计算机游戏场景,可视化设计,最近甚至生成令人惊叹的艺术作品。

FAIZAN SHAIKH最近总结了GAN网络的5个最有趣的应用。非常值得一看。

用于图像编辑的GAN

如今,大多数图像编辑软件都没有给我们很大的灵活性来对图片进行创造性的改变。 例如,假设你想通过改变他/她的发型来改变一个90岁的人的外表。 目前的图像编辑工具无法做到这一点。 但猜猜怎么了? 使用GAN,我们可以重建图像并尝试彻底改变外观。

生成对抗网络(GAN)的5个最有趣的应用

《Invertible Conditional GANs for image editing》这篇文章描述了如何编辑图片。

用于安全性GAN

对于大多数行业来说,人工智能的兴起是很棒的事情。 但真正令人担忧的是即使是深层神经网络也容易被黑客入侵。

算法在工业应用应当考虑对网络攻击具有鲁棒性。 生产线上有很多机密信息! 事实证明,GAN在这方面提供了巨大的帮助,直接解决了“对抗性攻击”的问题。

这些对抗性攻击使用各种技术来欺骗深度学习架构。 GAN用于使现有的深度学习模型对这些技术更加健壮。 它通过创建更多这样的假例子并训练模型来识别它们。 非常聪明的东西。

一种名为SSGAN(论文:《SSGAN: Secure Steganography Based on Generative Adversarial Networks》)的技术用于对图像进行隐写分析并检测不应存在的有害编码。

使用GAN生成数据

我们中间谁不愿意收集更多数据来构建我们的深度学习模型? 某些领域的数据可用性是必要的,特别是在需要训练数据来训练学习算法的领域,例如医疗行业。

GAN可用于生成有监督的合成数据。

例如,本文(《Learning from Simulated and Unsupervised Images through Adversarial Training 》)探讨了在GAN的帮助下通过创建逼真的眼睛图像来训练深度学习算法的合成数据的创建。

生成对抗网络(GAN)的5个最有趣的应用

注意力预测的GAN

当我们看到图像时,我们倾向于关注特定部分(而不是整个图像)。 这被称为注意力,是一种重要的人类特征。 了解一个人事先看到的位置对于企业来说肯定是一个有用的功能,因为他们可以更好地优化和定位他们的产品。

例如,游戏设计师可以专注于游戏的特定部分,以增强功能并使其更具吸引力。

本文(《SalGAN: visual saliency prediction with adversarial networks》)探讨了这个令人着迷的想法,作者尝试使用GAN识别给定图像中最吸引人的部分。

生成对抗网络(GAN)的5个最有趣的应用

用于3D对象生成的GAN

游戏设计师无数个小时重建3D头像和背景,给他们一种逼真的感觉。通过想象创建3D模型肯定需要付出很多努力。 所以我建议你观看这个视频。 您可能会相信GAN的强大功能,它们可用于自动化整个过程!

pix2vox是基于草图的堆叠生成对抗网络三维探索。

生成对抗网络(GAN)的5个最有趣的应用

这是一个Github库:https://github.com/maxorange/pix2vox

vieyahn2017 commented 4 years ago

生成对抗网络GAN---生成mnist手写数字图像示例(附代码) 原创陶将 发布于2018-08-06 23:48:05 阅读数 7232 收藏 展开 Ian J. Goodfellow等人于2014年在论文Generative Adversarial Nets中提出了一个通过对抗过程估计生成模型的新框架。框架中同时训练两个模型:一个生成模型(generative model)G,用来捕获数据分布;一个判别模型(discriminative model)D,用来估计样本来自于训练数据的概率。G的训练过程是将D错误的概率最大化。可以证明在任意函数G和D的空间中,存在唯一的解决方案,使得G重现训练数据分布,而D=0.5。

生成对抗网络(GAN,Generative Adversarial Networks)的基本原理很简单:假设有两个网络,生成网络G和判别网络D。生成网络G接受一个随机的噪声z并生成图片,记为G(z);判别网络D的作用是判别一张图片x是否真实,对于输入x,D(x)是x为真实图片的概率。在训练过程中, 生成器努力让生成的图片更加真实从而使得判别器无法辨别图像的真假,而D的目标就是尽量把分辨出真实图片和生成网络G产出的图片,这个过程就类似于二人博弈,G和D构成了一个动态的“博弈过程”。随着时间的推移,生成器和判别器在不断地进行对抗,最终两个网络达到一个动态平衡:生成器生成的图像G(z)接近于真实图像分布,而判别器识别不出真假图像,即D(G(z))=0.5。最后,我们就可以得到一个生成网络G,用来生成图片。 ———————————————— 版权声明:本文为CSDN博主「陶将」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/weixin_42111770/article/details/81449449

vieyahn2017 commented 4 years ago

使用Keras实现生成式对抗网络GAN https://www.jianshu.com/p/f1485a7deb47

生成式对抗网络(GAN)自2014年提出以来已经成为最受欢迎的生成模型。本文借鉴机器之心对 2014 GAN 论文的解读,在本机运行该Keras项目。

传送门: 机器之心GitHub项目:GAN完整理论推导与实现,Perfect!

接下来主要讲一下如何实现的:

  1. 定义一个生成模型:

    def generator_model():
    #下面搭建生成器的架构,首先导入序贯模型(sequential),即多个网络层的线性堆叠
    model = Sequential()
    #添加一个全连接层,输入为100维向量,输出为1024维
    model.add(Dense(input_dim=100, output_dim=1024))
    #添加一个激活函数tanh
    model.add(Activation('tanh'))
    #添加一个全连接层,输出为128×7×7维度
    model.add(Dense(128*7*7))
    #添加一个批量归一化层,该层在每个batch上将前一层的激活值重新规范化,即使得其输出数据的均值接近0,其标准差接近1
    model.add(BatchNormalization())
    model.add(Activation('tanh'))
    #Reshape层用来将输入shape转换为特定的shape,将含有128*7*7个元素的向量转化为7×7×128张量
    model.add(Reshape((7, 7, 128), input_shape=(128*7*7,)))
    #2维上采样层,即将数据的行和列分别重复2次
    model.add(UpSampling2D(size=(2, 2)))
    #添加一个2维卷积层,卷积核大小为5×5,激活函数为tanh,共64个卷积核,并采用padding以保持图像尺寸不变
    model.add(Conv2D(64, (5, 5), padding='same'))
    model.add(Activation('tanh'))
    model.add(UpSampling2D(size=(2, 2)))
    #卷积核设为1即输出图像的维度
    model.add(Conv2D(1, (5, 5), padding='same'))
    model.add(Activation('tanh'))
    return model
  2. 定义一个判别模型:

    def discriminator_model():
    #下面搭建判别器架构,同样采用序贯模型
    model = Sequential()
    
    #添加2维卷积层,卷积核大小为5×5,激活函数为tanh,输入shape在‘channels_first’模式下为(samples,channels,rows,cols)
    #在‘channels_last’模式下为(samples,rows,cols,channels),输出为64维
    model.add(
            Conv2D(64, (5, 5),
            padding='same',
            input_shape=(28, 28, 1))
            )
    model.add(Activation('tanh'))
    #为空域信号施加最大值池化,pool_size取(2,2)代表使图片在两个维度上均变为原长的一半
    model.add(MaxPooling2D(pool_size=(2, 2)))
    model.add(Conv2D(128, (5, 5)))
    model.add(Activation('tanh'))
    model.add(MaxPooling2D(pool_size=(2, 2)))
    #Flatten层把多维输入一维化,常用在从卷积层到全连接层的过渡
    model.add(Flatten())
    model.add(Dense(1024))
    model.add(Activation('tanh'))
    #一个结点进行二值分类,并采用sigmoid函数的输出作为概念
    model.add(Dense(1))
    model.add(Activation('sigmoid'))
    return model
  3. 拼接:

    def generator_containing_discriminator(g, d):
    #将前面定义的生成器架构和判别器架构组拼接成一个大的神经网络,用于判别生成的图片
    model = Sequential()
    #先添加生成器架构,再令d不可训练,即固定d
    #因此在给定d的情况下训练生成器,即通过将生成的结果投入到判别器进行辨别而优化生成器
    model.add(g)
    d.trainable = False
    model.add(d)
    return model
  4. 生成拼接的图片(即将一个batch所有生成图片放到一个图片中):

    def combine_images(generated_images):
    #生成图片拼接
    num = generated_images.shape[0]
    width = int(math.sqrt(num))
    height = int(math.ceil(float(num)/width))
    shape = generated_images.shape[1:3]
    image = np.zeros((height*shape[0], width*shape[1]),
                     dtype=generated_images.dtype)
    for index, img in enumerate(generated_images):
        i = int(index/width)
        j = index % width
        image[i*shape[0]:(i+1)*shape[0], j*shape[1]:(j+1)*shape[1]] = \
            img[:, :, 0]
    return image
  5. 训练:

    def train(BATCH_SIZE):
    
    # 国内好像不能直接导入数据集,试了几次都不行,后来将数据集下载到本地'~/.keras/datasets/',也就是当前目录(我的是用户文件夹下)下的.keras文件夹中。
    #下载的地址为:https://s3.amazonaws.com/img-datasets/mnist.npz
    (X_train, y_train), (X_test, y_test) = mnist.load_data()
    #image_data_format选择"channels_last"或"channels_first",该选项指定了Keras将要使用的维度顺序。
    #"channels_first"假定2D数据的维度顺序为(channels, rows, cols),3D数据的维度顺序为(channels, conv_dim1, conv_dim2, conv_dim3)
    
    #转换字段类型,并将数据导入变量中
    X_train = (X_train.astype(np.float32) - 127.5)/127.5
    X_train = X_train[:, :, :, None]   # None将3维的X_train扩展为4维
    X_test = X_test[:, :, :, None]
    # X_train = X_train.reshape((X_train.shape, 1) + X_train.shape[1:])
    
    #将定义好的模型架构赋值给特定的变量
    d = discriminator_model()
    g = generator_model()
    d_on_g = generator_containing_discriminator(g, d)
    
    #定义生成器模型判别器模型更新所使用的优化算法及超参数
    d_optim = SGD(lr=0.001, momentum=0.9, nesterov=True)
    g_optim = SGD(lr=0.001, momentum=0.9, nesterov=True)
    
    #编译三个神经网络并设置损失函数和优化算法,其中损失函数都是用的是二元分类交叉熵函数。编译是用来配置模型学习过程的
    g.compile(loss='binary_crossentropy', optimizer="SGD")
    d_on_g.compile(loss='binary_crossentropy', optimizer=g_optim)
    
    #前一个架构在固定判别器的情况下训练了生成器,所以在训练判别器之前先要设定其为可训练。
    d.trainable = True
    d.compile(loss='binary_crossentropy', optimizer=d_optim)
    
    #下面在满足epoch条件下进行训练
    for epoch in range(30):
        print("Epoch is", epoch)
    
        #计算一个epoch所需要的迭代数量,即训练样本数除批量大小数的值取整;其中shape[0]就是读取矩阵第一维度的长度
        print("Number of batches", int(X_train.shape[0]/BATCH_SIZE))
    
        #在一个epoch内进行迭代训练
        for index in range(int(X_train.shape[0]/BATCH_SIZE)):
    
            #随机生成的噪声服从均匀分布,且采样下界为-1、采样上界为1,输出BATCH_SIZE×100个样本;即抽取一个批量的随机样本
            noise = np.random.uniform(-1, 1, size=(BATCH_SIZE, 100))
    
            #抽取一个批量的真实图片
            image_batch = X_train[index*BATCH_SIZE:(index+1)*BATCH_SIZE]
    
            #生成的图片使用生成器对随机噪声进行推断;verbose为日志显示,0为不在标准输出流输出日志信息,1为输出进度条记录
            generated_images = g.predict(noise, verbose=0)
            #print(np.shape(generated_images)) # (BATCH_SIZE,28,28,1) # 表示用BATCH_SIZE个100维向量生成BATCH_SIZE个图像的过程
    
            #每经过100次迭代输出一张生成的图片
            if index % 100 == 0:
                image = combine_images(generated_images)
                image = image*127.5+127.5
                Image.fromarray(image.astype(np.uint8)).save(
                    "./GAN/"+str(epoch)+"_"+str(index)+".png")
    
            #将真实的图片和生成的图片以多维数组的形式拼接在一起,真实图片在上,生成图片在下
            X = np.concatenate((image_batch, generated_images))
            # print(np.shape(X)) # # (2*BATCH_SIZE,28,28,1)
    
            #生成图片真假标签,即一个包含两倍批量大小的列表;前一个批量大小都是1,代表真实图片,后一个批量大小都是0,代表伪造图片
            y = [1] * BATCH_SIZE + [0] * BATCH_SIZE
    
            #判别器的损失;在一个batch的数据上进行一次参数更新
            d_loss = d.train_on_batch(X, y)  # (2*BATCH_SIZE,28,28,1) -> (2*BATCH_SIZE,1)
            print("batch %d d_loss : %f" % (index, d_loss))  # 理论上,d的loss越来越大,因为生成图片和真实图片越来越像
    
            #随机生成的噪声服从均匀分布
            noise = np.random.uniform(-1, 1, (BATCH_SIZE, 100))
    
            #固定判别器
            d.trainable = False
    
            #计算生成器损失;在一个batch的数据上进行一次参数更新
            #生成器的目标是愚弄辨别器蒙混过关,需要达到的目标是对于生成的图片,输出为1(正好和鉴别器相反).
            g_loss = d_on_g.train_on_batch(noise, [1] * BATCH_SIZE) # (BATCH_SIZE,100) -> (BATCH_SIZE,28,28,1) -> (BATCH_SIZE,1)
    
            #令判别器可训练
            d.trainable = True
            print("batch %d g_loss : %f" % (index, g_loss)) # 理论上,g的loss越来越小,因为生成图像越接近真实,生成图像的label接近1
    
            #每100次迭代保存一次生成器和判别器的权重
            if index % 100 == 9:
                g.save_weights('generator', True)
                d.save_weights('discriminator', True)

    注意:运行加载MNIST数据集,调用mnist.load_data()函数需要翻墙。如果不翻墙,可在其他地方找到要加载的mnist.npz文件,把它放到Keras安装目录下的~/.keras/datasets/,也可以。不要试图用Tensorflow加载MNIST数据集的那个模块,因为那个模块对MNIST采取了one-hot的编码格式,得到的值都是归一化的数值。而Keras的函数mnist.load_data()加载的MNIST数据集是原始的像素值。

  6. 生成:

    def generate(BATCH_SIZE, nice= False ):
    #训练完模型后,可以运行该函数生成图片
    g = generator_model()
    g.compile(loss='binary_crossentropy', optimizer="SGD")
    g.load_weights('generator')
    if nice:
        d = discriminator_model()
        d.compile(loss='binary_crossentropy', optimizer="SGD")
        d.load_weights('discriminator')
        noise = np.random.uniform(-1, 1, (BATCH_SIZE*20, 100))
        generated_images = g.predict(noise, verbose=1)
        d_pret = d.predict(generated_images, verbose=1)
        index = np.arange(0, BATCH_SIZE*20)
        index.resize((BATCH_SIZE*20, 1))
        pre_with_index = list(np.append(d_pret, index, axis=1))
        pre_with_index.sort(key=lambda x: x[0], reverse=True)
        nice_images = np.zeros((BATCH_SIZE,) + generated_images.shape[1:3], dtype=np.float32)
        nice_images = nice_images[:, :, :, None]
        for i in range(BATCH_SIZE):
            idx = int(pre_with_index[i][1])
            nice_images[i, :, :, 0] = generated_images[idx, :, :, 0]
        image = combine_images(nice_images)
    else:
        noise = np.random.uniform(-1, 1, (BATCH_SIZE, 100))
        generated_images = g.predict(noise, verbose=0)
        image = combine_images(generated_images)
    image = image*127.5+127.5
    Image.fromarray(image.astype(np.uint8)).save(
        "./GAN/generated_image.png")

    以上代码在支持 Tensorflow、Keras 的ipython notebook中运行。

先训练模型(迭代30次):

train(100) # 100为batch大小,可以随意指定。 迭代的效果如下:

9_500.png

19_500.png

29_500.png 再生成模型:

generate(132) # 132为batch大小,可以随意指定。该值大小也决定了生成的图片中含有多少个数字。 generate(32) # 32为batch大小,可以随意指定。该值大小也决定了生成的图片中含有多少个数字。 生成的效果如下:

generated_image132.png

generated_image32.png 训练过程分析: 将 MNIST 数据集(60000)分块训练(如 BITCH_SIZE = 200),则一个 Epoch 就会循环 300 次。每一次循环,生成器 g 先根据 BITCH_SIZE 个 100 维的随机噪声生成 BITCH_SIZE 张和真实图像同样大小的图像,然后将这 BITCH_SIZE 张图像和真实的 BITCH_SIZE 张图像拼接起来,给它们打上标签,计算判别器 d 的损失值。接下来,同样生成 BITCH_SIZE 个 100 维的随机噪声,打上和真实图像一样的标签(因为这是 g 所期望的),放入 d_on_g 中。在 d_on_g 中,先由 g 生成 BITCH_SIZE 张图像图像,再固定 d,再由 d 计算这 BITCH_SIZE 张图像的损失值,即生成器 g 的损失值。循环 300 次后,这才是 1 个 Epoch。如果想要生成更真实的图像, 要有多个 Epoch(代码中是30次)。

损失函数分析: 正常情况下,生成模型的损失和判别模型的损失会在一定范围内交替上升与下降。因为判别模型损失小,意味着更容易区分真实图像和假图像;相反,此时的生成模型与它的目标(真实图像)相差很大,损失函数会增大。反之亦然。最终理想的生成器的损失函数应该和判别器的损失函数一样大,这时判别器无法区分真实图像和假图像,生成器也达到了它所能到达的最小损失值。

更多实现细节可以参考我的github:

https://github.com/xyxxmb/DeepLearning