I chose Mikhail Tal because I see him as eccentric, unique, bold and innovative. I wanted to see whether a small model could learn some of his move choices from his games.
I worked on this with AI and asked it to teach me the process. We went back and forth as I asked questions and worked out what to try next. The project grew from training a model on my PC into a local browser extension and a standalone chess engine.
The training data
The Tal archive contained 2,431 games. Training started with a broader collection from 18 player archives, then the model was fine-tuned on Tal’s own moves.
Each training example contained a board position, the move played and the final game result. The larger set had about 4.43 million examples. Keeping only Tal’s moves from his games produced another 82,132 examples for fine-tuning.
Positions were stored from the perspective of the player whose turn it was, so the model saw a consistent board orientation for either colour. When choosing a move, it could only pick from legal moves.
Training on my PC
Training ran on Windows 11 with a Radeon RX 7900 XTX, using Python, PyTorch and AMD’s ROCm stack. Getting the Python environment, GPU software and training code to work together was part of the job.
The choice of numerical format made a difference on this PC. The local benchmark notes recorded much slower GPU computation with bf16 than with fp16, so the trainer was changed to prefer fp16 automatically on the GPU. That benchmark measured GPU computation on this setup; it didn’t measure the whole training process.
The trainer saved the best model separately from the checkpoint used to resume a run. Fine-tuning reached 12 epochs, or passes through the training data. The best saved validation result came much earlier, at epoch 2.
Checking the result
The saved Tal checkpoint matched the recorded move in 50.61% of validation examples. That means its first choice was the same move as the one in the game record.
Whole games had been split between training and validation, but positions can repeat across games. A later check found validation positions that also appeared in training. That overlap means the score doesn’t establish how well the model handles entirely unseen positions.
Matching individual moves also doesn’t show whether it plays a good complete game or has picked up Tal’s style. Those questions need separate tests.
The model’s own choices
An early version used Stockfish checks and a handwritten preference for attacking moves alongside the model. Those rules could improve a suggestion, but they also changed it. I wanted to see what the model would choose, including its mistakes.
The extension and standalone player handle this differently:
- Tal Vision’s Astra mode normally keeps the model’s highest-probability legal move. It only consults Stockfish to check alternatives to a repeating position and reports when it intervenes.
- The standalone Python player chooses moves without Stockfish. It normally takes the model’s favourite legal move, avoiding threefold repetition unless its own position estimate suggests accepting the draw.
The match results below belong to the standalone player.
The local extension
The trained model was exported to ONNX for Tal Vision, a Chromium extension. After importing the model, you can paste a chess position and get a move suggestion. The model is stored and run locally. The extension doesn’t submit moves.
The interface needed to handle more than a successful prediction. Tests cover model imports, stale suggestions, finished games and warnings about unverified move history. There are also tests for communication between the standalone engine and a chess client.
A 20-game match
The standalone engine connected through UCI, a protocol used by chess programs. The saved local match log records 20 games against Stockfish at skill level 0. Colours alternated, with 60 seconds on the clock plus one second per move.
| Result | Games |
|---|---|
| Wins | 6 |
| Draws | 6 |
| Losses | 8 |
That adds up to 9 points out of 20, a 45% score, with no recorded losses on time. It’s a small baseline match against Stockfish’s lowest skill setting. It doesn’t establish an Elo rating or show performance against full-strength Stockfish.
The match runner had a bug that could distort the results: an engine crash counted as a win for its opponent. It was changed to stop the run if an engine process died, so a software failure couldn’t produce a free win.
What still needs work
The model still makes poor moves. The standalone player doesn’t search ahead, and its position estimate was learned from final game results. Endgame play needs work, as does an evaluation set with less overlap with training.
About the evidence and numbers
This write-up was checked against the local repository and its saved files on 12 September 2026. The latest inspected commit was 5a8e3b9, dated 5 September 2026. Training and the baseline match were not rerun.
- The Tal checkpoint stores a validation top-1 value of
0.5060780550223928at epoch 2. The last checkpoint records epoch 12. - Tal validation contains 4,689 rows and 4,207 unique full FEN strings. Of those unique strings, 527 also occur in Tal training and 1,227 occur in pretraining. These overlapping groups must not be added together. This was an exact-string check, not a complete audit of equivalent board positions.
- The base checkpoint has its own validation score on a different dataset; comparing it directly with 50.61% would not measure the benefit of fine-tuning on Tal.
- The original match log supplies the win/draw/loss counts. It also prints a relative Elo estimate for that small match, which is not a public or independently established rating.
- The browser image is an existing local test screenshot. Test code and historical test notes were inspected; live chess-site compatibility was not newly verified.
- The repository includes a Lichess deployment guide. Deployment and a public bot rating haven’t been verified for this write-up.