Instructions to use kessenma/gemma4-e2b-german-tutor-4bit with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- MLX
How to use kessenma/gemma4-e2b-german-tutor-4bit with MLX:
# Make sure mlx-vlm is installed # pip install --upgrade mlx-vlm from mlx_vlm import load, generate from mlx_vlm.prompt_utils import apply_chat_template from mlx_vlm.utils import load_config # Load the model model, processor = load("kessenma/gemma4-e2b-german-tutor-4bit") config = load_config("kessenma/gemma4-e2b-german-tutor-4bit") # Prepare input image = ["http://images.cocodataset.org/val2017/000000039769.jpg"] prompt = "Describe this image." # Apply chat template formatted_prompt = apply_chat_template( processor, config, prompt, num_images=1 ) # Generate output output = generate(model, processor, formatted_prompt, image) print(output) - Transformers
How to use kessenma/gemma4-e2b-german-tutor-4bit with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("image-text-to-text", model="kessenma/gemma4-e2b-german-tutor-4bit") messages = [ { "role": "user", "content": [ {"type": "image", "url": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/p-blog/candy.JPG"}, {"type": "text", "text": "What animal is on the candy?"} ] }, ] pipe(text=messages)# Load model directly from transformers import AutoProcessor, AutoModelForMultimodalLM processor = AutoProcessor.from_pretrained("kessenma/gemma4-e2b-german-tutor-4bit") model = AutoModelForMultimodalLM.from_pretrained("kessenma/gemma4-e2b-german-tutor-4bit", device_map="auto") messages = [ { "role": "user", "content": [ {"type": "image", "url": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/p-blog/candy.JPG"}, {"type": "text", "text": "What animal is on the candy?"} ] }, ] inputs = processor.apply_chat_template( messages, add_generation_prompt=True, tokenize=True, return_dict=True, return_tensors="pt", ).to(model.device) outputs = model.generate(**inputs, max_new_tokens=40) print(processor.decode(outputs[0][inputs["input_ids"].shape[-1]:])) - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- LM Studio
- vLLM
How to use kessenma/gemma4-e2b-german-tutor-4bit with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "kessenma/gemma4-e2b-german-tutor-4bit" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "kessenma/gemma4-e2b-german-tutor-4bit", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }'Use Docker
docker model run hf.co/kessenma/gemma4-e2b-german-tutor-4bit
- SGLang
How to use kessenma/gemma4-e2b-german-tutor-4bit with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "kessenma/gemma4-e2b-german-tutor-4bit" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "kessenma/gemma4-e2b-german-tutor-4bit", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "kessenma/gemma4-e2b-german-tutor-4bit" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "kessenma/gemma4-e2b-german-tutor-4bit", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }' - Unsloth Studio
How to use kessenma/gemma4-e2b-german-tutor-4bit with Unsloth Studio:
Install Unsloth Studio (macOS, Linux, WSL)
curl -fsSL https://unsloth.ai/install.sh | sh # Run unsloth studio unsloth studio -H 0.0.0.0 -p 8888 # Then open http://localhost:8888 in your browser # Search for kessenma/gemma4-e2b-german-tutor-4bit to start chatting
Install Unsloth Studio (Windows)
irm https://unsloth.ai/install.ps1 | iex # Run unsloth studio unsloth studio -H 0.0.0.0 -p 8888 # Then open http://localhost:8888 in your browser # Search for kessenma/gemma4-e2b-german-tutor-4bit to start chatting
Using HuggingFace Spaces for Unsloth
# No setup required # Open https://huggingface.co/spaces/unsloth/studio in your browser # Search for kessenma/gemma4-e2b-german-tutor-4bit to start chatting
Load model with FastModel
pip install unsloth from unsloth import FastModel model, tokenizer = FastModel.from_pretrained( model_name="kessenma/gemma4-e2b-german-tutor-4bit", max_seq_length=2048, ) - Docker Model Runner
How to use kessenma/gemma4-e2b-german-tutor-4bit with Docker Model Runner:
docker model run hf.co/kessenma/gemma4-e2b-german-tutor-4bit
Gemma 4 E2B German Tutor (4-bit MLX)
A German grammar-correction fine-tune of Gemma 4 E2B, 4-bit MLX quantized (3.4 GB on disk,
**2.7 GB peak RAM** during a normal turn). It is the small sibling of
kessenma/gemma4-e4b-german-tutor-4bit,
trained on the identical dataset, and exists so phones that can't afford the ~4.3 GB E4B model
still get a usable tutor.
Trained for one narrow job: look at one sentence a learner wrote and either accept it or return a correction, in a fixed format. Four target areas — verbs with fixed prepositions (warten auf), separable verbs (aufstehen), reflexive verbs incl. the accusative/dative split, and da-/wo-compounds (darauf/worauf).
⚠️ Read this before you use it: you must filter echoed corrections
This model's dominant failure is emitting FIX: <the input, unchanged> with a confabulated reason
attached, instead of saying OK. On a 32-item held-out set of already-correct sentences, 19 came
back "corrected" — and 18 of those 19 were the input verbatim.
That single behaviour is the difference between this model being unusable and being good:
| scored as | core suite | extension suite | false corrections | missed errors |
|---|---|---|---|---|
| raw output | 68% | 70% | 59% | 19% |
| with an echo filter | 83% | 85% | 3% | 19% |
The filter is a few lines and cannot cost you anything — it only ever converts a no-op "fix" into an accept:
import re
def apply_echo_filter(response: str, original: str) -> str:
"""A FIX line that restates the input is not a correction."""
def norm(s: str) -> str:
s = re.sub(r"\s+", " ", s.strip().strip("\"“”„'"))
return s.rstrip(".!?").strip().casefold()
m = re.search(r"FIX:\s*(.+)", response)
if m and norm(m.group(1)) == norm(original):
return "OK"
return response
Two things to get right if you reimplement it: keep it diacritic-sensitive (Madchen →
Mädchen is a real correction, not an echo), and fold trailing punctuation (models echo the
sentence and add a period).
This is not unique to this model — stock Gemma 4 E2B, stock E4B, Ministral 8B, Aya Expanse 8B and Apple's on-device model all show it, several far worse. It just matters most here, because without the filter the headline number looks disqualifying and with it the model is shippable.
Prompt format
System prompt (the correction task it was trained on):
You are a meticulous German teacher reviewing one line a student said during a spoken conversation.
The student's level is B1. Correct clear grammar mistakes, but ignore minor style issues.
The student uses the du form.
They may have mixed in an English word they didn't know — in your correction, replace it with the correct German word.
If the sentence is already correct and natural German, reply with exactly:
OK
Otherwise reply in EXACTLY this format and nothing else:
FIX: <the full corrected sentence in natural German>
WHY: <one short explanation in English, at most 18 words>
User turn: The student said: "<sentence>"\n\nEvaluate the student's sentence.
It was also trained on multi-turn tutor dialogue and flashcard-JSON generation, but the correction task is the one that's measured here.
Results
Held-out suites (60-item core over the four target areas, 61-item extension over ten untargeted areas), against the stock model, both as shipped 4-bit quantizations. All numbers with the echo filter applied to both sides:
| stock E2B | this model | |
|---|---|---|
| core four areas | 73% | 83% |
| extension (untargeted) | 85% | 85% |
| false corrections (n=32) | 0% | 3% |
| missed / mis-fixed errors (n=69) | 28% | 19% |
The fine-tune buys ~10 points of accuracy and cuts missed errors by a third, for one spurious correction in 32. Untargeted areas held level, so the general-instruction mix-in did its anti-forgetting job.
Known weaknesses
- Separable verbs regressed against the stock model (11/15 → 8/15). It sometimes asserts a separable verb is inseparable. If your use case centres on separable verbs, measure before adopting.
- One genuine bad rewrite survives the echo filter in the eval: it "corrects"
Kannst du bitte das Fenster zumachen?to…zuschlagen?— swapping "close" for "slam", changing the meaning. - The
WHY:explanations are less reliable than theFIX:line. It produces confabulated rules (e.g. calling aufstehen inseparable). Treat the explanation as the weakest part of the output — in the app it originates from, the note is discarded whenever the fix is. - Reflexives improved most (8/15 → 12/15) but the accusative/dative distinction remains the hardest area in the family.
- Judged only on German correction. No claims about anything else.
Training
QLoRA via Unsloth on unsloth/gemma-4-E2B-it: rank 8, alpha 16, lr 2e-4, 2 epochs, 362 steps,
1,447 examples (960 targeted German items + ~35% general German instruction data). Train loss
0.57 → low 0.2s, held-out eval loss flat ~0.75. ~26 minutes on a rented RTX 4000 Ada.
Data is synthetic but gated: a curated 268-entry verb+preposition table, 6,526 separable and 1,604 reflexive verbs from German Wiktionary, and seed sentences mined from Tatoeba, generated in the app's exact prompt formats and validated with self-hosted LanguageTool + spaCy checks, dedup, and a guard against held-out eval overlap.
Converted with mlx_vlm convert — mlx_lm.convert cannot convert Gemma 4 (it looks for
KV-projection weights the checkpoint legitimately omits).
Notes for MLX Swift users
An inference repo must contain exactly one model.safetensors; MLX Swift recursively merges
every *.safetensors it finds, so a stray adapter folder breaks loading with
Unhandled keys ["base_model"]. This repo is clean.
License & attribution
Apache 2.0, inherited from Gemma 4. Built with Google's Gemma; fine-tuned with Unsloth. Training data derives from German Wiktionary (CC-BY-SA) and Tatoeba (CC-BY 2.0 FR).
- Downloads last month
- 55
4-bit
Model tree for kessenma/gemma4-e2b-german-tutor-4bit
Base model
kessenma/gemma4-e2b-german-tutor