Text Generation
PEFT
Safetensors
Transformers
English
lora
qlora
bitsandbytes
connect4
game-playing
causal-lm
RenaudGaudron commited on
Commit
09015e3
·
verified ·
1 Parent(s): b4172c9

Upload 4 files (#1)

Browse files

- Upload 4 files (6665e36a46a250377d4e8db5c9d0b1d7b046d0e1)

README.md CHANGED
@@ -1,3 +1,214 @@
1
  ---
 
 
 
 
2
  license: mit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ model_name: Connect4 QLoRA Adapter for Qwen3-0.6B-Base
3
+ version: 1.0.0
4
+ library_name: peft
5
+ base_model: Qwen/Qwen3-0.6B-Base
6
  license: mit
7
+ tags:
8
+ - lora
9
+ - qlora
10
+ - bitsandbytes
11
+ - transformers
12
+ - connect4
13
+ - game-playing
14
+ - causal-lm
15
+ datasets:
16
+ - private/connect4-selfplay
17
+ pipeline_tag: text-generation
18
+ inference: false
19
+ language:
20
+ - en
21
+ quantization_config:
22
+ load_in_4bit: true
23
+ bnb_4bit_quant_type: nf4
24
+ bnb_4bit_compute_dtype: float16
25
  ---
26
+
27
+ # Connect 4 QLoRA Adapter for Qwen/Qwen3-0.6B-Base
28
+
29
+ ## Model Summary
30
+
31
+ This repository distributes a QLoRA adapter trained to steer the **Qwen/Qwen3-0.6B-Base** model toward Connect Four next-move prediction and short-form move generation. Prompts encode game history as concatenated column indices (`0`–`6`) along with the starter and side-to-move context, allowing the adapter to focus on legal column selection. The weights are stored separately from the base checkpoint; load or merge them into the matching base revision before running inference. Runs on a single consumer GPU with 4 GB of VRAM in 4bit mode.
32
+
33
+ ## How to Use
34
+
35
+ ### Load for inference with PEFT
36
+
37
+ ```python
38
+ from pathlib import Path
39
+
40
+ import torch
41
+ from peft import PeftModel
42
+ from transformers import AutoModelForCausalLM, AutoTokenizer
43
+
44
+ BASE_MODEL = "Qwen/Qwen3-0.6B-Base"
45
+ ADAPTER_PATH = "RenaudGaudron/Qwen3-0.6B-Connect4"
46
+
47
+ model = AutoModelForCausalLM.from_pretrained(
48
+ BASE_MODEL,
49
+ device_map="auto",
50
+ torch_dtype=torch.float16,
51
+ load_in_4bit=True,
52
+ bnb_4bit_compute_dtype=torch.float16,
53
+ bnb_4bit_quant_type="nf4",
54
+ bnb_4bit_use_double_quant=True,
55
+ )
56
+ tokenizer = AutoTokenizer.from_pretrained(BASE_MODEL)
57
+ model = PeftModel.from_pretrained(model, ADAPTER_PATH)
58
+ model.eval()
59
+
60
+ PROMPT_TEMPLATE = (
61
+ "Connect Four is played on a 7-column by 6-row grid. Players alternate"
62
+ " dropping discs that stack upwards, and full columns cannot be used."
63
+ "\nGame starter: Player {starter}"
64
+ "\nPlayer to move: Player {current_player}"
65
+ "\nMoves so far: {history}"
66
+ "\nSelect a legal column (0-6) for Player {current_player} and avoid full columns."
67
+ "\nAnswer with a single digit representing the column index."
68
+ "\nResponse:"
69
+ )
70
+
71
+ example_prompt = PROMPT_TEMPLATE.format(
72
+ starter=1,
73
+ current_player=2,
74
+ history="32344553",
75
+ )
76
+ inputs = tokenizer(example_prompt, return_tensors="pt").to(model.device)
77
+ with torch.inference_mode():
78
+ outputs = model.generate(
79
+ **inputs,
80
+ max_new_tokens=2,
81
+ do_sample=False,
82
+ )
83
+ print(tokenizer.decode(outputs[0], skip_special_tokens=True))
84
+ ```
85
+
86
+ ### Merge adapters into the base (optional)
87
+
88
+ ```python
89
+ from pathlib import Path
90
+
91
+ import torch
92
+ from peft import PeftModel
93
+ from transformers import AutoModelForCausalLM
94
+
95
+ BASE_MODEL = "Qwen/Qwen3-0.6B-Base"
96
+ ADAPTER_PATH = "RenaudGaudron/Qwen3-0.6B-Connect4"
97
+ OUTPUT_DIR = Path("./merged-connect4-qwen3-0.6b")
98
+
99
+ base_model = AutoModelForCausalLM.from_pretrained(
100
+ BASE_MODEL,
101
+ torch_dtype=torch.float16,
102
+ device_map="auto",
103
+ load_in_4bit=False,
104
+ )
105
+ model = PeftModel.from_pretrained(base_model, ADAPTER_PATH)
106
+ merged = model.merge_and_unload()
107
+ merged.save_pretrained(OUTPUT_DIR)
108
+ model.tokenizer.save_pretrained(OUTPUT_DIR)
109
+
110
+ print(f"Merged model saved to {OUTPUT_DIR}. Review the base model license before redistribution.")
111
+ ```
112
+
113
+ > **Prompt format.** Every training example uses the template shown above. The `Moves so far` line encodes the move history as a contiguous string of digits where each character represents the column index (0–6) chosen at that ply. Use `none` when the position is empty, and keep the history aligned with the game starter so that column legality can be inferred correctly. The response should be exactly one digit; sampling or constrained decoding can help maintain this format.
114
+
115
+ ## Training Details
116
+
117
+ ### LoRA configuration
118
+
119
+ * Method: QLoRA with a frozen 4-bit base model.
120
+ * Rank (`r`): 128.
121
+ * Scaling (`lora_alpha`): 256.
122
+ * Dropout (`lora_dropout`): 0.1.
123
+ * Target modules: attention projections (`q_proj`, `k_proj`, `v_proj`, `o_proj`) and MLP projections (`gate_proj`, `up_proj`, `down_proj`).
124
+ * Adapter bias: disabled; only rank update matrices are trainable.
125
+
126
+ QLoRA keeps the dense Qwen3 backbone quantised to 4-bit NF4 while learning a lightweight set of low-rank matrices. This dramatically reduces memory pressure (the run fit comfortably in <10 GB of VRAM) and allows experimentation on modest hardware without sacrificing too much accuracy.
127
+
128
+ ### Optimisation setup
129
+
130
+ * Optimiser: `adamw_torch_fused` with β₂ = 0.98 and ε = 1e-6.
131
+ * Learning rate: 5e-7 with `constant_with_warmup` scheduling and a warmup ratio of 5 % (no fixed warmup steps beyond the ratio).
132
+ * Weight decay: 0.0.
133
+ * Gradient accumulation: 8 steps with per-device batch size 8 → effective batch size 64 sequences.
134
+ * Max gradient norm: 25.0 with gradient clipping applied to stabilise updates.
135
+ * Label smoothing: disabled.
136
+ * Attention backend: PyTorch SDPA with math kernel fallback (flash and memory-efficient kernels were unavailable on the training GPU).
137
+
138
+ ### Precision and memory
139
+
140
+ * Base model loaded in 4-bit NF4 with double quantisation; LoRA weights in float32.
141
+ * Computation dtype: float16; BF16 enabled where supported (trainer configuration set `bf16=True`, `fp16=False`, `tf32=True`).
142
+ * Gradient checkpointing: disabled.
143
+
144
+ ### Data
145
+
146
+ * Dataset: private self-play Connect Four rollouts.
147
+ * Move encoding: each game history is serialised as digits `0`–`6`, one per ply, reflecting the column placements from left (0) to right (6).
148
+ * Total generated prompt-response pairs: 54 657 (49 191 train / 5 466 validation).
149
+ * Minimum move threshold: 6 plies before emitting supervision to ensure non-trivial contexts.
150
+ * Validation split: 10 % stratified by shuffle.
151
+
152
+ ### Training run
153
+
154
+ * Epochs: 5 planned (converged by step 30 k of 30 745 total optimiser steps).
155
+ * Total training runtime: ~26.7 h (96 205 s) with 0 out-of-memory retries.
156
+ * Throughput: 2.56 samples/s, 0.32 steps/s.
157
+ * Hardware: single-GPU Accelerate run (device 0, 4-bit load) on Windows; memory planner reserved 90 % of the device for model weights according to the log.
158
+ * Seed: 42 (applied to Python, NumPy, and PyTorch). Standard dataloader shuffles and CUDA kernels may still introduce nondeterminism.
159
+
160
+ ## Evaluation
161
+
162
+ Evaluation used the same prompt template and truncated each sequence to `eval_max_seq_length=128`. The primary metric is token-level cross-entropy (reported as loss), which acts as a proxy for perplexity over the next-move token.
163
+
164
+ * Final checkpoint validation loss: **0.8570**.
165
+ * Best observed validation loss during training: **0.7306** at step 30 000.
166
+
167
+ These values indicate the adapter tracks legal play patterns and improves over the base model for move prediction, but decoding quality remains sensitive to sampling strategy and prompt fidelity. No additional metrics (win-rate or exact-match) were computed for this release.
168
+
169
+ Stability controls included gradient clipping at a max norm of 25.0, continuous gradient-norm monitoring, and the warmup schedule above. No label smoothing or dropout beyond LoRA-specific dropout was used.
170
+
171
+ ## Special Tokens
172
+
173
+ The adapter relies on the base tokenizer without introducing new tokens. The provided `special_tokens.json` simply reuses token ID `151643` for both padding and EOS with right-side padding. No custom BOS token or move separator tokens were added.
174
+
175
+ ## Intended Use & Limitations
176
+
177
+ **Intended use.** Researchers and hobbyists experimenting with Connect Four agents can combine this adapter with Qwen/Qwen3-0.6B-Base to generate legal column suggestions. The model expects textual prompts that follow the documented template.
178
+
179
+ **Limitations.**
180
+
181
+ * The small 0.6 B parameter backbone limits long-horizon reasoning and nuanced board evaluation. Expect occasional illegal or weak moves, especially in edge cases near game end.
182
+ * Outputs are single-digit column indices; free-form text prompts or multi-turn conversations fall outside the training distribution.
183
+ * The adapter does not include safety layers, toxicity filtering, or alignment for open-domain generation. Avoid deploying it in user-facing production systems.
184
+ * Quality depends heavily on providing complete, legal move histories and using deterministic decoding (`do_sample=False` or constrained vocab sampling).
185
+
186
+ ## Compatibility
187
+
188
+ The adapter was trained and validated with the following stack:
189
+
190
+ * `transformers` ≥ 4.39.0
191
+ * `peft` ≥ 0.8.2
192
+ * `bitsandbytes` ≥ 0.43.0
193
+ * `torch` ≥ 2.1 (CUDA 12.4 compatible per bitsandbytes log)
194
+ * `accelerate` ≥ 0.25.0
195
+
196
+ The training environment ran on Windows with CUDA 12.4 bitsandbytes bindings. On Linux or other CUDA versions, Accelerate automatically falls back to the available SDPA kernels; if flash or memory-efficient SDPA kernels are missing (as during training), PyTorch will use the math implementation. Pin the same `Qwen/Qwen3-0.6B-Base` revision you fine-tune against to avoid key mismatches when loading the adapter.
197
+
198
+ ## Reproducibility & Seeds
199
+
200
+ The training script fixed the global seed to **42** across Python, NumPy, and PyTorch. Nevertheless, sources of nondeterminism remain (CUDA kernels, dataloader worker order, and filesystem scheduling). For faithful reproduction, combine the published configuration with deterministic CUDA flags and ensure dataset shuffling uses the same seed.
201
+
202
+ ## Changelog
203
+
204
+ * **2025-10-25** – Initial public adapter release.
205
+
206
+ ## License
207
+
208
+ The adapter is released under the **MIT License**. The base model, **Qwen/Qwen3-0.6B-Base**, is distributed under the Apache License 2.0; ensure downstream usage complies with both the adapter’s MIT terms and the base model’s requirements before deploying, fine-tuning further, or redistributing merged checkpoints.
209
+
210
+ ## Citations
211
+
212
+ * Hu et al., “LoRA: Low-Rank Adaptation of Large Language Models,” arXiv:2106.09685.
213
+ * Dettmers et al., “QLoRA: Efficient Finetuning of Quantized LLMs,” arXiv:2305.14314.
214
+ * Wolf et al., “HuggingFace's Transformers: State-of-the-Art Natural Language Processing,” arXiv:1910.03771.
adapter_config.json ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "alpha_pattern": {},
3
+ "auto_mapping": null,
4
+ "base_model_name_or_path": "Qwen/Qwen3-0.6B-Base",
5
+ "bias": "none",
6
+ "corda_config": null,
7
+ "eva_config": null,
8
+ "exclude_modules": null,
9
+ "fan_in_fan_out": false,
10
+ "inference_mode": true,
11
+ "init_lora_weights": true,
12
+ "layer_replication": null,
13
+ "layers_pattern": null,
14
+ "layers_to_transform": null,
15
+ "loftq_config": {},
16
+ "lora_alpha": 256,
17
+ "lora_bias": false,
18
+ "lora_dropout": 0.1,
19
+ "megatron_config": null,
20
+ "megatron_core": "megatron.core",
21
+ "modules_to_save": null,
22
+ "peft_type": "LORA",
23
+ "qalora_group_size": 16,
24
+ "r": 128,
25
+ "rank_pattern": {},
26
+ "revision": null,
27
+ "target_modules": [
28
+ "v_proj",
29
+ "down_proj",
30
+ "k_proj",
31
+ "o_proj",
32
+ "q_proj",
33
+ "up_proj",
34
+ "gate_proj"
35
+ ],
36
+ "target_parameters": null,
37
+ "task_type": "CAUSAL_LM",
38
+ "trainable_token_indices": null,
39
+ "use_dora": false,
40
+ "use_qalora": false,
41
+ "use_rslora": false
42
+ }
adapter_model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:b66474a92f61223dcbd73507a84ea9d78a35e3808edbaf650e1706578d852eac
3
+ size 161533944
special_tokens.json ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ {
2
+ "pad_token_id": 151643,
3
+ "eos_token_id": 151643,
4
+ "bos_token_id": null,
5
+ "padding_side": "right"
6
+ }