├── Basic Algorithm
├── Img
│ └── genetic_alg.png
└── genetic_agorithm_example.py
├── Genetic Algorithm on Neural Network Architecture and Hyperparameter Optimization
└── Neural Network Architecture and Hyperparameter Optimization.ipynb
├── Img
└── genetic_alg.png
├── Multi-Neural Network Weight Optimization with Genetic Algorithm
└── GA_weight_opti.ipynb
└── README.md
/Basic Algorithm/Img/genetic_alg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BY571/Genetic-Algorithms-Neural-Network-Optimization/723686463aa369bb32fbdebb162a7c0808984637/Basic Algorithm/Img/genetic_alg.png
--------------------------------------------------------------------------------
/Basic Algorithm/genetic_agorithm_example.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import random
3 | import matplotlib.pyplot as plt
4 | import argparse
5 |
6 | # class to creat a population of random members
7 | class Member():
8 | def __init__(self,length,DNA_pool, DNA = None):
9 |
10 | self.DNA_pool = DNA_pool # traits or "DNA-Pool"
11 | # final DNA length
12 | self.length = length
13 |
14 | # actual/ final DNA
15 | if DNA and len(DNA) == self.length:
16 | self.DNA = DNA
17 | else:
18 | self.DNA = ""
19 |
20 | self.fitness = 0.
21 |
22 | def create(self):
23 | for i in range(self.length):
24 | self.DNA += random.choice(self.DNA_pool)
25 |
26 | def print_DNA(self):
27 | print(self.DNA)
28 |
29 | def calc_fitness(self, goal):
30 | # Calculating the fitness by checking how many characters/DNA/traits already match
31 | for i, elem in enumerate(self.DNA):
32 | if str(elem) == goal[i]:
33 | self.fitness += 1
34 |
35 | def mutate(self,mutation_rate):
36 | for i in range(len(self.DNA)):
37 | if np.random.random() < mutation_rate:
38 |
39 | DNA_list = list(self.DNA)
40 | DNA_list[i] = random.choice(self.DNA_pool)
41 | self.DNA = "".join(DNA_list)
42 |
43 |
44 | def show_best_member(population,norm_pop):
45 | idx = np.argmax(norm_pop)
46 | member = population[idx]
47 | member.print_DNA()
48 |
49 | def normalize_fitness(population):
50 | # Normalizing the fitness of the population for a Probalistic Selection
51 | population_fitness= [i.fitness for i in population]
52 | norm_fit = []
53 | for mem in population:
54 | if sum(population_fitness) != 0:
55 | norm = mem.fitness/sum(population_fitness)
56 | else:
57 | norm = 0
58 | norm_fit.append(norm)
59 | return norm_fit
60 |
61 | def crossover(population, norm_pop):
62 | """
63 | Midpoint_Corssover method
64 | returns the DNA or new name of the child after crossover
65 | """
66 | #selects two parents probabilistic accroding to the fitness
67 | if sum(norm_pop) != 0:
68 | parent1 = np.random.choice(population, p = norm_pop)
69 | parent2 = np.random.choice(population, p = norm_pop)
70 | else:
71 | # if there are no "best" parents choose randomly
72 | parent1 = np.random.choice(population)
73 | parent2 = np.random.choice(population)
74 |
75 | # picking random midpoint for crossing over name/DNA
76 | #print(parent1)
77 | mid_point = np.random.choice([i for i in range(len(parent1.DNA))])
78 | # adding DNA-Sequences of the parents to final DNA
79 | child_DNA = parent1.DNA[:mid_point] + parent2.DNA[mid_point:]
80 | return child_DNA
81 |
82 | def reproduction(population, norm_pop,DNA_pool,DNA_length, mutation_rate):
83 | """
84 | Reproduces the Population with Crossover and Mutation
85 |
86 | Returns the new Population
87 | """
88 | new_population = []
89 | for n in range(len(population)):
90 | # CROSSOVER
91 | child_DNA = crossover(population, norm_pop)
92 | child = Member(DNA_length, DNA_pool, DNA = child_DNA)
93 | # Mutation
94 | child.mutate(mutation_rate)
95 | new_population.append(child)
96 | return new_population
97 |
98 |
99 | def init_population(population_size,DNA_pool,DNA_length):
100 | """
101 | Creates the initial Population:
102 | """
103 | population = []
104 | for mem in range(population_size):
105 | mem = Member(DNA_length, DNA_pool)
106 | mem.create()
107 | population.append(mem)
108 | return population
109 |
110 | def plotting(histogram_plot, mean_plot,max_plot,min_plot,goal_value):
111 |
112 | plt.subplot(122)
113 | plt.title("Single member fitness")
114 | bar_ = plt.bar(np.linspace(1,len(histogram_plot),num=len(histogram_plot)),histogram_plot)
115 | mean1 = np.mean(histogram_plot)
116 | goal_, = plt.plot(np.resize(mean1,len(histogram_plot)),"r")
117 | plt.legend([bar_,goal_],["fitness","mean"])
118 | plt.subplot(121)
119 | plt.title("Overall statistics")
120 | plt.ylabel("Fitness")
121 | plt.xlabel("Generation")
122 | max_, = plt.plot(max_plot)
123 | min_, = plt.plot(min_plot)
124 | mean_, = plt.plot(mean_plot)
125 | goal_, = plt.plot(np.resize(goal_value,len(mean_plot)))
126 | plt.legend([max_,min_,mean_,goal_],["Max","Min","Mean","Optimum"])
127 | plt.show()
128 | plt.pause(0.001)
129 | plt.clf()
130 |
131 | def genetic_algorithm( population_size,DNA_pool,goal, mutation_rate):
132 | DNA_length = len(goal)
133 | # initialize first population
134 | population = init_population(population_size, DNA_pool, DNA_length)
135 | done = False
136 | episode = 0
137 | mean_plot = []
138 | max_plot = []
139 | min_plot = []
140 | goal_value = len(list(goal))
141 | while not done:
142 |
143 | # Checking if goal reached:
144 | for member in population:
145 | if member.DNA == goal:
146 | print("Evolution goal reached!!!")
147 | print("Final answer: ")
148 | member.print_DNA()
149 | print("-------------------------")
150 | print("Do you want to end evolution?")
151 | input_ = input("[y/n]")
152 | if input_ == "y":
153 | exit()
154 | else:
155 | pass
156 | # calc_fitness:
157 | histogram_plot = []
158 | for member in population:
159 | member.calc_fitness(goal)
160 | histogram_plot.append(member.fitness)
161 | normalized_fitness = normalize_fitness(population)
162 | ### PRINT BEST MEMBER of current Population
163 | print("Population: {}, best member: ".format(episode))
164 | show_best_member(population, normalize_fitness)
165 | print("\nReproduction!")
166 |
167 | new_population = reproduction(population,normalized_fitness,DNA_pool,DNA_length, mutation_rate)
168 | population = new_population
169 | print("Death of the parents!")
170 | print("-------------------\n")
171 | episode += 1
172 | mean_plot.append(np.mean(histogram_plot))
173 | max_plot.append(max(histogram_plot))
174 | min_plot.append(min(histogram_plot))
175 | #Plotting
176 | #print(histogram_plot)
177 | plotting(histogram_plot,mean_plot,max_plot,min_plot,goal_value)
178 |
179 | def main(population_size,goal,mutation_rate):
180 | #goal = "this is sparta!"
181 | # define the traits you want to vary on // DNA!
182 | traits = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","?","!"," "]
183 | plt.ion()
184 | fig = plt.figure()
185 | genetic_algorithm(population_size = population_size,DNA_pool = traits, goal = goal, mutation_rate = mutation_rate)
186 |
187 |
188 | if __name__ == "__main__":
189 | parser = argparse.ArgumentParser()
190 | parser.add_argument("-p", "--Population_size",type = int,default = 50, help="Size of the Population- how many members per Population")
191 | parser.add_argument("-g", "--Goal",type = str,default = "genetic algorithm", help = "Sequenz of characters as the goal DNA to reach -- has to be lower case!")
192 | parser.add_argument("-m", "--Mutation_rate", type = float, default = 0.015, help = "Percentage of how probably it is that mutation occures")
193 | args = parser.parse_args()
194 | main(args.Population_size,args.Goal,args.Mutation_rate)
--------------------------------------------------------------------------------
/Genetic Algorithm on Neural Network Architecture and Hyperparameter Optimization/Neural Network Architecture and Hyperparameter Optimization.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "code",
5 | "execution_count": 1,
6 | "metadata": {},
7 | "outputs": [
8 | {
9 | "name": "stderr",
10 | "output_type": "stream",
11 | "text": [
12 | "Using TensorFlow backend.\n"
13 | ]
14 | }
15 | ],
16 | "source": [
17 | "import keras\n",
18 | "import numpy as np\n",
19 | "from keras.datasets import mnist\n",
20 | "from keras.models import Sequential\n",
21 | "from keras.layers import Dense, Flatten, Dropout\n",
22 | "from keras.layers.convolutional import Convolution2D\n",
23 | "from keras.layers.pooling import MaxPooling2D\n",
24 | "from keras.models import Model\n",
25 | "from keras.models import load_model\n",
26 | "import matplotlib.pyplot as plt\n",
27 | "#load mnist dataset"
28 | ]
29 | },
30 | {
31 | "cell_type": "code",
32 | "execution_count": 2,
33 | "metadata": {},
34 | "outputs": [
35 | {
36 | "name": "stdout",
37 | "output_type": "stream",
38 | "text": [
39 | "Training label shape: (60000,)\n",
40 | "First 5 training labels: [5 0 4 1 9]\n",
41 | "784\n",
42 | "(10000, 10)\n",
43 | "(10000, 784)\n"
44 | ]
45 | }
46 | ],
47 | "source": [
48 | "# Setup train and test splits\n",
49 | "(X_train, y_train), (X_test, y_test) = mnist.load_data()\n",
50 | "print(\"Training label shape: \", y_train.shape) # (60000,) -- 60000 numbers (all 0-9)\n",
51 | "print(\"First 5 training labels: \", y_train[:5]) # [5, 0, 4, 1, 9]\n",
52 | "\n",
53 | "# Convert to \"one-hot\" vectors using the to_categorical function\n",
54 | "num_classes = 10\n",
55 | "y_train = keras.utils.to_categorical(y_train, num_classes)\n",
56 | "y_test = keras.utils.to_categorical(y_test, num_classes)\n",
57 | "\n",
58 | "# splitting dataset for faster learning and debugging\n",
59 | "X_train, _,_,_,_,_ = np.split(X_train,6) #\n",
60 | "y_train, _,_,_,_,_ = np.split(y_train,6)\n",
61 | "# input and output shape\n",
62 | "input_shape = X_train[1].shape\n",
63 | "num_classes = 10\n",
64 | "# Flatten the images\n",
65 | "image_vector_size = 28*28\n",
66 | "print(image_vector_size)\n",
67 | "X_train = X_train.reshape(X_train.shape[0], image_vector_size)\n",
68 | "X_test = X_test.reshape(X_test.shape[0], image_vector_size)\n",
69 | "print(y_train.shape)\n",
70 | "print(X_train.shape)"
71 | ]
72 | },
73 | {
74 | "cell_type": "markdown",
75 | "metadata": {},
76 | "source": [
77 | "# DNA Parameter to optimize"
78 | ]
79 | },
80 | {
81 | "cell_type": "markdown",
82 | "metadata": {},
83 | "source": [
84 | "depth = [1,2,3,4,5,6,7,8,9,10]
\n",
85 | "neurons_per_layer = [16,32,64,128,256,512,1024]
\n",
86 | "activations = [\"tanh\",\"softmax\",\"relu\",\"sigmoid\"] #\"leakyrelu\",\"exponential\",\"elu\",\"selu\",\"softplus\",\"softsign\",\"hard_sigmoid\",\"linear\"
\n",
87 | "optimizer = [\"sgd\",\"rmsprop\",\"adagrad\",\"adadelta\",\"adam\",\"adamax\",\"nadam\"],
\n",
88 | "losses =[\"mean_squared_error\",\"mean_absolute_error\",\"mean_absolute_percentage_error\",\"mean_squared_logarithmic_error\",\"squared_hinge\",\"hinge\",\"categorical_hinge\",\"logcosh\",\"categorical_crossentropy\",\"sparse_categorical_crossentropy\",\"binary_crossentropy\",\"kullback_leibler_divergence\",\"poisson\",\"cosine_proximity\"]
\n",
89 | "#learning_rate \n",
90 | "\n",
91 | "\n",
92 | "##### not using loss: \"sparse_categorical_crossentropy\" since it messes with the shape of the output layer"
93 | ]
94 | },
95 | {
96 | "cell_type": "code",
97 | "execution_count": 48,
98 | "metadata": {},
99 | "outputs": [],
100 | "source": [
101 | "# DNA[0] = depth\n",
102 | "# DNA[1] = neurons_per_layer\n",
103 | "# DNA[2] = activations\n",
104 | "# DNA[3] = optimizer\n",
105 | "# DNA[4] = losses\n",
106 | "DNA_parameter = [[5,6,7,8,9,10],\n",
107 | " [16,32,64,128,256,512,1024],\n",
108 | " [\"tanh\",\"softmax\",\"relu\",\"sigmoid\",\"elu\",\"selu\",\"softplus\",\"softsign\",\"hard_sigmoid\",\"linear\"], #\"leakyrelu\",\n",
109 | " [\"sgd\",\"rmsprop\",\"adagrad\",\"adadelta\",\"adam\",\"adamax\",\"nadam\"],\n",
110 | " [\"mean_squared_error\",\"mean_absolute_error\",\"mean_absolute_percentage_error\",\"mean_squared_logarithmic_error\",\"squared_hinge\",\"hinge\",\"categorical_hinge\",\"logcosh\",\"categorical_crossentropy\",\"binary_crossentropy\",\"kullback_leibler_divergence\",\"poisson\",\"cosine_proximity\"] #\"sparse_categorical_crossentropy\",\n",
111 | " ]"
112 | ]
113 | },
114 | {
115 | "cell_type": "code",
116 | "execution_count": 91,
117 | "metadata": {},
118 | "outputs": [],
119 | "source": [
120 | "class Network:\n",
121 | " def __init__(self,input_shape,classes,DNA_param,epochs):\n",
122 | " \n",
123 | " self.architecture_DNA = [] # to save current parameters\n",
124 | " self.fitness = []\n",
125 | " self.acc_history = []\n",
126 | " self.input_shape = input_shape \n",
127 | " self.classes = classes\n",
128 | " self.epochs = epochs\n",
129 | "\n",
130 | " \n",
131 | " # unfold DNA_parameters:\n",
132 | " depth = DNA_param[0]\n",
133 | " neurons_per_layer = DNA_param[1]\n",
134 | " activations = DNA_param[2]\n",
135 | " optimizer = DNA_param[3]\n",
136 | " losses = DNA_param[4]\n",
137 | " \n",
138 | " model = Sequential()\n",
139 | " # Building the init network with random choices: \n",
140 | " network_depth = np.random.choice(depth)\n",
141 | " self.architecture_DNA.append(network_depth)\n",
142 | "\n",
143 | " for i in range(network_depth):\n",
144 | " if i == 0:\n",
145 | " neurons = np.random.choice(neurons_per_layer)\n",
146 | " activation = np.random.choice(activations)\n",
147 | " self.architecture_DNA.append([neurons, activation])\n",
148 | " model.add(Dense(neurons,input_shape = (self.input_shape,), activation = activation))\n",
149 | " if i == network_depth - 1:\n",
150 | " activation = np.random.choice(activations)\n",
151 | " self.architecture_DNA.append(activation)\n",
152 | " model.add(Dense(self.classes, activation = activation ))\n",
153 | " else:\n",
154 | " neurons = np.random.choice(neurons_per_layer)\n",
155 | " activation = np.random.choice(activations)\n",
156 | " self.architecture_DNA.append([neurons,activation])\n",
157 | " model.add(Dense(neurons, activation = activation))\n",
158 | " \n",
159 | " loss=np.random.choice(losses)\n",
160 | " optimizer=np.random.choice(optimizer)\n",
161 | " self.architecture_DNA.append([loss,optimizer])\n",
162 | " model.compile(loss=loss, optimizer= optimizer, metrics=['accuracy'])\n",
163 | " self.model = model\n",
164 | " \n",
165 | " ### STRUCTURE OF ACHITECTURE DNA ###\n",
166 | " # architecture_DNA[0] = Depth\n",
167 | " # architecture_DNA[1] = Input_layer with: [0] = neurons, [1] = activation\n",
168 | " # architecture_DNA[2 to Depth-1*] = Hidden layer with: [0] = neurons, [1] = activation \n",
169 | " # architecture_DNA[Depth] = Output layer activation\n",
170 | " # architecture_DNA[-1] = Hyperparameter with: [0] = loss, [1] = optimizer\n",
171 | " \n",
172 | " # *Depth-1 last hidden layer since last layer is output layer\n",
173 | " ####################################\n",
174 | " \n",
175 | " def create_children(self, children_DNA):\n",
176 | " model = Sequential()\n",
177 | " ####################################################################################\n",
178 | " # unfold children DNA: #\n",
179 | " # children_DNA[0] = Depth #\n",
180 | " # children_DNA[1] = Input_layer with: [0] = neurons, [1] = activation #\n",
181 | " # children_DNA[2 to Depth-1*] = Hidden layer with: [0] = neurons, [1] = activation # \n",
182 | " # children_DNA[Depth] = Output layer activation #\n",
183 | " # children_DNA[-1] = Hyperparameter with: [0] = loss, [1] = optimizer #\n",
184 | " ####################################################################################\n",
185 | " \n",
186 | " #print(\"DNA_length: \", len(children_DNA))\n",
187 | " children_depth = children_DNA[0]\n",
188 | " #print(\"Depth: \", children_depth)\n",
189 | " #print(children_DNA)\n",
190 | " for i in range(children_depth):\n",
191 | " if i == 0:\n",
192 | " #Input Layer\n",
193 | " model.add(Dense(children_DNA[1][0],input_shape = (self.input_shape,), activation = children_DNA[1][1]))\n",
194 | " if i == children_depth -1:\n",
195 | " model.add(Dense(self.classes, activation = children_DNA[children_depth]))\n",
196 | " else:\n",
197 | " #print(children_DNA[i+1])\n",
198 | " if i != children_depth -1:\n",
199 | " model.add(Dense(children_DNA[i+1][0], activation = children_DNA[i+1][1]))\n",
200 | " model.compile(loss = children_DNA[-1][0], optimizer = children_DNA[-1][1], metrics=['accuracy'])\n",
201 | " self.model = model\n",
202 | " self.architecture_DNA = children_DNA\n",
203 | " \n",
204 | " \n",
205 | " def give_fitness(self):\n",
206 | " return self.fitness\n",
207 | " \n",
208 | " \n",
209 | " def train(self):\n",
210 | " #start = time.time()\n",
211 | " self.model.fit(X_train,y_train, batch_size = 32, epochs = self.epochs, verbose = 1,shuffle = True) #, validation_data =(X_test, y_test)\n",
212 | " #end = time.time()\n",
213 | " #self.fitness[0] = end-start\n",
214 | " \n",
215 | " def test(self):\n",
216 | " loss, acc = self.model.evaluate(X_test,y_test)\n",
217 | " self.fitness = acc\n",
218 | " self.acc_history.append(acc)\n",
219 | " \n",
220 | " def give_DNA(self):\n",
221 | " return self.architecture_DNA\n",
222 | " \n",
223 | " def architecture(self):\n",
224 | " self.model.summary()"
225 | ]
226 | },
227 | {
228 | "cell_type": "code",
229 | "execution_count": 94,
230 | "metadata": {},
231 | "outputs": [],
232 | "source": [
233 | "class GeneticAlgorithm:\n",
234 | " def __init__(self, population_size, mutation_rate, generations = 50, Epochs = 2):\n",
235 | " self.population_size = population_size\n",
236 | " self.mutation_rate = mutation_rate\n",
237 | " self.generations = generations\n",
238 | " self.training_epochs = Epochs\n",
239 | " self.population = None\n",
240 | " self.children_population_DNA = []\n",
241 | " self.acces = []\n",
242 | " self.norm_acces = []\n",
243 | " \n",
244 | " def create_population(self):\n",
245 | " self.population = [Network(image_vector_size, num_classes, DNA_parameter,self.training_epochs) for i in range(self.population_size)]\n",
246 | " \n",
247 | " def train_generation(self):\n",
248 | " for member in self.population:\n",
249 | " member.train()\n",
250 | " \n",
251 | " def predict(self):\n",
252 | " for member in self.population:\n",
253 | " member.test()\n",
254 | " self.acc.append(member.give_fitness())\n",
255 | " \n",
256 | " def normalize(self):\n",
257 | " sum_ = sum(self.acc)\n",
258 | " self.norm_acc = [i/sum_ for i in self.acc] \n",
259 | " #print(\"\\nNormalization sum: \",sum(self.norm_acc))\n",
260 | " #assert sum(self.norm_acc) == 1\n",
261 | " \n",
262 | " def clear_losses(self):\n",
263 | " self.norm_acc = []\n",
264 | " self.acc = []\n",
265 | " \n",
266 | " def mutate(self):\n",
267 | " for child_DNA in self.children_population_DNA:\n",
268 | " for i in range(len(child_DNA)):\n",
269 | " if np.random.random() < self.mutation_rate:\n",
270 | " print(\"\\nMutation!\")\n",
271 | " if i == 0:\n",
272 | " new_depth = np.random.choice(DNA_parameter[0])\n",
273 | " child_DNA[0] = new_depth\n",
274 | " \n",
275 | " if i == len(child_DNA)-2:\n",
276 | " new_output_activation = np.random.choice(DNA_parameter[2])\n",
277 | " child_DNA[-2] = new_output_activation\n",
278 | " \n",
279 | " if i == len(child_DNA)-1:\n",
280 | " # random flip if loss or activation shall be changed\n",
281 | " if np.random.random() < 0.5:\n",
282 | " new_loss = np.random.choice(DNA_parameter[4])\n",
283 | " child_DNA[-1][0] = new_loss\n",
284 | " else:\n",
285 | " new_optimizer = np.random.choice(DNA_parameter[3])\n",
286 | " child_DNA[-1][1] = new_optimizer\n",
287 | " if i != 0 and i !=len(child_DNA)-2 and i != len(child_DNA)-1:\n",
288 | " #else:\n",
289 | " # 3/2 flif if number of neurons or activation function mutates:\n",
290 | " #print(child_DNA)\n",
291 | " if np.random.random() < 0.33:\n",
292 | " #print(child_DNA[i][1])\n",
293 | " new_activation = np.random.choice(DNA_parameter[2])\n",
294 | " #print(new_activation)\n",
295 | " child_DNA[i][1] = new_activation\n",
296 | " else:\n",
297 | " #print(child_DNA[i][0])\n",
298 | " new_neuron_count = np.random.choice(DNA_parameter[1])\n",
299 | " child_DNA[i][0] = new_neuron_count\n",
300 | " #print(new_neuron_count)\n",
301 | " #print(\"After mutation \", child_DNA)\n",
302 | "\n",
303 | " def reproduction(self):\n",
304 | " \"\"\" \n",
305 | " Reproduction through midpoint crossover method \n",
306 | " \"\"\"\n",
307 | " population_idx = [i for i in range(len(self.population))]\n",
308 | " for i in range(len(self.population)):\n",
309 | " #selects two parents probabilistic accroding to the fitness score\n",
310 | " if sum(self.norm_acc) != 0:\n",
311 | " parent1 = np.random.choice(population_idx, p = self.norm_acc)\n",
312 | " parent2 = np.random.choice(population_idx, p = self.norm_acc)\n",
313 | " else:\n",
314 | " # if there are no \"best\" parents choose randomly \n",
315 | " parent1 = np.random.choice(population_idx)\n",
316 | " parent2 = np.random.choice(population_idx)\n",
317 | "\n",
318 | " # picking random midpoint for crossing over name/DNA\n",
319 | " parent1_DNA = self.population[parent1].give_DNA()\n",
320 | " parent2_DNA = self.population[parent2].give_DNA()\n",
321 | " #print(parent1_DNA)\n",
322 | " \n",
323 | " mid_point_1 = np.random.choice([i for i in range(2,len(parent1_DNA)-2)])\n",
324 | " mid_point_2 = np.random.choice([i for i in range(2,len(parent2_DNA)-2)])\n",
325 | " # adding DNA-Sequences of the parents to final DNA\n",
326 | " child_DNA = parent1_DNA[:mid_point_1] + parent2_DNA[mid_point_2:]\n",
327 | " new_nn_depth = len(child_DNA)-2 # minus 2 because of depth parameter[0] and loss parameter[-1]\n",
328 | " child_DNA[0] = new_nn_depth\n",
329 | " self.children_population_DNA.append(child_DNA)\n",
330 | " # old population gets the new and proper weights\n",
331 | " self.mutate()\n",
332 | " keras.backend.clear_session() ## delete old models to free memory\n",
333 | " for i in range(len(self.population)):\n",
334 | " self.population[i].create_children(self.children_population_DNA[i])\n",
335 | " \n",
336 | " \n",
337 | " \n",
338 | " def run_evolution(self):\n",
339 | " for episode in range(self.generations):\n",
340 | " print(\"\\n--- Generation {} ---\".format(episode))\n",
341 | " self.clear_losses()\n",
342 | " self.train_generation()\n",
343 | " self.predict()\n",
344 | " if episode != self.generations -1:\n",
345 | " self.normalize()\n",
346 | " self.reproduction()\n",
347 | " \n",
348 | " else:\n",
349 | " pass\n",
350 | " self.children_population_DNA = []\n",
351 | " # plotting history:\n",
352 | " for a in range(self.generations):\n",
353 | " for member in self.population:\n",
354 | " plt.plot(member.acc_history)\n",
355 | " plt.xlabel(\"Generations\")\n",
356 | " plt.ylabel(\"Accuracy\")\n",
357 | " plt.show()"
358 | ]
359 | },
360 | {
361 | "cell_type": "code",
362 | "execution_count": 93,
363 | "metadata": {
364 | "scrolled": false
365 | },
366 | "outputs": [
367 | {
368 | "name": "stdout",
369 | "output_type": "stream",
370 | "text": [
371 | "\n",
372 | "--- Generation 0 ---\n",
373 | "Epoch 1/1\n",
374 | "10000/10000 [==============================] - 1s - loss: -0.0493 - acc: 0.3589 \n",
375 | "Epoch 1/1\n",
376 | "10000/10000 [==============================] - 1s - loss: 1.9455 - acc: 0.0951 \n",
377 | "Epoch 1/1\n",
378 | "10000/10000 [==============================] - 1s - loss: 1.0952 - acc: 0.1036 \n",
379 | "Epoch 1/1\n",
380 | "10000/10000 [==============================] - 1s - loss: 0.0383 - acc: 0.2925 \n",
381 | " 9056/10000 [==========================>...] - ETA: 0s\n",
382 | "Mutation!\n",
383 | "[15, [128, 'hard_sigmoid'], [128, 'hard_sigmoid'], [64, 'tanh'], [256, 'linear'], [512, 'selu'], [16, 'softsign'], [64, 'linear'], [32, 'selu'], [256, 'linear'], [512, 'selu'], [16, 'softsign'], [64, 'linear'], [32, 'selu'], [128, 'softmax'], 'softsign', ['cosine_proximity', 'adagrad']]\n",
384 | "\n",
385 | "Mutation!\n",
386 | "[15, [128, 'hard_sigmoid'], [128, 'hard_sigmoid'], [128, 'tanh'], [256, 'linear'], [512, 'selu'], [16, 'softsign'], [64, 'linear'], [32, 'selu'], [256, 'linear'], [512, 'selu'], [16, 'softsign'], [64, 'linear'], [32, 'selu'], [128, 'softmax'], 'softsign', ['cosine_proximity', 'adagrad']]\n",
387 | "\n",
388 | "Mutation!\n",
389 | "[13, [1024, 'elu'], [32, 'softsign'], [16, 'linear'], [128, 'selu'], [32, 'sigmoid'], [32, 'selu'], [64, 'relu'], [512, 'selu'], [16, 'softsign'], [64, 'linear'], [32, 'selu'], [32, 'softmax'], 'softsign', ['cosine_proximity', 'adagrad']]\n",
390 | "\n",
391 | "--- Generation 1 ---\n",
392 | "Epoch 1/1\n",
393 | "10000/10000 [==============================] - 1s - loss: 0.0384 - acc: 0.2902 \n",
394 | "Epoch 1/1\n",
395 | "10000/10000 [==============================] - 1s - loss: -0.0272 - acc: 0.0969 \n",
396 | "Epoch 1/1\n",
397 | "10000/10000 [==============================] - 2s - loss: -0.0311 - acc: 0.1102 \n",
398 | "Epoch 1/1\n",
399 | "10000/10000 [==============================] - 1s - loss: -0.0258 - acc: 0.1001 \n",
400 | " 9984/10000 [============================>.] - ETA: 0s\n",
401 | "Mutation!\n",
402 | "[17, [256, 'linear'], [256, 'softsign'], [256, 'softmax'], [16, 'elu'], [128, 'hard_sigmoid'], [128, 'tanh'], [256, 'linear'], [512, 'selu'], [16, 'linear'], [64, 'linear'], [32, 'selu'], [512, 'selu'], [16, 'linear'], [64, 'linear'], [32, 'selu'], [32, 'softmax'], 'softsign', ['cosine_proximity', 'adagrad']]\n",
403 | "\n",
404 | "--- Generation 2 ---\n",
405 | "Epoch 1/1\n",
406 | "10000/10000 [==============================] - 2s - loss: -0.0308 - acc: 0.1035 \n",
407 | "Epoch 1/1\n",
408 | "10000/10000 [==============================] - 1s - loss: 0.0414 - acc: 0.2247 \n",
409 | "Epoch 1/1\n",
410 | "10000/10000 [==============================] - 1s - loss: -0.0288 - acc: 0.1118 \n",
411 | "Epoch 1/1\n",
412 | "10000/10000 [==============================] - 2s - loss: -0.0311 - acc: 0.1085 \n",
413 | " 9856/10000 [============================>.] - ETA: 0s\n",
414 | "--- Generation 3 ---\n",
415 | "Epoch 1/1\n",
416 | "10000/10000 [==============================] - 1s - loss: -0.0449 - acc: 0.3255 \n",
417 | "Epoch 1/1\n",
418 | "10000/10000 [==============================] - 1s - loss: -0.0289 - acc: 0.0965 \n",
419 | "Epoch 1/1\n",
420 | "10000/10000 [==============================] - 1s - loss: 0.0514 - acc: 0.1377 \n",
421 | "Epoch 1/1\n",
422 | "10000/10000 [==============================] - 1s - loss: -0.0270 - acc: 0.1126 \n",
423 | " 8992/10000 [=========================>....] - ETA: 0s\n",
424 | "Mutation!\n",
425 | "\n",
426 | "--- Generation 4 ---\n",
427 | "Epoch 1/1\n",
428 | "10000/10000 [==============================] - 2s - loss: -0.0430 - acc: 0.3494 \n",
429 | "Epoch 1/1\n",
430 | "10000/10000 [==============================] - 2s - loss: -0.0473 - acc: 0.3925 \n",
431 | "Epoch 1/1\n",
432 | "10000/10000 [==============================] - 1s - loss: -0.0179 - acc: 0.1601 \n",
433 | "Epoch 1/1\n",
434 | "10000/10000 [==============================] - 1s - loss: -0.0283 - acc: 0.1093 \n",
435 | " 9216/10000 [==========================>...] - ETA: 0s\n",
436 | "--- Generation 5 ---\n",
437 | "Epoch 1/1\n",
438 | "10000/10000 [==============================] - 2s - loss: -0.0356 - acc: 0.2477 \n",
439 | "Epoch 1/1\n",
440 | "10000/10000 [==============================] - 1s - loss: -0.0288 - acc: 0.1089 \n",
441 | "Epoch 1/1\n",
442 | "10000/10000 [==============================] - 1s - loss: -0.0547 - acc: 0.6123 \n",
443 | "Epoch 1/1\n",
444 | "10000/10000 [==============================] - 2s - loss: -0.0497 - acc: 0.4826 \n",
445 | "10000/10000 [==============================] - 0s \n",
446 | " 9952/10000 [============================>.] - ETA: 0s"
447 | ]
448 | },
449 | {
450 | "data": {
451 | "image/png": "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\n",
452 | "text/plain": [
453 | ""
454 | ]
455 | },
456 | "metadata": {},
457 | "output_type": "display_data"
458 | }
459 | ],
460 | "source": [
461 | "GA = GeneticAlgorithm(population_size = 4,mutation_rate = 0.03, generations = 6,Epochs=1)\n",
462 | "GA.create_population()\n",
463 | "GA.run_evolution()"
464 | ]
465 | },
466 | {
467 | "cell_type": "markdown",
468 | "metadata": {},
469 | "source": [
470 | "# TODO:\n",
471 | "# fixing little bugs and Errors with mutiations\n",
472 | "# FItness implying training time (and loss?)\n",
473 | "# logging-data or architecture -- Process\n",
474 | "# liveplot?\n",
475 | "# "
476 | ]
477 | },
478 | {
479 | "cell_type": "code",
480 | "execution_count": null,
481 | "metadata": {},
482 | "outputs": [],
483 | "source": []
484 | }
485 | ],
486 | "metadata": {
487 | "kernelspec": {
488 | "display_name": "Python 3",
489 | "language": "python",
490 | "name": "python3"
491 | },
492 | "language_info": {
493 | "codemirror_mode": {
494 | "name": "ipython",
495 | "version": 3
496 | },
497 | "file_extension": ".py",
498 | "mimetype": "text/x-python",
499 | "name": "python",
500 | "nbconvert_exporter": "python",
501 | "pygments_lexer": "ipython3",
502 | "version": "3.6.5"
503 | }
504 | },
505 | "nbformat": 4,
506 | "nbformat_minor": 2
507 | }
508 |
--------------------------------------------------------------------------------
/Img/genetic_alg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/BY571/Genetic-Algorithms-Neural-Network-Optimization/723686463aa369bb32fbdebb162a7c0808984637/Img/genetic_alg.png
--------------------------------------------------------------------------------
/Multi-Neural Network Weight Optimization with Genetic Algorithm/GA_weight_opti.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "code",
5 | "execution_count": 1,
6 | "metadata": {},
7 | "outputs": [
8 | {
9 | "name": "stderr",
10 | "output_type": "stream",
11 | "text": [
12 | "Using TensorFlow backend.\n"
13 | ]
14 | }
15 | ],
16 | "source": [
17 | "import keras\n",
18 | "import numpy as np\n",
19 | "from keras.datasets import mnist\n",
20 | "from keras.models import Sequential\n",
21 | "from keras.layers import Dense, Flatten, Dropout\n",
22 | "from keras.layers.convolutional import Convolution2D\n",
23 | "from keras.layers.pooling import MaxPooling2D\n",
24 | "from keras.models import Model\n",
25 | "from keras.models import load_model\n",
26 | "import matplotlib.pyplot as plt\n",
27 | "#load mnist dataset\n",
28 | "(X_train_loaded, y_train_loaded), (X_test_loaded, y_test_loaded) = mnist.load_data()\n"
29 | ]
30 | },
31 | {
32 | "cell_type": "markdown",
33 | "metadata": {},
34 | "source": [
35 | "# Preparing the Dataset"
36 | ]
37 | },
38 | {
39 | "cell_type": "code",
40 | "execution_count": 54,
41 | "metadata": {},
42 | "outputs": [
43 | {
44 | "name": "stdout",
45 | "output_type": "stream",
46 | "text": [
47 | "(28, 28, 1)\n",
48 | "(10000, 28, 28, 1)\n"
49 | ]
50 | }
51 | ],
52 | "source": [
53 |
54 | "X_train = X_train_loaded.reshape(X_train_loaded.shape[0], 28, 28, 1)\n",
55 | "X_test = X_test_loaded.reshape(X_test_loaded.shape[0], 28, 28, 1)\n",
56 | "\n",
57 | "X_train = X_train.astype('float32')\n",
58 | "X_test = X_test.astype('float32')\n",
59 | "\n",
60 | "X_train/=255\n",
61 | "X_test/=255\n",
62 | "\n",
63 | "number_of_classes = 10\n",
64 | "y_train = keras.utils.to_categorical(y_train_loaded, num_classes=number_of_classes)\n",
65 | "y_test = keras.utils.to_categorical(y_test_loaded, num_classes=number_of_classes)\n",
66 | "\n",
67 | "#decreasing dataset to train faster for algorithm testing\n",
68 | "X_train, _,_,_,_,_ = np.split(X_train,6)\n",
69 | "y_train, _,_,_,_,_ = np.split(y_train,6)\n",
70 | "input_shape = X_train[1].shape\n",
71 | "print(input_shape)\n",
72 | "print(X_train.shape)"
73 | ]
74 | },
75 | {
76 | "cell_type": "code",
77 | "execution_count": 158,
78 | "metadata": {},
79 | "outputs": [],
80 | "source": [
81 | "class Network:\n",
82 | " def __init__(self):\n",
83 | " model = Sequential()\n",
84 | " model.add(Convolution2D(24, kernel_size=(3, 3), strides=(1, 1),activation='relu',input_shape=input_shape))\n",
85 | " model.add(Convolution2D(32, (3, 3), activation='relu'))\n",
86 | " model.add(MaxPooling2D(pool_size=(2, 2)))\n",
87 | " model.add(Flatten())\n",
88 | " model.add(Dropout(0.5))\n",
89 | " model.add(Dense(100,activation = \"relu\"))\n",
90 | " model.add(Dense(10, activation=\"softmax\"))\n",
91 | " model.compile(loss=\"categorical_crossentropy\", optimizer=\"adam\",metrics=['accuracy'])\n",
92 | " self.model = model\n",
93 | " self.acc_history = []\n",
94 | " \n",
95 | " def return_acc_history(self):\n",
96 | " return self.acc_history\n",
97 | " \n",
98 | " def get_layer_weight(self,i):\n",
99 | " return self.model.layers[i].get_weights()\n",
100 | " \n",
101 | " def set_layer_weight(self,i,weight):\n",
102 | " self.model.layers[i].set_weights(weight)\n",
103 | " \n",
104 | " def train(self):\n",
105 | " self.model.fit(X_train,y_train, batch_size = 32, epochs = 1, verbose = 1,shuffle = True) #, validation_data =(X_test, y_test)\n",
106 | " \n",
107 | " def test(self):\n",
108 | " loss, acc = self.model.evaluate(X_test,y_test)\n",
109 | " self.acc_history.append(acc)\n",
110 | " return acc\n",
111 | " \n",
112 | " def load_layer_weights(self,weights):\n",
113 | " self.model.set_weights(weights) \n",
114 | " \n",
115 | " def give_weights(self):\n",
116 | " return self.model.get_weights()\n",
117 | " def weight_len(self):\n",
118 | " i = 0 \n",
119 | " for j in self.model.layers:\n",
120 | " i+=1\n",
121 | " return i \n",
122 | " def architecture(self):\n",
123 | " self.model.summary()"
124 | ]
125 | },
126 | {
127 | "cell_type": "code",
128 | "execution_count": 170,
129 | "metadata": {},
130 | "outputs": [],
131 | "source": [
132 | "class GeneticAlgorithm:\n",
133 | " def __init__(self, population_size, mutation_rate, generations = 50):\n",
134 | " self.population_size = population_size\n",
135 | " self.mutation_rate = mutation_rate\n",
136 | " self.generations = generations\n",
137 | " self.population = None\n",
138 | " self.children_population_weights = []\n",
139 | " self.acces = []\n",
140 | " self.norm_acces = []\n",
141 | " \n",
142 | " def create_population(self):\n",
143 | " self.population = [Network() for i in range(self.population_size)]\n",
144 | " \n",
145 | " def train_generation(self):\n",
146 | " for member in self.population:\n",
147 | " member.train()\n",
148 | " \n",
149 | " def predict(self):\n",
150 | " for member in self.population:\n",
151 | " acc = member.test()\n",
152 | " self.acc.append(acc)\n",
153 | " #logging.info(\"Losses: {}\".format(loss))\n",
154 | " \n",
155 | " def normalize(self):\n",
156 | " sum_ = sum(self.acc)\n",
157 | " self.norm_acc = [i/sum_ for i in self.acc] \n",
158 | " print(\"\\nNormalization sum: \",sum(self.norm_acc))\n",
159 | " #assert sum(self.norm_acc) == 1\n",
160 | " \n",
161 | " def show_weights(self):\n",
162 | " for i in parent_weights:\n",
163 | " print(i)\n",
164 | " def clear_losses(self):\n",
165 | " self.norm_acc = []\n",
166 | " self.acc = []\n",
167 | " \n",
168 | " def mutate(self):\n",
169 | " for member in self.population:\n",
170 | " for i in range(member.weight_len()):\n",
171 | " if np.random.random() < self.mutation_rate:\n",
172 | " print(\"\\nMutation!\")\n",
173 | " old_weight = member.get_layer_weight(i)\n",
174 | " new_weight = [np.random.uniform(low=-1, high=1, size=old_weight[i].shape) for i in range(len(old_weight))]\n",
175 | " member.set_layer_weight(i, new_weight)\n",
176 | " \n",
177 | " def reproduction(self):\n",
178 | " \"\"\" \n",
179 | " Reproduction through midpoint crossover method \n",
180 | " \"\"\"\n",
181 | " population_idx = [i for i in range(len(self.population))]\n",
182 | " for i in range(len(self.population)):\n",
183 | " #selects two parents probabilistic accroding to the fitness\n",
184 | " if sum(self.norm_acc) != 0:\n",
185 | " parent1 = np.random.choice(population_idx, p = self.norm_acc)\n",
186 | " parent2 = np.random.choice(population_idx, p = self.norm_acc)\n",
187 | " else:\n",
188 | " # if there are no \"best\" parents choose randomly \n",
189 | " parent1 = np.random.choice(population_idx)\n",
190 | " parent2 = np.random.choice(population_idx)\n",
191 | "\n",
192 | " # picking random midpoint for crossing over name/DNA\n",
193 | " parent1_weights = self.population[parent1].give_weights()\n",
194 | " parent2_weights = self.population[parent2].give_weights()\n",
195 | " \n",
196 | " \n",
197 | " mid_point = np.random.choice([i for i in range(len(parent1_weights))])\n",
198 | " # adding DNA-Sequences of the parents to final DNA\n",
199 | " self.children_population_weights.append(parent1_weights[:mid_point] + parent2_weights[mid_point:])\n",
200 | " # old population gets the new and proper weights\n",
201 | " for i in range(len(self.population)):\n",
202 | " for j in range(len(self.children_population_weights)):\n",
203 | " self.population[i].load_layer_weights(self.children_population_weights[j])\n",
204 | " \n",
205 | " \n",
206 | " \n",
207 | " def run_evolution(self):\n",
208 | " for episode in range(self.generations):\n",
209 | " self.clear_losses()\n",
210 | " self.train_generation()\n",
211 | " self.predict()\n",
212 | " if episode != self.generations -1:\n",
213 | " self.normalize()\n",
214 | " self.reproduction()\n",
215 | " self.mutate()\n",
216 | " else:\n",
217 | " pass\n",
218 | " \n",
219 | " # plotting history:\n",
220 | " for a in range(self.generations):\n",
221 | " for member in self.population:\n",
222 | " plt.plot(member.acc_history)\n",
223 | " plt.xlabel(\"Generations\")\n",
224 | " plt.ylabel(\"Accuracy\")\n",
225 | " plt.show()"
226 | ]
227 | },
228 | {
229 | "cell_type": "code",
230 | "execution_count": 173,
231 | "metadata": {},
232 | "outputs": [
233 | {
234 | "name": "stdout",
235 | "output_type": "stream",
236 | "text": [
237 | "Epoch 1/1\n",
238 | "10000/10000 [==============================] - 7s - loss: 0.3771 - acc: 0.8849 \n",
239 | "Epoch 1/1\n",
240 | "10000/10000 [==============================] - 7s - loss: 0.4131 - acc: 0.8746 \n",
241 | "Epoch 1/1\n",
242 | "10000/10000 [==============================] - 7s - loss: 0.4243 - acc: 0.8704 \n",
243 | "Epoch 1/1\n",
244 | "10000/10000 [==============================] - 7s - loss: 0.4064 - acc: 0.8764 \n",
245 | " 9792/10000 [============================>.] - ETA: 0s\n",
246 | "Normalization sum: 1.0\n",
247 | "Epoch 1/1\n",
248 | "10000/10000 [==============================] - 1s - loss: 0.2247 - acc: 0.9298 \n",
249 | "Epoch 1/1\n",
250 | "10000/10000 [==============================] - 1s - loss: 0.2359 - acc: 0.9282 \n",
251 | "Epoch 1/1\n",
252 | "10000/10000 [==============================] - 1s - loss: 0.2324 - acc: 0.9304 \n",
253 | "Epoch 1/1\n",
254 | "10000/10000 [==============================] - 1s - loss: 0.2258 - acc: 0.9278 \n",
255 | " 9856/10000 [============================>.] - ETA: 0s\n",
256 | "Normalization sum: 1.0\n",
257 | "\n",
258 | "Mutation!\n",
259 | "\n",
260 | "Mutation!\n",
261 | "Epoch 1/1\n",
262 | "10000/10000 [==============================] - 1s - loss: 4.5053 - acc: 0.4987 \n",
263 | "Epoch 1/1\n",
264 | "10000/10000 [==============================] - 1s - loss: 0.1065 - acc: 0.9681 \n",
265 | "Epoch 1/1\n",
266 | "10000/10000 [==============================] - 1s - loss: 0.1098 - acc: 0.9668 \n",
267 | "Epoch 1/1\n",
268 | "10000/10000 [==============================] - 1s - loss: 0.1142 - acc: 0.9667 \n",
269 | " 9696/10000 [============================>.] - ETA: 0s\n",
270 | "Normalization sum: 1.0\n",
271 | "Epoch 1/1\n",
272 | "10000/10000 [==============================] - 1s - loss: 0.0706 - acc: 0.9778 \n",
273 | "Epoch 1/1\n",
274 | "10000/10000 [==============================] - 1s - loss: 0.0794 - acc: 0.9751 \n",
275 | "Epoch 1/1\n",
276 | "10000/10000 [==============================] - 1s - loss: 0.0783 - acc: 0.9764 \n",
277 | "Epoch 1/1\n",
278 | "10000/10000 [==============================] - 1s - loss: 0.0817 - acc: 0.9762 \n",
279 | " 9632/10000 [===========================>..] - ETA: 0s"
280 | ]
281 | },
282 | {
283 | "data": {
284 | "image/png": "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\n",
285 | "text/plain": [
286 | ""
287 | ]
288 | },
289 | "metadata": {},
290 | "output_type": "display_data"
291 | }
292 | ],
293 | "source": [
294 | "GA = GeneticAlgorithm(population_size = 4,mutation_rate = 0.05, generations = 4)\n",
295 | "GA.create_population()\n",
296 | "GA.run_evolution()"
297 | ]
298 | }
299 | ],
300 | "metadata": {
301 | "kernelspec": {
302 | "display_name": "Python 3",
303 | "language": "python",
304 | "name": "python3"
305 | },
306 | "language_info": {
307 | "codemirror_mode": {
308 | "name": "ipython",
309 | "version": 3
310 | },
311 | "file_extension": ".py",
312 | "mimetype": "text/x-python",
313 | "name": "python",
314 | "nbconvert_exporter": "python",
315 | "pygments_lexer": "ipython3",
316 | "version": "3.6.5"
317 | }
318 | },
319 | "nbformat": 4,
320 | "nbformat_minor": 2
321 | }
322 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Genetic-Algorithms
2 |
3 |
4 | ### 1 Basic Algorithm implementation
5 |
6 | Basic implementation of a genetic algorithm.
7 |
8 | [Implementation](https://github.com/BY571/Genetic-Algorithms/tree/master/Basic%20Algorithm)
9 |
10 | Goal: find a given string.
11 |
12 | #### Execution:
13 | `python genetic_algorithm_example.py`
14 | with arguments:
15 | - `-p` Population size e.g. `-p 100`
16 | - `-g` goal-string e.g. `-g string_to_find`
17 | - `-m` mutation-rate e.g. `-m 0.01`
18 | 
19 |
20 | #### Explanation:
21 | TODO
22 |
23 |
24 | ### 2 Multi Neural Network Weight Optimization with Genetic Algorithm
25 |
26 | [Implementation](https://github.com/BY571/Genetic-Algorithms/tree/master/Multi-Neural%20Network%20Weight%20Optimization%20with%20Genetic%20Algorithm)
27 |
28 |
29 | ### 3 Genetic Algorithm on Neural Network Architecture and Hyperparameter Optimization
30 |
31 | [Implementation](https://github.com/BY571/Genetic-Algorithms/tree/master/Genetic%20Algorithm%20on%20Neural%20Network%20Architecture%20and%20Hyperparameter%20Optimization)
32 |
--------------------------------------------------------------------------------