Tag: machine learning

  • Pretrained From Scratch for Naija: Introducing mist-encoder-base-ng

    Pretrained From Scratch for Naija: Introducing mist-encoder-base-ng

    A 30.9M-parameter encoder built from the ground up for Hausa, Yoruba, Igbo, and Nigerian Pidgin — with a tokenizer that beats GPT-4o’s on all four.


    Most “African language support” in NLP is an afterthought: a massive multilingual model where Yoruba got a sliver of the training budget and the tokenizer shreds every word into fragments. Today we’re releasing something different — mist-encoder-base-ng, a compact encoder pretrained entirely from scratch on Nigerian text, open-sourced on Hugging Face under Apache 2.0.

    It covers the four major languages of Nigeria — Hausa, Yoruba, Igbo, and Nigerian Pidgin — and it’s built to be a workhorse base: load the encoder body, attach a head, and fine-tune for classification, sentiment, language ID, or sentence embeddings.

    from transformers import AutoModel, AutoTokenizer
    tok = AutoTokenizer.from_pretrained("olaverse/mist-encoder-base-ng")
    enc = AutoModel.from_pretrained("olaverse/mist-encoder-base-ng")
    

    Why from scratch?

    Fine-tuning an existing multilingual model inherits that model’s tokenizer — and for Nigerian languages, general-purpose tokenizers are quietly terrible. Every extra subword a tokenizer needs per word means less real signal fits in a fixed context window, slower inference, and weaker per-token representations.

    So before training the model, we built the tokenizer: otk-bpe-50k, a unified byte-level BPE for Naija text with a ~50k vocabulary, NFC diacritic preservation, code-mixed English support, and 0% unknown tokens. The fertility numbers (tokens per word — lower is better) speak for themselves:

    TokenizerHausaYorubaIgboPidgin
    otk-bpe-50k (ours)1.2311.2961.4161.249
    GPT-4o (o200k)1.5891.6871.8071.304
    AfroXLMR1.6042.2772.5701.401

    Our tokenizer is more efficient on Nigerian text than the tokenizer behind GPT-4o — on all four languages. On Igbo, AfroXLMR needs over 2.5 tokens per word; ours needs 1.4.

    The model

    On top of that tokenizer sits a ModernBERT architecture — 6 layers, hidden size 384, 1024 max positions — pretrained with masked language modeling on ~480k carefully prepared chunks. The corpus combines FineWeb-2, the audited WURA corpus, and a fresh Nigerian Pidgin dataset, all cross-deduplicated (FineWeb-2 and WURA both descend from Common Crawl, so overlap was real), language-balanced so abundant Hausa didn’t drown out scarce Igbo and Pidgin, and chunked into windows so no text was wasted on truncation. Every source is attribution-only licensed, which is what makes the clean Apache 2.0 release possible.

    The result is 30.9M parameters — and since the embedding table is about two-thirds of that, the transformer itself is only ~11M. For comparison: AfriBERTa is 126M, mBERT is 178M, XLM-R is 270M.

    What a 30.9M specialist can do

    On MasakhaNEWS topic classification (macro-F1), the model is genuinely competitive at a quarter of the baselines’ size:

    ModelParamsHausaYorubaIgboPidgin
    mist-encoder-base-ng30.9M0.8780.8590.8030.898
    AfriBERTa126M0.9240.9210.9140.991
    mBERT178M0.8060.8860.8050.967

    It beats mBERT on Hausa and ties it on Igbo — with under a fifth of the parameters. That efficiency is the point: this is a model you can fine-tune on a laptop and deploy on-device or in low-resource environments where 126M+ models are impractical.

    What it can’t do (yet) — and exactly why

    We publish our weaknesses with the same rigor as our wins. Named-entity recognition is this model’s honest weak spot: it trails AfriBERTa and mBERT by roughly 10–20 entity-F1 on MasakhaNER 2.0, with Hausa NER lowest at 0.656.

    And we didn’t just report the gap — we diagnosed it. The deficit is structural, not a tuning artifact: it persists across random seeds and labeling schemes. Two causes: the unified 50k tokenizer fragments entity words (person and place names) more than language-specific tokenizers — on Hausa NER text, ~61% of entity words split into multiple subwords versus ~21% for AfriBERTa — and at 30.9M parameters the model has less capacity to reassemble meaning from those fragments. If NER accuracy is critical to your application, use a larger model. If sentence-level understanding at minimal cost is what you need, this is your base.

    The model card also lays out an evidence-backed v2 roadmap: more capacity (~50–80M), a less-fragmenting tokenizer for token-level tasks, and more Pidgin and Hausa data. Notably, longer pretraining isn’t on the list — eval loss plateaued by epoch 11, so we know that lever is spent.

    Part of a bigger picture

    mist-encoder-base-ng joins the Mist Embedder collection and sits alongside our LID models (which are fine-tuned classifiers) as complementary infrastructure: a from-scratch, Nigerian-first foundation others can build on. Two fine-tunes of it already exist on the Hub, and there’s a demo Space to try it.

    • Model: https://huggingface.co/olaverse/mist-encoder-base-ng
    • Tokenizer: https://huggingface.co/olaverse/otk-bpe-50k
    • Collection: https://huggingface.co/collections/olaverse/mist-embedder

    Apache 2.0, free for commercial use. If you fine-tune it for a Nigerian-language task — sentiment, topic tagging, embeddings, anything — open a discussion on the model page and show us. That’s the whole point of releasing the base.


    Olaverse Lab builds open-source AI with first-class support for underserved languages. Explore all our models at huggingface.co/olaverse.

  • The Small Model Behind a Better Chat List: Introducing mist-tg-0.3b

    The Small Model Behind a Better Chat List: Introducing mist-tg-0.3b

    A 300M-parameter open-source model that turns a user’s first message into a clean chat title — instantly, on almost any hardware.


    Open any chat app you’ve built and look at the sidebar. If it’s full of entries like “New chat”, “Untitled”, or the raw first 40 characters of whatever the user typed, you already know the problem mist-tg-0.3b solves.

    Today we’re releasing mist-tg-0.3b, a tiny title-generation model, open-sourced on Hugging Face under Apache 2.0. Give it a user’s first message and it returns a short, readable title — the kind you’d actually want in a conversation list.

    "My laptop keeps freezing every time I open more than
    five browser tabs, any idea why?"
            ↓
    "Laptop Freezing Impact"
    

    Why a dedicated model for something so small?

    Because the obvious alternatives are all worse in practice. Truncating the first message gives ugly, uninformative titles. Calling a large LLM for every new conversation adds cost and latency to the single most frequent event in a chat product. A purpose-built small model hits the sweet spot: at ~300M parameters, mist-tg-0.3b runs comfortably on CPU, adds negligible cost per conversation, and does exactly one job well.

    This is the same philosophy behind the rest of the Mist family: not every problem needs a frontier model. Some need a fast, cheap, reliable specialist.

    How it works

    mist-tg-0.3b is fine-tuned from google/byt5-small, a byte-level seq2seq T5. It was trained for one epoch on SupraLabs/chat-titles-filtered-115K, a filtered dataset of 115K real chat messages paired with titles.

    The byte-level base turns out to matter. Because ByT5 operates on raw bytes rather than a fixed subword vocabulary, there’s no tokenizer to fight and no prompt template to remember — you pass the raw message string in, and a title comes out:

    from transformers import AutoTokenizer, T5ForConditionalGeneration
    
    tok = AutoTokenizer.from_pretrained("olaverse/mist-tg-0.3b")
    model = T5ForConditionalGeneration.from_pretrained("olaverse/mist-tg-0.3b")
    
    message = "How do I get my sourdough starter to rise faster in a cold kitchen?"
    inputs = tok(message, return_tensors="pt", truncation=True, max_length=256)
    output_ids = model.generate(**inputs, max_new_tokens=32)
    print(tok.decode(output_ids[0], skip_special_tokens=True))
    

    Inputs are capped at 256 bytes and outputs at 64 — which fits the task: a first message and a sidebar-length title.

    An unexpected bonus: Latin-script transfer

    The model was trained end-to-end on English, and English is where it’s production-ready. But byte-level modeling produced a genuinely interesting side effect: because Latin-script languages share bytes with English, same-language title extraction often just works for them — despite the model never being trained on non-English targets.

    In an informal cross-language check across 25 languages (hand-written chat-style messages, qualitative judgment rather than a scored benchmark), roughly 17–19 of 25 languages produced usable same-language titles — almost all of them Latin-script: French, German, Dutch, Vietnamese, Indonesian, Yoruba, Igbo, Hausa, Swahili, Zulu, Xhosa, Shona, Somali, Afrikaans, and more.

    Where it doesn’t work — and why we’re telling you

    We believe model cards should say what a model can’t do as plainly as what it can. mist-tg-0.3b is not reliable for non-Latin scripts. All four non-Latin-script languages we tested — Japanese, Korean, Hindi, and Amharic — failed to produce same-language output, generating unrelated English text instead. The byte-level transfer trick depends on shared script; where the bytes diverge, so does the behavior. Fixing this properly would require training on non-English targets, which is a natural direction for a future version.

    If your user base writes in CJK, Hangul, Devanagari, or Ethiopic scripts, this model isn’t the right tool yet. If your users write in English or Latin-script languages — including many African languages that rarely get this kind of support — it very likely is.

    Getting started

    • Model: https://huggingface.co/olaverse/mist-tg-0.3b
    • Training data: https://huggingface.co/datasets/SupraLabs/chat-titles-filtered-115K
    • The full Mist Generators collection: https://huggingface.co/collections/olaverse/mist-generators

    It’s Apache 2.0 — free for commercial use, no strings. If you drop it into a chat product, we’d love to hear how it holds up on your real traffic. Open a discussion on the model page and tell us.


    Olaverse Lab builds open-source AI with first-class support for underserved languages. Explore all our models at huggingface.co/olaverse, or read about mist-qg-1.5b, our multilingual question generator.

  • One Passage In, Real Questions Out: Introducing mist-qg-1.5b

    One Passage In, Real Questions Out: Introducing mist-qg-1.5b

    A compact, open-source question generator for 25 languages — and a data factory for building better retrieval systems.


    Every retrieval system, quiz app, and search engine shares a hidden hunger: it needs questions. Questions to train retrievers, questions to test rerankers, questions to turn static content into interactive learning. For English, that data exists in abundance. For most of the world’s languages, it barely exists at all.

    Today we’re releasing mist-qg-1.5b, a compact multilingual question generation model, open-sourced on Hugging Face under Apache 2.0. Give it a passage in any of 25 languages and it returns natural, search-style questions that the passage directly answers — the kind of thing a real user would actually type into a search box.

    25 languages, one small model

    Coverage spans the languages your users actually speak:

    • European: English, French, German, Spanish, Portuguese, Italian, Dutch, Russian, Polish
    • Asian & Middle Eastern: Hindi, Japanese, Korean, Vietnamese, Indonesian, Turkish
    • African: Yoruba, Igbo, Hausa, Swahili, Amharic, Zulu, Xhosa, Shona, Somali, Afrikaans

    At ~1.5 billion parameters (fine-tuned from Qwen2.5-1.5B-Instruct), it runs comfortably on a single modest GPU — no cluster required.

    Two jobs, one model

    mist-qg-1.5b is deliberately dual-use:

    1. A question-generation service. Wrap it behind an endpoint and you have instant question generation for education platforms, quiz builders, FAQ tooling, and content pipelines. The model returns strict JSON ({"questions": [...]}), so outputs slot straight into production systems — and for guaranteed structural validity, serve it with vLLM guided JSON decoding.

    2. A data factory. This is the quieter superpower. Every question it generates, paired with its source passage, is a (query, positive) training pair — exactly what you need to fine-tune retrievers and rerankers for RAG. For languages where such data has never existed at scale, this model can mint it.

    The interesting part: round-trip verification

    Question generation models often produce questions that look plausible but aren’t actually answerable from the source. We attacked this at the data level.

    Training data was distilled from the Aya collection using Qwen2.5-32B-Instruct — but every candidate question had to pass a test before entering the training set: embedded alongside its source passage and a pool of distractors, the question had to retrieve its own passage at rank one. Fail the round trip, and the question was discarded. Roughly 150k questions across ~50k passages survived this filter.

    We evaluate the same way. On 625 held-out passages never seen during training, the model achieves an overall round-trip keep-rate of 0.955 — meaning 95.5% of generated questions successfully retrieve their source passage from a distractor pool (scored with the larger Qwen3-Embedding-4B, a stricter judge than the one used in training).

    Honest numbers, including the weak spots

    Six languages — English, Spanish, Portuguese, Turkish, Indonesian, and Afrikaans — scored a perfect 1.000 on the held-out eval, and most high-resource languages cluster between 0.95 and 1.00.

    We want to be equally upfront about the other end: Amharic, Somali, and Shona sit at 0.58–0.70, and outputs in those three languages should be treated with lower confidence. Our analysis suggests this gap tracks fine-tuning data volume (~2,000 source passages per language) rather than a hard model-capacity ceiling, which makes it a natural target for the next data-scaling pass. We’re publishing these numbers because per-language transparency is the only way users can make informed decisions — and because we intend to close the gap.

    Try it in one minute

    pip install -U transformers
    
    import json, torch
    from transformers import AutoModelForCausalLM, AutoTokenizer
    
    model_id = "olaverse/mist-qg-1.5b"
    tokenizer = AutoTokenizer.from_pretrained(model_id)
    model = AutoModelForCausalLM.from_pretrained(model_id, dtype=torch.bfloat16, device_map="auto")
    
    passage = "Tides are caused by the gravitational pull of the moon and, to a lesser extent, the sun, acting on Earth's oceans."
    
    messages = [
        {"role": "system", "content": "You write search-style questions that a passage directly answers."},
        {"role": "user", "content": f'''You are given a passage. Write 3 questions that the passage directly answers.
    
    Rules:
    - Each question must be answerable using ONLY this passage.
    - Vary the type: factual, yes/no, and a comparison or "why/how".
    - Natural, like a real user search query. Do NOT write "according to the passage".
    - Write the questions in English.
    
    Return ONLY JSON: {{"questions": ["...", "...", "..."]}}
    
    Passage: {passage}'''},
    ]
    
    input_ids = tokenizer.apply_chat_template(messages, tokenize=True,
        add_generation_prompt=True, return_tensors="pt").to(model.device)
    out = model.generate(input_ids, max_new_tokens=200, do_sample=False,
        pad_token_id=tokenizer.pad_token_id or tokenizer.eos_token_id)
    text = tokenizer.decode(out[0][input_ids.shape[1]:], skip_special_tokens=True)
    print(json.loads(text[text.index("{"): text.rindex("}") + 1])["questions"])
    # ["What causes ocean tides?", "Does the sun affect tides?",
    #  "Which has a bigger effect on tides, the moon or the sun?"]
    

    Or skip the setup entirely and try the live demo Space.

    Links

    • Model: https://huggingface.co/olaverse/mist-qg-1.5b
    • Training dataset: https://huggingface.co/datasets/olaverse/qg-passages-multi
    • Demo: https://huggingface.co/spaces/olaverse/Mist_Question_Gen
    • GitHub: https://github.com/Olaverse-Labs

    If you use mist-qg-1.5b to build something — a quiz app, a retriever for a language nobody else supports, anything — we’d genuinely love to hear about it. Open a discussion on the model page or reach us on Twitter/X.


    Olaverse Lab builds open-source AI with first-class support for underserved languages. Follow us on Hugging Face and LinkedIn.