File size: 7,507 Bytes
ba659db
228ef6a
 
ba659db
228ef6a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ba659db
228ef6a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
---
language:
- so
license: apache-2.0
tags:
- summarization
- headline-generation
- text2text-generation
- somali
- t5
- afriteva
datasets:
- custom
metrics:
- rouge
- sacrebleu
- meteor
- bertscore
pipeline_tag: summarization
---

# 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](https://huggingface.co/castorini/afriteva_v2_base), 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

```bash
pip install transformers torch evaluate sentencepiece
```

### Quick Start

```python
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:

```python
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):

```python
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:

```bibtex
@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](https://huggingface.co/castorini/afriteva_v2_base) by Castorini, and the Hugging Face `transformers` and `evaluate` libraries.