4.keras實現-->生成式深度學習之用GAN生成圖像


生成式對抗網絡(GAN,generative adversarial network)由Goodfellow等人於2014年提出,它可以替代VAE來學習圖像的潛在空間。它能夠迫使生成圖像與真實圖像在統計上幾乎無法區別,從而生成相當逼真的合成圖像。

                              

1.GAN是什么?

簡單來說就是由兩部分組成,生成器generator網絡和判別器discriminator網絡。一部分不斷進化,使其對立部分也不斷進化,實現共同進化的過程。

對GAN的一種直觀理解是,想象我們想要試圖生成一個二次元頭像。一開始,我們並不擅長這項任務,就將自己的一些噪音二次元頭像和真的二次元頭像混在一起,並將其展示給discriminator。discriminator對每個頭像進行真實性評估,並向我們給出反饋,告訴我們是什么讓二次元頭像看起來像真的二次元頭像,我們回到自己的工作室,並准備一些新的二次元頭像。隨着時間的推移,我們變得越來越擅長模仿二次元頭像的風格,discriminator也變得越來越擅長找出假的二次元頭像。最后,我們手上擁有了一些優秀的二次元頭像。

2.為什么?

【1】為什么我們有真的二次元頭像和假的二次元頭像,為什么不自己用監督學習生成新的二次元頭像呢?

  generator無法自己獨立學習的原因是,以vae為例,輸出layer層輸出的是各像素點,而他們在輸出時是獨立的,沒有相互作用的,因此無法判斷總體的效果進行自主學習。對於discriminator,其輸入是生成的整張圖像,因此可以從總體上進行判斷。

  需要注意的是,discriminator對於輸入的真實圖像都應是高分,那么如果訓練時只給它真實圖像的話,他就無法實現正確的判斷,會將所有輸入都判為高分。所以需要一些差的圖像送給discriminator進行訓練,並且這些差的圖像不應是簡單的加些噪聲之類的能讓它輕易分辨的。因此,訓練它的方法是,除真實圖像外先給它一些隨機生成的差的例子,然后對discriminator解argmaxD(x)做generation生成出一些他覺得好的圖像,然后將原本極差的圖像換為這些圖像再進行訓練,如此往復,discriminator會不斷產生更好的圖像,將這些作為negative examples給其學習,達到訓練的目的。

 

【2】discriminator對真的二次元頭像這么了解,為什么他不自己做,而是要來指導我們做呢?

那既然如此,為什么還需要generator呢?discriminator自己也可以生成圖像啊?

    這是因為discriminator生成圖像需要解argmaxD(x), 難度較大,一般需要假設一些條件才會好解,比如網絡假設為線性時,但這樣會限制圖像的生成效果。而generator生成非常快,因此將二者結合起來共同學習實現輸出好的結果。二者優缺點如下所示:

總而言之,因為generator沒有全局觀,所以需要結合discriminator學習,對於discriminator,使用generator生成圖像比自己解方程生成更簡單高效,這二者的優缺點相互補充。

 

GAN的目的是為了生成,而VAE目的是為了壓縮,目的不同效果自然不同。比如,由於二范數的原因,VAE的生成是模糊的。而GAN的生成是犀利的。

 

數據集為CIFAR10,包含50000張32*32的RGB圖像,這些圖像屬於10個類別(每個類別5000張圖像),這里我們只使用屬於“frog”(青蛙)類別的圖像

import keras
from keras import layers
import numpy as np

  

 

生成器網絡:將一個向量(來自潛在空間,訓練過程中對其隨機采樣)轉換為一張候選圖像

生成器從未直接見過訓練集中的圖像,它所知道的關於數據的信息都來自於判別器。

latent_dim = 32
height = 32
width = 32
channels = 3

generator_input = keras.Input(shape=(latent_dim,))

#將輸入轉換為大小為16*16的128個通道的特征圖
x = layers.Dense(128*16*16)(generator_input)
x = layers.LeakyReLU()(x)
x = layers.Reshape((16,16,128))(x)

x = layers.Conv2D(256,5,padding='same')(x)
x = layers.LeakyReLU()(x)

#上采樣為32*32
x = layers.Conv2DTranspose(256,4,strides=2,padding='same')(x)
x = layers.LeakyReLU()(x)

x = layers.Conv2D(256,5,padding='same')(x)
x = layers.LeakyReLU()(x)
x = layers.Conv2D(256,5,padding='same')(x)
x = layers.LeakyReLU()(x)

#生成一個大小為32*32的單通道特征圖(即CIFAR10圖像的形狀)
x = layers.Conv2D(channels,7,activation='tanh',padding='same')(x)
#將生成器模型實例化,它將形狀為(latent_dim,)的輸入映射到形狀為(32,32,3)的圖像
generator = keras.models.Model(generator_input,x)
generator.summary()

  

 

判別器網絡:它接收一張候選圖像(真實的或合成的)作為輸入,並將其划分到這兩個類別之一:"生成圖像"或"來自訓練集的真實圖像"

 

#GAN判別器網絡
discriminator_input = layers.Input(shape=(height,width,channels))
x = layers.Conv2D(128,3)(discriminator_input)
x = layers.LeakyReLU()(x)
x = layers.Conv2D(128,4,strides=2)(x)
x = layers.LeakyReLU()(x)
x = layers.Conv2D(128,4,strides=2)(x)
x = layers.LeakyReLU()(x)
x = layers.Conv2D(128,4,strides=2)(x)
x = layers.LeakyReLU()(x)
x = layers.Flatten()(x)

x = layers.Dropout(0.4)(x)

x = layers.Dense(1,activation='sigmoid')(x)#分類層

#將判別器模型實例化,它將形狀為(32,32,3)的輸入轉換為一個二進制分類決策(真/假)
discriminator = keras.models.Model(discriminator_input,x)
discriminator.summary()

  

  

 

discriminator_optimizer = keras.optimizers.RMSprop(
    lr=0.0008,
    clipvalue = 1.0, #在優化器中使用梯度裁剪(限制梯度值的范圍)
    decay = 1e-8,#為了穩定訓練過程,使用學習率衰減
)

discriminator.compile(optimizer=discriminator_optimizer,
                     loss='binary_crossentropy')

  

設置GAN,將生成器和判別器連接在一起 訓練時,這個模型將讓生成器向某個方向移動,從而提高它欺騙判別器的能力。這個模型將潛在空間的點轉換為一個分類決策(即"真"或"假") 它訓練的標簽都是"真實圖像"。因此,訓練gan將會更新generator得到權重,使得discriminator在觀測假圖像時更有可能預測為"真"。

對抗網絡

 

discriminator.trainable = True #將判別器權重設置為不可訓練(僅應用於gan模型)

gan_input = keras.Input(shape=(latent_dim,))
gan_output = discriminator(generator(gan_input))
gan = keras.models.Model(gan_input,gan_output)
gan_optimizer = keras.optimizers.RMSprop(lr=0.0004,clipvalue=1.0,decay=1e-8)
gan.compile(optimizer=gan_optimizer,loss='binary_crossentropy')

  

注意:在訓練過程中需要將判別器設置為凍結(即不可訓練),這樣在訓練gan時它的權重才不會更新。 如果在此過程中可以對判別器的權重進行更新,那么我們就是在訓練判別器始終預測"真",但這並不是我們想要的。

實現GAN的訓練

 

import os
from keras.preprocessing import image

(x_train,y_train),(_,_) = keras.datasets.cifar10.load_data() #cifar數據集
x_train = x_train[y_train.flatten() == 6]#選擇青蛙的圖像
x_train = x_train.reshape((x_train.shape[0],) + (height,width,channels)).astype('float32')/255.

  

 

 

 

iterations = 1000
batch_size = 2
save_dir = 'frog_dir'

start = 0

for step in range(iterations):
    random_latent_vectors = np.random.normal(size=(batch_size,latent_dim))
    generated_images = generator.predict(random_latent_vectors)#點-->虛假圖像
    
    stop = start + batch_size
    
    #混淆真實圖像和虛假圖像
    real_images = x_train[start:stop]
    combined_images = np.concatenate([generated_images,
                                      real_images])
    labels = np.concatenate([np.ones((batch_size,1)),
                             np.zeros((batch_size,1))])
    labels += 0.05 * np.random.random(labels.shape) #向標簽中添加噪聲
    
    #訓練判別器
    d_loss = discriminator.train_on_batch(combined_images,labels)
    
    #在潛在空間中采樣隨機點
    random_latent_vectors = np.random.normal(size=(batch_size,latent_dim))
    #合並標簽,全都是“真實圖像”(這是在撒謊)
    misleading_targets = np.zeros((batch_size,1))
    
    #通過gan模型來訓練生成器(此時凍結判別器模型)
    a_loss = gan.train_on_batch(random_latent_vectors,misleading_targets)
    
    start += batch_size
    if start > len(x_train) - batch_size:
        start = 0
    if step % 2 == 0:
        gan.save_weights('gan.h5')
        
        print('discriminator loss:',d_loss)
        print('adversarial loss:',a_loss)
        
        img = image.array_to_img(generated_images[0] * 255.,scale=False)
        img.save(os.path.join(save_dir,'generated_frog'+str(step)+'.png'))
        
        img = image.array_to_img(real_images[0]*255.,scale=False)
        img.save(os.path.join(save_dir,'real_frog'+str(step)+'.png'))

  

判別器損失:d_loss=(生成的圖像和真實圖像->標簽)

 

gan損失:a_loss=(隨機采樣的點->全是'真'的標簽) 

 

 

 第一次  

 

 最后一次   

 

 

 


免責聲明!

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



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