实现一个简单的GAN网络

GAN网络的一般组成架构:

  1. 生成器:尽可能生成真实的图像

  2. 鉴别器:尽可能鉴别真实图像和虚假图像

  3. 在不断迭代的过程会使得生成器和鉴别器不断升级。

以伪造货币的过程为理论例子,想象两种类型的特工

  • 罪犯目标:尽可能相处复杂的伪造货币的方法,使警察无法区分伪造货币和真钱
  • 警察目标:警察的主要目标是想出复杂的方法,以区分假币和真钱

随着这一过程的发展,警察开发了越来越复杂的技术来检测货币伪造,而犯罪分子则开发越来越复杂的技术来伪造货币

生成对抗网络利用对抗过程来训练两个相互竞争的神经网络,直到达到理想的平衡。其组织架构为:

  • 生成器网络G(Z),他接受输入随机噪声,尝试生成非常接近我们拥有的数据集的数据
  • 鉴别器网络D(X),他接受输入生成的数据,试图区分生成的数据和实际的数据

网络的核心在于实现了一个二分类问题,并输出输入数据实际来自真实数据集(而不是合成或虚假数据)的概率


整个网络的目标函数可以写成:

min ⁡ G max ⁡ D V ( D , G ) = E x ∼ p data  ( x ) [ log ⁡ D ( x ) ] + E z ∼ p z ( z ) [ log ⁡ ( 1 − D ( G ( z ) ) ) ] \min _G \max _D V(D, G)=\mathbb{E}_{\boldsymbol{x} \sim p_{\text {data }}(\boldsymbol{x})}[\log D(\boldsymbol{x})]+\mathbb{E}_{\boldsymbol{z} \sim p_{\boldsymbol{z}}(\boldsymbol{z})}[\log (1-D(G(\boldsymbol{z})))] minGmaxDV(D,G)=Expdata (x)[logD(x)]+Ezpz(z)[log(1D(G(z)))]

  1. 生成器网络(用 G 表示)和判别器网络(用 D 表示)
  2. x \boldsymbol{x} x 是从真实数据分布 p data ( x ) p_{\text{data}}(\boldsymbol{x}) pdata(x) 中采样得到的真实数据样本。
  3. D ( x ) D(\boldsymbol{x}) D(x) 是判别器网络的输出,用于计算输入样本 x \boldsymbol{x} x 来自真实数据分布的概率。
  4. z \boldsymbol{z} z 是从先验数据分布(通常是正态分布或均匀分布) p z ( z ) p_{\boldsymbol{z}}(\boldsymbol{z}) pz(z) 中采样得到的噪声向量。生成器网络 G 将这个随机噪声向量作为输入,并将其映射到合成数据样本 G ( z ) G(\boldsymbol{z}) G(z)
  5. 第一项 E x ∼ p data ( x ) [ log ⁡ D ( x ) ] \mathbb{E}_{\boldsymbol{x} \sim p_{\text{data}}(\boldsymbol{x})}[\log D(\boldsymbol{x})] Expdata(x)[logD(x)] 表示判别器对真实数据样本的判别概率的期望
  6. 第二项 E z ∼ p z ( z ) [ log ⁡ ( 1 − D ( G ( z ) ) ) ] \mathbb{E}_{\boldsymbol{z} \sim p_{\boldsymbol{z}}(\boldsymbol{z})}[\log (1-D(G(\boldsymbol{z})))] Ezpz(z)[log(1D(G(z)))] 表示判别器对合成数据样本的判别概率的期望

关于生成器网络(用 G 表示)和判别器网络(用 D 表示)的进一步解释:

  • D的目的: D ( x ) D(x) D(x)表示D网络判断真实图片是否真实的概率。由于 x x x本身就是真实的,因此D的能力越强, D ( x ) D(x) D(x)越大且接近1, D ( G ( x ) ) D(G(x)) D(G(x))应该越小,此时 V ( D , G ) V(D,G) V(D,G)会变大。因此式子对于D来说是求最大(max_D)
  • D ( G ( z ) ) D(G(z)) D(G(z))是D网络判断G生成的图片是否真实的概率,G应该希望自己生成的图片“越接近真实越好”,即希望 D ( G ( z ) ) D(G(z)) D(G(z))尽可能的大,这时 V ( D , G ) V(D,G) V(D,G)会变小

一个简单GAN网络的实现方法,具体任务目标可以参考代码中的链接。这个GAN网络用于将随机噪声转换为一个正弦函数
生成的拟合结果(根据迭代次数可视化拟合结果):

        
        
# -*- coding:utf-8 -*-
# Author: DylanDing
# Create: 2023-11-30
# Update:
# Description: 实现一个简单的GAN网络,来源:https://blog.paperspace.com/implementing-gans-in-tensorflow/


import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
import math

# 设置CPU
import os
os.environ["CUDA_VISIBLE_DEVICES"]="-1"

def get_y(x):
    # return 10 + x*x
    return 10 + 10*math.sin(math.pi/30*x)

def sample_data(n = 10000,scale = 100):
    '''
    使用numpy生成随机样本,并构造二次/正弦函数来构造我们的真实数据集
    
    Input:
        n: 数据集的样本数量
        scale: 随机样本的取值范围为(-scale/2,scale/2)
    '''
    
    data = []

    '''
    np.random.random_sample((n,))函数用于生成一个0到1之间的随机数
    ((n,))表示生成一个n维的数据
    -0.5控制生成的n维随机数,范围在(-0.5,0.5)
    '''
    x = scale*(np.random.random_sample((n,))-0.5)

    for i in range(n):
        yi = get_y(x[i])
        data.append([x[i], yi])

    return np.array(data)

def sample_Z(n,scale):
    '''
    生成随机噪声信号
    
    Input:
        n: 数据集的样本数量
        scale: 随机样本的取值范围为(-scale/2,scale/2)
    '''
    
    data = []

    '''
    np.random.random_sample((n,))函数用于生成一个0到1之间的随机数
    ((n,))表示生成一个n维的数据
    -0.5控制生成的n维随机数,范围在(-0.5,0.5)
    '''
    x = scale*(np.random.random_sample((n,))-0.5)
    y = scale*(np.random.random_sample((n,))-0.5)
    for i in range(n):
        data.append([x[i], y[i]])

    return np.array(data)


# 生成器
def generator(Z,hsize=[16, 16],reuse=False):
    '''
    GAN网络的生成器
    
    Input:
    - Z: 表示输入的随机噪声
    - hsize: 表示隐藏层的大小
    - reuse: 是否重用变量

    Output:
    - out: 生成数据
    '''

    '''
    tf.variable_scope("GAN/Generator",reuse=reuse) 的目的是定义变量的作用域为“GAN/Generator”,reuse参数表示是否重用变量
    
    当reuse为False时,表示不重用变量,每次调用generator函数时都会创建新的变量。
    
    当reuse为True时,表示重用变量
    如果之前已经在相同的作用域内创建了变量,则会直接使用已经存在的变量。这在使用训练好的生成器进行推理或生成样本时非常有用。 
    '''
    with tf.variable_scope("GAN/Generator",reuse=reuse):
        
        # 定义第一个全连接层,命名为h1。输出为Z,输出大小为hsize[0],激活函数为tf.nn.leaky_relu
        h1 = tf.layers.dense(Z,hsize[0],activation=tf.nn.leaky_relu)

        # 定义第二个全连接层,输入为第一层的输出h1,输出大小为hsize[1],激活函数同样为tf.nn.leaky_relu
        h2 = tf.layers.dense(h1,hsize[1],activation=tf.nn.leaky_relu)

        # 使用tf.layers.dense创建最后一层全连接层,输入为第二层的输出h2,输出大小为2
        out = tf.layers.dense(h2,2)

    return out

# 鉴别器
def discriminator(X,hsize=[16, 16],reuse=False):
    '''
    生成GAN的判别器,他的作用是将输入数据X分类为真实数据(1)或者生成数据(0)
    Input:
    - X: 输入数据
    - hesize: 隐藏层大小
    - reuse: 是否重用相同作用域下的变量

    Output:
    - out: 分类结果(0或1)
    - h3: 第三个全连接层的输出
    '''   

    '''
    使用 tf.variable_scope 创建一个变量作用域,命名为"GAN/Discriminator",并设置reuse参数
    '''
    with tf.variable_scope("GAN/Discriminator",reuse=reuse):
        
        # 定义第一个全连接层,输入为X,输出大小为hsize[0],激活函数为tf.nn.leaky_relu
        h1 = tf.layers.dense(X,hsize[0],activation=tf.nn.leaky_relu)

        # 定义第二个全连接层,输入为h1(上一层的输出),输出大小为hsize[1],激活函数为tf.nn.leaky_relu
        h2 = tf.layers.dense(h1,hsize[1],activation=tf.nn.leaky_relu)

        # 定义第三个全连接层,输入为h2(上一层的输出),输出大小为2
        h3 = tf.layers.dense(h2,2)

        # 定义第三个全连接层,输入为h3(上一层的输出),输出大小为1
        out = tf.layers.dense(h3,1)

    return out, h3


'''
使用tf.placeholder设置占位符变量X,Z。X表示实数样本,Z表示随机噪声样本

placeholder()又叫占位符,用于声明一个张量的数据格式,告诉系统这里会有一个这种格式的张量,但是还没有给定具体数值,具体的数值要在正式运行的时候给到。
占位变量是一种TensorFlow用来解决读取大量训练数据问题的机制.  它允许你现在不用给它赋值,随着训练的开始,再把训练数据传送给训练网络学习。

使用feed_dict参数传入具体的数据

placeholder(dtype, shape=None, name=None)
- dtype: 表示tensorflow的数据类型,dtype定占位符的数据类型,并且必须在声明占位符时指定
- shape: 表示数据类型,默认的None是一个一维的数值,shape=[None,5]表示行不定,列是5.
- name: 张量名称

'''

X = tf.placeholder(tf.float32,[None,2])
Z = tf.placeholder(tf.float32,[None,2])

# 获得生成数据
G_sample = generator(Z) 

# 鉴别器
r_logits, r_rep = discriminator(X)
f_logits, g_rep = discriminator(G_sample,reuse=True)

# 生成器和鉴别器网络的损失函数
disc_loss = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits=r_logits,labels=tf.ones_like(r_logits)) + tf.nn.sigmoid_cross_entropy_with_logits(logits=f_logits,labels=tf.zeros_like(f_logits)))
gen_loss = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits=f_logits,labels=tf.ones_like(f_logits)))


# 定义网络优化器
gen_vars = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES,scope="GAN/Generator")                   # 获取作用域为"GAN/Generator"的全局变量集合gen_vars
disc_vars = tf.get_collection(tf.GraphKeys.GLOBAL_VARIABLES,scope="GAN/Discriminator")              # 获取作用域为"GAN/Discriminator"的全局变量集合disc_vars

'''
tf.train.RMSPropOptimizer优化器,设置学习率为0.001。
通过minimize函数最小化gen_loss,并且只更新gen_vars中的变量,得到gen_step,即生成器的训练步骤。 
'''
gen_step = tf.train.RMSPropOptimizer(learning_rate=0.0005).minimize(gen_loss,var_list = gen_vars)    # G Train step

'''
使用相同的优化器和学习率,通过minimize函数最小化disc_loss,并且只更新disc_vars中的变量,得到disc_step,即判别器的训练步骤
'''
disc_step = tf.train.RMSPropOptimizer(learning_rate=0.0005).minimize(disc_loss,var_list = disc_vars) # D Train step

batch_size = 128
with tf.Session() as sess:
    sess.run(tf.initialize_all_variables())

    for i in range(500001):
        X_batch = sample_data(n=batch_size)
        Z_batch = sample_Z(batch_size,2)
        _, dloss = sess.run([disc_step, disc_loss], feed_dict={X: X_batch, Z: Z_batch})
        G,_,gloss = sess.run([G_sample,gen_step, gen_loss], feed_dict={Z: Z_batch})
        print("Iterations: %d\t Discriminator loss: %.4f\t Generator loss: %.4f"%(i,dloss,gloss))


        if i == 1000:
            plt.scatter(X_batch[:,0],X_batch[:,1])
            plt.scatter(G[:,0],G[:,1])
            plt.title('epoch=1000')
            plt.savefig('figs/epoch_1000.png')
            plt.close()
        
        if i == 10000:
            plt.scatter(X_batch[:,0],X_batch[:,1])
            plt.scatter(G[:,0],G[:,1])
            plt.title('epoch=10000')
            plt.savefig('figs/epoch_10000.png')
            plt.close()

        if i == 30000:
            plt.scatter(X_batch[:,0],X_batch[:,1])
            plt.scatter(G[:,0],G[:,1])
            plt.title('epoch=30000')
            plt.savefig('figs/epoch_30000.png')
            plt.close()

        if i == 50000:
            plt.scatter(X_batch[:,0],X_batch[:,1])
            plt.scatter(G[:,0],G[:,1])
            plt.title('epoch=50000')
            plt.savefig('figs/epoch_50000.png')
            plt.close()

        if i == 100000:
            plt.scatter(X_batch[:,0],X_batch[:,1])
            plt.scatter(G[:,0],G[:,1])
            plt.title('epoch=100000')
            plt.savefig('figs/epoch_100000.png')
            plt.close()

        if i == 500000:
            plt.scatter(X_batch[:,0],X_batch[:,1])
            plt.scatter(G[:,0],G[:,1])
            plt.title('epoch=500000')
            plt.savefig('figs/epoch_500000.png')
            plt.close()
``
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值