如果想虛擬出人類的五官,用大頭照是最適合的,這邊使用的訓練樣本是來自 Flickr 的資料庫—Flickr Faces HQ Dataset(FFHQ)。
- 圖片數目:70,000 張
- 圖片種類:PNG 圖檔
- 圖片尺寸:共三種(裁切臉部的高品質 HQ、低解像 Thumbnail、in-the-wild 相片原圖各 70,000 張)
- HQ:1024×1024 Thumbnail:128×128
- 圖片特性:每一張圖片皆使用 Dlib 自動裁切臉部及校正對齊
這次所使用的程式,參考自周凡剛老師所開的課程並作了些許修改(如果您對 GAN 有興趣,相當推薦周老師的這個課程唷!)
Generator模型
主要修改基本的 MNIST DCGAN 架構,讓 Generator 能產生 56×56 的彩色 RGB 圖片。由於每次採樣會增加二倍尺寸,因此一開始的 7×7 需要三次的 up sampling 才會達到所需的 56×56(7x7x128 🡪 14x14x128 🡪 28x28x128 🡪 56×56×128 🡪 56x56x3),因此使用迴圈的方式會比較簡潔;另外,這邊使用的 128 是參考 VGG 的作法,這個數值可以更改。
def generator_model(dim, base_gen, rand_dim):gen = Sequential()
gen.add(Dense(dim*dim*base_gen, input_dim=rand_dim, activation='relu'))
gen.add(Reshape((dim, dim, base_gen))) # 轉換成三維
for i in range(0, int(base_gen/28 + 1)):
# 上採樣, 長寬會變兩倍
gen.add(UpSampling2D(size=(2, 2)))
# (4, 4)卷積窗的卷積, DCGAN模型的標準用法。
gen.add(Conv2D(base_gen, kernel_size=(4, 4), activation='relu', padding='same'))
# 卷積層間使用BN來normalize,也是DCGAM的標準用法。
gen.add(BatchNormalization())
#因為是RGB圖片, 因此讓filter為3, 輸出channel為3的圖片
#一樣使用tanh作為激活
gen.add(Conv2D(3, kernel_size=(4, 4), activation='tanh', padding='same'))
return gen
Discriminator模型
基本上 Discriminator 方面,只要將輸入的部份修改成與 Generator 輸出的尺寸相同(56x56x3)即可。
def discriminator_model(in_shape, base_gen):discriminator = Sequential()
#步長2卷積,這是 DCGAN的標準用法。
discriminator.add(Conv2D(int(base_gen/8), kernel_size=4,
strides=2,
input_shape=in_shape,
padding="same",
activation='relu'))
#DCGAN都會在兩層卷積中加入BN
discriminator.add(BatchNormalization())
discriminator.add(Conv2D(int(base_gen/4), kernel_size=4,
strides=2,
padding="same",
activation='relu'))
discriminator.add(BatchNormalization())
discriminator.add(Conv2D(int(base_gen/2), kernel_size=4,
strides=2,
padding="same",
activation='relu'))
discriminator.add(BatchNormalization())
discriminator.add(Conv2D(base_gen, kernel_size=4,
strides=2,
padding="same",
activation='relu'))
discriminator.add(BatchNormalization())
#全連接層(MLP)
discriminator.add(Flatten())
discriminator.add(Dense(base_gen*2, activation='relu'))
discriminator.add(Dropout(0.25))
discriminator.add(Dense(1, activation='sigmoid'))
return discriminator
DCGAN的訓練
在進行 GAN 訓練時,雖然 Discriminator 與 Generator 都包含在 GAN 模型中,但是由於 Discriminator 的 trainable 設定為 False,這表示在訓練 GAN 時,只有 Generator 被訓練到,此時 GAN 輸出的 loss 視為 Generator 的 loss;而 Discriminator 自己單獨進行訓練,它的 loss 是由兩種 loss 加總:判斷 Generator 產生的虛擬圖片、判斷 Dataset 中真實的圖片。
loss_file = os.path.join(output_folder, "loss_" + str(epoch_count) + "epochs.csv")f = open(loss_file, 'w')
f.write("epoch,d_loss,g_loss \n")
f.close()
for epoch in range(0, epoch_count):
for batch_count in range(0, 100):
idx = np.random.randint(0, x_train.shape, batch_size)
imgs = x_train_shaped
valid = np.ones((batch_size, 1))
fake = np.zeros((batch_size, 1))
#Generator製造出虛擬圖片,這些圖片由Discriminator來判斷是否為False
noise = np.random.normal(0, 1, (batch_size, random_dim))
gen_imgs = generator.predict(noise)
discriminator.trainable = True
#由Discriminator判斷由Dataset來的圖片是否為True。
d_loss_real = discriminator.train_on_batch(imgs, valid)
#由Discriminator判斷由Generator產生的虛擬圖片。
d_loss_fake = discriminator.train_on_batch(gen_imgs, fake)
#兩者加總即為Discriminator的Loss
d_loss = (d_loss_real + d_loss_fake) / 2
discriminator.trainable = False
noise = np.random.normal(0, 1, (batch_size, random_dim))
#由於GAN模型中,Discriminator設為不可訓練,因此訓練GAN模型,可視為僅訓練Generator。
g_loss = gan.train_on_batch(noise, valid)
訓練時的資訊輸出
因為想要看看 Generator 在迭代訓練時,每批次訓練出來的臉孔圖片有否進步,同時也希望將模型的 loss值輸出以利統計,因此加入了下方的程式,這可以讓其在指定間隔的 epoch 次數達到時,輸出 Generator 的預測圖片、loss 值以及匯出模型的權重。
out_path = output_folder +'/' + str(epoch+1) + '/'
if (epoch + 1) % epoch_interval_output_log == 0:
f = open(loss_file, 'a')
msg_line = "{} epoch Discriminator loss:{} Generator loss:{}".format(epoch + 1, d_loss, g_loss)
print(msg_line)
f.write( "{},{},{}\n".format(epoch+1,d_loss,g_loss))
f.close()
if (epoch + 1) % epoch_interval_output_model == 0:
if( not os.path.isdir(out_path)):
os.mkdir(out_path)
print("Output models.")
output_model(out_path, output_images_num, random_dim, epoch+1, generator, discriminator, gan)
if (epoch + 1) % epoch_interval_output_img == 0:
if( not os.path.isdir(out_path)):
os.mkdir(out_path)
print("Output images.")
output_img(out_path, output_images_num, random_dim, epoch+1, generator, discriminator, gan)
訓練結果
將 Generator 和 Discriminator 的 loss 輸出後,會看到它的訓練曲線圖如下,看起來並沒有明顯的下降趨勢,這是由於其 loss 是雙方相互拉距拔河的結果,Generator 強時 Discriminator 弱,反之亦同,因此就沒有一般深度學習模型訓練 loss 愈來愈低的情況。
(圖片來源:曾成訓提供)
另外值得一提的是,起初在訓練時,Generator 處於很吃虧的情況,因為要由無生有地產生以假亂真的圖片,一定不知道要如何產生,因此它產生的圖片很容易就會被 Discriminator 判定為假,造成 Generator 不知道要如何產生真的圖片而被迫提早放棄。
幸好本範例使用 56×56 的圖片還能訓練出來,但如果擴大一倍到 112×112,對 Generator 來說可能就很難訓練了。
下面是 Generator 各訓練階段的虛擬能力:
只需不到短短一分鐘...
輸入您的信箱與ID註冊即可享有一切福利!
會員福利
免費電子報
會員搶先看
主題訂閱
好文收藏