| --- |
| language: |
| - en |
| - yo |
| - ha |
| - ig |
| - pcm |
| pipeline_tag: translation |
| --- |
| |
| # STORM-OS-MT-3B-BIDIRECTIONAL |
|
|
| A single, standalone machine translation model that translates **both |
| ways** between English and Yoruba, Hausa, Igbo, and Nigerian Pidgin β |
| local language into English, and English into any of the four local |
| languages. Fine-tuned from `facebook/nllb-200-3.3B` on a combined |
| forward + reverse dataset (204,094 examples), then merged into one clean |
| set of weights. No adapter loading or toggling required. |
|
|
| Nigerian Pidgin is not natively supported by the base model β this model |
| learned pcm as both a source and a target language from scratch. |
|
|
| ## Supported languages |
|
|
| | Code | Language | |
| |------|-------------------| |
| | yo | Yoruba | |
| | ha | Hausa | |
| | ig | Igbo | |
| | pcm | Nigerian Pidgin | |
|
|
| Both directions: local-language -> English, and English -> local-language. |
|
|
| ## Loading the model |
|
|
| import torch |
| from transformers import AutoModelForSeq2SeqLM, AutoTokenizer |
| |
| MODEL_ID = "wolethereader/STORM-OS-MT-3B-BIDIRECTIONAL" |
| |
| tokenizer = AutoTokenizer.from_pretrained(MODEL_ID) |
| model = AutoModelForSeq2SeqLM.from_pretrained( |
| MODEL_ID, |
| torch_dtype=torch.bfloat16, |
| device_map="auto", |
| ) |
| model.eval() |
| |
| ## Running translation |
|
|
| LANG_CODES = { |
| "yo": "yor_Latn", |
| "ha": "hau_Latn", |
| "ig": "ibo_Latn", |
| "pcm": "pcm_Latn", |
| } |
| EN = "eng_Latn" |
| |
| def translate(text: str, src_lang: str, tgt_lang: str, max_new_tokens: int = 128) -> str: |
| tokenizer.src_lang = src_lang |
| inputs = tokenizer(text, return_tensors="pt", truncation=True, max_length=128).to(model.device) |
| tgt_id = tokenizer.convert_tokens_to_ids(tgt_lang) |
| with torch.no_grad(): |
| out = model.generate( |
| **inputs, |
| forced_bos_token_id=tgt_id, |
| max_new_tokens=max_new_tokens, |
| max_length=None, |
| ) |
| return tokenizer.decode(out[0], skip_special_tokens=True) |
| |
| ### Example usage β local language -> English |
|
|
| print(translate("Bawo ni?", LANG_CODES["yo"], EN)) |
| # -> "How are you?" |
| |
| print(translate("Na Malay guy I wan, I wan...", LANG_CODES["pcm"], EN)) |
| # -> "I want a Malay guy, I want..." |
| |
| ### Example usage β English -> local language |
|
|
| print(translate("How are you doing today?", EN, LANG_CODES["ha"])) |
| # -> "Yaya kake yau?" |
| |
| print(translate("Please help me find the hospital.", EN, LANG_CODES["pcm"])) |
| # -> "Abeg help me find di hospital." |
| |
| ## Notes on quality |
|
|
| - Both directions are strong across all four languages, including on |
| longer, real-world sentences (dates, facts, informal speech). |
| - Nigerian Pidgin works well in both directions despite having no native |
| support in the base model β trained from scratch as part of this |
| fine-tune. |
| - Igbo greetings (e.g. "Kedu?") remain a known weak spot; results can be |
| inconsistent. |
|
|
| ## Background: why this exists as a separate model |
|
|
| An earlier approach trained the reverse (English -> local language) |
| capability as a LoRA adapter on top of the already-shipped forward model |
| (`wolethereader/STORM-OS-MT-3B`), toggled per-request |
| (`wolethereader/STORM-OS-MT-3B-REVERSE`). That works, and is still in |
| production, but requires loading both a base model and an adapter and |
| switching between them per request. |
|
|
| This model was trained differently: from the raw base model, on both |
| directions combined in a single training run, so it merges cleanly into |
| one standalone set of weights with no adapter required. It's kept as its |
| own separate repo rather than replacing the original forward model, so |
| existing integrations aren't affected. |
|
|
| ## Intended use |
|
|
| Machine translation component for PlotWeaver's voice infrastructure β |
| pairing with ASR transcription for full bidirectional speech<->text |
| translation across Yoruba, Hausa, Igbo, Nigerian Pidgin, and English. |
|
|