6
6
7
7
Usage:
8
8
ann_evolve.py [-h] [-g GENERATIONS] [-u MUTATION_RATE] [-b BOTTLENECK]
9
- [-d mutation_distance ] [-i PROCESSORS] [-o OUTPUT_FILE]
10
- [-k STARTING_POPULATION]
9
+ [-d MUTATION_DISTANCE ] [-i PROCESSORS] [-o OUTPUT_FILE]
10
+ [-k STARTING_POPULATION] [-n NOISE]
11
11
12
12
Options:
13
13
-h --help show this
14
- -g GENERATIONS how many generations to run the program for [default: 100 ]
15
- -u MUTATION_RATE mutation rate i.e. probability that a given value will flip [default: 0.1 ]
16
- -d MUTATION_DISTANCE amount of change a mutation will cause [default: 0.5 ]
17
- -b BOTTLENECK number of individuals to keep from each generation [default: 10 ]
18
- -i PROCESSORS number of processors to use [default: 1 ]
19
- -o OUTPUT_FILE file to write statistics to [default: ann_out .csv]
14
+ -g GENERATIONS how many generations to run the program for [default: 10000 ]
15
+ -u MUTATION_RATE mutation rate i.e. probability that a given value will flip [default: 0.4 ]
16
+ -d MUTATION_DISTANCE amount of change a mutation will cause [default: 10 ]
17
+ -b BOTTLENECK number of individuals to keep from each generation [default: 6 ]
18
+ -i PROCESSORS number of processors to use [default: 4 ]
19
+ -o OUTPUT_FILE file to write statistics to [default: weights .csv]
20
20
-k STARTING_POPULATION starting population size for the simulation [default: 5]
21
+ -n NOISE match noise [default: 0.0]
21
22
"""
22
23
23
-
24
- from __future__ import division
25
-
26
- import copy
27
- import random
24
+ import csv
25
+ from copy import deepcopy
26
+ from itertools import repeat
28
27
from multiprocessing import Pool
28
+ import os
29
+ import random
29
30
from statistics import mean , pstdev
30
31
31
32
from docopt import docopt
33
+ import numpy as np
32
34
33
- import axelrod as axl
34
35
from axelrod .strategies .ann import ANN , split_weights
35
- from axelrod_utils import mean , pstdev
36
+ from axelrod_utils import score_for , objective_match_score , objective_match_moran_win
36
37
38
+ ## Neural network specifics
37
39
38
40
def get_random_weights (number ):
39
41
return [random .uniform (- 1 , 1 ) for _ in range (number )]
40
42
41
- def score_single (my_strategy_factory , other_strategy_factory , length = 200 ):
42
- if other_strategy_factory ().classifier ['stochastic' ]:
43
- repetitions = 10
44
- else :
45
- repetitions = 1
46
- all_scores = []
47
- for _ in range (repetitions ):
48
- me = my_strategy_factory ()
49
- other = other_strategy_factory ()
50
- me .set_match_attributes (length = length )
51
- other .set_match_attributes (length = length )
52
-
53
- g = axl .Game ()
54
- for _ in range (length ):
55
- me .play (other )
56
- iteration_score = sum ([g .score (pair )[0 ] for pair in
57
- zip (me .history , other .history )]) / length
58
- all_scores .append (iteration_score )
59
- return sum (all_scores ) / repetitions
60
-
61
- def score_for (my_strategy_factory , other_strategies , iterations = 200 ):
62
- my_scores = list (map (
63
- lambda x : score_single (my_strategy_factory , x , iterations ),
64
- other_strategies ))
65
- my_average_score = sum (my_scores ) / len (my_scores )
66
- return my_average_score
67
-
68
- def score_weights (weights , strategies , input_values = 17 , hidden_layer_size = 10 ):
43
+ def score_weights (weights , strategies , noise , input_values = 17 ,
44
+ hidden_layer_size = 10 ):
69
45
in2h , h2o , bias = split_weights (weights , input_values , hidden_layer_size )
70
- return (score_for (lambda : ANN (in2h , h2o , bias ), strategies ), weights )
71
-
72
- from itertools import repeat
73
-
74
- def score_all_weights (population , strategies ):
75
- results = pool .starmap (score_weights , zip (population , repeat (strategies )))
46
+ args = [in2h , h2o , bias ]
47
+ return (score_for (ANN , args = args , opponents = strategies , noise = noise ,
48
+ objective = objective_match_score ), weights )
49
+
50
+ def score_all_weights (population , strategies , noise , hidden_layer_size = 10 ):
51
+ results = pool .starmap (score_weights ,
52
+ zip (population , repeat (strategies ), repeat (noise ),
53
+ repeat (10 ), repeat (hidden_layer_size )))
76
54
return list (sorted (results , reverse = True ))
77
55
56
+ ## Evolutionary Algorithm
57
+
58
+ def crossover (weights_collection ):
59
+ copies = []
60
+ for i , w1 in enumerate (weights_collection ):
61
+ for j , w2 in enumerate (weights_collection ):
62
+ if i == j :
63
+ continue
64
+ crosspoint = random .randrange (len (w1 ))
65
+ new_weights = deepcopy (w1 [0 :crosspoint ]) + deepcopy (w2 [crosspoint :])
66
+ copies .append (new_weights )
67
+ return copies
68
+
69
+ def mutate (copies , mutation_rate ):
70
+ randoms = np .random .random ((len (copies ), 190 ))
71
+ for i , c in enumerate (copies ):
72
+ for j in range (len (c )):
73
+ if randoms [i ][j ] < mutation_rate :
74
+ r = 1 + random .uniform (- 1 , 1 ) * mutation_distance
75
+ c [j ] = c [j ] * r
76
+ return copies
77
+
78
78
def evolve (starting_weights , mutation_rate , mutation_distance , generations ,
79
- bottleneck , strategies , output_file ):
79
+ bottleneck , strategies , output_file , noise , hidden_layer_size = 10 ):
80
80
81
- # Append scores
81
+ # Append scores of 0 to starting parameters
82
82
current_bests = [[0 , x ] for x in starting_weights ]
83
83
84
- for generation in range (generations ):
85
- with open (output_file , "a" ) as output :
86
- weights_to_copy = [x [1 ] for x in current_bests ]
87
- copies = []
88
- # Crossover
89
- for w1 in weights_to_copy :
90
- for w2 in weights_to_copy :
91
- crossover = random .randrange (len (w1 ))
92
- new_weights = copy .deepcopy (
93
- w1 [0 :crossover ]) + copy .deepcopy (w2 [crossover :])
94
- copies .append (new_weights )
84
+ with open (output_file , 'w' ) as output :
85
+ writer = csv .writer (output )
95
86
87
+ for generation in range (generations ):
88
+ print ("Generation " + str (generation ))
89
+
90
+ weights_to_copy = [x [1 ] for x in current_bests ] + \
91
+ [get_random_weights (19 * hidden_layer_size ) for _ in
92
+ range (2 )]
93
+ # Crossover
94
+ copies = crossover (weights_to_copy )
96
95
# Mutate
97
- for _ , c in copies :
98
- for i in range (len (c )):
99
- if random .random () < mutation_rate :
100
- r = 1 + random .uniform (- 1 , 1 ) * mutation_distance
101
- c [i ] = c [i ] * r
96
+ copies = mutate (copies , mutation_rate )
102
97
103
98
population = copies + weights_to_copy
104
99
105
100
# map the population to get a list of (score, weights) tuples
106
101
# this list will be sorted by score, best weights first
107
- results = score_all_weights (population , strategies )
102
+ results = score_all_weights (population , strategies , noise = noise ,
103
+ hidden_layer_size = hidden_layer_size )
108
104
109
105
current_bests = results [0 : bottleneck ]
110
106
111
107
# get all the scores for this generation
112
- scores = [score for score , weights in results ]
108
+ scores = [score for ( score , weights ) in results ]
113
109
114
- for value in [generation , results [0 ][1 ], results [0 ][0 ],
115
- mean (scores ), pstdev (scores ), mutation_rate ,
116
- mutation_distance ]:
117
- output .write (str (value ) + "\t " )
118
- output .write ("\n " )
119
- print ("Generation" , generation , "| Best Score:" , scores [0 ])
120
- mutation_rate *= 0.99
121
- mutation_distance *= 0.99
110
+ # Write the data
111
+ row = [generation , mean (scores ), pstdev (scores ), mutation_rate ,
112
+ mutation_distance , results [0 ][0 ]]
113
+ row .extend (results [0 ][1 ])
114
+ writer .writerow (row )
115
+ output .flush ()
116
+ os .fsync (output .fileno ())
117
+
118
+ print ("Generation" , generation , "| Best Score:" , results [0 ][0 ])
122
119
123
120
return current_bests
124
121
125
122
126
123
if __name__ == '__main__' :
127
- arguments = docopt (__doc__ , version = 'ANN Evolver 0.1 ' )
124
+ arguments = docopt (__doc__ , version = 'ANN Evolver 0.2 ' )
128
125
# set up the process pool
129
126
pool = Pool (processes = int (arguments ['-i' ]))
130
127
# Vars for the genetic algorithm
@@ -134,13 +131,15 @@ def evolve(starting_weights, mutation_rate, mutation_distance, generations,
134
131
mutation_distance = float (arguments ['-d' ])
135
132
starting_population = int (arguments ['-k' ])
136
133
output_file = arguments ['-o' ]
134
+ noise = float (arguments ['-n' ])
137
135
138
- starting_weights = [[0 , get_random_weights (190 )] for _ in range (starting_population )]
139
-
140
- strategies = [s for s in axl .strategies
141
- if not s ().classifier ['long_run_time' ]]
136
+ hidden_layer_size = 10
137
+ size = 19 * hidden_layer_size
142
138
143
- evolve (starting_weights , mutation_rate , mutation_distance , generations ,
144
- bottleneck , strategies , output_file )
139
+ starting_weights = [get_random_weights (size ) for _ in range (starting_population )]
145
140
141
+ # strategies = axl.short_run_time_strategies
146
142
143
+ evolve (starting_weights , mutation_rate , mutation_distance , generations ,
144
+ bottleneck , strategies , output_file , noise ,
145
+ hidden_layer_size = hidden_layer_size )
0 commit comments