Somali Headline Generation (Fine-tuned AfriTeVa v2)

This model generates short, natural Somali news headlines from full article text. It is a fine-tuned version of AfriTeVa v2, a T5-style sequence-to-sequence model pretrained on African languages, adapted here specifically for Somali headline generation.

Model Description

  • Base model: AfriTeVa v2 (T5-style, SentencePiece tokenizer)
  • Task: Abstractive headline generation (text2text-generation)
  • Language: Somali (so)
  • Input: A Somali news article (plain text)
  • Output: A short, cleaned headline summarizing the article

The model was fine-tuned on a dataset of Somali news articles paired with their original headlines, and evaluated using ROUGE, SacreBLEU, METEOR, and BERTScore.

Intended Uses & Limitations

Intended uses:

  • Automatic headline suggestion for Somali news articles
  • Research on low-resource language summarization/generation
  • Educational and non-commercial NLP experimentation

Limitations:

  • Trained on a specific news domain/style; may not generalize well to other domains (e.g., opinion pieces, social media text)
  • May occasionally produce generic or repetitive headlines for very long or unusual articles
  • Not intended for use in high-stakes or safety-critical decision-making
  • Output quality depends on input article length and clarity; performs best on articles under ~300 tokens (the model's max source length)

How to Use

Installation

pip install transformers torch evaluate sentencepiece

Quick Start

from transformers import AutoTokenizer, AutoModelForSeq2SeqLM, T5Tokenizer
import torch

MODEL_NAME = "your-username/your-model-name"  # replace with the actual repo id

# Load tokenizer (SentencePiece preferred; falls back to AutoTokenizer)
try:
    tokenizer = T5Tokenizer.from_pretrained(MODEL_NAME, use_fast=False)
except Exception:
    tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME, use_fast=True)

if tokenizer.pad_token is None and tokenizer.eos_token is not None:
    tokenizer.pad_token = tokenizer.eos_token

# Load model
model = AutoModelForSeq2SeqLM.from_pretrained(MODEL_NAME)
model.config.pad_token_id = tokenizer.pad_token_id
model.config.eos_token_id = tokenizer.eos_token_id
if model.config.decoder_start_token_id is None:
    model.config.decoder_start_token_id = tokenizer.pad_token_id

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device).eval()

# Prepare input
article = "Halkan geli qoraalka maqaalkaaga Soomaaliga ah..."
input_text = "headline: " + article

inputs = tokenizer(
    input_text,
    return_tensors="pt",
    truncation=True,
    max_length=300,
).to(device)

# Generate
with torch.inference_mode():
    output_ids = model.generate(
        **inputs,
        max_new_tokens=30,
        min_new_tokens=4,
        num_beams=4,
        no_repeat_ngram_size=3,
        length_penalty=1.3,
        repetition_penalty=1.15,
        pad_token_id=tokenizer.pad_token_id,
        eos_token_id=tokenizer.eos_token_id,
    )

headline = tokenizer.decode(output_ids[0], skip_special_tokens=True)
print(headline)

Recommended Post-Processing

Raw model output can sometimes contain trailing boilerplate (e.g., source website names, "akhriso", "daawo", stray URLs). We recommend applying a light cleanup step before displaying the headline to end users:

import re

_DOMAINS = r"(?:com|net|org|so|co|tv|fm|info|news|biz|press)"
_TAIL_WORDS = r"(?:akhriso|daawo|dhegayso|dhegeyso|sawirro?|sawiro?|fiiri|video|maqal|muuqaal)"

def clean_headline(h: str) -> str:
    if not isinstance(h, str):
        return h
    h = h.strip()
    h = re.sub(r"https?://\S+", "", h, flags=re.I)                          # remove URLs
    h = re.sub(rf"\b[\w.-]+\.{_DOMAINS}\S*", "", h, flags=re.I)             # remove bare domains
    h = re.sub(rf"\s*[-โ€“โ€”]\s*\b\w+\.{_DOMAINS}\S*$", "", h, flags=re.I)     # trailing "- site.com"
    h = re.sub(rf"\s*\b(?:{_TAIL_WORDS})\b\.?\s*$", "", h, flags=re.I)      # trailing tail words

    if "." in h:
        h = h.split(".", 1)[0]  # keep first sentence only

    h = re.sub(r"\s+([,.;:!?])", r"\1", h)      # fix spacing before punctuation
    h = re.sub(r"([,.;:!?])([^\s])", r"\1 \2", h)  # ensure spacing after punctuation
    h = re.sub(r"\s+", " ", h).strip()

    # remove a dangling trailing connector/particle
    h = re.sub(r"\s+\b(ayaa|ahaa|ka|ku|buu|ayuu|iyo|waxa)\b$", "", h, flags=re.I).strip()

    if h and h[-1] not in ".!?":
        h += "."
    if h:
        h = h[0].lower() + h[1:]
    return h

headline = clean_headline(headline)
print(headline)

Batch Inference

For generating headlines over a full dataset (e.g., a test_df with article and headline columns):

import pandas as pd

@torch.inference_mode()
def batched_generate(texts, batch_size=16):
    preds = []
    for i in range(0, len(texts), batch_size):
        batch_texts = ["headline: " + t for t in texts[i:i + batch_size]]
        enc = tokenizer(
            batch_texts,
            return_tensors="pt",
            padding=True,
            truncation=True,
            max_length=300,
        ).to(device)

        out = model.generate(
            **enc,
            max_new_tokens=30,
            min_new_tokens=4,
            num_beams=4,
            no_repeat_ngram_size=3,
            length_penalty=1.3,
            repetition_penalty=1.15,
            pad_token_id=tokenizer.pad_token_id,
            eos_token_id=tokenizer.eos_token_id,
        )
        for seq in out:
            text = tokenizer.decode(seq, skip_special_tokens=True)
            preds.append(clean_headline(text))
    return preds

test_df = pd.read_csv("your_test_set.csv")  # columns: ["article", "headline"]
predictions = batched_generate(test_df["article"].astype(str).tolist())

test_df["predicted_headline"] = predictions
test_df.to_csv("predictions.csv", index=False)

Evaluation

The model was evaluated on a held-out test set using the following metrics:

Metric Description
ROUGE-1 / ROUGE-2 / ROUGE-L / ROUGE-Lsum N-gram and longest-common-subsequence overlap
SacreBLEU BLEU score with standardized tokenization
METEOR Alignment-based metric accounting for synonyms/stems
BERTScore (P/R/F1) Semantic similarity via xlm-roberta-large embeddings

Training Details

  • Base checkpoint: castorini/afriteva_v2_base
  • Max source length: 300 tokens
  • Max target length: 30 tokens
  • Task prefix: "headline: "
  • Generation strategy: beam search (num_beams=4), with repetition penalty and n-gram blocking to reduce repeated phrases

Citation

If you use this model, please cite the base AfriTeVa v2 model and, optionally, this fine-tuned checkpoint:

@misc{afriteva_v2,
  title  = {AfriTeVa V2: African Text-to-Text Transformer},
  author = {Oladipo, Akintunde and others},
  year   = {2023},
  url    = {https://huggingface.co/castorini/afriteva_v2_base}
}

Acknowledgements

Built on top of AfriTeVa v2 by Castorini, and the Hugging Face transformers and evaluate libraries.

Downloads last month
69
Safetensors
Model size
0.4B params
Tensor type
F32
ยท
Inference Providers NEW
This model isn't deployed by any Inference Provider. ๐Ÿ™‹ Ask for provider support