目錄
- GAN------ ``以假亂真``
- 訓練流程
- 環境
- 資料集
- 完整代碼
- 結果展示
GAN------ 以假亂真
GAN 的基本理念其實非常簡單,其核心由兩個目標互相沖突的神經網路組成,這兩個網路會以越來越復雜的方法來“蒙騙”對方,這種情況可以理解為博弈論中的極大極小博弈樹,
在這個程序中,我們想象有兩類人:警察和罪犯,我們看看他們的之間互相沖突的目標:
- 罪犯的目標:他的主要目標就是想出偽造貨幣的復雜方法,從而讓警察無法區分假幣和真幣,
- 警察的目標:他的主要目標就是想出辨別貨幣的復雜方法,這樣就能夠區分假幣和真幣,
隨著這個程序不斷繼續,警察會想出越來越復雜的技術來鑒別假幣,罪犯也會想出越來越復雜的技術來偽造貨幣,這就是 GAN 中“對抗程序”的基本理念,
GAN 充分利用“對抗程序”訓練兩個神經網路,這兩個網路會互相博弈直至達到一種理想的平衡狀態,我們這個例子中的警察和罪犯就相當于這兩個神經網路,
其中一個神經網路叫做生成器網路 G(Z),它會使用輸入隨機噪聲資料,生成和已有資料集非常接近的資料;
另一個神經網路叫鑒別器網路 D(X),它會以生成的資料作為輸入,嘗試鑒別出哪些是生成的資料,哪些是真實資料,鑒別器的核心是實作二元分類,輸出的結果是輸入資料來自真實資料集(和合成資料或虛假資料相對)的概率,
我們在前面所說的 GAN 最終能達到一種理想的平衡狀態,是指生成器應該能模擬真實的資料,鑒別器輸出的概率應該為 0.5, 即生成的資料和真實資料一致,也就是說,它不確定來自生成器的新資料是真實還是虛假,二者的概率相等,
訓練流程

環境
- tensorflow 2.4.1
- numpy
- matplotlib
資料集
mnist 手寫數字
完整代碼
'''
tensorflow 2.4.1
numpy
matplotlib
'''
# 設定GPU記憶體按需分配
from tensorflow.compat.v1 import ConfigProto
from tensorflow.compat.v1 import InteractiveSession
config = ConfigProto()
config.gpu_options.allow_growth = True
session = InteractiveSession(config=config)
import numpy as np
import time
import cv2 as cv
from tensorflow.keras.datasets import mnist
(X_train, y_train), (X_test, y_test) = mnist.load_data()
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense,Activation,Flatten,Flatten, Reshape
from tensorflow.keras.layers import Conv2D, Conv2DTranspose, UpSampling2D
from tensorflow.keras.layers import LeakyReLU, Dropout
from tensorflow.keras.layers import BatchNormalization
from tensorflow.keras.optimizers import Adam,RMSprop
import matplotlib.pyplot as plt
class ElapsedTimer(object):
def __init__(self):
self.start_time = time.time()
def elapsed(self,sec):
if sec < 60:
return str(sec) + " sec"
elif sec < (60 * 60):
return str(sec / 60) + " min"
else:
return str(sec / (60 * 60)) + " hr"
def elapsed_time(self):
print("Elapsed: %s " % self.elapsed(time.time() - self.start_time) )
class DCGAN(object):
def __init__(self, img_rows=28, img_cols=28, channel=1):
self.img_rows = img_rows
self.img_cols = img_cols
self.channel = channel
self.D = None # discriminator
self.G = None # generator
self.AM = None # adversarial model
self.DM = None # discriminator model
# (W?F+2P)/S+1
# 判別模型
# 14 * 14 * 1
# 回傳一個置信度
def discriminator(self):
if self.D:
return self.D
self.D = Sequential()
depth = 64
dropout = 0.4
# In: 28 x 28 x 1, depth = 1
# Out: 14 x 14 x 1, depth=64
input_shape = (self.img_rows, self.img_cols, self.channel) # 14*14*1 的img
"""
padding = “SAME”輸入和輸出大小關系:
輸出大小等于輸入大小除以步長向上取整
padding = “VALID”輸入和輸出大小關系:
輸出大小等于輸入大小減去濾波器大小加上1,最后再除以步長
"""
"""
64個5*5大小的內核,步長為2,🔠input:(14,14,1),padding=‘same’保證intput和output一樣
"""
self.D.add(Conv2D(64, 5, strides=2, input_shape=input_shape,padding='same'))# 14*14*64
self.D.add(LeakyReLU(alpha=0.2))
self.D.add(Dropout(dropout))
self.D.add(Conv2D(128, 5, strides=2, padding='same')) # 7*7*128
self.D.add(LeakyReLU(alpha=0.2))
self.D.add(Dropout(dropout))
self.D.add(Conv2D(256, 5, strides=2, padding='same')) # 4*4*256 向上取整
self.D.add(LeakyReLU(alpha=0.2))
self.D.add(Dropout(dropout))
self.D.add(Conv2D(512, 5, strides=1, padding='same')) # 4*4*512
self.D.add(LeakyReLU(alpha=0.2))
self.D.add(Dropout(dropout))
self.D.add(Conv2D(256, 5, strides=1, padding='same')) # 4*4*256
self.D.add(LeakyReLU(alpha=0.2))
self.D.add(Dropout(dropout))
# Out: 1-dim probability
self.D.add(Flatten())#扁平 4096=4*4*256
self.D.add(Dense(1)) # 輸出 1個
self.D.add(Activation('sigmoid')) # 二分類
self.D.summary()
return self.D
# 生成模型
# 全連接 7*7*256
# 回傳一張圖 28*28*1
def generator(self):
if self.G:
return self.G
self.G = Sequential()
dropout = 0.4
depth = 64+64+64+64
dim = 7
# In: 100
# Out: dim x dim x depth
self.G.add(Dense(dim*dim*depth, input_dim=100))#全連接 7*7*256 的大小
"""
引數作用于mean和variance的計算上, 這里保留了歷史batch里的mean和variance值,即 moving_mean和moving_variance,
借鑒優化演算法里的momentum演算法將歷史batch里的mean和variance的作用延續到當前batch. 一般momentum的值為0.9 , 0.99等.
多個batch后, 即多個0.9連乘后,最早的batch的影響會變弱.
"""
self.G.add(BatchNormalization(momentum=0.9))
self.G.add(Activation('relu'))
self.G.add(Reshape((dim, dim, depth))) # 7*7*256
self.G.add(Dropout(dropout))
# In: dim x dim x depth
# Out: 2*dim x 2*dim x depth/2
self.G.add(UpSampling2D()) # 翻倍 14*14*256
"""
輸入影像通過卷積操作提取特征后,輸出的尺寸常會變小,而有時我們需要將影像恢復到原來的尺寸以便進行進一步的計算(比如:影像的語意分割),
那么我們需要實作影像由小解析度到大解析度的映射的操作,叫做上采樣(Upsample),
"""
self.G.add(Conv2DTranspose(int(depth/2), 5, padding='same')) # 反卷積 14*14*128
self.G.add(BatchNormalization(momentum=0.9))
self.G.add(Activation('relu'))
self.G.add(UpSampling2D())# 28*28*128
self.G.add(Conv2DTranspose(int(depth/4), 5, padding='same')) # 28*28*64
self.G.add(BatchNormalization(momentum=0.9))
self.G.add(Activation('relu'))
self.G.add(Conv2DTranspose(int(depth/8), 5, padding='same')) # 28*28*32
self.G.add(BatchNormalization(momentum=0.9))
self.G.add(Activation('relu'))
# Out: 28 x 28 x 1 grayscale image [0.0,1.0] per pix
self.G.add(Conv2DTranspose(1, 5, padding='same')) # 28*28*1 輸出一張特征圖(就是生成的影像)
self.G.add(Activation('sigmoid'))
self.G.summary()
return self.G
def discriminator_model(self):
if self.DM:
return self.DM
optimizer = RMSprop(lr=0.0002, decay=6e-8)
self.DM = Sequential()
self.DM.add(self.discriminator())
# print("DM")
# self.DM.summary()
self.DM.compile(loss='binary_crossentropy', optimizer=optimizer,\
metrics=['accuracy'])
return self.DM
def adversarial_model(self):
if self.AM:
return self.AM
optimizer =RMSprop(lr=0.0001, decay=3e-8)
self.AM = Sequential()
self.AM.add(self.generator())
self.AM.add(self.discriminator())
# print('AM')
# self.AM.summary()
self.AM.compile(loss='binary_crossentropy', optimizer=optimizer,\
metrics=['accuracy'])
return self.AM
class MNIST_DCGAN(object):
def __init__(self):
self.img_rows = 28
self.img_cols = 28
self.channel = 1
(X_train, y_train), (X_test, y_test) = mnist.load_data()
X_train = X_train / 255.0
self.x_train = X_train.reshape(-1, 28, 28, 1).astype(np.float32)
self.DCGAN = DCGAN()
self.discriminator = self.DCGAN.discriminator_model()
self.adversarial = self.DCGAN.adversarial_model()
self.generator = self.DCGAN.generator()
def train(self, train_steps=2000, batch_size=256, save_interval=0):
noise_input = None
if save_interval>0:
noise_input = np.random.uniform(-1.0, 1.0, size=[16, 100])
for i in range(train_steps):
""""
第一輪,由于是沒有權重,隨機噪聲
再后我們對判別器進行訓練之后,loss更新,生成器網路權重更新
"""
images_train = self.x_train[np.random.randint(0,self.x_train.shape[0], size=batch_size), :, :, :] # 隨機選取128張影像 [128,28,28,1]
noise = np.random.uniform(-1.0, 1.0, size=[batch_size, 100]) #128,100 的隨機【-1,1】之間的數
images_fake = self.generator.predict(noise) # 生成模型訓練,圖 [128,28,28,1]
"""
影像保存 每5輪保存一次生成器所生成的image
"""
if i%5==0:
plt.figure(figsize=(24, 24))
for j in range(16):
plt.subplot(4, 4, j + 1)
image = images_fake[j, :, :, :]
image = np.reshape(image, [28,28])
plt.imshow(image, cmap='gray')
plt.axis('off')
plt.tight_layout()
filename = './g/img_{}'.format(i)
# plt.savefig(filename)
plt.close('all')
""""
在鑒別器的訓練程序中,它顯示為真實影像,并用于計算鑒別器損耗,
它對來自生成器的真實和偽造影像進行分類,如果對任何影像進行了不正確分類,則鑒別器損失將對鑒別器進行懲罰,
通過反向傳播,鑒別器更新其權重
類似地,為生成器提供了噪聲輸入以生成偽影像, 這些影像被提供給鑒別器,并且發生器損失懲罰了發生器以產生鑒別器網路分類為偽造的樣本,
權重通過從鑒別器到生成器的反向傳播進行更新
"""
x = np.concatenate((images_train, images_fake)) #256*28*28*1 維度相加 陣列拼接(將訓練圖片與生成的向量拼接), axis=0 按照行拼接,axis=1 按照列拼接,默認0
print('4',x.shape)
y = np.ones([2*batch_size, 1]) # 生成(256,1)的全是1的陣列
y[batch_size:, :] = 0 # 256*1 第128-256行的所有列全為0
d_loss = self.discriminator.train_on_batch(x, y)#鑒別
"""
核心
"""
y = np.ones([batch_size, 1]) # 128*1
noise = np.random.uniform(-1.0, 1.0, size=[batch_size, 100]) #128*100
a_loss = self.adversarial.train_on_batch(noise, y)
log_mesg = "%d: [D loss: %f, acc: %f]" % (i, d_loss[0], d_loss[1])
log_mesg = "%s [A loss: %f, acc: %f]" % (log_mesg, a_loss[0], a_loss[1])
print(log_mesg)
if save_interval>0:
if (i+1)%save_interval==0:
self.plot_images(save2file=True, samples=noise_input.shape[0],\
noise=noise_input, step=(i+1))
def plot_images(self, save2file=False, fake=True, samples=16, noise=None, step=0):
filename = 'mnist.png'
if fake:
if noise is None:
noise = np.random.uniform(-1.0, 1.0, size=[samples, 100])
else:
filename = "mnist_%d.png" % step
images = self.generator.predict(noise)
else:
i = np.random.randint(0, self.x_train.shape[0], samples)
images = self.x_train[i, :, :, :]
plt.figure(figsize=(10,10))
for i in range(images.shape[0]):
plt.subplot(4, 4, i+1)
image = images[i, :, :, :]
image = np.reshape(image, [self.img_rows, self.img_cols])
plt.imshow(image, cmap='gray')
plt.axis('off')
plt.tight_layout()
# if save2file:
# plt.savefig(filename)
# plt.close('all')
# else:
# plt.show()
if __name__ == '__main__':
mnist_dcgan = MNIST_DCGAN()
timer = ElapsedTimer()
mnist_dcgan.train(train_steps=10000, batch_size=128, save_interval=1000)
timer.elapsed_time()
mnist_dcgan.plot_images(fake=True)
mnist_dcgan.plot_images(fake=False, save2file=True)
結果展示

【參考文獻】
https://www.cnblogs.com/dereen/p/gan.html
https://zhuanlan.zhihu.com/p/43047326
https://www.zhihu.com/question/306213462
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/344110.html
標籤:AI
上一篇:Python 大白從零開始 OpenCV 專案實戰 (2)影像讀取與顯示
下一篇:Https網路安全傳輸詳解
