1. 程式人生 > >【Tensorflow tf 掏糞記錄】筆記四——tensorflow搭建GAN神經網路

【Tensorflow tf 掏糞記錄】筆記四——tensorflow搭建GAN神經網路

GAN,對抗神經網路。簡單通俗的說就是類似老頑童的左右互博術這種東西。機器試圖自己打敗自己,畢竟世界上最大的敵人是自己。哈哈哈哈。

2018/3/31 18:35 pm
下面是我最新訓練生成的結果,網路深度為512,個別的類似3,5,2的複雜的數我是用1024深的網路指定標籤針對訓練的。
這裡寫圖片描述這裡寫圖片描述這裡寫圖片描述這裡寫圖片描述這裡寫圖片描述這裡寫圖片描述這裡寫圖片描述這裡寫圖片描述這裡寫圖片描述這裡寫圖片描述

簡要介紹GAN

GAN神經網路主要就是在網路中有一個生成器(Generator)一個鑑別器(Discriminator),鑑別器鑑別真假以及預測標籤。生成器則生成目標檔案送去鑑別器鑑別。生成器努力生成鑑別器無法鑑別出為假的檔案,鑑別器努力的試圖鑑別出來送檢的假貨。然後就他們兩個不斷的對抗,就像煉蠱一樣。

專案結構

這次的專案中主要有3個檔案。使用的是MNIST資料集

  • config.yam:
    • 用來設定各種類似學習率(learning_rate), Leaky Relu所使用的alpha,迭代次數(epoch)等。我們可以指定訓練的影象的標籤。比如說可以單獨訓練標籤為‘8’的資料,並生成類似‘8’的資料。方便我們調參。
  • train.py:
    • 放置訓練使用的程式碼與神經網路。這裡我是用了tensorboard,tensorboard將會把生成器(Generator)的loss與鑑別器(Discriminator)的loss分別展示到tensorboard中。我們可以在訓練的過程中實時觀察各個loss的走勢。以及我把每次生成器(Generator)生成的圖片都儲存在了硬碟中,我們也可以實時的觀察機器學習的成果。
  • utils.py:
    • 放置組成神經網路,預處理資料等train.py中需要用到的工具。例如data_batch()等。

Utils工具

生成器(Generator)

生成器我用的是全連線神經網路,因為MNIST資料集十分的簡單,全連線神經網路的表現已經十分的優秀了。當然卷積也行,看個人喜好。

因為tensorflow中沒有Leaky Relu函式,所以需要自己實現。所以tf.layer.dense()中的activation = None我不希望它自動呼叫了其他啟用函式。

最後的啟用函式我呼叫tf.tanh()

我把這段程式碼中的變數命名為‘generator’,為了方便我日後根據變數名儲存變數。從而實現只儲存生成器,丟棄鑑別器。

def generator( z, out_dim, n_units = 128, reuse = False, alpha = 0.01 ):
        with tf.variable_scope( 'generator', reuse = reuse ):    #命名閉包中程式碼的變數
            h1 = tf.layers.dense( z, n_units, activation = None )    # 宣告全連線神經網路

            h1 = tf.maximum( alpha * h1, h1 )    # Leaky Relu
            logits = tf.layers.dense( h1, out_dim, activation = None )
            out = tf.tanh( logits )

         return out

鑑別器(Discriminator)

分類器用的啟用函式也是Leaky Relu函式。結構跟生成器(Generator)類似,只不過最後的一層的啟用函式換成了tf.sigmoid(),而且最一層全連線層的神經元數我只設定了一個,因為我發現tf.sigmoid()啟用函式並沒有對模型有很大的幫助。

同樣的我也為這段程式碼中的變數命名了。

def discriminator( x, n_units = 128, reuse = False, alpha = 0.01 ):
    with tf.variable_scope( 'discriminator', reuse = reuse ):

        h1 = tf.layers.dense( x, n_units, activation = None )    

        # Leacy ReLU
        h1 = tf.maximum( alpha * h1, h1 )

        logits = tf.layers.dense( h1, 1, activation = None )
        out = tf.sigmoid( logits )

        return out, logits

train訓練

建立網路

構建我們需要跑的網路,其中生成器(Generator)輸入的是白噪音。這裡只是佔位符,還沒初始化。

這裡呼叫了兩次鑑別器(Discriminator),其中第一次是輸入真圖片,並努力不誤判為假圖片。第二次是輸入生成器(Generator)生成的假圖片,嘗試努力鑑別出為假圖片。為了保證第一次與第二次所用的生成器的效能一致,我使用了reuse = True來保證tensorflow會重新使用相同的鑑別器(Discriminator)而不是重新new一個。

tf.reset_default_graph()

input_real, input_z = utils.model_inputs( input_size, z_size )    # 建立佔位符

'''--------建立網路--------'''
g_model = utils.generator( input_z, input_size, n_units = g_hidden_size, alpha = alpha )    # 建立生成器

d_model_real, d_logits_real = utils.discriminator( input_real, n_units = d_hidden_size, alpha = alpha )    # 鑑別真圖片

d_model_fake, d_logits_fake = utils.discriminator( g_model, reuse = True, n_units = d_hidden_size, alpha = alpha )    # 鑑別假圖片

生成器與鑑別器的Loss的計算

在GAN神經網路中,需要計算3個Loss。分別是:生成器(Generator)的Loss,鑑別器(Discriminator)鑑別真圖片的Loss,鑑別器(Discriminator)鑑別假圖片的Loss

同時在鑑別真圖片中把真標籤中減去了smooth的值,為了幫助模型更好的泛化。

生成器的Loss計算:輸入資料為鑑別器鑑別假圖片的結果,輸入標籤為1。
在這裡,對抗的性質體現的十分的明顯。鑑別器努力的鑑別假圖片,生成器努力的生成鑑別器鑑別不出來的假圖片

d_loss_real = tf.reduce_mean( tf.nn.sigmoid_cross_entropy_with_logits( logits = d_logits_real,
                                                                       labels = tf.ones_like( d_logits_real ) * ( 1 - smooth ), name = 'd_loss_real' ) )    # 計算鑑別器鑑別真圖片的Loss

d_loss_fake = tf.reduce_mean( tf.nn.sigmoid_cross_entropy_with_logits( logits = d_logits_fake,
                                                                       labels = tf.zeros_like( d_logits_fake ), name = 'd_loss_fake' ) )    # 計算鑑別器鑑別假圖片的Loss

d_loss = d_loss_real + d_loss_fake    # 鑑別器總Loss為兩個鑑別器Loss的總和        

tf.summary.scalar('d_loss', d_loss)    # 把資料加入tensorboard

g_loss = tf.reduce_mean( tf.nn.sigmoid_cross_entropy_with_logits( logits = d_logits_fake,
                                                                  labels = tf.ones_like( d_logits_fake ), name = 'g_loss' ) )    # 計算生成器的Loss
tf.summary.scalar('g_loss', g_loss)    # 把資料加入tensorboard

工具的組合與運用

這裡有個小坑,tensorflow自己下載的MNIST資料集,裡面的資料最大值為1。所以很尷尬。我開始無論怎麼訓練的出來的都是一坨黑。

需要乘255才能在.jpg格式的圖片中顯示。

這裡只儲存生成器的變數,因為這個專案的重點是生成資料而不是鑑別資料。

batch_size = FLAGS.batch_size
epoches = FLAGS.epoches
samples = []
# losses = []
saver = tf.train.Saver( var_list = g_vars )    # 我們只儲存生成器的變數
with tf.Session() as sess:
    merged, writer = utils.print_training_loss(sess)

    sess.run( tf.global_variables_initializer() )
    for e in range( epoches ):
        for batch in batches:
            batch_images = batch
            batch_images = batch_images * 2 - 1

            batch_z = np.random.uniform( -1, 1, size = ( batch_size, z_size ) )
            '''--------執行optimizers--------'''
            _ = sess.run( d_train_opt, feed_dict = {input_real : batch_images, input_z : batch_z} )
            _ = sess.run( g_train_opt, feed_dict = {input_z : batch_z} )

        train_loss_d = sess.run( d_loss, {input_z : batch_z, input_real : batch_images} )    # 列印Loss
        train_loss_g = g_loss.eval( {input_z : batch_z} )

        print( 'Epoch {}/{}...' . format( e + 1, epoches ),
               'Discriminator Loss: {:.4f}...' . format( train_loss_d ),
               'Generator Loss: {:.4f}' . format( train_loss_g ) )   

        # 加入tensorboard
        rs = sess.run(merged, feed_dict={input_z: batch_z, input_real: batch_images})
        writer.add_summary(rs, e)

        '''--------用當前的生成器生成圖片並儲存--------'''
        sample_z = np.random.uniform( -1, 1, size = ( 16, z_size ) )
        gen_samples = sess.run(
            utils.generator( input_z, input_size, n_units = g_hidden_size, reuse = True, alpha = alpha),
            feed_dict = {input_z : sample_z} )


        gen_image = gen_samples.reshape( ( -1, 28, 28, 1 ) )    # 把陣列轉換成high,width,channal形式
        gen_image = tf.cast( np.multiply( gen_image, 255 ), tf.uint8 )    # 數值乘255
        for r in range( gen_image.shape[0] ):
            with open( FLAGS.generate_file + str(e) + ' ' + str( r ) + '.jpg', 'wb' ) as img:
                img.write( sess.run( tf.image.encode_jpeg( gen_image[r] ) ) )    # 儲存圖片

        samples.append( gen_samples )
        saver.save( sess, './checkpoint/generator.ckpt' )

執行結果

配置好了config.yml直接跑train.py
這裡寫圖片描述
然後開始漫長的等待。。。。。
看個人電腦配置把。一般20多分鐘就結束了。
然後可以進入儲存生成圖片的資料夾可以看到一開始是一堆亂七八糟的東西這裡寫圖片描述
大約在結束,才生成了我能看懂的東西
這裡寫圖片描述
一個4, 一個9, 一個1

完整程式碼

train.py檔案:

import pickle as pkl
import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt
import argparse
import os

import utils

from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets( 'MNIST_data' )


'''--------Load the config file--------'''
def parse_args():
    parser = argparse.ArgumentParser()
    parser.add_argument( '-c', '--config', default = 'config.yml', help = 'The path to the config file' )

    return parser.parse_args()


args = parse_args()
FLAGS = utils.read_config_file( args.config )

if not( os.path.exists( FLAGS.generate_file ) ):
    os.makedirs( FLAGS.generate_file )


'''--------Preprocessing data--------'''
if( FLAGS.select_label != 'All' ):
    datas = utils.select_data( mnist, FLAGS.select_label )
else:
    datas = mnist.train.images    # shape ( 55000, 784 )

batches = utils.batch_data( datas, FLAGS.batch_size )


'''-----------Hyperparameters------------'''
# Size of input image to discriminator
input_size = 784
# Size of latent vector to genorator
z_size = 100
# Size of hidden layers in genorator and discriminator
g_hidden_size = FLAGS.g_hidden_size
d_hidden_size = FLAGS.d_hidden_size
# Leak factor for leaky ReLU
alpha = FLAGS.alpha
# Smoothing
smooth = 0.1


'''------------Build network-------------'''
tf.reset_default_graph()

# Creat out input placeholders
input_real, input_z = utils.model_inputs( input_size, z_size )

# Build the model
g_model = utils.generator( input_z, input_size, n_units = g_hidden_size, alpha = alpha )
# g_model is the generator output

d_model_real, d_logits_real = utils.discriminator( input_real, n_units = d_hidden_size, alpha = alpha )
d_model_fake, d_logits_fake = utils.discriminator( g_model, reuse = True, n_units = d_hidden_size, alpha = alpha )


'''---Discriminator and Generator Losses---'''
# Calculate losses
d_loss_real = tf.reduce_mean( tf.nn.sigmoid_cross_entropy_with_logits( logits = d_logits_real,
                                                                       labels = tf.ones_like( d_logits_real ) * ( 1 - smooth ), name = 'd_loss_real' ) )

d_loss_fake = tf.reduce_mean( tf.nn.sigmoid_cross_entropy_with_logits( logits = d_logits_fake,
                                                                       labels = tf.zeros_like( d_logits_fake ), name = 'd_loss_fake' ) )
d_loss = d_loss_real + d_loss_fake
# add d_loss to summary scalar
tf.summary.scalar('d_loss', d_loss)

g_loss = tf.reduce_mean( tf.nn.sigmoid_cross_entropy_with_logits( logits = d_logits_fake,
                                                                  labels = tf.ones_like( d_logits_fake ), name = 'g_loss' ) )
# add g_loss to summary scalar
tf.summary.scalar('g_loss', g_loss)


'''---------------Optimizers----------------'''
# Optimizers
learning_rate = FLAGS.learning_rate

# Get the trainable_variables, split into G and D parts
t_vars = tf.trainable_variables()
g_vars = [var for var in t_vars if var.name.startswith( 'generator' )]
d_vars = [var for var in t_vars if var.name.startswith( 'discriminator' )]

d_train_opt = tf.train.AdamOptimizer( learning_rate ).minimize( d_loss, var_list = d_vars )
g_train_opt = tf.train.AdamOptimizer( learning_rate ).minimize( g_loss, var_list = g_vars )


'''-----------------Traing---------------------'''
batch_size = FLAGS.batch_size
epoches = FLAGS.epoches
samples = []
# losses = []
# Only save generator variables
saver = tf.train.Saver( var_list = g_vars )
with tf.Session() as sess:
    # Tensorboard Print Loss
    merged, writer = utils.print_training_loss(sess)

    sess.run( tf.global_variables_initializer() )
    for e in range( epoches ):
        for batch in batches:
            batch_images = batch
            batch_images = batch_images * 2 - 1

            # Sample random noise for G
            batch_z = np.random.uniform( -1, 1, size = ( batch_size, z_size ) )

            # Run optimizers
            _ = sess.run( d_train_opt, feed_dict = {input_real : batch_images, input_z : batch_z} )
            _ = sess.run( g_train_opt, feed_dict = {input_z : batch_z} )

        # At the end of each epoch, get the losses and print them out
        train_loss_d = sess.run( d_loss, {input_z : batch_z, input_real : batch_images} )
        train_loss_g = g_loss.eval( {input_z : batch_z} )

        print( 'Epoch {}/{}...' . format( e + 1, epoches ),
               'Discriminator Loss: {:.4f}...' . format( train_loss_d ),
               'Generator Loss: {:.4f}' . format( train_loss_g ) )

        # Add data to tensorboard
        rs = sess.run(merged, feed_dict={input_z: batch_z, input_real: batch_images})
        writer.add_summary(rs, e)

        sample_z = np.random.uniform( -1, 1, size = ( 16, z_size ) )
        gen_samples = sess.run(
            utils.generator( input_z, input_size, n_units = g_hidden_size, reuse = True, alpha = alpha),
            feed_dict = {input_z : sample_z} )


        gen_image = gen_samples.reshape( ( -1, 28, 28, 1 ) )
        gen_image = tf.cast( np.multiply( gen_image, 255 ), tf.uint8 )
        for r in range( gen_image.shape[0] ):
            with open( FLAGS.generate_file + str(e) + ' ' + str( r ) + '.jpg', 'wb' ) as img:
                img.write( sess.run( tf.image.encode_jpeg( gen_image[r] ) ) )

        samples.append( gen_samples )
        saver.save( sess, './checkpoint/generator.ckpt' )

utils.py檔案:

import tensorflow as tf
import yaml

def model_inputs( real_dim, z_dim ):
    inputs_real = tf.placeholder( tf.float32, ( None, real_dim ), name = 'input_real' )
    inputs_z = tf.placeholder( tf.float32, ( None, z_dim ), name = 'input_z' )

    return inputs_real, inputs_z

def generator( z, out_dim, n_units = 128, reuse = False, alpha = 0.01 ):
    with tf.variable_scope( 'generator', reuse = reuse ):
        # Hidden layer
        h1 = tf.layers.dense( z, n_units, activation = None )# Leaky ReLUense( z, n_units, activation = None )    # 全連結層的高階封裝介面

        h1 = tf.maximum( alpha * h1, h1 )

        # Logits and tanh output
        logits = tf.layers.dense( h1, out_dim, activation = None )
        out = tf.tanh( logits )

        return out

def discriminator( x, n_units = 128, reuse = False, alpha = 0.01 ):
    with tf.variable_scope( 'discriminator', reuse = reuse ):
        # Hidden layer
        h1 = tf.layers.dense( x, n_units, activation = None )    # 全連結層的高階封裝介面
        # Leacy ReLU
        h1 = tf.maximum( alpha * h1, h1 )

        logits = tf.layers.dense( h1, 1, activation = None )
        out = tf.sigmoid( logits )

        return out, logits

def print_training_loss( sess ):
    # tf.summary.scalar( 'd_loss', d_loss )
    # tf.summary.scalar( 'g_loss', g_loss )

    # merge all summary together
    merged = tf.summary.merge_all()
    writer = tf.summary.FileWriter( 'logs/', sess.graph )

    return merged, writer

class Flag( object ):
    def __init__( self,**entries  ):
        self.__dict__.update( entries )

def read_config_file( config_file ):
    with open( config_file ) as f:
        FLAGES = Flag( **yaml.load( f ) )
    return FLAGES

def select_data( mnist, label ):
    data_idx = []
    for i in range( mnist.train.images.shape[0] ):
        if mnist.train.labels[i] == label:
            data_idx.append( i )
    datas = mnist.train.images[data_idx]
    return datas

def batch_data( datas, batch_size ):
    batches = []
    for i in range( datas.shape[0] // batch_size ):
        batch = datas[i * batch_size : ( i + 1 ) * batch_size, :]
        batches.append( batch )

    return batches

config.yml檔案:

## set Basic configuration and alpha
g_hidden_size: 256
d_hidden_size: 256
alpha: 0.01

## set Batch size and epoche
batch_size: 100
epoches: 100

## set learning_rate
learning_rate: 0.002

## the path of the generate file
generate_file: generate/

## num to generate mast in range 9, or equ to "All"
select_label: 'All'

專案程式碼