Chess1MChallenge / TEMPLATE_README.md
nathanael-fijalkow's picture
fixed readme
39126ad
# Chess Challenge: 1M Parameter Transformer
Train a transformer (from scratch!) with less than 1M parameters to play legal chess moves.
---
## 1. Overview & Objective
Your model must:
- **Stay under 1M parameters** (hard limit)
- **Create a custom tokenizer**
- **Create a custom model architecture** (your own transformer)
- **Play legal chess** (model must learn the rules)
- **Do NOT use python-chess to filter moves** (the model must generate legal moves itself)
---
## 2. Dataset & Notation
We use the Lichess dataset: [`dlouapre/lichess_2025-01_1M`](https://huggingface.co/datasets/dlouapre/lichess_2025-01_1M)
**Notation:**
- `W`/`B` prefix for White/Black
- Piece letter: `P`=Pawn, `N`=Knight, `B`=Bishop, `R`=Rook, `Q`=Queen, `K`=King
- Source and destination squares (e.g., `e2e4`)
- Special suffixes: `(x)`=capture, `(+)`=check, `(+*)`=checkmate, `(o)`/`(O)`=castling
Example game:
```
WPe2e4 BPe7e5 WNg1f3 BNb8c6 WBf1b5 BPa7a6 WBb5c6(x) BPd7c6(x) ...
```
---
## 3. Directory Structure
Your project should look like this:
```
my_model/
config.json
model.safetensors
tokenizer_config.json
vocab.json
model.py
tokenizer.py
```
---
## 4. Step-by-Step Instructions
### Step 1: Build Your Tokenizer
Create `tokenizer.py` implementing a subclass of `PreTrainedTokenizer`, say MyChessTokenizer.
**Build the vocabulary:**
One possibility is to look at the dataset, but it's by far not the only option:
```python
from datasets import load_dataset
import json
dataset = load_dataset("dlouapre/lichess_2025-01_1M", split="train")
vocab = {"[PAD]": 0, "[BOS]": 1, "[EOS]": 2, "[UNK]": 3}
for game in dataset:
for move in game["text"].split():
if move not in vocab:
vocab[move] = len(vocab)
with open("vocab.json", "w") as f:
json.dump(vocab, f, indent=2)
```
### Step 2: Build Your Model
Create `model.py` implementing a subclass of `PreTrainedModel`, say MyChessModel, and a config class, say MyChessConfig.
**Tips:**
- Use weight tying to save parameters.
- Keep the vocabulary small.
- 4-6 transformer layers is usually enough.
### Step 3: Training
Create `train.py` to train your model:
```python
from model import MyChessConfig, MyChessModel
from tokenizer import MyChessTokenizer
from datasets import load_dataset
from transformers import Trainer, TrainingArguments
tokenizer = MyChessTokenizer(vocab_file="vocab.json")
config = MyChessConfig(vocab_size=tokenizer.vocab_size, n_embd=128, n_layer=4, n_head=4)
model = MyChessModel(config)
dataset = load_dataset("dlouapre/lichess_2025-01_1M", split="train")
def tokenize_function(examples):
return tokenizer(examples["text"], truncation=True, max_length=256, padding="max_length")
tokenized_dataset = dataset.map(tokenize_function, batched=True)
training_args = TrainingArguments(
output_dir="./my_model",
num_train_epochs=3,
per_device_train_batch_size=32,
learning_rate=5e-4,
save_steps=1000,
logging_steps=100,
)
trainer = Trainer(model=model, args=training_args, train_dataset=tokenized_dataset)
trainer.train()
model.save_pretrained("./my_model/final")
tokenizer.save_pretrained("./my_model/final")
```
### Step 4: Prepare for Submission
Your model directory (`my_model/final/`) **must** contain:
```
config.json # Model configuration
model.safetensors # Model weights
tokenizer_config.json # Tokenizer configuration
vocab.json # Vocabulary
model.py # Your model class
tokenizer.py # Your tokenizer class
```
#### Add `auto_map` for remote loading
Edit `config.json`:
```json
"auto_map": {
"AutoConfig": "model.MyChessConfig",
"AutoModelForCausalLM": "model.MyChessModel"
}
```
Edit `tokenizer_config.json`:
```json
"auto_map": {
"AutoTokenizer": "tokenizer.MyChessTokenizer"
}
```
Or do it programmatically:
```python
model.config.auto_map = {
"AutoConfig": "model.MyChessConfig",
"AutoModelForCausalLM": "model.MyChessModel",
}
tokenizer.register_for_auto_class("AutoTokenizer")
model.save_pretrained("./my_model/final")
tokenizer.save_pretrained("./my_model/final")
import shutil
shutil.copy("model.py", "./my_model/final/model.py")
shutil.copy("tokenizer.py", "./my_model/final/tokenizer.py")
```
---
### Step 5: Local Evaluation (Recommended)
Before submitting, evaluate your model locally:
```bash
python -m src --model ./my_model/final
```
This runs the same evaluation as the leaderboard (500 moves, deterministic seed).
---
### Step 6: Submit
Submit your model to the leaderboard:
```bash
python submit.py --model_path ./my_model/final --model_name your-model-name
```
The script will:
- Validate all required files
- Check auto_map
- Count parameters
- Log you into HuggingFace (if needed)
- Upload to the LLM-course organization
---
## 5. Evaluation & Leaderboard
After submission, go to the [Chess Challenge Arena](https://huggingface.co/spaces/LLM-course/Chess1MChallenge) to run evaluation.
**Evaluation steps:**
1. Parameter check (<1M)
2. Security check (no python-chess for move filtering)
3. 500 moves against a deterministic opponent
4. 3 retries per move (greedy, then sampling)
5. Scoring: legal move rate (first try and with retries)
**Scoring Table:**
| Metric | Description |
|--------|-------------|
| **Legal Rate (1st try)** | % of moves legal on first attempt |
| **Legal Rate (with retries)** | % of moves legal within 3 attempts |
**Target:** >90% legal rate = excellent
---
## 6. Example Solution
See `example_solution/` for a full working reference:
- `model.py`, `tokenizer.py`, `train.py`, `data.py`
---