Text Generation
Transformers
Safetensors
Arabic
llama
arabic
reasoning
chain-of-thought
math
gsm8k
small-language-model
slm
sft
conversational
text-generation-inference
Instructions to use oddadmix/Nawah-Math-Reasoning with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use oddadmix/Nawah-Math-Reasoning with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="oddadmix/Nawah-Math-Reasoning") messages = [ {"role": "user", "content": "Who are you?"}, ] pipe(messages)# Load model directly from transformers import AutoTokenizer, AutoModelForCausalLM tokenizer = AutoTokenizer.from_pretrained("oddadmix/Nawah-Math-Reasoning") model = AutoModelForCausalLM.from_pretrained("oddadmix/Nawah-Math-Reasoning", device_map="auto") messages = [ {"role": "user", "content": "Who are you?"}, ] inputs = tokenizer.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(tokenizer.decode(outputs[0][inputs["input_ids"].shape[-1]:])) - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use oddadmix/Nawah-Math-Reasoning with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "oddadmix/Nawah-Math-Reasoning" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "oddadmix/Nawah-Math-Reasoning", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker
docker model run hf.co/oddadmix/Nawah-Math-Reasoning
- SGLang
How to use oddadmix/Nawah-Math-Reasoning 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 "oddadmix/Nawah-Math-Reasoning" \ --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": "oddadmix/Nawah-Math-Reasoning", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'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 "oddadmix/Nawah-Math-Reasoning" \ --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": "oddadmix/Nawah-Math-Reasoning", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }' - Docker Model Runner
How to use oddadmix/Nawah-Math-Reasoning with Docker Model Runner:
docker model run hf.co/oddadmix/Nawah-Math-Reasoning
| """ | |
| Reasoning SFT for oddadmix/50M-2048-Emhotob on Arabic_Reasoning_Dataset. | |
| ChatML format with the derivation wrapped in <think>...</think>. Loss is computed on the | |
| assistant turn only — the user prompt is masked out, same as the earlier Emhotob SFT runs. | |
| No TRL; plain HF Trainer. | |
| """ | |
| import json | |
| import os | |
| from dataclasses import dataclass | |
| from pathlib import Path | |
| import torch | |
| from torch.utils.data import Dataset | |
| from transformers import ( | |
| AutoModelForCausalLM, | |
| AutoTokenizer, | |
| Trainer, | |
| TrainingArguments, | |
| ) | |
| os.environ.setdefault("CUDA_VISIBLE_DEVICES", "0") | |
| # Defaults reproduce v1 (Arabic_Reasoning_Dataset); every value can be overridden by env var | |
| # so the same recipe can be pointed at a different corpus. | |
| def _env(name, default, cast=str): | |
| return cast(os.environ.get(name, default)) | |
| BASE_MODEL = _env("BASE_MODEL", "/notebooks/50M/50M-2048-Emhotob") | |
| OUTPUT_DIR = _env("OUTPUT_DIR", "./Nawah-Reasoning-v1") | |
| TRAIN_FILE = _env("TRAIN_FILE", "data/train.jsonl") | |
| EVAL_FILE = _env("EVAL_FILE", "data/eval.jsonl") | |
| MAX_LENGTH = _env("MAX_LENGTH", 768, int) # v1: p100 of that corpus is 708 tokens | |
| IGNORE_INDEX = -100 | |
| LEARNING_RATE = _env("LEARNING_RATE", 3e-4, float) # same as the Emhotob translation SFT ladder | |
| EPOCHS = _env("EPOCHS", 8, int) # v1 is tiny (~840k tok/epoch); best checkpoint wins | |
| BATCH_SIZE = _env("BATCH_SIZE", 16, int) | |
| GRAD_ACCUM = _env("GRAD_ACCUM", 2, int) | |
| WARMUP_STEPS = _env("WARMUP_STEPS", 100, int) | |
| EVAL_STEPS = _env("EVAL_STEPS", 100, int) | |
| # On the v3 mix, eval loss is a bad model selector: the repeated Arabic_Reasoning rows start | |
| # memorising around epoch 1.4 and drag the loss up while generation quality on *both* halves is | |
| # still improving. Set LOAD_BEST=0 there and keep the final checkpoint. | |
| LOAD_BEST = _env("LOAD_BEST", 1, int) == 1 | |
| # Point at a checkpoint dir to continue an interrupted run (optimizer/scheduler/RNG/step are | |
| # restored from it). Empty = fresh run, so v1-v5 still reproduce exactly. | |
| RESUME = _env("RESUME", "") or None | |
| WEIGHT_DECAY = 0.0 | |
| MAX_GRAD_NORM = 1.0 | |
| SEED = 42 | |
| SPECIAL_TOKENS = ["<|im_start|>", "<|im_end|>", "<think>", "</think>"] | |
| CHAT_TEMPLATE = ( | |
| "{% for message in messages %}" | |
| "{{ '<|im_start|>' + message['role'] + '\n' + message['content'] + '<|im_end|>' + '\n' }}" | |
| "{% endfor %}" | |
| "{% if add_generation_prompt %}{{ '<|im_start|>assistant\n' }}{% endif %}" | |
| ) | |
| PROMPT_TMPL = "<|im_start|>user\n{instruction}<|im_end|>\n<|im_start|>assistant\n" | |
| RESPONSE_TMPL = "<think>\n{reasoning}\n</think>\n{answer}<|im_end|>" | |
| def load_jsonl(path): | |
| with open(path, encoding="utf-8") as fh: | |
| return [json.loads(line) for line in fh] | |
| class ReasoningDataset(Dataset): | |
| """Prompt tokens are masked so loss falls only on <think>…</think> + answer.""" | |
| def __init__(self, rows, tokenizer, max_length): | |
| self.rows = rows | |
| self.tok = tokenizer | |
| self.max_length = max_length | |
| def __len__(self): | |
| return len(self.rows) | |
| def __getitem__(self, idx): | |
| row = self.rows[idx] | |
| prompt = PROMPT_TMPL.format(instruction=row["instruction"]) | |
| response = RESPONSE_TMPL.format(reasoning=row["reasoning"], answer=row["answer"]) | |
| prompt_ids = [self.tok.bos_token_id] + self.tok.encode(prompt, add_special_tokens=False) | |
| response_ids = self.tok.encode(response, add_special_tokens=False) | |
| input_ids = (prompt_ids + response_ids)[: self.max_length] | |
| prompt_len = min(len(prompt_ids), len(input_ids)) | |
| labels = [IGNORE_INDEX] * prompt_len + input_ids[prompt_len:] | |
| return { | |
| "input_ids": torch.tensor(input_ids, dtype=torch.long), | |
| "labels": torch.tensor(labels, dtype=torch.long), | |
| } | |
| class PaddingCollator: | |
| pad_token_id: int | |
| def __call__(self, features): | |
| longest = max(len(f["input_ids"]) for f in features) | |
| input_ids, labels, attention = [], [], [] | |
| for f in features: | |
| pad = longest - len(f["input_ids"]) | |
| input_ids.append(torch.cat([f["input_ids"], torch.full((pad,), self.pad_token_id, dtype=torch.long)])) | |
| labels.append(torch.cat([f["labels"], torch.full((pad,), IGNORE_INDEX, dtype=torch.long)])) | |
| attention.append(torch.cat([torch.ones(len(f["input_ids"]), dtype=torch.long), torch.zeros(pad, dtype=torch.long)])) | |
| return { | |
| "input_ids": torch.stack(input_ids), | |
| "labels": torch.stack(labels), | |
| "attention_mask": torch.stack(attention), | |
| } | |
| def main(): | |
| print("[*] loading tokenizer + base model") | |
| tok = AutoTokenizer.from_pretrained(BASE_MODEL) | |
| added = tok.add_special_tokens({"additional_special_tokens": SPECIAL_TOKENS}) | |
| tok.chat_template = CHAT_TEMPLATE | |
| print(f" added {added} special tokens -> vocab {len(tok)}") | |
| model = AutoModelForCausalLM.from_pretrained(BASE_MODEL, dtype=torch.float32) | |
| model.resize_token_embeddings(len(tok)) | |
| model.config.use_cache = False | |
| print(f" params: {sum(p.numel() for p in model.parameters()) / 1e6:.2f}M") | |
| train_rows, eval_rows = load_jsonl(TRAIN_FILE), load_jsonl(EVAL_FILE) | |
| print(f"[*] train {len(train_rows)} / eval {len(eval_rows)}") | |
| args = TrainingArguments( | |
| output_dir=OUTPUT_DIR, | |
| num_train_epochs=EPOCHS, | |
| per_device_train_batch_size=BATCH_SIZE, | |
| per_device_eval_batch_size=BATCH_SIZE, | |
| gradient_accumulation_steps=GRAD_ACCUM, | |
| learning_rate=LEARNING_RATE, | |
| lr_scheduler_type="cosine", | |
| warmup_steps=WARMUP_STEPS, | |
| weight_decay=WEIGHT_DECAY, | |
| max_grad_norm=MAX_GRAD_NORM, | |
| bf16=True, | |
| logging_steps=25, | |
| eval_strategy="steps", | |
| eval_steps=EVAL_STEPS, | |
| save_strategy="steps", | |
| save_steps=EVAL_STEPS, | |
| save_total_limit=2, | |
| load_best_model_at_end=LOAD_BEST, | |
| metric_for_best_model="eval_loss", | |
| greater_is_better=False, | |
| report_to=[], | |
| seed=SEED, | |
| dataloader_num_workers=2, | |
| remove_unused_columns=False, | |
| ) | |
| trainer = Trainer( | |
| model=model, | |
| args=args, | |
| train_dataset=ReasoningDataset(train_rows, tok, MAX_LENGTH), | |
| eval_dataset=ReasoningDataset(eval_rows, tok, MAX_LENGTH), | |
| data_collator=PaddingCollator(pad_token_id=tok.pad_token_id), | |
| ) | |
| if RESUME: | |
| print(f"[*] resuming from {RESUME}") | |
| trainer.train(resume_from_checkpoint=RESUME) | |
| print("[*] saving best checkpoint") | |
| im_end_id = tok.convert_tokens_to_ids("<|im_end|>") | |
| model.config.use_cache = True | |
| model.generation_config.eos_token_id = [tok.eos_token_id, im_end_id] | |
| model.generation_config.pad_token_id = tok.pad_token_id | |
| trainer.save_model(OUTPUT_DIR) | |
| tok.save_pretrained(OUTPUT_DIR) | |
| metrics = trainer.evaluate() | |
| print("[*] final eval:", metrics) | |
| Path(OUTPUT_DIR, "train_metrics.json").write_text( | |
| json.dumps({"final_eval": metrics, "log_history": trainer.state.log_history}, ensure_ascii=False, indent=2), | |
| encoding="utf-8", | |
| ) | |
| print(f"[+] done -> {OUTPUT_DIR}") | |
| if __name__ == "__main__": | |
| main() | |