Logo
All posts

How to train a Transformer (as a software engineer with no ML experience)

In this blog we will see how I trained a transformer for it, step by step, and what each experiment did for my goals.

Gaurav Sen/12 min read

This November, I entered CodinGame's AI bot challenge, an eight-day contest with participants from around the World.[1] Claude agents wrote all of my code, while I set the harness around it.

I noticed that engineers from Amadeus did quite well here, which helps create aviation planning software. Are optimisation problems common in flight planning? Who knows. 🤷

As with any AI contest, the best submission wins. To do this, I needed my bot to look beyond simple algorithms: I needed it to train a neural network designed specifically for the contest.

Since I had never trained a neural network, I kept simple goals:

  1. The network had to understand a game board.
  2. It had to run inside 50 ms, in one source file of at most 100,000 characters.
  3. A network that looked better in training had to play better in real games.

In this blog we will see how I trained a transformer for it, step by step, and what each experiment did for my goals.

1. Decide what the network does

Where does a network fit in a bot that searches? At the end, where the bot has to choose.

One 50 ms turn of the bot, left to right: a tree search for the opening, an annealer that makes about 14,000 edits to the plan, best response against the opponent's reply, the judge, which is the neural network, and the orders the bot sends.
One 50 ms turn of the bot, left to right: a tree search for the opening, an annealer that makes about 14,000 edits to the plan, best response against the opponent's reply, the judge, which is the neural network, and the orders the bot sends.

Each turn, the search improved one plan for all our units, with about 14,000 edits in a mid-game turn. The judge then built a table of our candidate plans against the opponent's likely replies.

Each cell of that table was played forward one turn and scored by the network, about 64 times in a mid-game turn. So the network's job was narrow: look at a position, and say how good it is for us.

2. Convert application state to tokens

How does a transformer read an application's state? The same way it reads text: as a list of tokens.[2] In our bot, the application state was the game board.

A small game board turned into tokens. Four houses, A, B, C and D, and one triangle, T, become six tokens: one for the whole board, one for each house and one for the triangle. Arcs show attention between tokens: A and B are one step apart, and C is a corner of T. The tokens go into a transformer, which answers with a win chance and a margin.
A small game board turned into tokens. Four houses, A, B, C and D, and one triangle, T, become six tokens: one for the whole board, one for each house and one for the triangle. Arcs show attention between tokens: A and B are one step apart, and C is a corner of T. The tokens go into a transformer, which answers with a win chance and a margin.

Attention lets each token look at every other token and weigh which ones matter.

Our teacher network had one token for every house, one for every triangle, and one for the whole board. Each house token carried its units, its owner and its position. Each triangle token carried its owner and whether either side could capture it.

Attention also got a learned bias for how two tokens relate. Two houses one step apart relate differently from two houses four steps apart. A house that is a corner of a triangle is marked as one.

With those tokens and relations, the network saw every board from the side about to move. It answered two questions: how likely is this side to win, and by what margin?

That's the one design decision I needed to understand. The agents wrote the model, its training loop and its tests.

3. Train it to meet application-specific goals

Where does a network learn to judge, when nobody labels positions for you? AlphaGo answered that in three steps.[3]

  1. Supervised learning: learn the moves and results of strong players from their games.
  2. Reinforcement learning: play against yourself, and retrain on those games.
  3. Distillation: a large, slow teacher labels positions, and a small student learns to copy it.

Distillation came first for me. On 4 November, a small network learned to score positions the way a slower, stronger search did. Once the judge also saw the opponent's own plans, that bot beat the previous one 177-79 and placed 34.65, my best score in Silver.

Self-play came next, because I asked for it. On 5 November the bot played itself on a farm of AWS machines and my graphics card. On a rating scale with the previous bot at 1500, the self-play bots climbed to 1758.

Online, the first of them placed 17.54, at 198 of 426 in Gold. The bot it replaced had placed 16.77, at 172: a higher score, but a worse rank. Better against itself didn't mean better against real players.

So on 7 November the networks learned from 1,918 games of top players instead. That bundle beat the previous build 248-120-32 and raised my placement by 1.20 points. It also carried a lead guard and a timeout fix, so the network can't take all the credit.

4. Make it fit in 50 ms

Can a network ship inside a single source file? Ours had to, in at most 100,000 characters.

The live student had about 99,000 parameters, so it was trained to keep its accuracy at 7 bits per weight. Its weights were packed into a string with an entropy coder, and took about 18,010 characters.

Size still ended ideas. A variant with a second embedding packaged to 102,999 characters, and was cut.

So the student had to get smaller. On the last day, a 42-minute run distilled it into five smaller networks at once.

The run drew from 1.47 million positions, swapping the players and reflecting the maps. It held out 14,111 real positions for testing.

Time for one network evaluation on the same machine. The live student, width 64 and 4 layers, took 98.73 microseconds. The distilled d24L2, width 24 and 2 layers, took 10.74 microseconds.
Time for one network evaluation on the same machine. The live student, width 64 and 4 layers, took 98.73 microseconds. The distilled d24L2, width 24 and 2 layers, took 10.74 microseconds.

The pick, d24L2, had 9,515 parameters and ran about nine times faster. On the held-out positions it chose its teacher's top move 79.8% of the time. Its value agreed with the teacher's at a correlation of 0.990.

5. Test it inside the bot

Does a network that's better in training make the bot better? Not always, as d24L2 showed.

With 96 games sharing the test machine, the bot with d24L2 and a deeper search won 64.5% against the live bot. With 48 games, one per core, it won 55.7%. It also failed my opening tests, and online it placed 25.69.

Reinforcement learning did worse than d24L2 inside the bot. One overnight policy beat its own parent 819-203 when both played by policy alone, and scored 46.0% inside the search.

Every reinforcement-learning attempt against the live bot, as its best share of decisive games, all under the 55% gate. Self-play retrain: 45.7%. Local RL loop: 46.6%. RL under rule v3: 49.5%. Distilling a four-times search into the judge: 50.6%. The AlphaGo-style pipeline inside the search: 46.0%.
Every reinforcement-learning attempt against the live bot, as its best share of decisive games, all under the 55% gate. Self-play retrain: 45.7%. Local RL loop: 46.6%. RL under rule v3: 49.5%. Distilling a four-times search into the judge: 50.6%. The AlphaGo-style pipeline inside the search: 46.0%.

A policy that was better on its own made the searched bot worse.

So judge a network inside the system it serves, under that system's clock and load. Its own training numbers aren't the test.

6. Weigh the network against the rest

How much did the transformer move my goal, the placement? Less than I hoped. Of 11 network and reinforcement-learning ideas, 5 passed a test against the live bot, and 1 raised the placement.

The placement's biggest gain of the contest came from structure. The bot planned against the opponent's best reply to its own plan, then re-planned.

More compute against a better decision rule, as a share of decisive games. Ten times the search won 54.8%, and ten times against four times won 54.2%, both under the 55% gate. Best response at equal time won 67.6%.
More compute against a better decision rule, as a share of decisive games. Ten times the search won 54.8%, and ten times against four times won 54.2%, both under the 55% gate. Best response at equal time won 67.6%.

At the same compute, that rule won 67.6%. Ten times the search won only 54.8%.

The network was one part of the bot. The decision rule around it did more for the placement than the network, or ten times the search.

Summary

  1. A transformer reads a list of tokens, and attention lets each token weigh every other token.
  2. Application state becomes tokens. A game board became one token for each house, one for each triangle and one for the whole board.
  3. AlphaGo's recipe trains a network in three steps: learning from strong players' games, learning from self-play, and distillation.
  4. Distillation trains a small student to copy a large teacher. The student d24L2 picked its teacher's top move on 79.8% of held-out positions, and ran nine times faster.
  5. A network trained for 7-bit weights fits inside one source file.
  6. A network is tested inside the system it serves. A policy that beat its parent 819-203 alone scored 46.0% inside the search.

Sources

  1. Fall Challenge 2026, CodinGame.
  2. Attention Is All You Need, Ashish Vaswani and others, 2017.
  3. Mastering the game of Go with deep neural networks and tree search, David Silver and others, Nature, 2016.
WhatsApp