Downloads · 30 days
33
10% of all-time downloads
andre15silva/pt-es-hi-sft
pt-es-hi-sft is a text generation model from andre15silva. Use it when you need the model to write or continue text. It is set up for pytorch. The card lists the license as apache-2.0.
A 138M-parameter (205M including embeddings) decoder-only GPT for Portuguese, Spanish and Hindi, instruction-tuned from a pretrained checkpoint. Trained from scratch as a teaching/research exercise — it is small, and…
Downloads · 30 days
33
10% of all-time downloads
All-time downloads
318
Public
Parameters
172M
690 MB on disk
Likes
0
Public
Click a slice to open those files.
.safetensors690 MB · 100%
From the Hugging Face model README
A 138M-parameter (205M including embeddings) decoder-only GPT for Portuguese, Spanish and Hindi, instruction-tuned from a pretrained checkpoint. Trained from scratch as a teaching/research exercise — it is small, and the limitations below are the interesting part of this card.
This is not a transformers model. The architecture (learned
positional embeddings and qk-norm) matches no stock HF class, so
AutoModel.from_pretrained will not load it. Load it against the
training repo:
import json, torch
from huggingface_hub import hf_hub_download
from safetensors.torch import load_file
from src.model.gpt import GPT, GPTConfig # from the training repo
from transformers import AutoTokenizer
repo = "andre15silva/pt-es-hi-sft"
cfg = json.load(open(hf_hub_download(repo, "config.json")))
model = GPT(GPTConfig(**{k: cfg[k] for k in
("vocab_size", "block_size", "n_layer", "n_head", "n_embd", "dropout", "bias", "qk_norm")}))
model.load_state_dict(load_file(hf_hub_download(repo, "model.safetensors")), strict=False) # lm_head is tied
model.eval()
tok = AutoTokenizer.from_pretrained(repo)
prompt = tok.apply_chat_template(
[{"role": "user", "content": "Olá! Como estás?"}], tokenize=False, add_generation_prompt=True)
ids = torch.tensor([tok(prompt, add_special_tokens=False)["input_ids"]])
print(tok.decode(model.generate(ids, max_new_tokens=80, temperature=0.7, top_k=40)[0]))
strict=False is required: lm_head.weight is tied to
transformer.wte.weight and is therefore not stored separately.
GPT.__init__ re-ties them.
Always use the chat template. The model was fine-tuned on it exclusively
and degrades on bare text, leaking <|eot_id|><|start_header_id|> into
its output. Turns end with <|eot_id|>, not the tokenizer's
<|end_of_text|> — stop generation on both.
| Base checkpoint | pretrained on pt/es/hi, 2.2e18 FLOPs, iteration 240,204 |
| Architecture | 11 layers, 16 heads, n_embd 1024, block_size 1024, qk-norm, tied embeddings |
| Vocabulary | 32,000 (SentencePiece-style BPE trained on the same pt/es/hi corpus) |
| SFT data | 158,834 conversations / 90.9M tokens |
| Schedule | 2 epochs (19,854 steps), batch 16, lr 5e-5 → 5e-6, warmup 397, bf16 |
| Hardware | 1× A100-40GB, 3h24m |
| Loss | val 2.723 → 1.606 (perplexity 4.98), still falling at the end |
Loss is computed on assistant tokens only; every other position is masked
to -1. One conversation per batch row, right-padded to the longest in
the batch, with length-grouped batching (98.4% of the padded tensor was
real tokens).
| source | tokens | share |
|---|---|---|
aya_hi (templated headline + news) | 30.3M | 33.3% |
utter-project/EuroBlocks-SFT-2512 (es) | 17.8M | 19.6% |
utter-project/EuroBlocks-SFT-2512 (pt) | 14.1M | 15.5% |
duarteocarmo/smoltalk2PT (4 configs) | 26.7M | 29.4% |
ai4bharat/indic-instruct-data-v0.1 (hh-rlhf/hi) | 1.8M | 2.0% |
By language: pt 44.9%, hi 35.3%, es 19.6%.
48.7% of rendered conversations (154,049 of 316,124) were dropped for
exceeding the 1,024-token context window — a hard ceiling from the base
model's learned positional embeddings. The filter is not uniform:
magpie_ultra, the multi-turn general-instruction source, lost 88% of
its rows; smol_rewrite lost 2.
Base checkpoint vs this model, n=500 per task, zero-shot. "chat" is the chat template; "raw" is plain ICL text.
| benchmark | metric | base·raw | SFT·raw | base·chat | SFT·chat |
|---|---|---|---|---|---|
| PT-Culture | token_f1 | 0.177 | 0.169 | 0.193 | 0.292 |
| ChatRAG-Hi | token_f1 | 0.285 | 0.304 | 0.262 | 0.233 |
| CALAME-PT | accuracy | 0.408 | 0.422 | — | — |
| Portugal Basic QA (n=50) | accuracy | 0.560 | 0.520 | 0.480 | 0.420 |
| Belebele-es | acc | 0.260 | 0.222 | 0.240 | 0.230 |
| COPA-es | acc | 0.556 | 0.526 | 0.526 | 0.522 |
| OpenBookQA-es | acc | 0.200 | 0.190 | 0.182 | 0.172 |
| XStoryCloze-es | acc | 0.532 | 0.516 | 0.514 | 0.514 |
Reading these honestly:
It behaves like an assistant. It does not know very much.
"Olá! Como estás?" → "Olá! Estou bem, obrigado por perguntar.
E você, como está? 😊" ✓ replies, stops
"Qual é a capital de França?" → "A capital de França é Paris." ✓
"Qual é a capital de Portugal?" → "A capital de Portugal é Porto de Ave." ✗
Lisboa never appeared across five seeds. The base model can complete
"Lisboa é a capital de …" → "Portugal" correctly, but neither
checkpoint answers the question form — a reversal-curse failure inherited
from pretraining, not caused by SFT. Fine-tuning made confident
fabrication more fluent: wrong answers arrive in the same assured,
markdown-formatted register as correct ones.
Other known limitations:
uv run scripts/prepare_sft_data.py --tokenizer-dir <tokenizer> \
--out-dir data/sft --max-example-tokens 1024
uv run scripts/train_sft.py --init-from <pretrain ckpt> --data-dir data/sft \
--out-dir runs/sft --block-size 1024 --batch-size 16 --max-iters 19854 \
--lr 5e-5 --min-lr 5e-6 --wandb
See scripts/slurm/13_prepare_sft_data.sh and 14_train_sft.sh in the
training repo for the cluster versions with the derived iteration counts.