(轉)干貨|這篇TensorFlow實例教程文章告訴你GANs為何引爆機器學習?(附源碼)


干貨|這篇TensorFlow實例教程文章告訴你GANs為何引爆機器學習?(附源碼)

 

該博客來源自:https://mp.weixin.qq.com/s?__biz=MzA4NzE1NzYyMw==&mid=2247492203&idx=5&sn=3020c3a43bd4dd678782d8aa24996745&chksm=903f1c73a74895652ee688d070fd807771e3fe6a8947f77f3a15a44a65557da0313ac5ad592c&mpshare=1&scene=23&srcid=0710IS5zrYtth0rx07f08yXE#rd 

 

想象有一天,我們可以利用一個神經網絡觀看電影並制作自己的電影,或者聽歌和創作歌曲。神經網絡將從它看到的內容中學習,而且你並不需要明確地告訴它,這種使神經網絡學習的方式被稱為無監督學習。

 

實際上,以無監督的方式訓練的GAN(生成對抗網絡)在過去三年中獲得了極大的關注,被認為是目前AI領域最熱門的話題之一。就像Facebook AI的主管Yann LeCun認為的那樣:生成對抗網絡是機器學習過去十年最有趣的想法。GAN是理想的神經網絡,它在看到某些圖像后生成新圖像。那么,這可以用來做什么?為什么這很重要?

 

 生成的卧室圖像:https://arxiv.org/abs/1511.06434v2

直到最近,神經網絡(特別是卷積神經網絡)只擅長分類任務,如在貓和狗、飛機和汽車之間進行分類。但現在,他們可以用來生成圖片的貓或狗(即使它們看起來很奇怪),這告訴我們他們已經學會記住特征。

 

GAN的這種非凡的能力可以應用於許多驚人的應用程序,如:

•生成給定文本描述的圖像

文本到圖像 :https://arxiv.org/pdf/1605.05396v2.pdf

The major advancements in Deep Learning in 2016:https://tryolabs.com/blog/2016/12/06/major-advancements-deep-learning-2016/

 

•圖像到圖像的翻譯:

這可能是GAN最酷的應用。圖像到圖像翻譯可用於很多場景,比如從草圖生成逼真的圖像,將白天拍攝的圖像轉換為夜間圖像,甚至將黑白圖像轉換為彩色圖像。

圖像到圖像的翻譯:https://phillipi.github.io/pix2pix/

查看:基於條件抗網絡的圖像到圖像的翻譯:https://phillipi.github.io/pix2pix/

 

讓我們了解一下GAN有什么能力讓所有人都對齊大肆吹捧。我們用一個簡單的GAN實例生成玫瑰圖像。

 

我們來看看GAN到底是什么?

在我們開始構建GAN之前,我們可以了解它的工作原理。 生成對抗網絡包含兩個神經網絡,一個鑒別器和一個生成器。鑒別器是一個卷積神經網絡(CNN)(不知道CNN是什么?請看這個帖子),學習區分真實和假的圖像。真實的圖像是從數據庫中獲取的,而假的圖像來自生成器。

鑒別器

生成器的工作原理就像CNN反向運行,它將一個隨機數向量作為輸入,並在輸出端生成一個圖像。

生成器

稍后我們將介紹生成器和鑒別器的工作和實現,但現在我們通過一個非常有名的實例來解釋GAN(濫用生成對抗網絡生成8位像素藝術:https://medium.com/@ageitgey/abusing-generative-adversarial-networks-to-make-8-bit-pixel-art-e45d9b96cee7)。

 

我們可以把生成器比作一個偽造者,而把鑒別器視作一個警察,他必須從兩枚貨幣中區分真假。在最開始的時候,我們要確保偽造貨幣的偽造者和警察都是同樣不擅長他們的工作的。因此,偽造者首先生成一些隨機的噪聲圖像。

偽造者產生的噪聲圖像

現在決策接受訓練來區分偽造者產生假的的圖像和真實的貨幣。

訓練決策

偽造者現在已經知道它的圖像已被歸類為“假”,而決策正在尋找貨幣所具有的一些獨特的特征(如顏色和圖案)。偽造者現在在學習這些特征,並生成具有這些特征的圖像。

訓練偽造者

現在,決策再次區分出數據集中的出真正貨幣和來自偽造者新改進的圖像,並要求對它們進行分類,因此,該決策將會學到更多的關於真實圖像的特征(如貨幣的表面特征)。

用新的虛假圖像來訓練決策

而偽造者再次學習這些特征,並產生更好看的假圖像。

再次訓練偽造者

偽造者和警察之間的這場拉鋸戰將一直持續,直到偽造者生成的圖像看起來與真實的圖像完全相同,而且決策將無法對其進行分類。

真假難辨

在Tensorflow上生成玫瑰花

我們只用tensorflow而不用其它(除了pillow)來構建一個簡單的DCGAN(深度卷積生成對抗式網絡)。那么,DCGAN是什么呢?

 

DCGAN是普通GAN的一個修改版本,以解決普通GAN所涵蓋的一些難題,例如:使偽造的圖像視覺上看起來比較滿意,通過反復輸出符合鑒別器正在尋找的但不在實際圖像附近的數據分布的圖像,在訓練過程中提高穩定性,從而使發生器不會在鑒別器中找到缺陷。

鑒別器架構

可以看出,它將圖像作為輸入並輸出一個logit(1為真類,0為偽類)。接下來,我們用一個生成器架構,它由conv_transpose層組成,它們將一組隨機數作為輸入,並在輸出端生成一個圖像。 

生成器架構

 

DCGAN可直接產生這篇論文(https://arxiv.org/pdf/1511.06434.pdf)中提到的變化:

  • 用分段卷積(鑒別器)和分數階卷積(生成器)替換任何合並層。

  • 在發生器和鑒別器中使用batchnorm。

  • 刪除完全連接的隱藏層以進行更深層次的體系結構。

  • 在除了使用Tanh的輸出之外的所有圖層中的生成器中使用ReLU激活函數。

  • 在所有層的鑒別器中使用LeakyReLU激活函數。

 

我們首先需要收集玫瑰圖像。一個簡單方法就是在Google上進行玫瑰圖像搜索,並使用諸如ImageSpark這樣的Chrome插件下載搜索結果中的所有圖像。

 

我們收集了67張圖片(當然是越多越好)並在這里可用。在以下目錄中提取這些圖像:https://drive.google.com/file/d/0B068a_0Gq8kYSGZ3UmdveFczM0U/view

GANs_N_Roses:https://github.com/Naresh1318/GANs_N_Roses

 

既然我們已經有了圖像,下一步就是通過將它們重構為64 * 64,並將其縮放值設置為-1和1之間,以預處理這些圖像。

def load_dataset(path, data_set='birds', image_size=64):

    """

    Loads the images from the specified path

    :param path: string indicating the dataset path.

    :param data_set: 'birds' -> loads data from birds directory, 'flowers' -> loads data from the flowers directory.

    :param image_size: size of images in the returned array

    :return: numpy array, shape : [number of images, image_size, image_size, 3]

    """

    all_dirs = os.listdir(path)

    image_dirs = [i for i in all_dirs if i.endswith(".jpg") or i.endswith(".jpeg") or i.endswith(".png")]

    number_of_images = len(image_dirs)

    images = []

    print("{} images are being loaded...".format(data_set[:-1]))

    for c, i in enumerate(image_dirs):

         images.append(np.array(ImageOps.fit(Image.open(path + '/' + i),

                                              (image_size, image_size), Image.ANTIALIAS))/127.5 - 1.)

         sys.stdout.write("\r Loading : {}/{}"

                             .format(c + 1, number_of_images))

     print("\n")

     images = np.reshape(images, [-1, image_size, image_size, 3])

     return images.astype(np.float32)

 

首先,我們寫出可用於執行卷積、卷積轉置、致密完全連接層和LeakyReLU激活(因為它在Tensorflow上不可用)的函數。

 

def conv2d(x, inputFeatures, outputFeatures, name):

    with tf.variable_scope(name):

        w = tf.get_variable("w", [5, 5, inputFeatures, outputFeatures],

                            initializer=tf.truncated_normal_initializer(stddev=0.02))

        b = tf.get_variable("b", [outputFeatures], initializer=tf.constant_initializer(0.0))

        conv = tf.nn.conv2d(x, w, strides=[1, 2, 2, 1], padding="SAME") + b

        return conv

實現卷積層的函數

我們使用get_variable()而不是通常的Variable(),在tensorflow上創建一個變量,以便以后在不同的函數調用之間共享權重和偏差。 查看這篇文章了解更多有關共享變量的信息。

def conv_transpose(x, outputShape, name):

    with tf.variable_scope(name):

        w = tf.get_variable("w", [5, 5, outputShape[-1], x.get_shape()[-1]],

                            initializer=tf.truncated_normal_initializer(stddev=0.02))

        b = tf.get_variable("b", [outputShape[-1]], initializer=tf.constant_initializer(0.0))

        convt = tf.nn.conv2d_transpose(x, w, output_shape=outputShape, strides=[1, 2, 2, 1])

        return convt

實現卷積轉置的函數

# fully-conected layer

def dense(x, inputFeatures, outputFeatures, scope=None, with_w=False):

    with tf.variable_scope(scope or "Linear"):

        matrix = tf.get_variable("Matrix", [inputFeatures, outputFeatures], tf.float32,

                                 tf.random_normal_initializer(stddev=0.02))

        bias = tf.get_variable("bias", [outputFeatures], initializer=tf.constant_initializer(0.0))

        if with_w:

            return tf.matmul(x, matrix) + bias, matrix, bias

        else:

            return tf.matmul(x, matrix) + bias

實現致密完全連接層的函數

def lrelu(x, leak=0.2, name="lrelu"):

    with tf.variable_scope(name):

        f1 = 0.5 * (1 + leak)

        f2 = 0.5 * (1 - leak)

        return f1 * x + f2 * abs(x)

Leaky ReLU

下一步是構建生成器和鑒別器。我們先從主角—生成器開始。我們需要構建的生成器架構如下所示:

我們又一次試圖實現的生成器架構

 

def generator(z, z_dim):

    """

    Used to generate fake images to fool the discriminator.

    :param z: The input random noise.

    :param z_dim: The dimension of the input noise.

    :return: Fake images -> [BATCH_SIZE, IMAGE_SIZE, IMAGE_SIZE, 3]

    """

    gf_dim = 64

    z2 = dense(z, z_dim, gf_dim * 8 * 4 * 4, scope='g_h0_lin')

    h0 = tf.nn.relu(batch_norm(tf.reshape(z2, [-1, 4, 4, gf_dim * 8]),

                               center=True, scale=True, is_training=True, scope='g_bn1'))

    h1 = tf.nn.relu(batch_norm(conv_transpose(h0, [mc.BATCH_SIZE, 8, 8, gf_dim * 4], "g_h1"),

                               center=True, scale=True, is_training=True, scope='g_bn2'))

    h2 = tf.nn.relu(batch_norm(conv_transpose(h1, [mc.BATCH_SIZE, 16, 16, gf_dim * 2], "g_h2"),

                               center=True, scale=True, is_training=True, scope='g_bn3'))

    h3 = tf.nn.relu(batch_norm(conv_transpose(h2, [mc.BATCH_SIZE, 32, 32, gf_dim * 1], "g_h3"),

                               center=True, scale=True, is_training=True, scope='g_bn4'))

    h4 = conv_transpose(h3, [mc.BATCH_SIZE, 64, 64, 3], "g_h4")

    return tf.nn.tanh(h4)

generator()函數使用上圖中的體系架構構建一個生成器。諸如除去所有完全連接層,僅在發生器上使用ReLU以及使用批量歸一化,這些任務DCGAN要求已經達標。

 

類似地,鑒別器也可以很容易地構造成如下圖所示:

鑒別器架構

def discriminator(image, reuse=False):

    """

    Used to distinguish between real and fake images.

    :param image: Images feed to the discriminate.

    :param reuse: Set this to True to allow the weights to be reused.

    :return: A logits value.

    """

    df_dim = 64

    if reuse:

        tf.get_variable_scope().reuse_variables()

 

    h0 = lrelu(conv2d(image, 3, df_dim, name='d_h0_conv'))

    h1 = lrelu(batch_norm(conv2d(h0, df_dim, df_dim * 2, name='d_h1_conv'),

                          center=True, scale=True, is_training=True, scope='d_bn1'))

    h2 = lrelu(batch_norm(conv2d(h1, df_dim * 2, df_dim * 4, name='d_h2_conv'),

                          center=True, scale=True, is_training=True, scope='d_bn2'))

    h3 = lrelu(batch_norm(conv2d(h2, df_dim * 4, df_dim * 8, name='d_h3_conv'),

                          center=True, scale=True, is_training=True, scope='d_bn3'))

    h4 = dense(tf.reshape(h3, [-1, 4 * 4 * df_dim * 8]), 4 * 4 * df_dim * 8, 1, scope='d_h3_lin')

    return h4

 

我們再次避免了密集的完全連接的層,使用了Leaky ReLU,並在Discriminator處進行了批處理。下面到了有趣的部分,我們要訓練這些網絡:鑒別器和發生器的損耗函數如下所示:

 鑒別器損耗函數

 

生成器損耗函數

            G = generator(zin, z_dim)  # G(z)

    Dx = discriminator(images)  # D(x)

    Dg = discriminator(G, reuse=True)  # D(G(x))

我們將隨機輸入傳遞給發生器,輸入阻抗為[BATCH_SIZE,Z_DIM],生成器現在應該在其輸出端給出BATCH_SIZE偽圖像數。生成器輸出的大小現在將為[BATCH_SIZE,IMAGE_SIZE,IMAGE_SIZE,3]。

 

D(x)是識別真實圖像或虛假圖像,進行訓練以便區分它們的鑒別器。為了在真實圖像上訓練鑒別器,我們將真實圖像批處理傳遞給D(x),並將目標設置為1。類似地,想要在來自生成器的假圖像上對其進行訓練的話,我們將使用D(g)將生成器的輸出連接到鑒別器的輸入上。D的損失是使用tensorflow的內置函數實現的:

 

d_loss_real = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits=Dx, targets=tf.ones_like(Dx)))

d_loss_fake = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits=Dg, targets=tf.zeros_like(Dg))) d_loss_fake = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits=Dg, targets=tf.zeros_like(Dg)))

dloss = d_loss_real + d_loss_fake

 

我們接下來需要訓練生成器,以便D(g)將輸出為1,即我們將修正鑒別器上的權重,並且僅在生成器權重上返回,以便鑒別器總是輸出為1。

 

因此,生成器的損耗函數為:

gloss = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits=Dg, targets=tf.ones_like(Dg)))

 

接下來我們將收集鑒別器和生成器的所有權重(以后需要對發生器或判別器進行訓練):

# Get the variables which need to be trained

t_vars = tf.trainable_variables()

d_vars = [var for var in t_vars if 'd_' in var.name]

g_vars = [var for var in t_vars if 'g_' in var.name]

 

我們使用tensorflow的AdamOptimizer來學習權重。接下來我們將需要修改的權重傳遞給鑒別器和生成器的優化器。

with tf.variable_scope(tf.get_variable_scope(), reuse=False) as scope:

    d_optim = tf.train.AdamOptimizer(learning_rate, beta1=beta1).minimize(dloss, var_list=d_vars)

g_optim = tf.train.AdamOptimizer(learning_rate, beta1=beta1).minimize(gloss, var_list=g_vars)

 

最后一步是運行會話並將所需的圖像批處理傳遞給優化器。我們將對這個模型進行30000次迭代訓練,並定期顯示鑒別器和發生器損耗。

with tf.Session() as sess:

        tf.global_variables_initializer().run()

        writer = tf.summary.FileWriter(logdir=logdir, graph=sess.graph)

        if not load:

            for idx in range(n_iter):

                batch_images = next_batch(real_img, batch_size=batch_size)

                batch_z = np.random.uniform(-1, 1, [batch_size, z_dim]).astype(np.float32)

 

                for k in range(1):

                    sess.run([d_optim], feed_dict={images: batch_images, zin: batch_z})

                for k in range(1):

                    sess.run([g_optim], feed_dict={zin: batch_z})

 

                print("[%4d/%4d] time: %4.4f, " % (idx, n_iter, time.time() - start_time))

 

                if idx % 10 == 0:

                    # Display the loss and run tf summaries

                    summary = sess.run(summary_op, feed_dict={images: batch_images, zin: batch_z})

                    writer.add_summary(summary, global_step=idx)

                    d_loss = d_loss_fake.eval({zin: display_z, images: batch_images})

                    g_loss = gloss.eval({zin: batch_z})

                    print("\n Discriminator loss: {0} \n Generator loss: {1} \n".format(d_loss, g_loss))

                    

                if idx % 1000 == 0:

                    # Save the model after every 1000 iternations

                    saver.save(sess, saved_models_path + "/train", global_step=idx)

 

為了簡化調整超參數,並在每次運行時保存結果,我們實現了form_results函數和mission_control.py文件。

 

該網絡的所有超參數可以在mission_control.py文件中進行修改,之后運行的main.py文件將自動為每次運行創建文件夾,並保存數據庫文件和生成的圖像。

 

"""

Contains all the variables necessary to run gans_n_roses.py file.

"""

 

# Set LOAD to True to load a trained model or set it False to train a new one.

LOAD = False

 

# Dataset directories

DATASET_PATH = './Dataset/Roses/'

DATASET_CHOSEN = 'roses'  # required by utils.py -> ['birds', 'flowers', 'black_birds']

 

# Model hyperparameters

Z_DIM = 100  # The input noise vector dimension

BATCH_SIZE = 12

N_ITERATIONS = 30000

LEARNING_RATE = 0.0002

BETA_1 = 0.5

IMAGE_SIZE = 64  # Change the Generator model if the IMAGE_SIZE needs to be changed to a different value

 

我們可以通過打開tensorboard,在訓練期間的每次迭代中查鑒別器和生成器的損耗,並將其指向在每個運行文件夾下創建的Tensorboard目錄中。 

訓練期間發生器損耗的變化 

 

訓練期間鑒別器損耗的變化

從這些圖可以看出,在訓練階段,鑒別器和生成器損耗在不斷增加,表明生成器和鑒別器都試圖相互執行。代碼還可以為每次運行保存生成的圖像,其中一些圖像如下所示:

在第0次迭代:

第100次迭代:

第1000次迭代:

圖像在第30000次迭代中被過度擬合 

這些圖像是有希望實現目標的,但是經過大約1000次迭代,可以看出,發生器只是從訓練數據集中再現圖像。我們可以使用較大的數據集,並進行較少數量的迭代訓練,以減少過度擬合。

 

GAN易於實現,但如果沒有正確的超參數和網絡架構,是難以進行訓練的。我們寫這篇文章主要是為了幫助人們開始使用生成網絡。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM