前提
pythonとkerasを用いて128*128の猫の顔画像を生成するDCGANを作成しています。
学習データとしてkaggleのCATデータセットから顔の部分を抽出して利用しています。
https://www.kaggle.com/datasets/crawford/cat-dataset
本やサイトを参考にしながら実装してみたのですが、500バッチほど学習させたあたりから生成結果にほとんど変化がなくなってしまいます。
初期状態
500バッチ学習後
5000バッチ学習後
学習のログを見ると、バッチ数が一定を超えると識別器Dと生成器Gの損失関数の値lossがほとんどゼロになっていることで重みが更新されていないことがわかります。
log
13342 [D loss: 0.000000, acc.: 100.00%] [G loss: 0.000055] 23343 [D loss: 0.000000, acc.: 100.00%] [G loss: 0.000006] 33344 [D loss: 0.000000, acc.: 100.00%] [G loss: 0.000045] 43345 [D loss: 0.000000, acc.: 100.00%] [G loss: 0.000082] 53346 [D loss: 0.000000, acc.: 100.00%] [G loss: 0.000120] 63347 [D loss: 0.000000, acc.: 100.00%] [G loss: 0.000083] 73348 [D loss: 0.000000, acc.: 100.00%] [G loss: 0.000016] 83349 [D loss: 0.000000, acc.: 100.00%] [G loss: 0.000048] 93350 [D loss: 0.000000, acc.: 100.00%] [G loss: 0.000042] 103351 [D loss: 0.000001, acc.: 100.00%] [G loss: 0.000153] 113352 [D loss: 0.000000, acc.: 100.00%] [G loss: 0.000104] 123353 [D loss: 0.000000, acc.: 100.00%] [G loss: 0.000020]
質問
このDCGANの学習を正常に進ませるにはどうすればよいでしょうか?
またこの停滞はなぜ起こっているのでしょうか?(ハイパーパラメーターの調整不足?実装ミス?)
試したこと
初めに実装ミスを疑い様々なサイトの実装例を見比べてみましたが自分では問題を発見できませんでした。
該当のソースコード
python
1from keras.layers import Dense, Reshape, Flatten, Dropout 2from keras.layers import BatchNormalization, Activation, ZeroPadding2D 3from keras.layers.activation import LeakyReLU 4from keras.layers.convolutional import UpSampling2D, Conv2D 5from keras.models import Sequential 6from keras.optimizers import Adam 7import pickle 8from PIL import Image 9from matplotlib import pyplot as plt 10import cv2 11import numpy as np 12 13class DCGAN(): 14 def __init__(self): 15 16 optimizer = Adam(lr=0.0002, beta_1=0.5) 17 18 self.discriminator = self.build_discriminator() 19 self.discriminator.compile(loss='binary_crossentropy', optimizer=optimizer, metrics=['accuracy']) 20 21 self.generator = self.build_generator() 22 23 self.discriminator.trainable = False 24 25 self.combined = self.build_combined() 26 self.combined.compile(loss='binary_crossentropy', optimizer=optimizer) 27 28 def build_generator(self): 29 noise_shape = (100,) 30 model = Sequential() 31 32 model.add(Dense(128 * 32 * 32, activation="relu", input_shape=noise_shape)) 33 model.add(Reshape((32, 32, 128))) 34 model.add(BatchNormalization(momentum=0.8)) 35 model.add(UpSampling2D()) 36 model.add(Conv2D(128, kernel_size=3, padding="same")) 37 model.add(Activation("relu")) 38 model.add(BatchNormalization(momentum=0.8)) 39 model.add(UpSampling2D()) 40 model.add(Conv2D(64, kernel_size=3, padding="same")) 41 model.add(Activation("relu")) 42 model.add(BatchNormalization(momentum=0.8)) 43 model.add(Conv2D(3, kernel_size=3, padding="same")) 44 model.add(Activation("tanh")) 45 46 model.summary() 47 48 return model 49 50 def build_discriminator(self): 51 img_shape = (128,128,3) 52 model = Sequential() 53 54 model.add(Conv2D(32, kernel_size=3, strides=2, input_shape=img_shape, padding="same")) 55 model.add(LeakyReLU(alpha=0.2)) 56 model.add(Dropout(0.25)) 57 model.add(Conv2D(64, kernel_size=3, strides=2, padding="same")) 58 model.add(ZeroPadding2D(padding=((0, 1), (0, 1)))) 59 model.add(LeakyReLU(alpha=0.2)) 60 model.add(Dropout(0.25)) 61 model.add(BatchNormalization(momentum=0.8)) 62 model.add(Conv2D(128, kernel_size=3, strides=2, padding="same")) 63 model.add(LeakyReLU(alpha=0.2)) 64 model.add(Dropout(0.25)) 65 model.add(BatchNormalization(momentum=0.8)) 66 model.add(Conv2D(256, kernel_size=3, strides=1, padding="same")) 67 model.add(LeakyReLU(alpha=0.2)) 68 model.add(Dropout(0.25)) 69 70 model.add(Flatten()) 71 model.add(Dense(1, activation='sigmoid')) 72 73 model.summary() 74 75 return model 76 77 def build_combined(self): 78 self.discriminator.trainable = False 79 model = Sequential([self.generator, self.discriminator]) 80 81 return model 82 83 def train(self, iterations, batch_size=128, save_interval=50): 84 #生成を確かめる用のノイズベクトル 85 check_noise = noise = np.random.uniform(-1, 1, (9, 100)) 86 87 #pklファイルから学習する画像データを読み込む 88 with open("./kaggle_cat/data.pkl","rb") as f: 89 X_train = pickle.load(f) 90 91 #学習する画像データを-1~1に収める 92 X_train = (X_train.astype(np.float32) - 127.5) / 127.5 93 94 half_batch = int(batch_size / 2) 95 96 for iteration in range(iterations): 97 98 #識別器の学習 99 real = X_train[np.random.randint(0, 1000, half_batch)] 100 101 noise = np.random.uniform(-1, 1, (half_batch, 100)) 102 103 fake = self.generator.predict(noise) 104 105 d_loss_real = self.discriminator.train_on_batch(real, np.ones((half_batch, 1))) 106 d_loss_fake = self.discriminator.train_on_batch(fake, np.zeros((half_batch, 1))) 107 108 d_loss = 0.5 * np.add(d_loss_real, d_loss_fake) 109 110 #生成器の学習 111 noise = np.random.uniform(-1, 1, (batch_size, 100)) 112 113 g_loss = self.combined.train_on_batch(noise, np.ones((batch_size, 1))) 114 115 print("%d [D loss: %f, acc.: %.2f%%] [G loss: %f]" % (iteration, d_loss[0], 100 * d_loss[1], g_loss)) 116 117 118 #500バッチごとに結果を入力 119 if iteration % save_interval == 0: 120 121 check_img = self.generator.predict(check_noise) 122 123 check_img = check_img*127.5+127.5 124 check_img = check_img.astype("uint8") 125 126 # タイル状に pm × pm 枚配置 127 pm = 3 128 # 空の入れ物(リスト)を準備 129 d = [] 130 131 for i in range(len(check_img)): 132 img = Image.fromarray(check_img[i]) 133 img = np.asarray(img) 134 #img = cv2.resize(img, (300, 300), cv2.INTER_LANCZOS4) 135 d.append(img) 136 # タイル状に画像を一覧表示 137 fig, ax = plt.subplots(pm, pm, figsize=(10, 10)) 138 fig.subplots_adjust(hspace=0, wspace=0) 139 for i in range(pm): 140 for j in range(pm): 141 ax[i, j].xaxis.set_major_locator(plt.NullLocator()) 142 ax[i, j].yaxis.set_major_locator(plt.NullLocator()) 143 ax[i, j].imshow(d[pm*i+j], cmap="bone") 144 plt.savefig("./result/"+ str(iteration) + "_result.png") 145 146 147 148 149if __name__ == '__main__': 150 dcgan = DCGAN() 151 dcgan.train( 152 iterations=200000, 153 batch_size=32, 154 save_interval=500, 155 )
補足情報(FW/ツールのバージョンなど)
python 3.9.7
keras 2.9.0
回答1件