How to use from
Docker Model Runner
docker model run hf.co/CatQualia/gnarp-m2
Quick Links

gnarp-m2

A 360M-parameter language model fine-tuned via QLoRA on 69,945 cross-domain isomorphism and verification-labeled instruction pairs (74,395 rows read; every row labelled negative is discarded by the trainer). gnarp-m2 specializes in cross-domain structural transfer โ€” mapping mechanisms from one domain (biology, physics, anime, economics, etc.) to software engineering constructs.

Model Details

Property Value
Base model HuggingFaceTB/SmolLM2-360M-Instruct
Method QLoRA (4-bit NF4, double quantization)
LoRA rank 16
LoRA alpha 32
LoRA dropout 0.05
Target modules q_proj, k_proj, v_proj, o_proj, gate_proj, up_proj, down_proj
Trainable params 8,683,520 (2.34% of total)
Total params 370,504,640
Adapter size 34.8 MB (rank-16, alpha-32)
Merged model size 1.4 GB
Architecture LlamaForCausalLM
Max sequence length 768 tokens

Training Data

Corpus: clean_corpus_v5.jsonl โ€” 74,395 rows read, of which 69,945 were train-eligible

Source Rows Description
isomorphism_sft.jsonl 53,403 Anime-to-software structural isomorphisms. Drawn from the 17,801-row moat corpus, which carries a non-empty failure_class on every row โ€” but this file does not: the corpus contains no failure_class field at all (see Corrections below)
gpu_assay_verdicts.jsonl 11,989 GPU assay verification-labeled pairs
anime_metaphor_engine.jsonl 4,523 Cross-domain metaphor engine outputs
forge_bloom 2,117 Forge pipeline bloom outputs
capability/reasoning/seed.jsonl 1,232 Reasoning capability seed data
fleet_toolforge.jsonl 187 Fleet tool use pairs
Others (30+ sources) 944 Security, orchestration, calibration, refusal, compliance, etc.

Label composition, measured: 11,669 rows labelled positive, 4,450 labelled negative, 58,276 unlabelled. Positive and unlabelled rows only; negative rows are excluded from SFT targets โ€” and in this pipeline they are dropped outright, before the normalised corpus is written (run_qlora_gnarpm2.py: if r.get("label") == "negative": continue). So all 4,450 refuted rows were excluded from the loss, and the model did not train on them as context, as masked targets, or as contrastive pairs.

Training Configuration

Parameter Value
Epochs 1
Learning rate 1e-4 (cosine schedule, 5% warmup)
Batch size 1
Gradient accumulation 8
Effective batch size 8
Optimizer AdamW (bf16)
Eval split 10% held out
Eval strategy Every 500 steps
Best model selection eval_loss (load_best_model_at_end)
Seed 7
Training hardware RTX 3080 Laptop (8 GB VRAM)
Training time ~13 hours

Training Results

Metric Value
Final train loss 2.613
Final eval loss 2.509
Token accuracy 55.55%
Perplexity (train) 13.64

Evaluation: Cross-Domain Transfer Benchmark v2

36 cross-domain transfer tasks spanning anime, biology, physics, economics, fiction, geography, music, cooking, ecology, martial arts, psychology, logistics, chemistry, sports, agriculture, linguistics, city planning, finance, navigation, and architecture.

Scoring: Heuristic rubric (keyword + structural analysis). Trust DELTAS between models on the same tasks, not absolutes.

Model Judge Mean Delta vs Base Avg Response (chars) Avg Latency (s)
gnarp-m2 0.7839 +14.1% 1,108 4.9
base (SmolLM2-360M-Instruct) 0.6871 โ€” 1,489 6.9

Prior Model Lineage (heldout benchmark, qwen3:8b judge)

Model Transfer Score Heldout Loss Perplexity Refusal Rate Training Data
base 0.709 1.625 5.08 0.130 โ€”
v1 0.218 1.850 6.36 0.385 ~2,152 rows
v2 0.713 1.800 6.05 0.340 ~2,152 rows
v3 0.561 1.790 5.99 0.400 ~2,152 rows
m2 0.7839* โ€” โ€” not measured 69,945 trained (of 74,395 read)

*m2 scored on transfer_benchmark_v2 (heuristic-only), not the qwen3:8b-judged heldout benchmark. Cross-benchmark comparisons should be treated with caution. The lineage is also not monotonic on this table: v1 (0.218) scores far below base (0.709). The 10%โ†’26%โ†’31%โ†’37% progression quoted elsewhere refers to a different eval (animeโ†’architecture, two LLM judges) and should not be read as the heldout benchmark.

m2's refusal rate was never measured. The row is blank because no computation was performed, not because the result was unfavourable. The 0.130โ†’0.400 figure sometimes quoted alongside this model belongs to the earlier lineage (baseโ†’v3) and does not describe m2. Calibration drift for m2 is therefore unmeasured.

Corrections

Two claims previously made about this model did not survive re-reading its own artefacts, and are recorded here rather than quietly edited.

  1. "Trained on data that includes the claims the system refuted against itself." False, and the opposite of what happens. The trainer discards every row labelled negative before the normalised corpus is written; 4,450 of 74,395 rows carry that label. The model never saw a refuted example, so it cannot have learnt "the shape of a wrong answer" from one. Any downstream claim resting on contrastive training over refutations does not hold for this checkpoint.

  2. "failure_class from the 17,801-row moat." The moat corpus does carry a non-empty failure_class on all 17,801 of its rows. The corpus that trained this model carries the field on zero of its rows, and the normaliser emits only instruction/input/output, so such a label could not have reached the loss even if present. The only per-row taxonomy in the corpus is verdict_raw, with four values (VERIFIED/GENERATIVE 7,758; REFUTED/DECORATIVE 4,231; GENERATIVE 3,160; REFUTED 12). The fine-grained error grammar is absent from the training signal, not collapsed within it.

  3. Row count. The headline figure of 74,395 is the pre-filter count; 69,945 rows were train-eligible. Every row in the corpus also carries train_eligible: true, including the 4,450 the trainer discards โ€” two mechanisms in one pipeline disagreeing about the same fact.

Not yet run: the ablation that would separate the value of the corpus structure from the value of the labels โ€” same rows, failure labels stripped or shuffled, same benchmark. It cannot be run as originally stated, because the labels it would strip are not in the corpus.

Limitations

  1. 360M parameters. Small model. Cannot match larger models on complex reasoning, long-form generation, or nuanced instruction following.
  2. Single GPU, single epoch. Trained on consumer hardware (RTX 3080 8GB) for one epoch. More training could improve results but risks overfitting.
  3. Heuristic eval. The transfer benchmark v2 uses keyword/structural heuristic scoring, not a strong LLM judge. The +14.1% delta is directionally meaningful but not precisely calibrated.
  4. Cross-benchmark caveat. v1/v2/v3 were scored with a qwen3:8b judge; m2 was scored with heuristic-only. Direct numerical comparison across the two benchmarks is not valid.
  5. Domain-specific training data. Over 71% of training data is isomorphism pairs. The model is optimized for cross-domain structural transfer and may underperform on general chat or coding tasks.
  6. No safety fine-tuning beyond refusal data. The model includes 19 refusal pairs but is not extensively safety-tuned.

How to Use

With Ollama (recommended for local inference)

# Create the Modelfile
cat > Modelfile << 'EOF'
FROM ./model/merged_gnarpm2
TEMPLATE """### Instruction: {{ .Prompt }} ### Response: """
PARAMETER num_ctx 4096
PARAMETER temperature 0.3
PARAMETER num_predict 512
SYSTEM You are gnarp-m2, a cross-domain transfer specialist.
EOF

ollama create gnarp-m2 -f Modelfile
ollama run gnarp-m2

With Transformers

from transformers import AutoModelForCausalLM, AutoTokenizer

model_id = "gnarp/gnarp-m2"  # or local path
tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForCausalLM.from_pretrained(model_id, device_map="auto")

prompt = "### Instruction:\nApply the concept of biological apoptosis to software deployment strategy.\n### Response:\n"
inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
outputs = model.generate(**inputs, max_new_tokens=512, temperature=0.3)
print(tokenizer.decode(outputs[0], skip_special_tokens=True))

With PEFT (adapter only)

from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import PeftModel

base = AutoModelForCausalLM.from_pretrained("HuggingFaceTB/SmolLM2-360M-Instruct")
model = PeftModel.from_pretrained(base, "path/to/adapter_gnarpm2")
model = model.merge_and_unload()

License

  • Model weights and code: CatQualia Open-Or-Pay License (COPL) v1.0 โ€” free for research, individuals and academia with attribution; commercial use requires either opening the derivative stack or a commercial waiver.
  • Training data (corpus): CatQualia Structural Isomorphism License (CSIL) v3.0 โ€” derived from the WaveMotionExpansion isomorphism engine.
  • Base model: Apache 2.0 (SmolLM2-360M-Instruct by HuggingFace)

Full terms in this repository: LICENSE (CSIL v3.0) and COPL-LICENSE.md (COPL v1.0). Both also at https://catqualia.com/licensing

Citation

If you use gnarp-m2, please cite the model together with the publication that describes the corpus it was trained on.

@model{gnarp-m2,
  title={gnarp-m2: Cross-Domain Transfer Fine-Tuned Language Model},
  author={Betances, Christopher},
  year={2026},
  base_model={HuggingFaceTB/SmolLM2-360M-Instruct},
  method={QLoRA},
  training_rows={69945},
  corpus_rows_read={74395},
  transfer_benchmark={0.7839},
  url={https://huggingface.co/CatQualia/gnarp-m2}
}

Related records:

Downloads last month
949
Safetensors
Model size
0.4B params
Tensor type
F32
ยท
Inference Providers NEW
This model isn't deployed by any Inference Provider. ๐Ÿ™‹ Ask for provider support

Model tree for CatQualia/gnarp-m2

Finetuned
(180)
this model