【深度學習】遺傳算法優(yōu)化GAN
編譯 | VK?
來源?| Towards Data Science

GANs是計算量最大的模型之一,因為它相當于同時訓練兩個神經網絡。對于我那臺糟糕的便攜式計算機來說,訓練一個GAN直到收斂是非常困難的。我寫了一個通用的遺傳算法,可以適應許多不同的問題。我采用這種遺傳算法來訓練GANs,生成手寫數字。
什么是遺傳算法
遺傳算法是一種學習算法,它利用兩個好的神經網絡的權值交叉的思想,可以得到一個更好的神經網絡。
遺傳算法之所以如此有效,是因為沒有直接的優(yōu)化算法,允許有可能產生極其不同的結果。此外,他們通常會提出非常有趣的解決方案,這些解決方案往往能為問題提供有價值的結論。
它們是如何工作的
生成一組隨機權重。這是第一個智能體的神經網絡。對智能體執(zhí)行一組測試。智能體收到基于測試的分數。重復此操作幾次以創(chuàng)建種群。選擇種群的前10%可以進行繁衍。從前10%中隨機選擇兩個父母,他們的權重是交叉的。每次交叉發(fā)生時,都有一個很小的突變機會:這是一個隨機值,不在父對象的權重中。
隨著智能體慢慢適應環(huán)境,此過程會慢慢優(yōu)化智能體的性能。
優(yōu)缺點:
優(yōu)點:
計算不密集
不需要進行線性代數計算。唯一需要的機器學習計算是通過神經網絡前向傳播。因此,與深層神經網絡相比,需求非常廣泛。
適應性強
人們可以調整和插入許多不同的測試和方法來操縱遺傳算法的靈活性。人們可以在遺傳算法中創(chuàng)建一個GAN,方法是讓智能體作為生成器網絡,通過鑒別器進行測試。這是一個關鍵的好處,它使我相信遺傳算法在未來的應用將更加廣泛。
可以理解
對于普通神經網絡,該算法的學習模式是神秘的。對于遺傳算法來說,我們很容易理解為什么會發(fā)生一些事情:例如,當一個遺傳算法被賦予Tic-Tac-Toe環(huán)境時,某些可識別的策略會慢慢發(fā)展。這是一個很大的好處,因為使用機器學習是為了使用技術來幫助我們深入了解重要問題。
缺點:
需要很長時間
不好的交叉和突變會對程序的準確性產生負面影響,從而使程序收斂速度減慢或達到某個損失閾值。
代碼
既然你對遺傳算法及其優(yōu)點和缺點有了相當全面的了解,我現在就可以向你展示該程序:
import?random
import?numpy?as?np
from?IPython.display?import?clear_output
from?keras.layers?import?Reshape
from?keras.layers?import?Flatten
from?keras.layers?import?Conv2D
from?keras.layers?import?Conv2DTranspose
from?keras.layers?import?LeakyReLU
from?keras.layers?import?Dropout,Dense
from?keras.optimizers?import?Adam
from?keras.models?import?Sequential
from?keras.datasets.mnist?import?load_data
(trainX,?trainy),?(testX,?testy)?=?load_data()
鑒別器需要Keras,但是遺傳算法中的神經網絡是由下面的代碼創(chuàng)建的,在代碼中,它是以numpy為基礎構建的。
class?genetic_algorithm:
????????
????def?execute(pop_size,generations,threshold,network):
????????class?Agent:
????????????def?__init__(self,network):
這是“genetic_algorithm”類的創(chuàng)建,它包含了所有與遺傳算法有關的函數以及它應該如何工作。主要函數是execute函數,它以pop_size,generations,threshold,network為參數。pop_size是生成的種群的大小,generations是表示epoch的術語,threshold是你滿意的損失值。X和y表示標記數據。network是神經網絡的結構。
class?neural_network:
????????????????????def?__init__(self,network):
????????????????????????self.weights?=?[]
????????????????????????self.activations?=?[]
????????????????????????for?layer?in?network:
????????????????????????????if?layer[0]?!=?None:
????????????????????????????????input_size?=?layer[0]
????????????????????????????else:
????????????????????????????????input_size?=?network[network.index(layer)-1][1]
????????????????????????????output_size?=?layer[1]
????????????????????????????activation?=?layer[2]
????????????????????????????
????????????????????????????self.weights.append(np.random.randn(input_size,output_size))
????????????????????????????self.activations.append(activation)
????????????????????def?propagate(self,data):
????????????????????????input_data?=?data
????????????????????????for?i?in?range(len(self.weights)):
????????????????????????????z?=?np.dot(input_data,self.weights[i])
????????????????????????????a?=?self.activations[i](z)
????????????????????????????input_data?=?a
????????????????????????yhat?=?a
????????????????????????return?yhat
????????????????self.neural_network?=?neural_network(network)
????????????????self.fitness?=?0
此腳本描述每個智能體的神經網絡的權重初始化和網絡傳播。
def?generate_agents(population,?network):
????????????return?[Agent(network)?for?_?in?range(population)]
此函數創(chuàng)建將要測試的第一組智能體。
def?fitness(agents):
????????????for?agent?in?agents:
????????????????dataset_len?=?100
????????????????fake?=?[]
????????????????real?=?[]
????????????????y?=?[]
????????????????for?i?in?range(dataset_len//2):
????????????????fake.append(agent.neural_network.propagate(np.random.randn(latent_size)).reshape(28,28))
????????????????????y.append(0)
????????????????????real.append(random.choice(trainX))
????????????????????y.append(1)
????????????????X?=?fake+real
????????????????X?=?np.array(X).astype('uint8').reshape(len(X),28,28,1)
????????????????y?=?np.array(y).astype('uint8')
????????????????model.fit(X,y,verbose?=?0)
????????????????
????????????????fake?=?[]
????????????????real?=?[]
????????????????y?=?[]
????????????????for?i?in?range(dataset_len//2):
????????????????????fake.append(agent.neural_network.propagate(np.random.randn(latent_size)).reshape(28,28))
????????????????????y.append(0)
????????????????????real.append(random.choice(trainX))
????????????????????y.append(1)
????????????????X?=?fake+real
????????????????X?=?np.array(X).astype('uint8').reshape(len(X),28,28,1)
????????????????y?=?np.array(y).astype('uint8')
????????????????agent.fitness?=?model.evaluate(X,y,verbose?=?0)[1]*100
????????????return?agents
適應度函數是該遺傳算法的獨特部分:
稍后將定義鑒別器型神經網絡。此模型將基于先前加載的MNIST數據集進行訓練。該模型以卷積網絡的形式返回結果。
def?selection(agents):
????????????agents?=?sorted(agents,?key=lambda?agent:?agent.fitness,?reverse=False)
????????????print('\n'.join(map(str,?agents)))
????????????agents?=?agents[:int(0.2?*?len(agents))]
????????????return?agents
這個函數模仿了進化中的選擇理論:最好的種群生存,而其他則任由他們死去。
def?unflatten(flattened,shapes):
????????????newarray?=?[]
????????????index?=?0
????????????for?shape?in?shapes:
????????????????size?=?np.product(shape)
????????????????newarray.append(flattened[index?:?index?+?size].reshape(shape))
????????????????index?+=?size
????????????return?newarray
要執(zhí)行交叉和變異函數,需要將權重展平,并將其展開為原始形狀。
def?crossover(agents,network,pop_size):
????????????offspring?=?[]
????????????for?_?in?range((pop_size?-?len(agents))?//?2):
????????????????parent1?=?random.choice(agents)
????????????????parent2?=?random.choice(agents)
????????????????child1?=?Agent(network)
????????????????child2?=?Agent(network)
????????????????
????????????????shapes?=?[a.shape?for?a?in?parent1.neural_network.weights]
????????????????
????????????????genes1?=?np.concatenate([a.flatten()?for?a?in?parent1.neural_network.weights])
????????????????genes2?=?np.concatenate([a.flatten()?for?a?in?parent2.neural_network.weights])
????????????????
????????????????split?=?random.ragendint(0,len(genes1)-1)child1_genes?=?np.asrray(genes1[0:split].tolist()?+?genes2[split:].tolist())
????????????????child2_genes?=?np.array(genes1[0:split].tolist()?+?genes2[split:].tolist())
????????????????
????????????????child1.neural_network.weights?=?unflatten(child1_genes,shapes)
????????????????child2.neural_network.weights?=?unflatten(child2_genes,shapes)
????????????????
????????????????offspring.append(child1)
????????????????offspring.append(child2)
????????????agents.extend(offspring)
????????????return?agents
交叉函數是程序中最復雜的函數之一。它生成兩個新的“子代”智能體,其權重被替換為兩個父代的交叉。這是創(chuàng)建權重的過程:
Flatten化父代的權重
生成兩個分割點
使用分割點作為索引來設置兩個子智能體的權重
這是特征交叉的全過程。
def?mutation(agents):
????????????for?agent?in?agents:
????????????????if?random.uniform(0.0,?1.0)?<=?0.1:
????????????????????weights?=?agent.neural_network.weights
????????????????????shapes?=?[a.shape?for?a?in?weights]flattened?=?np.concatenate([a.flatten()?for?a?in?weights])
????????????????????randint?=?random.randint(0,len(flattened)-1)
????????????????????flattened[randint]?=?np.random.randn()newarray?=?[a?]
????????????????????indeweights?=?0
????????????????????for?shape?in?shapes:
????????????????????????size?=?np.product(shape)
????????????????????????newarray.append(flattened[indeweights?:?indeweights?+?size].reshape(shape))
????????????????????????indeweights?+=?size
????????????????????agent.neural_network.weights?=?newarray
????????????return?agents
這就是突變函數。展平與交叉函數相同。不是拆分點,而是選擇一個隨機點,用一個隨機值替換。
for?i?in?range(generations):
????????????print('Generation',str(i),':')
????????????agents?=?generate_agents(pop_size,network)
????????????agents?=?fitness(agents)
????????????agents?=?selection(agents)
????????????agents?=?crossover(agents,network,pop_size)
????????????agents?=?mutation(agents)
????????????agents?=?fitness(agents)
????????????
????????????if?any(agent.fitness?for?agent?in?agents):
????????????????print('Threshold?met?at?generation?'+str(i)+'?!')
????????????????
????????????if?i?%?100:
????????????????clear_output()
????????????????
????????return?agents[0]
這是execute函數的最后一部分,它執(zhí)行已定義的所有函數。
image_size?=?28
latent_size?=?100
model?=?Sequential()
model.add(Conv2D(64,?(3,3),?strides=(2,?2),?padding='same',?input_shape=(image_size,image_size,1)))
model.add(LeakyReLU(alpha=0.2))
model.add(Dropout(0.4))
model.add(Conv2D(64,?(3,3),?strides=(2,?2),?padding='same'))
model.add(LeakyReLU(alpha=0.2))
model.add(Dropout(0.4))
model.add(Flatten())
model.add(Dense(1,?activation='sigmoid'))
opt?=?Adam(lr=0.0002,?beta_1=0.5)
model.compile(loss='binary_crossentropy',?optimizer=opt,?metrics=['accuracy'])
network?=?[[latent_size,100,sigmoid],[None,image_size**2,sigmoid]]
ga?=?genetic_algorithm
agent?=?ga.execute(1000,1000,90,network)
(trainX,?trainy),?(testX,?testy)?=?load_data()
weights?=?agent.neural_network.weights
對于網絡變量,每個嵌套列表包含輸入神經元數、輸出神經元數和激活函數。execute函數返回最佳智能體。
結論
顯然,遺傳算法不會像基于梯度的算法那樣快速收斂,但是計算工作會在較長的時間內進行,這使得它在計算機上的密集度降低了!
感謝你閱讀我的文章!
往期精彩回顧
本站知識星球“黃博的機器學習圈子”(92416895)
本站qq群704220115。
加入微信群請掃碼:
