A full SFT fine-tune of
google/gemma-4-E4B-it on the complete open-source
GLM-5.1-1000000x dataset (~614k conversations), trained with LoRA + DDP across 6× A100-40GB GPUs.
The model produces structured, step-by-step chain-of-thought reasoning in the GLM-5.1 style, covering multi-step mathematics, PhD-level science, and multilingual STEM problems.
The recommended way to run this model is via
vLLM, which gives you an OpenAI-compatible API server with full 128k context support.
1vllm serve Dhiaul/Gemma-4-E4B-GLM5.1-distill \
2 --dtype bfloat16 \
3 --max-model-len 131072 \
4 --gpu-memory-utilization 0.92 \
5 --enable-chunked-prefill \
6 --max-num-batched-tokens 8192 \
7 --served-model-name gemma4-glm \
8 --host 0.0.0.0 \
9 --port 8000
1curl http://localhost:8000/v1/chat/completions \
2 -H "Content-Type: application/json" \
3 -d '{
4 "model": "gemma4-glm",
5 "messages": [
6 {"role": "user", "content": "Prove that sqrt(2) is irrational, step by step."}
7 ],
8 "max_tokens": 2048
9 }'
1from openai import OpenAI
2
3client = OpenAI(base_url="http://localhost:8000/v1", api_key="unused")
4
5response = client.chat.completions.create(
6 model="gemma4-glm",
7 messages=[{
8 "role": "user",
9 "content": "Solve: A train travels 120 km at 60 km/h, then 80 km at 40 km/h. What is the average speed for the whole journey? Show all steps."
10 }],
11 max_tokens=1024,
12 temperature=1.0,
13 top_p=0.95,
14)
15print(response.choices[0].message.content)
1from transformers import AutoModelForCausalLM, AutoTokenizer
2import torch
3
4model_id = "Dhiaul/Gemma-4-E4B-GLM5.1-distill"
5
6tokenizer = AutoTokenizer.from_pretrained(model_id)
7model = AutoModelForCausalLM.from_pretrained(
8 model_id,
9 torch_dtype=torch.bfloat16,
10 device_map="auto",
11)
12
13messages = [{"role": "user", "content": "If f(x) = 3x² - 2x + 1, find f'(x) and explain each step."}]
14prompt = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
15inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
16
17with torch.no_grad():
18 outputs = model.generate(
19 **inputs,
20 max_new_tokens=1024,
21 do_sample=True,
22 temperature=1.0,
23 top_p=0.95,
24 )
25
26response = outputs[0][inputs.input_ids.shape[1]:]
27print(tokenizer.decode(response, skip_special_tokens=True))
GLM-5.1-1000000x is an open-source (Apache 2.0) dataset of distilled long-form reasoning traces in the GLM style. We used all three available subsets:
1LoraConfig(
2 r=16,
3 lora_alpha=32,
4 lora_dropout=0.05,
5 target_modules=["q_proj", "k_proj", "v_proj", "o_proj",
6 "gate_proj", "up_proj", "down_proj"],
7 # Gemma 4 vision/audio towers use Gemma4ClippableLinear — not LoRA-injectable
8 exclude_modules=r".*(?:vision_tower|audio_tower|embed_vision|embed_audio).*",
9 bias="none",
10 task_type="CAUSAL_LM",
11)
1SFTConfig(
2 num_train_epochs=1,
3 per_device_train_batch_size=1,
4 per_device_eval_batch_size=1,
5 gradient_accumulation_steps=8, # effective batch = 1 × 8 × 6 GPUs = 48
6 learning_rate=2e-4,
7 lr_scheduler_type="cosine",
8 warmup_ratio=0.03,
9 max_length=2048,
10 bf16=True,
11 gradient_checkpointing=True,
12 optim="adamw_torch_fused",
13 ddp_find_unused_parameters=False,
14)
Key non-obvious detail for multi-GPU reproduction: pre-tokenize your parquet files before training (scripts/tokenize_parquet.py). TRL's dataset preparation runs inside a main_process_first() barrier — rank 0 tokenizes all 614k rows while other ranks wait at an NCCL barrier. Without pre-tokenization this takes ~30 minutes and hits PyTorch's 1800s NCCL timeout, killing the job silently.
This fine-tune inherits the
Gemma license from the base model. The training dataset is Apache 2.0.