dexmac commited on
Commit
9e2e4a8
·
verified ·
1 Parent(s): e80c317

LoRA adapter, model card and training history

Browse files
README.md ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: gemma
3
+ base_model: google/gemma-4-E2B-it
4
+ library_name: peft
5
+ tags:
6
+ - lora
7
+ - peft
8
+ - doom
9
+ - vizdoom
10
+ - freedoom
11
+ - decision-model
12
+ - system-one
13
+ - laya
14
+ - gemma
15
+ language:
16
+ - en
17
+ ---
18
+
19
+ # doomGemma — Gemma 4 E2B LoRA for doomLaya decisions
20
+
21
+ A LoRA adapter that turns **Gemma 4 E2B** into a typed decision model for the
22
+ [doomLaya](https://github.com/azalio/doomLaya) FreeDoom agent. It was trained on
23
+ **exactly the same 979 labelled questions** that were used to train
24
+ [Laya v3](https://huggingface.co/azalio/laya-doom-v3) (a 421M "System One" encoder),
25
+ so the two can be compared on equal data.
26
+
27
+ The model does not play by itself. The doomLaya agent turns the game state into text
28
+ (health, weapons, visible enemies with ids and distances, items, doors) and asks two
29
+ multiple-choice questions, `command` and `weapon`; an executor aims, plans paths and
30
+ presses buttons. **All choices belong to the model.**
31
+
32
+ ## Results (RTX 4070, 25 Sep 2026)
33
+
34
+ 130 held-out validation questions of doomLaya v3:
35
+
36
+ | model | command | weapon | ECE (raw) |
37
+ |---|---|---|---|
38
+ | Gemma 4 E2B, zero-shot | 0.51 | 0.78 | 0.38 |
39
+ | Laya v3 (azalio/laya-doom-v3) | 0.725 | 0.98 | 0.06 |
40
+ | **this adapter (Q8_0 GGUF, via llama.cpp)** | **0.96** | **1.00** | **0.04** |
41
+
42
+ FreeDoom **MAP01**, skill 3, 180 s limit, seeds 48–53 (none used for training or validation):
43
+
44
+ | player | exits | mean time | range | deaths |
45
+ |---|---|---|---|---|
46
+ | Laya v3 | 6/6 | 59.5 s | 42.9–71.0 s | 0 |
47
+ | **this adapter** | 6/6 | 60.0 s | 56.6–64.2 s | 0 |
48
+ | rule-based teacher that produced the labels | 6/6 | 81.6 s | 64.5–124.9 s | 1 |
49
+
50
+ The models exit faster than the rule-based teacher because they imitate it imperfectly
51
+ and pick up fewer items; the teacher does not optimise time-to-exit.
52
+
53
+ Latency on the same RTX 4070, same 1,416 recorded game packets:
54
+
55
+ | setup | p50 | p99 |
56
+ |---|---|---|
57
+ | Laya v3, its own server | 23 ms | 25 ms |
58
+ | this adapter, llama.cpp Q8_0 + grammar | 59 ms | 118 ms |
59
+ | this adapter, direct PyTorch bf16 | 84 ms | 100 ms |
60
+
61
+ ## Limitations
62
+
63
+ - Trained and validated **only on MAP01**. On **MAP02** nobody exits (0/3), **including
64
+ the rule-based teacher**: the chain stops at a **yellow-key door** (sector 37, linedef
65
+ special 27), which doomLaya offers as an openable door while the key is far away and
66
+ out of sight. Generalisation to new maps **cannot be measured** on this bench.
67
+ - The labels come from a **rule-based teacher**, not from human play: the adapter learns
68
+ to imitate those rules.
69
+ - The validation set is small (130 questions); calibration numbers carry noticeable noise.
70
+
71
+ ## How it is used
72
+
73
+ Each option becomes a letter. A grammar allows a single token among the valid letters,
74
+ and the option probabilities are read from the logprobs of that token
75
+ (one prefill + one token, no free-form generation). The exact prompt:
76
+
77
+ ```python
78
+ LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
79
+ SYSTEM = ("You control a DOOM player in real time. "
80
+ "Pick the best option for each question. Reply only with the letters.")
81
+
82
+ def build(state, name, question): # one question per request
83
+ keys = list(question["criteria"])
84
+ lines = [f"{LETTERS[i]}) {question['criteria'][k]}" for i, k in enumerate(keys)]
85
+ user = (state + "\n\n" + f"{name.upper()} — {question.get('instructions', '')}\n"
86
+ + "\n".join(lines) + f"\n\nAnswer with 1 letter, in this order: {name.upper()}.")
87
+ return [{"role": "system", "content": SYSTEM}, {"role": "user", "content": user}], keys
88
+ ```
89
+
90
+ The chat template is applied with `add_generation_prompt=True, enable_thinking=False`;
91
+ the answer is the next token.
92
+
93
+ ## Training
94
+
95
+ - Data: `training/v3/train.json` (979) and `validation.json` (130) from azalio/doomLaya
96
+ at commit `b25edd3` (Apache-2.0). Game seeds 42/43 for training, 44 for validation, MAP01 only.
97
+ - LoRA rank 16, alpha 32, dropout 0.05 on `q,k,v,o,gate,up,down` of the language model;
98
+ AdamW, lr 1e-4, weight decay 0.01, gradient accumulation 8, clip 1.0, seed 771.
99
+ **Loss only on the answer letter.** Best epoch by validation accuracy (command + weapon):
100
+ **epoch 2 of 3**, 550 s per epoch on one RTX 4070 (history in `training_history.json`).
101
+ - Base weights: `google/gemma-4-E2B-it` (`model.safetensors` sha256 `2db5482b…`).
102
+
103
+ ## Credits and licences
104
+
105
+ - Base model: Google **Gemma 4 E2B**. This adapter is a Gemma derivative and is
106
+ distributed under the [Gemma Terms of Use](https://ai.google.dev/gemma/terms).
107
+ - Training data and game harness: [azalio/doomLaya](https://github.com/azalio/doomLaya),
108
+ Apache-2.0 (see their NOTICE).
109
+ - Laya: [Convai Innovations](https://huggingface.co/convaiinnovations/laya), Apache-2.0.
110
+ - FreeDoom and ViZDoom under their respective licences.
adapter_config.json ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "alora_invocation_tokens": null,
3
+ "alpha_pattern": {},
4
+ "arrow_config": null,
5
+ "auto_mapping": {
6
+ "base_model_class": "Gemma4ForConditionalGeneration",
7
+ "parent_library": "transformers.models.gemma4.modeling_gemma4"
8
+ },
9
+ "base_model_name_or_path": "google/gemma-4-E2B-it",
10
+ "bias": "none",
11
+ "corda_config": null,
12
+ "ensure_weight_tying": false,
13
+ "eva_config": null,
14
+ "exclude_modules": null,
15
+ "fan_in_fan_out": false,
16
+ "inference_mode": true,
17
+ "init_lora_weights": true,
18
+ "kasa_config": null,
19
+ "layer_replication": null,
20
+ "layers_pattern": null,
21
+ "layers_to_transform": null,
22
+ "loftq_config": {},
23
+ "lora_alpha": 32,
24
+ "lora_bias": false,
25
+ "lora_dropout": 0.05,
26
+ "lora_ga_config": null,
27
+ "megatron_config": null,
28
+ "megatron_core": "megatron.core",
29
+ "modules_to_save": null,
30
+ "monteclora_config": null,
31
+ "peft_type": "LORA",
32
+ "peft_version": "0.21.0",
33
+ "qalora_group_size": 16,
34
+ "r": 16,
35
+ "rank_pattern": {},
36
+ "revision": null,
37
+ "target_modules": ".*language_model.*\\.(q_proj|k_proj|v_proj|o_proj|gate_proj|up_proj|down_proj)",
38
+ "target_parameters": null,
39
+ "task_type": null,
40
+ "trainable_token_indices": null,
41
+ "use_bdlora": null,
42
+ "use_dora": false,
43
+ "use_qalora": false,
44
+ "use_rslora": false,
45
+ "velora_config": null
46
+ }
adapter_model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e1ac4eb4853311b509d87b5cf33be663e81e8220f223858ff02489253e289a83
3
+ size 96693360
best_epoch.json ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ {
2
+ "best_epoch": 2,
3
+ "validation_accuracy": {
4
+ "command": 0.95,
5
+ "weapon": 1.0
6
+ }
7
+ }
training_history.json ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "epoch": 0,
4
+ "validation_accuracy": {
5
+ "command": 0.5125,
6
+ "weapon": 0.76
7
+ }
8
+ },
9
+ {
10
+ "epoch": 1,
11
+ "validation_accuracy": {
12
+ "command": 0.7875,
13
+ "weapon": 1.0
14
+ },
15
+ "seconds": 550.1
16
+ },
17
+ {
18
+ "epoch": 2,
19
+ "validation_accuracy": {
20
+ "command": 0.95,
21
+ "weapon": 1.0
22
+ },
23
+ "seconds": 549.8
24
+ },
25
+ {
26
+ "epoch": 3,
27
+ "validation_accuracy": {
28
+ "command": 0.9375,
29
+ "weapon": 1.0
30
+ },
31
+ "seconds": 550.1
32
+ }
33
+ ]