def rate_all(): games = Game.query.all() users = sanitized_users(games) g_vec = sanitized_games(games) ratings = {u.id: u.last_rating() for u in users} rating_prior = {u: v.rating if (v and v.rating) else 0 for u,v in ratings.items()} neighbors = rm.neighbors(games) neighbor_avgs = rm.compute_avgs(games, rating_prior) t_min = min(games, key=lambda g: g.date_played or datetime.datetime.now()).date_played t_max = max(games, key=lambda g: g.date_played or datetime.datetime.now()).date_played iters = 100 lam = .37 # Weight for controlling 'pull' of neighborhood weighted average. Higher = stays in the neighborhood. lrn = lambda i: ((1. + .1*iters)/(i + .1 * iters))**.6 #Control the learning rate over time. for i in range(iters): loss = 0 # Accumulate the neighborhood loss prior to changing the ratings around for id, neighbor_wgt in neighbor_avgs.items(): loss += lam * ((rating_prior[id] - neighbor_wgt) ** 2) # Shuffle the vector of result-tuples random.shuffle(g_vec) for g in g_vec: w, b, actual, t, handi, komi = g odds = rm.expect(rating_prior[b], rating_prior[w], handi, komi) weight = rm.time_weight(t, t_min, t_max) rating_prior[w] -= lrn(i) * (weight*(odds - actual)*odds*(1-odds) + (lam/len(neighbors[w]) * (rating_prior[w] - neighbor_avgs[w]))) rating_prior[b] -= lrn(i) * (-1.0 * weight*(odds - actual)*odds*(1-odds) + (lam/len(neighbors[b]) * (rating_prior[b] - neighbor_avgs[b]))) loss += weight * ((odds - actual) ** 2) # Scale the ratings r_min = min(rating_prior.values()) r_max = max(rating_prior.values()) if r_max != r_min: for k,v in rating_prior.items(): rating_prior[k] = (rating_prior[k] - r_min) / (r_max - r_min) * 40.0 neighbor_avgs = rm.compute_avgs(games, rating_prior) print('%d : %.4f' % (i, loss)) # Update the ratings and show how we did. wins, losses = {}, {} for g in g_vec: wins[g[0]] = wins.get(g[0], 0) + g[2] losses[g[0]] = losses.get(g[0], 0) + 1-g[2] wins[g[1]] = wins.get(g[1], 0) + 1-g[2] losses[g[1]] = losses.get(g[1], 0) + g[2] for k in sorted(rating_prior, key=lambda k: rating_prior[k]): db.session.add(Rating(user_id=k, rating=rating_prior[k])) print("%d: %f (%d - %d)" % (k,rating_prior[k], wins.get(k,0), losses.get(k,0)) ) db.session.commit()
def test_compute_avgs(self): ratings = {1:2, 2:4, 3:2, 4:3} games = [Game(Player(1), Player(2), 'W+R', date(2000,1,1)), Game(Player(1), Player(3), 'B+R', date(2000,1,1)), Game(Player(1), Player(4), 'W+R', date(2000,1,1)), Game(Player(3), Player(2), 'B+R', date(2000,1,1))] averages = rm.compute_avgs(games, ratings) self.assertEqual(averages, {1: 3.0, 2: 2.0, 3: 3.0, 4: 2.0})
def test_compute_avgs(self): ratings = {1: 2, 2: 4, 3: 2, 4: 3} games = [ Game(Player(1), Player(2), 'W+R', date(2000, 1, 1)), Game(Player(1), Player(3), 'B+R', date(2000, 1, 1)), Game(Player(1), Player(4), 'W+R', date(2000, 1, 1)), Game(Player(3), Player(2), 'B+R', date(2000, 1, 1)) ] averages = rm.compute_avgs(games, ratings) self.assertEqual(averages, {1: 3.0, 2: 2.0, 3: 3.0, 4: 2.0})
def test_compute_avgs(self): ratings = {1:2, 2:4, 3:2, 4:3, 5:2} games = [(1, 2, '1', date(2000,1,1).timestamp(), 0, 7), (1, 3, '0', date(2000,1,2).timestamp(), 0, 7), (1, 4, '1', date(2000,1,3).timestamp(), 0, 7), (2, 5, '1', date(2000,1,3).timestamp(), 0, 7), (3, 2, '0', date(2000,1,4).timestamp(), 0, 7)] averages = rm.compute_avgs(games, ratings) # 2 & 4 both only played people with the same rating, # so they should have the same weighted average opponent rating self.assertEqual(averages[2], averages[4]) # 1 faced lower average opposition than 3 did, so 3's average opponent rating should be higher. self.assertLess(averages[1], averages[3]) # a clear strongest player, two equal players ratings = {1:10, 2:3, 3:3} games = [(1, 2, '1', date(2000,1,1).timestamp(), 0, 7), (1, 3, '0', date(2000,1,3).timestamp(), 0, 7), (2, 3, '1', date(2000,1,5).timestamp(), 0, 7)] averages = rm.compute_avgs(games, ratings) #the player who faced the strongest player most recently should have the higher average self.assertLess(averages[2], averages[3]) # all the same strength, but 2 gave 3 a 3-handicap game. ratings = {1:3, 2:3, 3:3} games = [(1, 2, '1', date(2000,1,1).timestamp(), 0, 7), (1, 3, '0', date(2000,1,1).timestamp(), 0, 7), (2, 3, '1', date(2000,1,1).timestamp(), 3, 0)] averages = rm.compute_avgs(games, ratings) # The player who gave stones should have a higher average self.assertGreater(averages[2], averages[3]) # the player who received them should have a lower one self.assertGreater(averages[1], averages[3])
def test_compute_avgs(self): ratings = {1: 2, 2: 4, 3: 2, 4: 3, 5: 2} games = [(1, 2, '1', date(2000, 1, 1).timestamp(), 0, 7), (1, 3, '0', date(2000, 1, 2).timestamp(), 0, 7), (1, 4, '1', date(2000, 1, 3).timestamp(), 0, 7), (2, 5, '1', date(2000, 1, 3).timestamp(), 0, 7), (3, 2, '0', date(2000, 1, 4).timestamp(), 0, 7)] averages = rm.compute_avgs(games, ratings) # 2 & 4 both only played people with the same rating, # so they should have the same weighted average opponent rating self.assertEqual(averages[2], averages[4]) # 1 faced lower average opposition than 3 did, so 3's average opponent rating should be higher. self.assertLess(averages[1], averages[3]) # a clear strongest player, two equal players ratings = {1: 10, 2: 3, 3: 3} games = [(1, 2, '1', date(2000, 1, 1).timestamp(), 0, 7), (1, 3, '0', date(2000, 1, 3).timestamp(), 0, 7), (2, 3, '1', date(2000, 1, 5).timestamp(), 0, 7)] averages = rm.compute_avgs(games, ratings) #the player who faced the strongest player most recently should have the higher average self.assertLess(averages[2], averages[3]) # all the same strength, but 2 gave 3 a 3-handicap game. ratings = {1: 3, 2: 3, 3: 3} games = [(1, 2, '1', date(2000, 1, 1).timestamp(), 0, 7), (1, 3, '0', date(2000, 1, 1).timestamp(), 0, 7), (2, 3, '1', date(2000, 1, 1).timestamp(), 3, 0)] averages = rm.compute_avgs(games, ratings) # The player who gave stones should have a higher average self.assertGreater(averages[2], averages[3]) # the player who received them should have a lower one self.assertGreater(averages[1], averages[3])
def rate_all(): games = Game.query.all() users = sanitized_users(games) g_vec = sanitized_games(games) ratings = {u.id: u.last_rating() for u in users} rating_prior = { u: v.rating if (v and v.rating) else 0 for u, v in ratings.items() } neighbors = rm.neighbors(games) neighbor_avgs = rm.compute_avgs(games, rating_prior) t_min = min( games, key=lambda g: g.date_played or datetime.datetime.now()).date_played t_max = max( games, key=lambda g: g.date_played or datetime.datetime.now()).date_played iters = 100 lam = .37 # Weight for controlling 'pull' of neighborhood weighted average. Higher = stays in the neighborhood. lrn = lambda i: ( (1. + .1 * iters) / (i + .1 * iters))**.6 #Control the learning rate over time. for i in range(iters): loss = 0 # Accumulate the neighborhood loss prior to changing the ratings around for id, neighbor_wgt in neighbor_avgs.items(): loss += lam * ((rating_prior[id] - neighbor_wgt)**2) # Shuffle the vector of result-tuples random.shuffle(g_vec) for g in g_vec: w, b, actual, t, handi, komi = g odds = rm.expect(rating_prior[b], rating_prior[w], handi, komi) weight = rm.time_weight(t, t_min, t_max) rating_prior[w] -= lrn(i) * ( weight * (odds - actual) * odds * (1 - odds) + (lam / len(neighbors[w]) * (rating_prior[w] - neighbor_avgs[w]))) rating_prior[b] -= lrn(i) * ( -1.0 * weight * (odds - actual) * odds * (1 - odds) + (lam / len(neighbors[b]) * (rating_prior[b] - neighbor_avgs[b]))) loss += weight * ((odds - actual)**2) # Scale the ratings r_min = min(rating_prior.values()) r_max = max(rating_prior.values()) if r_max != r_min: for k, v in rating_prior.items(): rating_prior[k] = (rating_prior[k] - r_min) / (r_max - r_min) * 40.0 neighbor_avgs = rm.compute_avgs(games, rating_prior) print('%d : %.4f' % (i, loss)) # Update the ratings and show how we did. wins, losses = {}, {} for g in g_vec: wins[g[0]] = wins.get(g[0], 0) + g[2] losses[g[0]] = losses.get(g[0], 0) + 1 - g[2] wins[g[1]] = wins.get(g[1], 0) + 1 - g[2] losses[g[1]] = losses.get(g[1], 0) + g[2] for k in sorted(rating_prior, key=lambda k: rating_prior[k]): db.session.add(Rating(user_id=k, rating=rating_prior[k])) print("%d: %f (%d - %d)" % (k, rating_prior[k], wins.get(k, 0), losses.get(k, 0))) db.session.commit()
def rate_all(t_from=datetime.datetime.utcfromtimestamp(1.0), t_to=datetime.datetime.now(), iters=200, lam=.22): """ t_from -- datetime obj, rate all games after this t_to -- datetime obj, rate all games up to this iters -- number of iterations lam -- 'neighborhood pull' parameter. higher = more error from moving a rank away from ranks in its neighborhood """ games = Game.query.filter(Game.date_played < t_to, Game.date_played > t_from) g_vec = sanitized_games(games) users = sanitized_users(g_vec) print("found %d users with %d valid games" % (len(users), len(g_vec)) ) aga_ids_to_uids = dict([(int(u.aga_id), u.id) for u in users]) ratings = {int(u.aga_id): u.last_rating() for u in users} rating_prior = {id: v.rating if (v and v.rating) else 20 for id,v in ratings.items()} print ("%d users with no priors" % len(list(filter(lambda v: v == 20, rating_prior.values())))) neighbors = rm.neighbors(g_vec) neighbor_avgs = rm.compute_avgs(g_vec, rating_prior) t_min = min([g[3] for g in g_vec]) t_max = max([g[3] for g in g_vec]) lrn = lambda i: ((1. + .1*iters)/(i + .1 * iters))**.3 #Control the learning rate over time. for i in range(iters): loss = 0 # Accumulate the neighborhood loss prior to changing the ratings around for id, neighbor_wgt in neighbor_avgs.items(): loss += lam * ((rating_prior[id] - neighbor_wgt) ** 2) # Shuffle the vector of result-tuples and step through them, accumulating error. random.shuffle(g_vec) for g in g_vec: w, b, actual, t, handi, komi = g odds = rm.expect(rating_prior[b], rating_prior[w], handi, komi) weight = rm.time_weight(t, t_min, t_max) rating_prior[w] -= lrn(i) * (weight*(odds - actual)*odds*(1-odds) + (lam/len(neighbors[w]) * (rating_prior[w] - neighbor_avgs[w]))) rating_prior[b] -= lrn(i) * (-1.0 * weight*(odds - actual)*odds*(1-odds) + (lam/len(neighbors[b]) * (rating_prior[b] - neighbor_avgs[b]))) loss += weight * ((odds - actual) ** 2) # Scale the ratings r_min = min(rating_prior.values()) r_max = max(rating_prior.values()) if r_max != r_min: for k,v in rating_prior.items(): rating_prior[k] = (rating_prior[k] - r_min) / (r_max - r_min) * 40.0 #update neighborhood averages? neighbor_avgs = rm.compute_avgs(g_vec, rating_prior) if (i % 50 == 0): print('%d : %.4f' % (i, loss)) # Update the ratings and show how we did. wins, losses = {}, {} for g in g_vec: wins[g[0]] = wins.get(g[0], 0) + g[2] losses[g[0]] = losses.get(g[0], 0) + 1-g[2] wins[g[1]] = wins.get(g[1], 0) + 1-g[2] losses[g[1]] = losses.get(g[1], 0) + g[2] for k in sorted(rating_prior, key=lambda k: rating_prior[k])[-10:]: print("%d (uid: %d): %f (%d - %d)" % (k, aga_ids_to_uids[k], rating_prior[k], wins.get(k,0), losses.get(k,0)) ) for k in sorted(rating_prior, key=lambda k: rating_prior[k]): db.session.add(Rating(user_id=aga_ids_to_uids[k], rating=rating_prior[k], created=t_to)) db.session.commit()
def rate_all(t_from=datetime.datetime.utcfromtimestamp(1.0), t_to=datetime.datetime.now(), iters=200, lam=.22): """ t_from -- datetime obj, rate all games after this t_to -- datetime obj, rate all games up to this iters -- number of iterations lam -- 'neighborhood pull' parameter. higher = more error from moving a rank away from ranks in its neighborhood """ games = Game.query.filter(Game.date_played < t_to, Game.date_played > t_from) g_vec = sanitized_games(games) users = sanitized_users(g_vec) print("found %d users with %d valid games" % (len(users), len(g_vec))) aga_ids_to_uids = dict([(int(u.aga_id), u.id) for u in users]) ratings = {int(u.aga_id): u.last_rating() for u in users} rating_prior = { id: v.rating if (v and v.rating) else 20 for id, v in ratings.items() } print("%d users with no priors" % len(list(filter(lambda v: v == 20, rating_prior.values())))) neighbors = rm.neighbors(g_vec) neighbor_avgs = rm.compute_avgs(g_vec, rating_prior) t_min = min([g[3] for g in g_vec]) t_max = max([g[3] for g in g_vec]) lrn = lambda i: ( (1. + .1 * iters) / (i + .1 * iters))**.3 #Control the learning rate over time. for i in range(iters): loss = 0 # Accumulate the neighborhood loss prior to changing the ratings around for id, neighbor_wgt in neighbor_avgs.items(): loss += lam * ((rating_prior[id] - neighbor_wgt)**2) # Shuffle the vector of result-tuples and step through them, accumulating error. random.shuffle(g_vec) for g in g_vec: w, b, actual, t, handi, komi = g odds = rm.expect(rating_prior[b], rating_prior[w], handi, komi) weight = rm.time_weight(t, t_min, t_max) rating_prior[w] -= lrn(i) * ( weight * (odds - actual) * odds * (1 - odds) + (lam / len(neighbors[w]) * (rating_prior[w] - neighbor_avgs[w]))) rating_prior[b] -= lrn(i) * ( -1.0 * weight * (odds - actual) * odds * (1 - odds) + (lam / len(neighbors[b]) * (rating_prior[b] - neighbor_avgs[b]))) loss += weight * ((odds - actual)**2) # Scale the ratings r_min = min(rating_prior.values()) r_max = max(rating_prior.values()) if r_max != r_min: for k, v in rating_prior.items(): rating_prior[k] = (rating_prior[k] - r_min) / (r_max - r_min) * 40.0 #update neighborhood averages? neighbor_avgs = rm.compute_avgs(g_vec, rating_prior) if (i % 50 == 0): print('%d : %.4f' % (i, loss)) # Update the ratings and show how we did. wins, losses = {}, {} for g in g_vec: wins[g[0]] = wins.get(g[0], 0) + g[2] losses[g[0]] = losses.get(g[0], 0) + 1 - g[2] wins[g[1]] = wins.get(g[1], 0) + 1 - g[2] losses[g[1]] = losses.get(g[1], 0) + g[2] for k in sorted(rating_prior, key=lambda k: rating_prior[k])[-10:]: print("%d (uid: %d): %f (%d - %d)" % (k, aga_ids_to_uids[k], rating_prior[k], wins.get( k, 0), losses.get(k, 0))) for k in sorted(rating_prior, key=lambda k: rating_prior[k]): db.session.add( Rating(user_id=aga_ids_to_uids[k], rating=rating_prior[k], created=t_to)) db.session.commit()