def unbuffer_data(self): # flatten buf = ''.join(self.rxd) while True: buf_len = len(buf) if buf_len < self.header.size: break payload_len = self.header.unpack_from(buf[:self.header.size])[0] if buf_len < payload_len + self.header.size: break # good, we have a message offset = self.header.size compressed_data = buf[offset:offset + payload_len] offset += payload_len data = zlib.decompress(compressed_data) msg = attrutil.json_to_attr(data) yield msg buf = buf[offset:] # compact self.rxd = [] if len(buf): self.rxd.append(buf)
def __init__(self, conf_filename): super().__init__() self.conf_filename = conf_filename if os.path.exists(conf_filename): conf = attrutil.json_to_attr(open(conf_filename).read()) assert isinstance(conf, confs.WorkerConfig) else: conf = default_conf() self.conf = conf print "CONF", attrutil.pprint(conf) self.save_our_config() self.register(msgs.Ping, self.on_ping) self.register(msgs.RequestConfig, self.on_request_config) self.register(msgs.ConfigureSelfPlay, self.on_configure) self.register(msgs.RequestSamples, self.on_request_samples) self.register(msgs.RequestNetworkTrain, self.on_train_request) self.nn = None self.sm = None self.game_info = None self.supervisor = None self.self_play_conf = None # will be created on demand self.trainer = None self.cmds_running = [] # connect to server reactor.callLater(0, self.connect)
def render_GET(self, request): summary_path = matches_path("summary.json") self.obj = attrutil.json_to_attr(open(summary_path).read()) x = self.obj x.queryMatches = x.queryMatches[:20] augment_header(request.responseHeaders) return json.dumps(attr.asdict(self.obj))
def render_GET(self, request): try: obj = attrutil.json_to_attr(open(self.match_path).read()) augment_header(request.responseHeaders) return json.dumps(attr.asdict(obj)) except Exception as exc: log.debug("ERROR %s" % exc) return ""
def get_summary(self, create=False): if create or not os.path.exists(self.summary_path): summary = datadesc.GenDataSummary(game=self.transformer.game, gen_prefix=self.gen_prefix, last_updated=timestamp(), total_samples=0) else: summary = attrutil.json_to_attr(open(self.summary_path).read()) return summary
def load_network(self, game, generation_name): json_str = open(self.generation_path(game, generation_name)).read() generation_descr = attrutil.json_to_attr(json_str) json_str = open(self.model_path(game, generation_name)).read() keras_model = keras_models.model_from_json(json_str) keras_model.load_weights(self.weights_path(game, generation_name)) transformer = self.get_transformer(game, generation_descr) return NeuralNetwork(transformer, keras_model, generation_descr)
def load_and_check_data(self): log.info("checking if generation data available") try: gen_samples = attrutil.json_to_attr( gzip.open(self.sample_data_filename).read()) log.info("data exists, with generation: %s, adding %s samples" % (gen_samples.with_generation, gen_samples.num_samples)) self.add_new_samples(gen_samples.samples, dedupe=False) except IOError as exc: log.info("Not such file for generation: %s" % exc)
def __init__(self, conf_filename, conf=None): Broker.__init__(self) self.conf_filename = conf_filename if conf is None: assert os.path.exists(conf_filename) conf = attrutil.json_to_attr(open(conf_filename).read()) assert isinstance(conf, confs.ServerConfig) attrutil.pprint(conf) self.conf = conf self.game_info = lookup.by_name(self.conf.game) self.workers = {} self.free_players = [] self.the_nn_trainer = None self.accumulated_samples = [] self.unique_states_set = set() self.unique_states = [] # when a generation object is around, we are in the processing of training self.pending_gen_samples = None self.training_in_progress = False # register messages: self.register(msgs.Pong, self.on_pong) self.register(msgs.Ok, self.on_ok) self.register(msgs.WorkerConfigMsg, self.on_worker_config) self.register(msgs.RequestSampleResponse, self.on_sample_response) self.check_files_exist() self.load_and_check_data() self.create_self_play_config() self.save_our_config() # finally start listening on port reactor.listenTCP(conf.port, ServerFactory(self)) # save the samples periodically self.checkpoint_cb = reactor.callLater(self.conf.checkpoint_interval, self.checkpoint)
def __init__(self, conf_filename, cb_fn=None): super().__init__() self.conf_filename = conf_filename self.cb_fn = cb_fn if os.path.exists(conf_filename): conf = attrutil.json_to_attr(open(conf_filename).read()) assert isinstance(conf, confs.WorkerConfig) else: conf = default_conf() self.conf = conf print "CONF", attrutil.pprint(conf) self.save_our_config() self.register(msgs.Ping, self.on_ping) self.register(msgs.RequestConfig, self.on_request_config) self.register(msgs.ConfigureSelfPlay, self.on_configure) self.register(msgs.RequestSamples, self.on_request_samples) self.register(msgs.RequestNetworkTrain, self.on_train_request) self.nn = None self.sm = None self.game_info = None self.supervisor = None self.self_play_conf = None # will be created on demand self.trainer = None self.cmds_running = [] if False and self.conf.callback: fn_py_path = self.conf.callback mod_name, func_name = fn_py_path.rsplit('.', 1) mod = importlib.import_module(mod_name) func = getattr(mod, func_name) func() # connect to server reactor.callLater(0, self.connect)
def files_to_sample_data(self, conf): man = get_manager() assert isinstance(conf, confs.TrainNNConfig) step = conf.next_step - 1 starting_step = conf.starting_step if starting_step < 0: starting_step = max(step + starting_step, 0) while step >= starting_step: store_path = man.samples_path(conf.game, conf.generation_prefix) fn = os.path.join(store_path, "gendata_%s_%s.json.gz" % (conf.game, step)) if fn not in self.sample_data_cache: raw_data = attrutil.json_to_attr(gzip.open(fn).read()) data = SamplesData(raw_data.game, raw_data.with_generation, raw_data.num_samples) total_draws = 0 for s in raw_data.samples: if abs(s.final_score[0] - 0.5) < 0.01: total_draws += 1 draws_ratio = total_draws / float(len(raw_data.samples)) log.info("Draws ratio %.2f" % draws_ratio) for s in raw_data.samples: data.add_sample(s) if len(data.samples) != data.num_samples: # pretty inconsequential, but we should at least notify msg = "num_samples (%d) versus actual samples (%s) differ... trimming" log.warning(msg % (data.num_samples, len(data.samples))) data.num_samples = min(len(data.samples), data.num_samples) data.samples = data.samples[:data.num_samples] self.sample_data_cache[fn] = data yield fn, self.sample_data_cache[fn] step -= 1
def test_attrs_recursive(): print 'test_attrs_recursive.1' c = Container(DummyMsg('a'), DummyMsg('b'), DummyMsg('c')) m = Container(DummyMsg('o'), DummyMsg('p'), DummyMsg(c)) d = attrutil.asdict_plus(m) pprint(d) r = attrutil.fromdict_plus(d) assert isinstance(r, Container) assert r.x.what == 'o' assert r.z.what.x.what == 'a' json_str = attrutil.attr_to_json(m, indent=4) print json_str k = attrutil.json_to_attr(json_str) assert k.x.what == 'o' assert k.z.what.x.what == 'a'
def update_summaries(game, match_info): # generate a TiltyardMatchSummary summary = TiltyardMatchSummary() for k in ("randomToken playerNamesFromHost scrambled startTime " "playClock tournamentNameFromHost startClock matchId gameMetaURL " "isAborted isCompleted goalValues".split()): setattr(summary, k, getattr(match_info, k)) summary.matchURL = "http://simulated.tech:8800/%s/%s" % (game, match_info.randomToken) try: the_summaries = attrutil.json_to_attr(open(summary_path()).read()) except: the_summaries = MatchSummaries() # find the summary: for idx, s in enumerate(the_summaries.queryMatches): if s.randomToken == summary.randomToken: the_summaries.queryMatches[idx] = summary return the_summaries # add to front the_summaries.queryMatches = [summary] + the_summaries.queryMatches return the_summaries
def sync(self): # check summary matches current set of files if not self.check_summary() or not self.verify_db(): self.get_summary(create=True) self.create_db() for step, file_path, md5sum in self.files_to_process(): # lets delete any spurious memory gc.collect() log.debug("Processing %s" % file_path) data = attrutil.json_to_attr(gzip.open(file_path).read()) if len(data.samples) != data.num_samples: # pretty inconsequential, but we should at least notify msg = "num_samples (%d) versus actual samples (%s) differ... trimming" log.warning(msg % (data.num_samples, len(data.samples))) data.num_samples = min(len(data.samples), data.num_samples) data.samples = data.samples[:data.num_samples] log.debug("Game %s, with gen: %s and sample count %s" % (data.game, data.with_generation, data.num_samples)) indx = self.db.size stats = StatsAccumulator() t = self.transformer # ZZZ really slow # ZZZ profile/gather times in loop... (guessing the time is in decoding state) time_check = 0 time_stats = 0 time_decode = 0 time_decode_prevs = 0 time_channels = 0 time_outputs = 0 time_db_resize = 0 time_db_insert = 0 cur_size = indx for sample in self.augment_data(data.samples): et = ElaspedTime() #t.check_sample(sample) time_check += et.update() stats.add(sample) time_stats += et.update() # add channels # only decode if not already decoded (as in the case of augmentation) state = fast_decode_state(sample.state) time_decode += et.update() prev_states = [ fast_decode_state(s) for s in sample.prev_states ] time_decode_prevs += et.update() cols = [t.state_to_channels(state, prev_states)] time_channels += et.update() for ri, policy in enumerate(sample.policies): cols.append(t.policy_to_array(policy, ri)) time_outputs += et.update() cols.append(t.value_to_array(sample.final_score)) # is this an efficient way to do things? if indx >= cur_size: cur_size += 20 self.db.resize(cur_size) time_db_resize += et.update() for ii, name in enumerate(self.db.names): self.db[name][indx] = cols[ii] indx += 1 time_db_insert += et.update() print "time_check: %.2f" % time_check print "time_stats: %.2f" % time_stats print "time_decode: %.2f" % time_decode print "time_decode_prevs: %.2f" % time_decode_prevs print "time_channels: %.2f" % time_channels print "time_outputs: %.2f" % time_outputs print "time_db_resize: %.2f" % time_db_resize print "time_db_insert: %.2f" % time_db_insert if indx != cur_size: cur_size = indx self.db.resize(indx) self.db.flush() log.debug("Added %d samples to db" % stats.num_samples) # add to the summary and save it step_sum = datadesc.StepSummary( step=step, filename=file_path, with_generation=data.with_generation, num_samples=stats.num_samples, md5sum=md5sum, stats_unique_matches=stats.unique_matches, stats_draw_ratio=stats.draw_ratio, stats_bare_policies_ratio=stats.bare_policies_ratio, stats_av_starting_depth=stats.av_starting_depth, stats_av_ending_depth=stats.av_ending_depth, stats_av_resigns=stats.av_resigns, stats_av_resign_false_positive=stats.av_resign_false_positive, stats_av_puct_visits=stats.av_puct_visits, stats_ratio_of_roles=stats.ratio_of_roles, stats_av_final_scores=stats.av_final_scores, stats_av_puct_score_dist=stats.av_puct_score_dist) print attrutil.attr_to_json(step_sum, pretty=True) self.summary.last_updated = timestamp() self.summary.total_samples = self.db.size self.summary.step_summaries.append(step_sum) self.save_summary_file() log.debug("Saved summary file") # lets delete any spurious memory gc.collect() self.save_summary_file() log.info("Data cache synced, saved summary file.")
def gen_elo(match_info, all_players, filename, move_generator=None, verbose=False): if os.path.exists(filename): ratings = at.json_to_attr(open(filename).read()) else: ratings = AllRatings(match_info.name) # only add random if in all_players if "random" in all_players: ratings.players.append(PlayerRating("random", fixed=True, elo=500.0)) # add in all the players # slow add one playeer slow_add_count = 0 for p in all_players: if verbose: print "Adding", p.get_name() playerinfo = None for info in ratings.players: if info.name == p.get_name(): playerinfo = info if playerinfo is None: if slow_add_count >= MAX_ADD_COUNT: if verbose: print "SKIPPING for now", playerinfo continue else: playerinfo = PlayerRating(p.get_name(), 0, STARTING_ELO) ratings.players.append(playerinfo) p.rating = playerinfo if playerinfo.played < 20: slow_add_count += 1 # check no leftover ratings for players for rated_player in ratings.players: found = False for p in all_players: if rated_player.name == p.get_name(): assert not found, "bad config %s" % rated_player.name found = True if not found: log.warning("Dangling rating in elo file: %s" % rated_player.name) # update the ratings with players elo_dump_and_save(filename, ratings) for i in range(NUM_GAMES): players = choose_players(all_players) if players is None: break player0, player1 = players moves = None if move_generator: moves = move_generator() # play the game try: res = match_info.play(players, MOVE_TIME, moves=moves, resign_score=RESIGN_PCT, verbose=True) (_, score0), (_, score1) = res[1] res_str = "" k = INITIAL_K if score0 == 100: res_str = "1st player wins" player0_wins = True elif score1 == 100: res_str = "2nd player wins" player0_wins = False else: res_str = "Draws" k /= 2.0 # fake a win for player with lower elo player0_wins = player0.rating.elo < player1.rating.elo res_str = "%s: %s (%.1f) / %s (%.1f) " % (res_str, player0.get_name(), player0.rating.elo, player1.get_name(), player1.rating.elo) print res_str ratings.log.append(res_str) except MatchTooLong as exc: err = 'MatchTooLong, %s v %s' % (player0, player1) ratings.log.append(err) print "match aborted", exc continue except Exception as exc: print "match aborted", str(exc) raise def getk(r, o): if r.fixed: return 0.0 if r.played < 10: return k * 2 if r.played < 20: scale = 1.0 + (20 - r.played) / 2.0 return k * scale if r.played < 40: return k if r.played < 60: return k / 2.0 if r.played > 60: kx = k / 2.5 else: kx = k # extra penalty if o not established if o.played < 10: kx /= 10.0 elif o.played < 20: kx /= 3.0 elif o.played < 40: kx /= 2.0 return kx player0.rating.played += 1 player1.rating.played += 1 (player0.rating.elo, player1.rating.elo) = next_elo_rating(player0.rating.elo, player1.rating.elo, getk(player0.rating, player1.rating), getk(player1.rating, player0.rating), player0_wins) elo_dump_and_save(filename, ratings) # check if there are any LG games waiting, and finish up if so if check_lg(): break
def main(genname_mapping, filename, gen_modifier=None, ignore_non_models=False, check_evals=800): ratings = at.json_to_attr(open(filename).read()) genmodel_to_data = {} # side effect of setting the size of graph plt.figure(figsize=(18, 14)) def get(name): if name not in genmodel_to_data: genmodel_to_data[name] = ([], []) return genmodel_to_data[name] for p in ratings.players: if "_" in p.name: if gen_modifier is not None: gen = gen_modifier(p.name) else: gen = int(p.name.split('_')[-1]) was_evals = False for genname in genname_mapping: if genname in p.name: datapoints = get(genname) datapoints[0].append(gen) datapoints[1].append(p.elo) if check_evals is not None: was_evals = str(check_evals) in p.name else: was_evals = True break else: print "UNHANDLED", p.name continue txt = "* " if not was_evals else "" if p.played < Runner._elo_min: txt += " %s" % p.played if txt: plt.text(gen, p.elo, txt) else: if not ignore_non_models: plt.plot(-10, [p.elo], "bx") txt = " " + p.name if p.played < Runner._elo_min: txt += " %s" % p.played plt.text(-10, p.elo, txt) for name, color in genname_mapping.items(): datapoints = get(name) if len(datapoints[0]): plt.plot(datapoints[0], datapoints[1], color, label=name) plt.ylabel("ELO") plt.xlabel("Generation") plt.legend(loc='lower right') plt.show()
def sync(self): # check summary matches current set of files if not self.check_summary() or not self.verify_db(): self.get_summary(create=True) self.create_db() for step, file_path, md5sum in self.files_to_process(): # lets delete any spurious memory gc.collect() log.debug("Processing %s" % file_path) data = attrutil.json_to_attr(gzip.open(file_path).read()) if len(data.samples) != data.num_samples: # pretty inconsequential, but we should at least notify msg = "num_samples (%d) versus actual samples (%s) differ... trimming" log.warning(msg % (data.num_samples, len(data.samples))) data.num_samples = min(len(data.samples), data.num_samples) data.samples = data.samples[:data.num_samples] log.debug("Game %s, with gen: %s and sample count %s" % (data.game, data.with_generation, data.num_samples)) indx = self.db.size stats = StatsAccumulator() t = self.transformer # ZZZ really slow # ZZZ profile/gather times in loop... (guessing the time is in decoding state) time_check = 0 time_stats = 0 time_decode = 0 time_decode_prevs = 0 time_channels = 0 time_outputs = 0 time_db_resize = 0 time_db_insert = 0 cur_size = indx for sample in self.augment_data(data.samples): # ensure that final scores are clamped before adding to db sample.final_score = [min(1.0, v) for v in sample.final_score] sample.final_score = [max(0.0, v) for v in sample.final_score] sample_is_draw = False if abs(sample.final_score[0] - 0.5) < 0.01: assert abs(sample.final_score[1] - 0.5) < 0.01 sample_is_draw = True # XXX highly experimental if sample_is_draw and self.score_draw_as_random_hack: # the idea is just to randomly asign a win or loss to train on. Then the # network can average out over a 'bazillion' draw samples and determine that # the value should be 0.5. In theory. XXX Who knows? if random.random() > 0.5: sample.final_score = [1.0, 0] else: sample.final_score = [0, 1.0] et = ElaspedTime() # XXX too slow, and only useful for debugging serious bugs - disable # t.check_sample(sample) time_check += et.update() stats.add(sample, was_draw=sample_is_draw) time_stats += et.update() # add channels # only decode if not already decoded (as in the case of augmentation) state = fast_decode_state(sample.state) time_decode += et.update() prev_states = [fast_decode_state(s) for s in sample.prev_states] time_decode_prevs += et.update() cols = [t.state_to_channels(state, prev_states)] time_channels += et.update() for ri, policy in enumerate(sample.policies): cols.append(t.policy_to_array(policy, ri)) time_outputs += et.update() cols.append(t.value_to_array(sample.final_score)) # is this an efficient way to do things? if indx >= cur_size: cur_size += 20 self.db.resize(cur_size) time_db_resize += et.update() for ii, name in enumerate(self.db.names): self.db[name][indx] = cols[ii] indx += 1 time_db_insert += et.update() print "time_check: %.2f" % time_check print "time_stats: %.2f" % time_stats print "time_decode: %.2f" % time_decode print "time_decode_prevs: %.2f" % time_decode_prevs print "time_channels: %.2f" % time_channels print "time_outputs: %.2f" % time_outputs print "time_db_resize: %.2f" % time_db_resize print "time_db_insert: %.2f" % time_db_insert if indx != cur_size: cur_size = indx self.db.resize(indx) self.db.flush() log.debug("Added %d samples to db" % stats.num_samples) # add to the summary and save it step_sum = datadesc.StepSummary(step=step, filename=file_path, with_generation=data.with_generation, num_samples=stats.num_samples, md5sum=md5sum, stats_unique_matches=stats.unique_matches, stats_draw_ratio=stats.draw_ratio, stats_bare_policies_ratio=stats.bare_policies_ratio, stats_av_starting_depth=stats.av_starting_depth, stats_av_ending_depth=stats.av_ending_depth, stats_av_resigns=stats.av_resigns, stats_av_resign_false_positive=stats.av_resign_false_positive, stats_av_puct_visits=stats.av_puct_visits, stats_ratio_of_roles=stats.ratio_of_roles, stats_av_final_scores=stats.av_final_scores, stats_av_puct_score_dist=stats.av_puct_score_dist) print attrutil.attr_to_json(step_sum, pretty=True) self.summary.last_updated = timestamp() self.summary.total_samples = self.db.size self.summary.step_summaries.append(step_sum) self.save_summary_file() log.debug("Saved summary file") # lets delete any spurious memory gc.collect() self.save_summary_file() log.info("Data cache synced, saved summary file.")
setup_once() from ggpzero.util.keras import init init() import tensorflow as tf os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' tf.logging.set_verbosity(tf.logging.ERROR) if __name__ == "__main__": ''' to create a template config file, use -c config_filename. otherwise to run, provide the config_filename ''' setup() conf_filename = sys.argv[1] if os.path.exists(conf_filename): config = at.json_to_attr(open(conf_filename).read()) else: print "Creating config" config = template_config() # save it - pick up new features with open(conf_filename, "w") as f: contents = at.attr_to_json(config, pretty=True) f.write(contents) lg = LittleGolemConnection(config) lg.loop_forever()