生成對抗網絡(GAN)由2個重要的部分構成:python
訓練過程網絡
公式中 \(p_i\) 和 \(q_i\) 爲真實的樣本分佈和生成器的生成分佈dom
假定 \(y_1\) 爲正確樣本分佈,那麼對應的( \(1-y_1\) )就是生成樣本的分佈。\(D\) 表示判別器,則 \(D(x_1)\) 表示判別樣本爲正確的機率, \(1-D(x_1)\) 則對應着判別爲錯誤樣本的機率。則有以下式子(這裏僅僅是對當前狀況下的交叉熵損失的具體化)。ide
對於GAN中的樣本點 \(x_i\) ,對應於兩個出處,要麼來自於真實樣本,要麼來自於生成器生成的樣本 $\tilde{x} - G(z) $ ( 這裏的 \(z\) 是服從於投到生成器中噪聲的分佈)。函數
對於來自於真實的樣本,咱們要判別爲正確的分佈 \(y_i\) 。來自於生成的樣本咱們要判別其爲錯誤分佈( \(1-y_i\) )。將上面式子進一步使用機率分佈的指望形式寫出(爲了表達無限的樣本狀況,至關於無限樣本求和狀況),而且讓 \(y_i\) 爲 1/2 且使用 \(G(z)\) 表示生成樣本能夠獲得以下公式:性能
對於論文中的公式優化
實際上是與上面公式同樣的,下面作解釋ui
卷積對抗生成網絡(DCGAN)是在GAN的基礎上加入了CNN,主要是改進了網絡結構,在訓練過程當中狀態穩定,而且能夠有效實現高質量圖片的生成以及相關的生成模型應用。DCGAN的生成器網絡結構以下圖:
spa
DCGAN的改進:3d
shenduimport numpy as np import matplotlib.pyplot as plt import tensorflow as tf from tensorflow import keras from tensorflow.keras import optimizers, losses, layers, Sequential, Model
class DCGAN(): ''' 實現深度對抗神經網絡 生成 MNIST 手寫數字圖片 輸入的噪聲爲服從正態分佈均值爲 0 方差爲 1 的分佈, shape:(None, 100) 生成器(G)輸入 噪聲, 輸出爲 (None, 28, 28, 1)的圖片 分類器(D)輸入爲 (None, 28, 28, 1)的圖片,輸出圖片的分類真假 ''' 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) optimizer = optimizers.Adam(0.0002) # 構建編譯分類器 self.discriminator = self.build_discriminator() self.discriminator.compile(loss='binary_crossentropy', optimizer=optimizer, metrics=['accuracy']) # 構建編譯生成器 self.generator = self.build_generator() self.generator.compile(loss='binary_crossentropy', optimizer=optimizer) # 生成器輸入爲噪音,生成圖片 z = layers.Input(shape=(100,)) img = self.generator(z) # 對於整個對抗網絡模型只優化生成器的參數 self.discriminator.trainable = False # 用生成的圖片輸入分類器判斷 valid = self.discriminator(img) # 對於整個對抗網絡 輸入噪音 => 生成圖片 => 決定圖片是否有效 self.combined = Model(z, valid) self.combined.compile(loss='binary_crossentropy', optimizer=optimizer) def build_generator(self): ''' 構建生成器 ''' noise_shape = (100,) model = tf.keras.Sequential() # 添加全鏈接層 model.add(layers.Dense(7*7*256, use_bias=False, input_shape=noise_shape)) # 添加 BatchNormalization 層,對數據進行歸一化 model.add(layers.BatchNormalization()) model.add(layers.LeakyReLU()) model.add(layers.Reshape((7, 7, 256))) # 添加逆卷積層,卷積核大小爲 5X5,數量 128, 步長爲 1 model.add(layers.Conv2DTranspose(128, (5, 5), strides=(1, 1), padding='same', use_bias=False)) assert model.output_shape == (None, 7, 7, 128) model.add(layers.BatchNormalization()) model.add(layers.LeakyReLU()) # 添加逆卷積層,卷積核大小爲 5X5,數量 64, 步長爲 2 model.add(layers.Conv2DTranspose(64, (5, 5), strides=(2, 2), padding='same', use_bias=False)) assert model.output_shape == (None, 14, 14, 64) model.add(layers.BatchNormalization()) model.add(layers.LeakyReLU()) # 添加逆卷積層,卷積核大小爲 5X5,數量 1, 步長爲 2 model.add(layers.Conv2DTranspose(1, (5, 5), strides=(2, 2), padding='same', use_bias=False, activation='tanh')) assert model.output_shape == (None, 28, 28, 1) model.summary() noise = layers.Input(shape=noise_shape) img = model(noise) # 返回 Model 對象,輸入爲 噪聲, 輸出爲 圖像 return keras.Model(noise, img) def build_discriminator(self): ''' 構建分類器 ''' img_shape = (self.img_rows, self.img_cols, self.channels) model = tf.keras.Sequential() model.add(layers.Conv2D(64, (5, 5), strides=(2, 2), padding='same', input_shape=img_shape)) model.add(layers.LeakyReLU()) # 添加 Dropout 層,減小參數數量 model.add(layers.Dropout(0.3)) model.add(layers.Conv2D(128, (5, 5), strides=(2, 2), padding='same')) model.add(layers.LeakyReLU()) model.add(layers.Dropout(0.3)) # 把數據鋪平 model.add(layers.Flatten()) model.add(layers.Dense(1)) model.summary() img = layers.Input(shape=img_shape) validity = model(img) return keras.Model(img, validity) def train(self, epochs, batch_size=128, save_interval=50): ''' 網絡訓練 ''' # 加載 數據集 (X_train, _), (_, _) = keras.datasets.mnist.load_data() # 把數據縮放到 [-1, 1] X_train = (X_train.astype(np.float32) - 127.5) / 127.5 # 添加通道維度 X_train = np.expand_dims(X_train, axis=3) half_batch = int(batch_size / 2) for epoch in range(epochs): # --------------------- # 訓練分類器 # --------------------- # 隨機的選擇一半的 batch 數量圖片 idx = np.random.randint(0, X_train.shape[0], half_batch) imgs = X_train[idx] noise = np.random.normal(0, 1, (half_batch, 100)) # 生成一半 batch 數量的 圖片 gen_imgs = self.generator.predict(noise) # 分類器損失 d_loss_real = self.discriminator.train_on_batch(imgs, np.ones((half_batch, 1))) d_loss_fake = self.discriminator.train_on_batch(gen_imgs, np.zeros((half_batch, 1))) d_loss = 0.5 * np.add(d_loss_real, d_loss_fake) # --------------------- # 訓練生成器 # --------------------- noise = np.random.normal(0, 1, (batch_size, 100)) # The generator wants the discriminator to label the generated samples # as valid (ones) # 對於生成器,但願分類器把更多的圖片判爲 有效 (用 1 表示) valid_y = np.array([1] * batch_size) # 訓練生成器 g_loss = self.combined.train_on_batch(noise, valid_y) # 打印訓練進度 print ("%d [D loss: %f, acc.: %.2f%%] [G loss: %f]" % (epoch, d_loss[0], 100*d_loss[1], g_loss)) # 每一個 save_interval 週期保存一張圖片 if epoch % save_interval == 0: self.save_imgs(epoch) def save_imgs(self, epoch): r, c = 5, 5 noise = np.random.normal(0, 1, (r * c, 100)) gen_imgs = self.generator.predict(noise) # 把圖片數據縮放到 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("dcgan/images/mnist_%d.png" % epoch) plt.close() if __name__ == '__main__': dcgan = DCGAN() dcgan.train(epochs=10000, batch_size=32, save_interval=200)
網絡參數信息
下面是循環了 10000 次 epoch 後,從開始每隔 2000 個 epoch 生成器生成的圖片