基於Keras的GAN面向對象代碼

keras相比於疼搜人flow代碼更加簡潔,減少了開發的成本。在開始之前建議大家先去了解下keras. 這裏仍然是以最簡單的手寫數字集合爲例。 也可以用cifar10, celebA, 或者別的數據集合。


from __future__ import print_function, division

from keras.datasets import mnist
from keras.layers import Input, Dense, Reshape, Flatten, Dropout
from keras.layers import BatchNormalization, Activation, ZeroPadding2D
from keras.layers.advanced_activations import LeakyReLU
from keras.layers.convolutional import UpSampling2D, Conv2D
from keras.models import Sequential, Model
from keras.optimizers import Adam

import matplotlib.pyplot as plt

import sys

import numpy as np
from tensorflow.examples.tutorials.mnist import input_data


class GAN():
    def __init__(self):
        # 手寫數字圖片的大小
        self.img_rows = 28
        self.img_cols = 28
        self.channels = 1
        self.img_shape = (self.img_rows, self.img_cols, self.channels)
        # 生成對抗網絡的噪聲長度
        self.latent_dim = 100
        # 優化器
        optimizer = Adam(0.0002, 0.5)

        # 構建並且編譯判別器
        self.discriminator = self.build_discriminator()
        self.discriminator.compile(loss='binary_crossentropy',
            optimizer=optimizer,
            metrics=['accuracy'])

        # 構造生成器、判別器的堆疊,  z --->img---->validity, 使得由噪音可以直接得到判別結果,不用每次
        #都要先生成圖片,在調用判別器判定
        self.generator = self.build_generator()
        # z爲生成器的輸入,長度是100的噪聲
        z = Input(shape=(self.latent_dim,))
        # 將z放入生成器中,最終會得到生成器的輸出, 函數式模型的特性
        img = self.generator(z)
        # For the combined model we will only train the generator
        self.discriminator.trainable = False
        # 判別器對圖片的判別結果
        validity = self.discriminator(img)
        self.combined = Model(z, validity)
        # 進行編譯, 可看成2分類問題,真實圖片爲1, 生成圖片爲0
        self.combined.compile(loss='binary_crossentropy', optimizer=optimizer)

    # 生成器函數
    def build_generator(self):
        # 序慣模型構建生成器
        model = Sequential()
        # 增加256個神經元
        model.add(Dense(256, input_dim=self.latent_dim))
        # 使用relu激活函數
        model.add(Activation("relu"))
        # 正則化
        model.add(Dropout(0.4))
        model.add(BatchNormalization(momentum=0.8))
        model.add(Dense(512))
        model.add(Activation("relu"))
        model.add(Dropout(0.4))
        # batchNormalization 處理, 減少隨機生成參數出現的誤差
        model.add(BatchNormalization(momentum=0.8))
        # 增加1024個神經元,使用relu激活函數
        model.add(Dense(1024))
        model.add(Activation("relu"))
        model.add(Dropout(0.4))
        model.add(BatchNormalization(momentum=0.8))
        # 相當於增加28x28x1個神經元
        model.add(Dense(np.prod(self.img_shape), activation='tanh'))
        # 使輸出變爲28x28x1的圖片
        model.add(Reshape(self.img_shape))
        # 輸出模型結構
        model.summary()

        noise = Input(shape=(self.latent_dim,))
        img = model(noise)
        # 返回結果 --->給定一個噪音,就會生成一個28x28x1的圖片
        return Model(noise, img)

    # 判別器函數
    def build_discriminator(self):
        # 序貫模型
        model = Sequential()
        # 將輸入的圖片變爲一維數組
        model.add(Flatten(input_shape=self.img_shape))
        # 增加神經元節點
        model.add(Dense(512))
        model.add(Activation("relu"))
        model.add(Dense(256))
        model.add(Activation("relu"))
        model.add(Dropout(0.4))
        # 最後一層使用sigmoid激活
        model.add(Dense(1, activation='sigmoid'))
        model.summary()

        img = Input(shape=self.img_shape)
        validity = model(img)
        # 返回的結果類似一個函數,---給定一個28x28x1的圖片輸出,產生一個概率值
        return Model(img, validity)

    # 開始訓練
    def train(self, epochs, batch_size=128, sample_interval=50):

        # 加載數據集合,這裏有一個巨坑, tensorflow中的mnist圖片讀進來之後像素值0,1, Keras中是0-255,
        # 使用tensorflow讀取數據的時候要在像素值後面乘以255
        X_train = input_data.read_data_sets("MNIST_DATA", one_hot=True).train.images
        X_train = np.reshape(X_train, (-1, 28, 28, 1)) * 255
        X_train = X_train / 127.5 - 1.


        # 真實圖片的標籤爲1
        valid = np.ones((batch_size, 1))
        # 生成圖案標籤爲0
        fake = np.zeros((batch_size, 1))

        for epoch in range(epochs):
            # 從訓練數據中隨機選擇batch_size張
            idx = np.random.randint(0, X_train.shape[0], batch_size)
            imgs = X_train[idx]

            # 生成batch_size 個噪聲
            noise = np.random.normal(0, 1, (batch_size, self.latent_dim))

            # 將噪聲輸入到生成器中生成圖片
            gen_imgs = self.generator.predict(noise)

            # 定義生成器和判別器的損失, 判別器努力分辨出真假圖片
            d_loss_real = self.discriminator.train_on_batch(imgs, valid)
            d_loss_fake = self.discriminator.train_on_batch(gen_imgs, fake)
            d_loss = 0.5 * np.add(d_loss_real, d_loss_fake)

            noise = np.random.normal(0, 1, (batch_size, self.latent_dim))
            # 生成器, self.combined 的目的是給出一個噪聲可以直接獲得預測結果,也可以拆開
            # 努力使得生成的圖片更接近1,和判別是一個對抗過程
            g_loss = self.combined.train_on_batch(noise, valid)

            # Plot the progress
            print ("%d [D loss: %f, acc.: %.2f%%] [G loss: %f]" % (epoch, d_loss[0], 100*d_loss[1], g_loss))

            # If at save interval => save generated image samples
            if epoch % sample_interval == 0:
                self.sample_images(epoch)

    # 畫圖函數
    def sample_images(self, epoch):
        r, c = 5, 5
        noise = np.random.normal(0, 1, (r * c, self.latent_dim))
        gen_imgs = self.generator.predict(noise)

        # Rescale images 0 - 1
        gen_imgs = 0.5 * gen_imgs + 0.5
        fig, axs = plt.subplots(r, c)
        cnt = 0
        for i in range(r):
            for j in range(c):
                axs[i,j].imshow(gen_imgs[cnt, :,:,0], cmap='gray')
                axs[i,j].axis('off')
                cnt += 1
        fig.savefig("images/%d.png" % epoch)
        plt.close()


if __name__ == '__main__':
    gan = GAN()
    gan.train(epochs=20000, batch_size=32, sample_interval=100)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章