# 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` ---