对抗生成网络学习(一)——GAN实现mnist手写数字生成(tensorflow实现)

一、背景

对抗神经网络GAN最早是2014年Ian goodfellow等人[1]提出的一个新的神经网络模型。在这个模型中,通过生成器G和判别器D相互博弈,以提高模型自身的泛化性能,使得生成器G最终能够产生与真实样本接近的数据。

本实验以mnist数据集为例,尽可能的以最少的代码实现GAN。

[1]文章链接https://arxiv.org/pdf/1406.2661.pdf

二、GAN原理

GAN的原理在CSDN上的介绍非常多,本文不再做过多介绍,需要注意的就是GAN模型中没有用到卷积,只是简单的多层神经网络,因此也比较容易实现。附上部分介绍比较详细的链接:

[2]深度学习----GAN(生成对抗神经网络)原理解析

[3]GAN笔记——理论与实现

[4]生成对抗网络(GAN)的理论与应用完整入门介绍

在文献[1]中,作者给出了对抗神经网络的伪代码:

GAN的结构可以简单表示为:

三、GAN的实现——以mnist数据集为例

1.数据准备

这里提供两种下载数据的方法,一种是到 http://yann.lecun.com/exdb/mnist 直接下载数据集,另一种是利用python代码下载。具体代码如下:

# mnist数据集下载,下载之后的文件在MNIST_data文件夹下
from tensorflow.examples.tutorials.mnist import input_data

data = input_data.read_data_sets('MNIST_data/')

下载完数据之后,所有数据为压缩包形式,我们需要对train数据进行解压

为了方便后续处理,我们需要数据转换成numpy可以读取的格式,转换方法如下:

# 定义load_data()函数以读取数据
def load_data(data_path):
    '''
    函数功能:导出MNIST数据
    输入: data_path   传入数据所在路径(解压后的数据)
    输出: train_data  输出data,形状为(60000, 28, 28, 1)    
         train_label  输出label,形状为(60000, 1)                 
    '''

    f_data = open(os.path.join(data_path, 'train-images.idx3-ubyte'))
    loaded_data = np.fromfile(file=f_data, dtype=np.uint8)
    #前16个字符为说明符,需要跳过
    train_data = loaded_data[16:].reshape((-1, 784)).astype(np.float)

    f_label = open(os.path.join(data_path, 'train-labels.idx1-ubyte'))
    loaded_label = np.fromfile(file=f_label, dtype=np.uint8)
    #前8个字符为说明符,需要跳过
    train_label = loaded_label[8:].reshape((-1)).astype(np.float)

    return train_data, train_label

2. 定义GAN网络需要的超参数

超参数是指开始学习之前设置的参数,这一步主要作用是设置后面所需要的超参数即可。

# 导入需要的包
import os                         #读取路径下文件
import shutil                     #递归删除文件
import tensorflow as tf           #编写神经网络
import numpy as np                #矩阵运算操作
from skimage.io import imsave     #保存影像
from tensorflow.examples.tutorials.mnist import input_data  #第一次下载数据时用

# 图像的size为(28, 28, 1)
image_height = 28           
image_width = 28
image_size = image_height * image_width

# 是否训练和存储设置
train = True
restore = False              #是否存储训练结果
output_path = "./output/"    #存储文件的路径

# 实验所需的超参数
max_epoch = 500
batch_size = 256
h1_size = 256         #第一隐藏层的size,即特征数
h2_size = 512         #第二隐藏层的size,即特征数
z_size = 128          #生成器的传入参数

3. 编写GAN的网络结构

GAN的网络结构包括两部分:生成器 generator 和判别器 discrimnator。

首先是生成器generator部分:

#导入tensorflow
import tensorflow as tf

#定义GAN的生成器
def generator(z_prior):
    '''
    函数功能:生成影像,参与训练过程
    输入:z_prior,       #输入tf格式,size为(batch_size, z_size)的数据
    输出:x_generate,    #生成图像
         g_params,      #生成图像的所有参数
    '''
    # 第一个链接层 
    #以2倍标准差stddev的截断的正态分布中生成大小为[z_size, h1_size]的随机值,权值weight初始化。
    w1 = tf.Variable(tf.truncated_normal([z_size, h1_size], stddev=0.1), name="g_w1", dtype=tf.float32)
    #生成大小为[h1_size]的0值矩阵,偏置bias初始化
    b1 = tf.Variable(tf.zeros([h1_size]), name="g_b1", dtype=tf.float32)
    #通过矩阵运算,将输入z_prior传入隐含层h1。激活函数为relu
    h1 = tf.nn.relu(tf.matmul(z_prior, w1) + b1)

    # 第二个链接层 
    #以2倍标准差stddev的截断的正态分布中生成大小为[h1_size, h2_size]的随机值,权值weight初始化。
    w2 = tf.Variable(tf.truncated_normal([h1_size, h2_size], stddev=0.1), name="g_w2", dtype=tf.float32)
    #生成大小为[h2_size]的0值矩阵,偏置bias初始化
    b2 = tf.Variable(tf.zeros([h2_size]), name="g_b2", dtype=tf.float32)
    #通过矩阵运算,将h1传入隐含层h2。激活函数为relu
    h2 = tf.nn.relu(tf.matmul(h1, w2) + b2)

    # 第三个链接层 
    #以2倍标准差stddev的截断的正态分布中生成大小为[h2_size, image_size]的随机值,权值weight初始化。
    w3 = tf.Variable(tf.truncated_normal([h2_size, image_size], stddev=0.1), name="g_w3", dtype=tf.float32)
    #生成大小为[image_size]的0值矩阵,偏置bias初始化
    b3 = tf.Variable(tf.zeros([image_size]), name="g_b3", dtype=tf.float32)
    #通过矩阵运算,将h2传入隐含层h3。
    h3 = tf.matmul(h2, w3) + b3
    #利用tanh激活函数,将h3传入输出层
    x_generate = tf.nn.tanh(h3)

    #将所有参数合并到一起
    g_params = [w1, b1, w2, b2, w3, b3]

    return x_generate, g_params

接下来是判别器discriminator的实现:

# 定义GAN的判别器 
def discriminator(x_data, x_generated, keep_prob):
    '''
    函数功能:对输入数据进行判断,并保存其参数
    输入:x_data,        #输入的真实数据 
        x_generated,     #生成器生成的虚假数据
        keep_prob,      #dropout率,防止过拟合
    输出:y_data,        #判别器对batch个数据的处理结果
        y_generated,     #判别器对余下数据的处理结果
        d_params,       #判别器的参数
    '''

    # 合并输入数据,包括真实数据x_data和生成器生成的假数据x_generated
    x_in = tf.concat([x_data, x_generated], 0) 

    # 第一个链接层
    #以2倍标准差stddev的截断的正态分布中生成大小为[image_size, h2_size]的随机值,权值weight初始化。
    w1 = tf.Variable(tf.truncated_normal([image_size, h2_size], stddev=0.1), name="d_w1", dtype=tf.float32)
    #生成大小为[h2_size]的0值矩阵,偏置bias初始化
    b1 = tf.Variable(tf.zeros([h2_size]), name="d_b1", dtype=tf.float32)
    #通过矩阵运算,将输入x_in传入隐含层h1.同时以一定的dropout率舍弃节点,防止过拟合
    h1 = tf.nn.dropout(tf.nn.relu(tf.matmul(x_in, w1) + b1), keep_prob)

    # 第二个链接层
    #以2倍标准差stddev的截断的正态分布中生成大小为[h2_size, h1_size]的随机值,权值weight初始化。
    w2 = tf.Variable(tf.truncated_normal([h2_size, h1_size], stddev=0.1), name="d_w2", dtype=tf.float32)
    #生成大小为[h1_size]的0值矩阵,偏置bias初始化
    b2 = tf.Variable(tf.zeros([h1_size]), name="d_b2", dtype=tf.float32)
    #通过矩阵运算,将h1传入隐含层h2.同时以一定的dropout率舍弃节点,防止过拟合
    h2 = tf.nn.dropout(tf.nn.relu(tf.matmul(h1, w2) + b2), keep_prob)

    # 第三个链接层
    #以2倍标准差stddev的截断的正态分布中生成大小为[h1_size, 1]的随机值,权值weight初始化。
    w3 = tf.Variable(tf.truncated_normal([h1_size, 1], stddev=0.1), name="d_w3", dtype=tf.float32)
    #生成0值,偏置bias初始化
    b3 = tf.Variable(tf.zeros([1]), name="d_b3", dtype=tf.float32)
    #通过矩阵运算,将h2传入隐含层h3
    h3 = tf.matmul(h2, w3) + b3

    #从h3中切出batch_size张图像
    y_data = tf.nn.sigmoid(tf.slice(h3, [0, 0], [batch_size, -1], name=None))
    #从h3中切除余下的图像
    y_generated = tf.nn.sigmoid(tf.slice(h3, [batch_size, 0], [-1, -1], name=None))

    #判别器的所有参数
    d_params = [w1, b1, w2, b2, w3, b3]

    return y_data, y_generated, d_params

定义完生成器和判别器之后,再添加一个显示结果的函数,可以在实验的过程中知道我们的训练情况:

#显示结果的函数
def show_result(batch_res, fname, grid_size=(8, 8), grid_pad=5):
    '''
    函数功能:输入相关参数,将运行结果以图片的形式保存到当前路径下
    输入:batch_res,       #输入数据
        fname,             #输入路径
        grid_size=(8, 8),  #默认输出图像为8*8张
        grid_pad=5,       #默认图像的边缘留白为5像素
    输出:无
    '''
    
    #将batch_res进行值[0, 1]归一化,同时将其reshape成(batch_size, image_height, image_width)
    batch_res = 0.5 * batch_res.reshape((batch_res.shape[0], image_height, image_width)) + 0.5
    #重构显示图像格网的参数
    img_h, img_w = batch_res.shape[1], batch_res.shape[2]
    grid_h = img_h * grid_size[0] + grid_pad * (grid_size[0] - 1)
    grid_w = img_w * grid_size[1] + grid_pad * (grid_size[1] - 1)
    img_grid = np.zeros((grid_h, grid_w), dtype=np.uint8)
    for i, res in enumerate(batch_res):
        if i >= grid_size[0] * grid_size[1]:
            break
        img = (res) * 255.
        img = img.astype(np.uint8)
        row = (i // grid_size[0]) * (img_h + grid_pad)
        col = (i % grid_size[1]) * (img_w + grid_pad)
        img_grid[row:row + img_h, col:col + img_w] = img
    #保存图像
    imsave(fname, img_grid)

4.编写训练过程

训练过程的代码如下:

# 定义训练过程
def train():
    '''
    函数功能:训练整个GAN网络,并随机生成手写数字
    输入:无
    输出:sess.saver()
    '''

    # 加载数据
    train_data, train_label = load_data("MNIST_data")
    size = train_data.shape[0]

    # 构建模型---------------------------------------------------------------------
    # 定义GAN网络的输入,其中x_data为[batch_size, image_size], z_prior为[batch_size, z_size]
    x_data = tf.placeholder(tf.float32, [batch_size, image_size], name="x_data") # (batch_size, image_size)
    z_prior = tf.placeholder(tf.float32, [batch_size, z_size], name="z_prior") # (batch_size, z_size)
    # 定义dropout率
    keep_prob = tf.placeholder(tf.float32, name="keep_prob") 
    global_step = tf.Variable(0, name="global_step", trainable=False)

    # 利用生成器生成数据x_generated和参数g_params
    x_generated, g_params = generator(z_prior)
    # 利用判别器判别生成器的结果
    y_data, y_generated, d_params = discriminator(x_data, x_generated, keep_prob)

    # 定义判别器和生成器的loss函数
    d_loss = - (tf.log(y_data) + tf.log(1 - y_generated))
    g_loss = - tf.log(y_generated)

    # 设置学习率为0.0001,用AdamOptimizer进行优化
    optimizer = tf.train.AdamOptimizer(0.0001)

    # 判别器discriminator 和生成器 generator 对损失函数进行最小化处理
    d_trainer = optimizer.minimize(d_loss, var_list=d_params)
    g_trainer = optimizer.minimize(g_loss, var_list=g_params)
    # 模型构建完毕--------------------------------------------------------------------

    # 全局变量初始化
    init = tf.global_variables_initializer()

    # 启动会话sess
    saver = tf.train.Saver()
    sess = tf.Session()
    sess.run(init)

    # 判断是否需要存储
    if restore:
        #若是,将最近一次的checkpoint点存到outpath下
        chkpt_fname = tf.train.latest_checkpoint(output_path)
        saver.restore(sess, chkpt_fname)
    else:
        #若否,判断目录是存在,如果目录存在,则递归的删除目录下的所有内容,并重新建立目录
        if os.path.exists(output_path):
            shutil.rmtree(output_path)
        os.mkdir(output_path)

    # 利用随机正态分布产生噪声影像,尺寸为(batch_size, z_size)
    z_sample_val = np.random.normal(0, 1, size=(batch_size, z_size)).astype(np.float32)

    # 逐个epoch内训练
    for i in range(sess.run(global_step), max_epoch):
        # 图像每个epoch内可以放(size // batch_size)个size
        for j in range(size // batch_size):
            if j%20 == 0:
                print("epoch:%s, iter:%s" % (i, j))
            
            # 训练一个batch的数据
            batch_end = j * batch_size + batch_size
            if batch_end >= size:
                batch_end = size - 1
            x_value = train_data[ j * batch_size : batch_end ]
            # 将数据归一化到[-1, 1]
            x_value = x_value / 255.
            x_value = 2 * x_value - 1
            
            # 以正太分布的形式产生随机噪声
            z_value = np.random.normal(0, 1, size=(batch_size, z_size)).astype(np.float32)
            # 每个batch下,输入数据运行GAN,训练判别器
            sess.run(d_trainer,
                     feed_dict={x_data: x_value, z_prior: z_value, keep_prob: np.sum(0.7).astype(np.float32)})
            # 每个batch下,输入数据运行GAN,训练生成器
            if j % 1 == 0:
                sess.run(g_trainer,
                         feed_dict={x_data: x_value, z_prior: z_value, keep_prob: np.sum(0.7).astype(np.float32)})
        # 每一个epoch中的所有batch训练完后,利用z_sample测试训练后的生成器
        x_gen_val = sess.run(x_generated, feed_dict={z_prior: z_sample_val})
        # 每一个epoch中的所有batch训练完后,显示生成器的结果,并打印生成结果的值
        show_result(x_gen_val, os.path.join(output_path, "sample%s.jpg" % i))
        print(x_gen_val)
        # 每一个epoch中,生成随机分布以重置z_random_sample_val
        z_random_sample_val = np.random.normal(0, 1, size=(batch_size, z_size)).astype(np.float32)
        # 每一个epoch中,利用z_random_sample_val生成手写数字图像,并显示结果
        x_gen_val = sess.run(x_generated, feed_dict={z_prior: z_random_sample_val})
        show_result(x_gen_val, os.path.join(output_path, "random_sample%s.jpg" % i))
        # 保存会话
        sess.run(tf.assign(global_step, i + 1))
        saver.save(sess, os.path.join(output_path, "model"), global_step=global_step)

5. 训练

编辑好了上述所有文件之后,这一步只需输入这样的代码,直接训练即可:

if __name__ == '__main__':
    if train:
        train()

四、实验结果

实验1个epoch的结果:

实验10个epoch的结果:

实验50个epoch的结果:

实验100个epoch的结果:

实验300个epoch的结果:

从实验结果可以看出,当进行100个epoch时,GAN已经能够生成较为清晰的影像。

五、分析

1.实验进行GAN实验时,是将二维MNSIT数据拉伸成了一维数据进行处理,且GAN模型中没有用到卷积,只是多层神经网络的叠加,实现相对容易。

2.GAN的生成器和判别器中使用不同的激活函数。

3.当epoch=0的时候,所有的生成结果看起来非常类似?

这其实是错觉,我把图像放大并截取了出来,大家可以仔细看左上角红色框和右上角黄色框的结果,仔细看还是可以看出区别的,并非所有的结果都是一样的。

至于为什么epoch=0的时候所有的生成结果非常类似,我个人觉得是因为学习次数太少,generator可能只学到了一点点特征,并偶然欺骗到了discriminator,这样所有的生成结果就会向这个特征靠拢,这也就导致了第一次生成的结果非常类似。随着训练次数增多,generator逐渐学到了更多特征,生成的结果才慢慢表现出了明显的差异。

4.整个代码的结构为:

import...
from skimage.io import imsave     #保存影像

#from tensorflow.examples.tutorials.mnist import input_data  #第一次下载数据时解注释
#data = input_data.read_data_sets('MNIST_data/')             #第一次下载数据时解注释

#设置超参数
image_height = ...
...

#定义load_data()函数以读取数据
def load_data(data_path):...

#定义GAN的生成器
def generator(z_prior):...

#定义GAN的判别器
def discriminator(x_data, x_generated, keep_prob):...

#显示结果的函数
def show_result(batch_res, fname, grid_size=(8, 8), grid_pad=5):...

#定义训练过程
def train():...


if __name__ == '__main__':
    if train:
        train()

 

  • 30
    点赞
  • 163
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 80
    评论
### 回答1: 使用TensorFlow来训练并测试手写数字识别的MNIST数据集十分简单。首先,我们需要导入TensorFlowMNIST数据集: import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data 接下来,我们可以使用input_data.read_data_sets()函数加载MNIST数据集,其中参数为下载数据集的路径。我们可以将数据集分为训练集、验证集和测试集。这里我们将验证集作为模型的参数调整过程,测试集用于最终模型评估。 mnist = input_data.read_data_sets("MNIST_data/", one_hot=True) 接下来,我们可以使用TensorFlow创建一个简单的深度学习模型。首先,我们创建一个输入占位符,用于输入样本和标签。由于MNIST数据集是28x28的图像,我们将其展平为一个784维的向量。 x = tf.placeholder(tf.float32, [None, 784]) y = tf.placeholder(tf.float32, [None, 10]) 接下来,我们可以定义一个简单的全连接神经网络,包含一个隐藏层和一个输出层。我们使用ReLU激活函数,并使用交叉熵作为损失函数。 hidden_layer = tf.layers.dense(x, 128, activation=tf.nn.relu) output_layer = tf.layers.dense(hidden_layer, 10, activation=None, name="output") cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=output_layer, labels=y)) 然后,我们可以使用梯度下降优化器来最小化损失函数,并定义正确预测的准确率。这样就完成了模型的构建。 train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy) correct_prediction = tf.equal(tf.argmax(output_layer, 1), tf.argmax(y, 1)) accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32)) 接下来,我们可以在一个会话中运行模型。在每次迭代中,我们从训练集中随机选择一批样本进行训练。在验证集上进行模型的参数调整过程,最后在测试集上评估模型的准确率。 with tf.Session() as sess: sess.run(tf.global_variables_initializer()) for i in range(1000): batch_x, batch_y = mnist.train.next_batch(100) sess.run(train_step, feed_dict={x: batch_x, y: batch_y}) val_accuracy = sess.run(accuracy, feed_dict={x: mnist.validation.images, y: mnist.validation.labels}) print("Validation Accuracy:", val_accuracy) test_accuracy = sess.run(accuracy, feed_dict={x: mnist.test.images, y: mnist.test.labels}) print("Test Accuracy:", test_accuracy) 通过这个简单的代码,我们可以使用TensorFlow训练并测试MNIST数据集,并得到测试集上的准确率。 ### 回答2: gan tensorflow mnist是指使用TensorFlow框架训练生成对抗网络GAN)来生成手写数字图像的任务。 首先,手写数字数据集是一个非常常见且经典的机器学习数据集。MNIST数据集包含了由0到9之间的手写数字的图像样本。在gan tensorflow mnist任务中,我们的目标是使用GAN生成与这些手写数字样本类似的新图像。 GAN是一种由生成器和判别器组成的模型。生成器任务是生成看起来真实的图像,而判别器任务是判断给定图像是真实的(来自训练数据集)还是生成的(来自生成器)。这两个模型通过对抗训练来相互竞争和提高性能。 在gan tensorflow mnist任务中,我们首先需要准备和加载MNIST数据集。利用TensorFlow的函数和工具,我们可以轻松地加载和处理这些图像。 接下来,我们定义生成器和判别器模型。生成器模型通常由一系列的卷积、反卷积和激活函数层组成,以逐渐生成高质量的图像。判别器模型则类似于一个二分类器,它接收图像作为输入并输出真实或生成的预测结果。 我们使用TensorFlow的优化器和损失函数定义GAN模型的训练过程。生成器的目标是误导判别器,使其将生成的图像误认为是真实图像,从而最大限度地降低判别器的损失函数。判别器的目标是准确地区分真实和生成的图像,从而最大限度地降低自身的损失函数。 最后,我们使用训练数据集来训练GAN模型。通过多次迭代,生成器和判别器的性能会随着时间的推移而得到改善。一旦训练完成,我们可以使用生成器模型来生成新的手写数字图像。 总结来说,gan tensorflow mnist是指使用TensorFlow框架训练生成对抗网络生成手写数字图像的任务。通过定义生成器和判别器模型,使用优化器和损失函数进行训练,我们可以生成类似于MNIST数据集手写数字的新图像。 ### 回答3: 用TensorFlow训练MNIST数据集可以实现手写数字的分类任务。首先我们需要导入相关库和模块,如tensorflow、keras以及MNIST数据集。接着,我们定义模型的网络结构,可以选择卷积神经网络(CNN)或者全连接神经网络(DNN)。对于MNIST数据集,我们可以选择使用CNN,因为它能更好地处理图像数据。 通过调用Keras中的Sequential模型来定义网络结构,可以添加多个层(如卷积层、池化层、全连接层等),用来提取特征和做出分类。其中,输入层的大小与MNIST图片的大小相对应,输出层的大小等于类别的数量(即0~9的数字)。同时,我们可以选择优化器(如Adam)、损失函数(如交叉熵)和评估指标(如准确率)。 接下来,我们用模型编译来配置模型的学习过程。在编译时,我们可以设置优化器、损失函数和评估指标。然后,我们用训练数据对模型进行拟合,通过迭代优化来调整模型的权重和偏置。迭代次数可以根据需要进行调整,以达到训练效果的需求。 训练结束后,我们可以使用测试数据对模型进行评估,获得模型在测试集上的准确率。最后,我们可以使用模型对新的未知数据进行预测,得到相应的分类结果。 综上所述,使用TensorFlow训练MNIST数据集可以实现手写数字的分类任务,通过定义模型结构、编译模型、拟合模型、评估模型和预测来完成整个过程。这个过程需要一定的编程知识和理解深度学习的原理,但TensorFlow提供了方便的api和文档,使我们能够相对容易地实现这个任务。
评论 80
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

全部梭哈迟早暴富

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值