ChessAggro / benchmark.py
TobiasLogic's picture
Upload folder using huggingface_hub
24a2b5c verified
Raw
History Blame Contribute Delete
3.69 kB
import math
import random
import sys
import chess
import chess.pgn
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
def clean_san(s):
return s.replace("+", "").replace("#", "")
class Model:
def __init__(self, path, device, temp=1.0):
self.tok = AutoTokenizer.from_pretrained(path)
self.model = AutoModelForCausalLM.from_pretrained(path).to(device).eval()
self.device = device
self.temp = temp
self.name = path.rstrip("/").split("/")[-1]
self.prompt_ids = self.tok("1.", return_tensors="pt").input_ids.to(device)
@torch.no_grad()
def pick(self, board):
legal = list(board.legal_moves)
seqs = [tuple(self.tok(clean_san(board.san(m)) + " ",
add_special_tokens=False).input_ids) for m in legal]
active = list(range(len(seqs)))
step = 0
cur, past = self.prompt_ids, None
while True:
out = self.model(cur, past_key_values=past, use_cache=True)
past = out.past_key_values
logits = out.logits[0, -1]
allowed = {}
for i in active:
if step < len(seqs[i]):
allowed.setdefault(seqs[i][step], []).append(i)
if not allowed:
return legal[active[0]]
toks = list(allowed.keys())
probs = torch.softmax(logits[torch.tensor(toks, device=self.device)] / self.temp, 0)
choice = toks[torch.multinomial(probs, 1).item()]
active = allowed[choice]
step += 1
done = [i for i in active if len(seqs[i]) == step]
if done:
return legal[done[0]]
cur = torch.tensor([[choice]], device=self.device)
class Rand:
name = "random"
def pick(self, board):
return random.choice(list(board.legal_moves))
def make(p, d):
return Rand() if p == "random" else Model(p, d)
def main():
a, b, n = sys.argv[1], sys.argv[2], int(sys.argv[3])
pgn_out = sys.argv[4] if len(sys.argv) > 4 else None
d = "cuda" if torch.cuda.is_available() else "cpu"
A, B = make(a, d), make(b, d)
print("blind arena: %s vs %s, %d games" % (A.name, B.name, n), flush=True)
w = l = dr = 0
pgns = []
for g in range(n):
board = chess.Board()
a_white = g % 2 == 0
white, black = (A, B) if a_white else (B, A)
plies = 0
while not board.is_game_over(claim_draw=True) and plies < 250:
board.push((white if board.turn == chess.WHITE else black).pick(board))
plies += 1
res = board.result(claim_draw=True)
apt = 0.5 if res == "1/2-1/2" or res == "*" else (
1.0 if (res == "1-0") == a_white else 0.0)
w += apt == 1.0
l += apt == 0.0
dr += apt == 0.5
if pgn_out is not None:
game = chess.pgn.Game.from_board(board)
game.headers.update(White=A.name if a_white else B.name,
Black=B.name if a_white else A.name, Result=res)
pgns.append(str(game))
print("game %d/%d: %s [%s W%d L%d D%d]" % (g + 1, n, res, A.name, int(w), int(l), int(dr)), flush=True)
score = w + 0.5 * dr
p = score / n
se = math.sqrt(0.25 / n)
z = (p - 0.5) / se
print("\nFINAL: %s vs %s = %dW %dL %dD over %d games" % (A.name, B.name, int(w), int(l), int(dr), n))
print(" score %.1f/%d = %.1f%% z=%.2f" % (score, n, 100 * p, z))
if pgn_out is not None:
with open(pgn_out, "w") as f:
f.write("\n\n".join(pgns))
print(" PGNs -> %s" % pgn_out)
if __name__ == "__main__":
main()