Checklist OAI 2025 Final (Stage III) · Task 3
SOTA-AI Editorial
Translation Styling
Fine-tune an English→Polish MarianMT model so that it leaves specified machine-learning terms untranslated.
Hints
Hint 1
The target references already preserve most listed terms verbatim; first measure how often each term occurs in the source and target.
Hint 2
Fine-tune the supplied translation model on the provided pairs before adding more complicated prompt instructions.
Hint 3
Mark exact keyword spans in source text, use the same transform during training and evaluation, and leave the tokenizer unchanged.
Hint 4
Check sentence-level BLEU on released validation because the references do not preserve every listed phrase exactly.
The solution in one line
a train-only fine-tuning candidate is saved, but missing Marian weights block validation BLEU measurement; the hidden-test score is unknown.
The editorial
Translation Stylization
Problem, domain, and metric
- Domain: English-to-Polish machine translation with terminology preservation.
- Task: adapt the MarianMT model
gsarti/opus-mt-tc-en-plso selected AI/ML keywords remain in English, using the provided English/Polish training pairs and keyword lists. The evaluator callsprocess_example(en, keywords), generates a Polish hypothesis, and compares it with one reference. - Metric: the notebook averages NLTK's unsmoothed
sentence_bleuover examples. The score is 0 at BLEU ≤0.82, 100 at BLEU ≥0.86, and linear in between. The test set has no Polish references available to the solution. - Abridged statement: train a translation model and preprocess each English input so that the target's domain terms follow the corpus style of staying untranslated.
Released data and EDA
The official task notebook and datasets provide 2,808 training and 856 validation examples. Mean sentence lengths are 92.3/96.7 characters for training English/Polish and 93.1/97.0 for validation. The training set has 1,312 distinct keyword strings; validation has 993, of which 973 appear in training. Examples have one to five keywords, most often two.
Whole-phrase, case-insensitive matching finds 95.43% of training keyword occurrences in the English source and 94.81% in the Polish reference. For validation the corresponding rates are 95.13% and 93.55%. Thus inline keyword protection has a strong signal, but exact preservation is not universal; blindly forcing every phrase could disagree with some references.
Experiments and score evidence
| Experiment | BLEU / points | Status |
|---|---|---|
| Starter Marian model with no fine-tuning | Not measured | Marian weights are not cached locally; no starter output is saved in the official notebook |
| Inline keyword markers plus supervised Marian fine-tuning | Not measured | Candidate implementation saved; cannot load base weights in this environment |
The official evaluator and score function were inspected, but there is no official/released BLEU result to cite. Network requests to Hugging Face fail through the environment proxy with HTTP tunnel 403, and the model files are absent locally. This is a model-access blocker, not a BLEU failure. No validation translation targets were used for training.
Selected candidate
translation_stylization.py provides process_example and train_translation_model. Preprocessing wraps each whole-word keyword occurrence in plain-text [KEEPTERM] markers, without changing the tokenizer. Fine-tuning uses only the official training pairs, with the original Polish translation as target, full-model AdamW at 2e-5, batch size 16, and two epochs. The same preprocessing is used during training and inference.
This is an unscored candidate, not a verified 100-point solution. The official starter already loads the specified base model/tokenizer; the function expects those objects rather than downloading or modifying the tokenizer itself.
Runtime, alternatives, and caveats
The platform allows ten minutes total for training and evaluation on GPU. Runtime was not measured because the base Marian checkpoint could not be loaded and this workspace has CPU-only PyTorch. The 2,808-example training set is small, but whether two full-model epochs fit the official budget remains unverified here.
Alternatives include fine-tuning on raw English only, using LoRA, or adding a textual instruction listing terms. These require a working base checkpoint and BLEU evaluation before they can be ranked. Validation keywords and references are available for normal scoring, but the selected training routine deliberately reads only train_dataset.
Code
Solution code
"""Fine-tuning and input preprocessing for Translation Stylization (2025 OAI)."""
from __future__ import annotations
import re
import torch
from torch.utils.data import DataLoader
def _split_keywords(keywords):
if keywords is None:
return []
if isinstance(keywords, (list, tuple)):
values = keywords
else:
values = str(keywords).split(",")
return sorted({str(value).strip() for value in values if str(value).strip()},
key=len, reverse=True)
def process_example(en: str, keywords: str) -> str:
"""Mark source occurrences of technical terms for copy-preserving tuning.
The markers are plain text (the tokenizer is left unchanged). Training uses
this same preprocessing, with the original Polish reference as target.
"""
text = str(en)
terms = _split_keywords(keywords)
if not terms:
return text
alternatives = "|".join(re.escape(term) for term in terms)
pattern = re.compile(
r"(?<!\w)(" + alternatives + r")(?!\w)", flags=re.IGNORECASE
)
return pattern.sub(
lambda match: f"[KEEPTERM] {match.group(1)} [/KEEPTERM]", text
)
def train_translation_model(
train_dataset,
tokenizer,
model,
*,
epochs: int = 2,
batch_size: int = 16,
learning_rate: float = 2e-5,
max_length: int = 192,
device=None,
):
"""Fine-tune the supplied Marian model on training examples only.
``train_dataset`` is the Dataset loaded by the starter notebook, with
English ``en``, Polish ``pl``, and comma-separated ``keywords`` columns.
Validation translations are never used by this function.
"""
if device is None:
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
if torch.cuda.is_available():
torch.manual_seed(42)
torch.cuda.manual_seed_all(42)
def collate(rows):
source_text = [
process_example(row["en"], row.get("keywords", "")) for row in rows
]
target_text = [row["pl"] for row in rows]
source = tokenizer(
source_text,
padding=True,
truncation=True,
max_length=max_length,
return_tensors="pt",
)
target = tokenizer(
text_target=target_text,
padding=True,
truncation=True,
max_length=max_length,
return_tensors="pt",
)
labels = target["input_ids"]
labels[labels == tokenizer.pad_token_id] = -100
return {
"input_ids": source["input_ids"],
"attention_mask": source["attention_mask"],
"labels": labels,
}
generator = torch.Generator().manual_seed(42)
loader = DataLoader(
train_dataset,
batch_size=batch_size,
shuffle=True,
collate_fn=collate,
num_workers=0,
generator=generator,
)
model.to(device)
model.train()
optimizer = torch.optim.AdamW(model.parameters(), lr=learning_rate)
old_use_cache = getattr(model.config, "use_cache", None)
if old_use_cache is not None:
model.config.use_cache = False
for _ in range(epochs):
for batch in loader:
batch = {key: value.to(device) for key, value in batch.items()}
result = model(**batch)
result.loss.backward()
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
optimizer.step()
optimizer.zero_grad(set_to_none=True)
if old_use_cache is not None:
model.config.use_cache = old_use_cache
model.eval()
return model
Editorials are written by members of the SOTA community, not by the olympiad. The official statement and scoring win wherever an editorial differs.