作者:Ahmed Gad
翻譯:張睿毅
校對:丁楠雅
本文
4700
字,
建議閱讀
15
分鐘。
本教程主要使用numpy和sklearn來討論如何使用遺傳算法(genetic algorithm,GA)來減少從python中的Fruits360數據集提取的特征向量。
標簽:
深度學習,特征工程,遺傳算法,神經網絡,numpy,python,scikit-learn
?
本教程主要使用numpy和sklearn來討論如何使用遺傳算法(genetic algorithm,GA)來減少從python中的Fruits360數據集提取的特征向量。
?
導言
?
在某些情況下,使用原始數據訓練機器學習算法可能不是合適的選擇。該算法在接受原始數據訓練時,必須進行特征挖掘,以檢測不同組之間的差異。但這需要大量的數據來自動執行特征挖掘。對于小數據集,數據科學家最好自己進行特征挖掘步驟,之后告訴機器學習算法要使用哪個特征集。
使用的特征集必須能代表數據樣本,因此我們必須注意選擇最佳特征。數據科學家建議使用一些類型的特征,這些特征似乎有助于根據以前的經驗來表示數據樣本。一些特征可以證明它們在表示樣本時的穩健性,而其他特征則不能。
可能存在一些類型的特征,會降低分類問題的準確性或增加回歸問題的誤差,進而影響訓練模型的結果。例如,特征向量中可能存在一些噪音元素,因此它們應該被刪除。特征向量也可能包含2個或更多相關元素。只使用一個元素就可以替代另一個元素。為了刪除這些類型的元素,有兩個有用的步驟,即特征選擇和約簡。本教程重點介紹特征約簡。
假設有3個特征f1、f2和f3,每個特征都有3個特征元素。因此,特征向量長度為3x3=9。特征選擇只選擇特定類型的特征,不包括其他類型的特征。例如,只需選擇f1和f3并刪除f3。特征向量長度變成了6而不是9。在特征約簡中,可以排除每個特征的特定元素。例如,此步驟可能會在保留第二個元素的同時從f3中刪除第一個和第三個元素。因此,特征向量長度從9減少到7。
在開始本教程之前,值得一提的是,它是我的LinkedIn配置文件中先前發布的2個教程的擴展。
第一個教程的標題是 “使用numpy的人工神經網絡實現Fruits360圖像數據集的分類”。 它首先從Fruits360數據集的4個類中提取長度為360的特征向量。然后,利用numpy從零開始構建人工神經網絡(ANN),對數據集進行分類。
第一個教程可從以下網址獲取:
https://www.linkedin.com/pulse/artificial-neural-network-implementation-using-numpy-fruits360-gad
其Github項目可從以下網址獲得:
https://github.com/ahmedfgad/NumPyAN
第二個教程是 “使用遺傳算法的人工神經網絡優化” 。建立并使用遺傳算法對神經網絡參數進行優化,以提高分類精度。
第二個教程可從以下網址獲?。?
https://www.linkedin.com/pulse/artificial-neural-networks-optimization-using-genetic-ahmed-gad。
其Github項目也可從以下網址獲得:
https://github.com/ahmedfgad/NeuralGeneti
本教程討論了如何使用遺傳算法來減少從長度360的Fruits360數據集中提取的特征向量。本教程首先討論要遵循的步驟。其次通過使用NumPy和Sklearn在python實現這些步驟。
本教程的實現可在我的Github頁面中找到:
https://github.com/ahmedfgad/FeatureReductionGeneti
遺傳算法從一個初始群體開始,該群體由若干染色體(即解決方法)組成,其中每個染色體都有一系列基因。使用適應函數,遺傳算法選擇最佳的解決方案作為父母來創建一個新的群體。在這樣一個新的群體中,通過在雙親上應用兩個操作,即雜交和變異來創建新的解決方案。當把遺傳算法應用到一個給定的問題上時,我們必須確定基因的表示、合適的適應函數以及雜交和變異是如何應用的。接下來讓我們看看運行原理。
?
更多關于遺傳算法的信息
?
你可以從我準備的如下資源中讀到關于遺傳算法的更多知識:
1. 遺傳算法優化介紹?
https://www.linkedin.com/pulse/introduction-optimization-genetic-algorithm-ahmed-gad/
https://www.kdnuggets.com/2018/03/introduction-optimization-with-genetic-algorithm.html
https://towardsdatascience.com/introduction-to-optimization-with-genetic-algorithm-2f5001d9964b
2. 遺傳算法優化-逐步示例
https://www.slideshare.net/AhmedGadFCIT/genetic-algorithm-ga-optimization-stepbystep-example
3. python中的遺傳算法實現
https://www.linkedin.com/pulse/genetic-algorithm-implementation-python-ahmed-gad/
https://www.kdnuggets.com/2018/07/genetic-algorithm-implementation-python.html
https://towardsdatascience.com/genetic-algorithm-implementation-in-python-5ab67bb124a6
https://github.com/ahmedfgad/GeneticAlgorithmPython
我在2018年還寫了一本書,其中一章介紹了遺傳算法。這本書的標題是“利用CNN進行深度學習的實用計算機視覺應用”,可在Springer上找到。
Springer鏈接:
https://www.springer.com/us/book/978148424166
染色體的表達
遺傳算法中的基因是染色體的組成部分。首先,我們需要確定染色體內的基因。為此,考慮到可能影響結果的每一種屬性都應被視為一個基因。因為我們問題的目標是選擇最好的一組特征元素,所以如果選擇或不選擇,每個特征元素都可能影響結果。因此,每個特征元素都被視為一個基因。染色體將由所有基因(即所有特征元素)組成。因為有360個特征元素,那么就有360個基因。一個很好的信息現在很清楚,染色體的長度是360。
在確定所選基因是什么之后,下一步就是確定基因的表達。有不同的表示形式,如十進制、二進制、浮點、字符串等。我們的目標是知道基因(即特征元素)是否在減少的特征集中被選擇。因此,分配給基因的值應該反映它是否被選擇。基于這種描述,很明顯每個基因有兩個可能的值。一個值表示該基因已被選中,另一個值表示未被選中。因此,二進制表示是最佳選擇。當基因值為1時,將在減少的特征集中進行選擇。當為0時,則忽略它。
總之,染色體將由360個基因組成,以二進制表示。根據下一個圖,特征向量和染色體之間有一對一的映射。這是染色體中的第一個基因與特征向量中的第一個元素相連。當該基因的值為1時,這意味著選擇了特征向量中的第一個元素。
?
適應函數
?
通過了解如何創建染色體,可以很容易地對初始種群進行隨機初始化。初始化后,將選擇父級。遺傳算法基于達爾文的“適者生存”理論。這是目前選擇的最佳解決方案進行組合,以產生更好的解決方案。通過保留好的解和消除壞的解,我們可以得到最優或半最優解。
選擇雙親的標準是與每個解決方案(即染色體)相關聯的適應值。適合度越高,解決方案越好。使用適應度函數計算適應度值。那么,在我們的問題中,最適合使用的功能是什么?我們問題的目標是創建一個約簡的特征向量,以提高分類精度。因此,判斷一個解是否好的標準是分類的準確性。因此,fitness函數將返回一個數字,指定每個解決方案的分類精度。精度越高,解決方案越好。
為了返回分類的準確度,必須有一個機器學習模型來通過每個解決方案返回的特征元素進行訓練。對于這種情況,我們將使用支持向量分類器(SVC)。
數據集分為訓練樣本和測試樣本。根據訓練數據,SVC將使用人群中每個解決方案選擇的特征元素進行訓練。經過訓練后,根據測試數據進行測試。
根據每個解的適合度值,我們可以選擇其中最好的作為父母。這些父母被放在一起組合以產生后代,這將是下一代的新人口的成員。這種后代是通過對選定的親本應用交叉和突變操作而產生的。讓我們按照下面討論的方式配置這些操作。
遺傳和變異
基于適應度函數,我們可以篩選出當前群體中的最優解,即父輩。遺傳算法假設匹配2個好的解決方案將產生第三個更好的解決方案。組合意味著從兩個父母那里交換一些基因。使用遺傳操作交換基因。有不同的方法可以應用這種操作。本教程使用單點交叉,其中一個點分割染色體。點前的基因取自一組解,點后的基因取自另一組解。
通過應用遺傳,所有的基因都來自于以前的父母。在新的后代中沒有引入新的基因。如果所有的父母都有一個壞基因,那么這個基因就會轉移到后代身上。正因為如此,為了在后代中引入新的基因,采用了突變操作。在基因的二元表示中,突變是通過翻轉一些隨機選擇的基因的值來實現的。如果基因值為1,則為0,反之亦然。
在產生后代之后,我們可以創造下一代的新種群。除了后代之外,這個群體還包括以前的父輩。
此時,將討論所有步驟。接下來是用Python實現它們。注意,我以前寫過一篇題為“Python中的遺傳算法實現”的教程,用于在Python中實現遺傳算法,我將修改它的代碼來解決我們的問題。最好讀一下。
?
利用Python實現
?
該項目分為兩個文件。一個文件名為GA.py,它將遺傳算法步驟的實現保存為函數。另一個文件是主文件,它只導入這個文件,并在循環中調用它的函數,該循環將迭代幾代。
根據下面的代碼,主文件首先讀取從Fruits360數據集提取的特性。這些特性返回到數據輸入變量中。有關提取這些功能的詳細信息,請參閱本教程開頭提到的2個教程。該文件還讀取與數據輸出變量中的樣本相關聯的類標簽。
選擇一些樣本進行訓練,其索引存儲在train_indices變量中。同樣,測試樣本索引存儲在test_indices變量中。
?
import?numpy
import?GA
import?pickle
import?matplotlib.pyplot
f?=?open("dataset_features.pkl",?"rb")
data_inputs?=?pickle.load(f)
f.close()
f?=?open("outputs.pkl",?"rb")
data_outputs?=?pickle.load(f)
f.close()
num_samples?=?data_inputs.shape[0]
num_feature_elements?=?data_inputs.shape[1]
train_indices?=?numpy.arange(1,?num_samples,?4)
test_indices = numpy.arange(0, num_samples, 4)
print("Number?of?training?samples:?",?train_indices.shape[0])
print("Number?of?test?samples:?",?test_indices.shape[0])
"""
Genetic?algorithm?parameters:
????Population?size
????Mating?pool?size
????Number?of?mutations
"""
sol_per_pop?=?8?#?Population?size.
num_parents_mating?=?4?#?Number?of?parents?inside?the?mating?pool.
num_mutations = 3 # Number of elements to mutate.
#?Defining?the?population?shape.
pop_shape = (sol_per_pop, num_feature_elements)
#?Creating?the?initial?population.
new_population?=?numpy.random.randint(low=0,?high=2,?size=pop_shape)
print(new_population.shape)
best_outputs?=?[]
num_generations = 100
?
它初始化了遺傳算法的所有參數。這包括根據sol_per_pop變量設置為8的每個群體的解的數量、num _parents_mating變量設置為4的子代數量以及num_mutations變量設置為3的突變數量。之后,它會在一個名為“new_population”的變量中隨機創建初始總體。
有一個名為best_outputs的空列表,它在每一代之后都保存著最好的結果。這有助于可視化遺傳算法在完成所有代之后的進展。num_generations變量中的代數設置為100。請注意,您可以更改所有這些參數,從而獲得更好的結果。
在準備好特性、類標簽和算法參數之后,我們可以根據下一個代碼對算法進行迭代。首先,通過調用GA文件中定義的名為cal_pop_fitness()的適應函數來計算所有解決方案的適應值。此函數接受當前總體、提取的特征、類標簽、列車索引和測試索引。函數返回名為fitness的變量中所有解的適應值。請記住,適合度值表示分類精度。最佳(即最高)分類精度保存在最佳輸出列表中。
根據計算出的適合度值,使用GA.py文件中定義的select_matching_pool()函數選擇分類精度最高的最佳解決方案作為匹配池中的父級。它接受當前的人口、適合度值和要返回的父母人數。它將所選雙親返回到父級變量中。
?
for?generation?in?range(num_generations):
????print("Generation?:?",?generation)
????
????#?Measuring?the?fitness?of?each?chromosome?in?the?population.
????fitness?=?GA.cal_pop_fitness(new_population,?data_inputs,?data_outputs,?train_indices,?test_indices)
????best_outputs.append(numpy.max(fitness))
????#?The?best?result?in?the?current?iteration.
print("Best result : ", best_outputs[-1])
????#?Selecting?the?best?parents?in?the?population?for?mating
parents = GA.select_mating_pool(new_population, fitness, num_parents_mating)
???
????#?Generating?next?generation?using?crossover
????offspring_crossover?=?GA.crossover(parents,?offspring_size=(pop_shape[0]-parents.shape[0],?num_feature_elements))
????
????#?Adding?some?variations?to?the?offspring?using?mutation.
????offspring_mutation?=?GA.mutation(offspring_crossover,?num_mutations=num_mutations)
????#?Creating?the?new?population?based?on?the?parents?and?offspring.
????new_population[0:parents.shape[0],?:]?=?parents
????new_population[parents.shape[0]:,?:]?=?offspring_mutation
接下來是對選定的父代應用組合操作以創建子代。這是在GA.py文件中定義的crossover()函數內完成的。它接受父數組和子數組的形狀,以便稍后返回到offspring_crossover變量中。然后,使用在GA.py文件中也可用的mutation()函數在該數組上應用突變操作。除了交叉結果,這個函數接受突變的數量。
因為新的種群由選定的親本和后代組成,所以親本和offspring_crossover數組都保存到new_population變量中。在那之后,新一代被應用于新的人口。
在所有代完成后,將執行下一個代碼,以返回最佳選擇的功能元素集和所選元素的數量。在100代完成后,該算法使用174個特征元素,以達到99.59%的精度。
?
fitness = GA.cal_pop_fitness(new_population, data_inputs, data_outputs, train_indices, test_indices)
#?Then?return?the?index?of?that?solution?corresponding?to?the?best?fitness.
best_match_idx = numpy.where(fitness == numpy.max(fitness))[0]
best_match_idx?=?best_match_idx[0]
best_solution?=?new_population[best_match_idx,?:]
best_solution_indices?=?numpy.where(best_solution?==?1)[0]
best_solution_num_elements?=?best_solution_indices.shape[0]
best_solution_fitness?=?fitness[best_match_idx]
print("best_match_idx?:?",?best_match_idx)
print("best_solution?:?",?best_solution)
print("Selected?indices?:?",?best_solution_indices)
print("Number?of?selected?elements?:?",?best_solution_num_elements)
print("Best?solution?fitness?:?",?best_solution_fitness)
matplotlib.pyplot.plot(best_outputs)
matplotlib.pyplot.xlabel("Iteration")
matplotlib.pyplot.ylabel("Fitness")
matplotlib.pyplot.show()
?
上面的代碼展示了一張圖,顯示了算法在所有代中的進度,如下所示。
?
以下是主文件中的完整代碼。
?
import?numpy
import?GA
import?pickle
import matplotlib.pyplot
f?=?open("dataset_features.pkl",?"rb")
data_inputs?=?pickle.load(f)
f.close()
f?=?open("outputs.pkl",?"rb")
data_outputs?=?pickle.load(f)
f.close()
num_samples?=?data_inputs.shape[0]
num_feature_elements?=?data_inputs.shape[1]
train_indices?=?numpy.arange(1,?num_samples,?4)
test_indices = numpy.arange(0, num_samples, 4)
print("Number?of?training?samples:?",?train_indices.shape[0])
print("Number?of?test?samples:?",?test_indices.shape[0])
"""
Genetic?algorithm?parameters:
????Population?size
????Mating?pool?size
Number of mutations
"""
sol_per_pop?=?8?#?Population?size
num_parents_mating?=?4?#?Number?of?parents?inside?the?mating?pool.
num_mutations?=?3?#?Number?of?elements?to?mutate.
#?Defining?the?population?shape.
pop_shape?=?(sol_per_pop,?num_feature_elements)
#?Creating?the?initial?population.
new_population?=?numpy.random.randint(low=0,?high=2,?size=pop_shape)
print(new_population.shape)
best_outputs?=?[]
num_generations?=?100
for generation in range(num_generations):
????print("Generation?:?",?generation)
????#?Measuring?the?fitness?of?each?chromosome?in?the?population.
????fitness?=?GA.cal_pop_fitness(new_population,?data_inputs,?data_outputs,?train_indices,?test_indices)
best_outputs.append(numpy.max(fitness))
????#?The?best?result?in?the?current?iteration.
print("Best result : ", best_outputs[-1])
????#?Selecting?the?best?parents?in?the?population?for?mating.
????parents?=?GA.select_mating_pool(new_population,?fitness,?num_parents_mating)
???
????#?Generating?next?generation?using?crossover.
????offspring_crossover?=?GA.crossover(parents,?offspring_size=(pop_shape[0]-parents.shape[0],?num_feature_elements))
????#?Adding?some?variations?to?the?offspring?using?mutation.
offspring_mutation = GA.mutation(offspring_crossover, num_mutations=num_mutations)
????#?Creating?the?new?population?based?on?the?parents?and?offspring.
????new_population[0:parents.shape[0],?:]?=?parents
????new_population[parents.shape[0]:,?:]?=?offspring_mutation
?
#?Getting?the?best?solution?after?iterating?finishing?all?generations.
#?At?first,?the?fitness?is?calculated?for?each?solution?in?the?final?generation.
fitness = GA.cal_pop_fitness(new_population, data_inputs, data_outputs, train_indices, test_indices)
#?Then?return?the?index?of?that?solution?corresponding?to?the?best?fitness.
best_match_idx?=?numpy.where(fitness?==?numpy.max(fitness))[0]
best_match_idx?=?best_match_idx[0]
best_solution?=?new_population[best_match_idx,?:]
best_solution_indices?=?numpy.where(best_solution?==?1)[0]
best_solution_num_elements = best_solution_indices.shape[0]
best_solution_fitness = fitness[best_match_idx]
print("best_match_idx?:?",?best_match_idx)
print("best_solution?:?",?best_solution)
print("Selected?indices?:?",?best_solution_indices)
print("Number?of?selected?elements?:?",?best_solution_num_elements)
print("Best solution fitness : ", best_solution_fitness)
matplotlib.pyplot.plot(best_outputs)
matplotlib.pyplot.xlabel("Iteration")
matplotlib.pyplot.ylabel("Fitness")
matplotlib.pyplot.show()
?
GA.py的實現
GA.py文件的實現如下所示。在cal_pop_fitness()函數中,SVC根據每個解決方案選擇的特征元素進行培訓。在訓練前,根據所選的基因值為1的元素過濾特征。這是在reduce_features()函數中完成的。除了所有示例的完整功能外,它還接受當前的解決方案。
訓練后,使用reduce_features()函數計算分類精度。此函數返回存儲在cal pop_fitness()函數中名為accuracies的數組中的精度。
crossover()和mutation()函數的實現與我之前的教程“Python中的遺傳算法實現”中討論的非常相似。一個主要的區別是,mutation()函數通過翻轉隨機選擇的基因的值來改變它們,因為我們使用的是二進制表示。
?
import numpy
import sklearn.svm
def reduce_features(solution, features):
selected_elements_indices = numpy.where(solution == 1)[0]
reduced_features = features[:, selected_elements_indices]
return reduced_features
def classification_accuracy(labels, predictions):
correct = numpy.where(labels == predictions)[0]
accuracy = correct.shape[0]/labels.shape[0]
return accuracy
def cal_pop_fitness(pop, features, labels, train_indices, test_indices):
accuracies = numpy.zeros(pop.shape[0])
??? idx?=?0
for curr_solution in pop:
reduced_features = reduce_features(curr_solution, features)
train_data = reduced_features[train_indices, :]
test_data = reduced_features[test_indices, :]
train_labels = labels[train_indices]
test_labels = labels[test_indices]
SV_classifier = sklearn.svm.SVC(gamma='scale')
??????? SV_classifier.fit(X=train_data,?y=train_labels)
predictions = SV_classifier.predict(test_data)
??????? accuracies[idx]?=?classification_accuracy(test_labels,?predictions)
idx = idx + 1
return accuracies
def select_mating_pool(pop, fitness, num_parents):
# Selecting the best individuals in the current generation as parents for producing the offspring of the next generation.
parents = numpy.empty((num_parents, pop.shape[1]))
for parent_num in range(num_parents):
max_fitness_idx = numpy.where(fitness == numpy.max(fitness))
max_fitness_idx = max_fitness_idx[0][0]
??????? parents[parent_num,?:]?=?pop[max_fitness_idx,?:]
??????? fitness[max_fitness_idx]?=?-99999999999
????return?parents
def crossover(parents, offspring_size):
??? offspring?=?numpy.empty(offspring_size)
# The point at which crossover takes place between two parents. Usually, it is at the center.
crossover_point = numpy.uint8(offspring_size[1]/2)
??? for?k?in?range(offspring_size[0]):
# Index of the first parent to mate.
??????? parent1_idx?=?k%parents.shape[0]
# Index of the second parent to mate.
parent2_idx = (k+1)%parents.shape[0]
# The new offspring will have its first half of its genes taken from the first parent.
??????? offspring[k,?0:crossover_point]?=?parents[parent1_idx,?0:crossover_point]
# The new offspring will have its second half of its genes taken from the second parent.
??????? offspring[k,?crossover_point:]?=?parents[parent2_idx,?crossover_point:]
????return?offspring
def mutation(offspring_crossover, num_mutations=2):
mutation_idx = numpy.random.randint(low=0, high=offspring_crossover.shape[1], size=num_mutations)
????#?Mutation?changes?a?single?gene?in?each?offspring?randomly.
for idx in range(offspring_crossover.shape[0]):
# The random value to be added to the gene.
??????? offspring_crossover[idx,?mutation_idx]?=?1?-?offspring_crossover[idx,?mutation_idx]
return offspring_crossover
原文標題:
Feature Reduction using Genetic Algorithm with Python
原文鏈接:
https://www.kdnuggets.com/2019/03/feature-reduction-genetic-algorithm-python.html
譯者簡介

張睿毅 , 北京郵電大學大二物聯網在讀。我是一個愛自由的人。在郵電大學讀第一年書我就四處跑去蹭課,折騰整一年驚覺,與其在當下焦慮,不如在前輩中沉淀。于是在大二以來,堅持讀書,不敢稍歇。 資本主義國家的科學觀不斷刷新我的認知框架,同時因為出國考試很早出分,也更早地感受到自己才是那個一直被束縛著的人。太多真英雄在社會上各自閃耀著光芒。這才開始,立志終身向遇到的每一個人學習。做一個純粹的計算機科學里面的小學生。
翻譯組招募信息
工作內容:
需要一顆細致的心,將選取好的外文文章翻譯成流暢的中文。如果你是數據科學/統計學/計算機類的留學生,或在海外從事相關工作,或對自己外語水平有信心的朋友歡迎加入翻譯小組。
你能得到: 定期的翻譯培訓提高志愿者的翻譯水平,提高對于數據科學前沿的認知,海外的朋友可以和國內技術應用發展保持聯系,THU數據派產學研的背景為志愿者帶來好的發展機遇。
其他福利: 來自于名企的數據科學工作者,北大清華以及海外等名校學生他們都將成為你在翻譯小組的伙伴。
點擊文末“ 閱讀原文 ”加入數據派團隊~
轉載須知
如需轉載,請在開篇顯著位置注明作者和出處(轉自:數據派ID:datapi),并在文章結尾放置數據派醒目二維碼。有原創標識文章,請發送【文章名稱-待授權公眾號名稱及ID】至聯系郵箱,申請白名單授權并按要求編輯。
發布后請將鏈接反饋至聯系郵箱(見下方)。未經許可的轉載以及改編者,我們將依法追究其法律責任。
致轉行AI的在校大學生的一封信
轉行AI需要看的一些文章
轉行學AI,具體細分方向如何選,來自一線工程師的感悟
用法律武器,痛擊騰訊侵權行為?。?!灣區人工智能可以改善知識產權現狀嗎?
【送書PDF】Python編程從入門到實踐
Python從入門到精通,深度學習與機器學習資料大禮包!
【免費】某機構最新3980元機器學習/大數據課程高速下載,限量200份

點擊 “閱讀原文” 擁抱組織
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
