CoolFace
Modelpublic

CraneAILabs/edu-ganda-gemma-e2b-v5

sourceHugging Facegemmaupdated 22d agoView on Hugging Face
0likes844downloads
Model Card

edu-ganda-gemma-e2b-v5 (EXPERIMENTAL — current best)

Experimental research checkpoint — Crane AI Labs' current best Luganda primary-education assistant. A SLERP merge (alpha 0.5) of two Crane experimental Gemma-4-E2B checkpoints:

  • —a065_polished_v2 — Luganda-vocabulary-repaired base (good Luganda content, weak instruction-following), and
  • —ganda-e2b-v4 (edu-ganda-gemma-e2b-v4) — strong instruction-following (weaker Luganda content).

The merge combines V2's Luganda accuracy with v4's instruction-following, and beats V2, every finetuning attempt (GRPO/SFT), and every alternative merge we tried (including pre-QAT-v4 merges).

Good at (guard-free, vs the a065polishedv2 base)

Metricv5 mergeV2 base
Instruction-following (IF-adherence)65.9%40.9% (+25)
Judge task quality /104.63.76
Judge Luganda /107.847.52
Math (mn100)72%70.7%
Doom (repetition)lower than base–

Bad at

  • —FLORES lug→en translation — chrF about −3.3 vs base.
  • —Guarded followed% caps around ~44% (n=25, a coarse judge metric).

Required serving settings (OPTIMAL — from a guard sweep)

  • —`repetition_penalty=1.0` (off) + `no_repeat_ngram_size=4` → doom 0/0, math 68.3, IF 68.3. This is the recommended guard. The older 1.15 / 3 guard is over-tuned and costs roughly −7 pt math and −3 pt IF for no doom benefit here.
  • —eos_token_id=[<eos>, <end_of_turn>].
  • —Prepend <bos> (id 2) before the chat-templated prompt.

Base models

  • —a065_polished_v2 (Crane experimental, Luganda-vocab-repaired)
  • —ganda-e2b-v4 = CraneAILabs/edu-ganda-gemma-e2b-v4 (Crane experimental)

Experimental research checkpoint. Best-effort Luganda content; review before high-stakes use.

Usage

python
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer

MODEL = "CraneAILabs/edu-ganda-gemma-e2b-v5"
tok = AutoTokenizer.from_pretrained(MODEL)
model = AutoModelForCausalLM.from_pretrained(MODEL, torch_dtype=torch.bfloat16, device_map="auto").eval()
eos_ids = [tok.eos_token_id] + tok.convert_tokens_to_ids(["<end_of_turn>"])

def chat(prompt, max_new_tokens=256):
    text = tok.apply_chat_template([{"role": "user", "content": prompt}],
                                   add_generation_prompt=True, tokenize=False)
    inputs = tok(text, return_tensors="pt").to(model.device)
    out = model.generate(**inputs, max_new_tokens=max_new_tokens, do_sample=False,
                         repetition_penalty=1.0, no_repeat_ngram_size=4,   # guard: prevents loops
                         eos_token_id=eos_ids, pad_token_id=tok.pad_token_id or tok.eos_token_id)
    return tok.decode(out[0, inputs.input_ids.shape[1]:], skip_special_tokens=True).strip()

print(chat("Nnyonnyola engeri y'okunaaba engalo mu Luganda."))

Recommended decoding: greedy with repetition_penalty=1.0, no_repeat_ngram_size=4 (best doom/math/instruction-following tradeoff from a guard sweep), eos_token_id=[<eos>, <end_of_turn>]. The n-gram guard is needed to prevent loops.