Checklist ROAI 2024 National Stage · NLP task
Grammar Is Everything: Grammatical Error Correction
Romanian title: Gramatica este totul. Corectarea erorilor gramaticale
Build and publish an encoder–decoder grammatical error correction model for Romanian, training only on provided clean Romanian Wikipedia text.
The task
Grammatical error correction (GEC) turns a possibly erroneous sentence into its corrected version. For Romanian, errors include missing diacritics, one- or two-letter misspellings, wrong prepositions, gender/number/case/person disagreements, cacophonies and punctuation mistakes; the notebook gives three example sentence pairs.
Contestants must build the best GEC model for Romanian under these rules: an encoder–decoder model (e.g. based on mBART or mT5), only 'base' variants, no GEC data or GEC-fine-tuned models from others, and training only on the provided Romanian Wikipedia texts, which may be altered freely to create training pairs. Evaluation is sentence by sentence on a diverse set of sentences.
Abridged and translated by SOTA from the official Romanian materials. The official statement has the exact rules, and it wins wherever this summary differs.
In English
This task was published in Romanian. SOTA translated it into English on 16 September 2026. Only the words changed in the notebooks: markdown, code comments, messages and printed output. The code, file names and paths are the original's, so a translated notebook runs with the original data.
Read the task notebook in English
Grammar Is Everything. Grammatical Error Correction
Introduction
Grammatical error correction (GEC) deals with correcting various types of errors in text, such as spelling, punctuation or grammatical errors. A GEC system receives a potentially erroneous sentence as input and is expected to transform it into its corrected version.
You must build a GEC system adapted to the Romanian language. Possible mistakes in Romanian include: missing diacritics, spelling mistakes of one or two letters per word, incorrect prepositions, disagreements of gender/number/case/person, cacophonies, or punctuation mistakes.
A few examples of texts with mistakes and their corrected versions (the texts are in Romanian; an English gloss follows each example):
- Example 1:
eu am duar 10 ani si stiu melodia asta de la 7 anieste preferata mea e frumoasa si eu stiu ca te chinui mult -> Eu am doar 10 ani și știu melodia asta de la 7 ani, este preferata mea, e frumoasă și eu știu că te chinui mult.
(Gloss: "I am only 10 years old and I have known this song since I was 7, it is my favourite, it is beautiful and I know you are struggling a lot.")
- Example 2:
Biatul acesta este special,vocea lui,cred ca toi am avut acest gand -> Băiatul acesta este special, vocea lui, cred că toți am avut acest gând.
(Gloss: "This boy is special, his voice, I think we have all had this thought.")
- Example 3:
Am mai auzit melodi dar asta este cea mai bun -> Am mai auzit melodii dar asta este cea mai bună.
(Gloss: "I have heard other songs before, but this one is the best.")
Objective
The goal is to build the best-performing GEC model for Romanian, working under the following restrictions:
- The model must be of the encoder-decoder type (for example, based on mBART or mT5).
- Use only the "base" variants of the models (where they exist).
- You are not allowed to use data already generated by other parties for GEC, or models already fine-tuned for this.
- For training, you have access only to the data we provide. These are texts taken from Romanian Wikipedia articles, and we will consider them correct. You may use them in any way you wish and alter them in any way you consider beneficial. You are not allowed to use any other texts!
Tips
- Starting from clean data, try to generate training data automatically.
- The evaluation will be done on a diverse set of sentences, but at sentence level. Thus, during evaluation your model will have to correct one sentence at a time.
Deliverables
You must submit the following:
- A model uploaded to the Huggingface Hub (see the push_to_hub parameter; alternatively, you can upload the model directly on Huggingface, from the browser).
- A technical report of at most two pages in which you explain how you solved the problem. The report may be written in English or in Romanian.
Prerequisites
HuggingFace configuration
Before actually starting to solve the problem, you must:
-
Go to the HuggingFace page and request access to the data.
-
In the settings, create Access Tokens, one for "read" and one for "write", and save them in Colab Secrets as
hf_readandhf_write.
from google.colab import userdata
read_access_token = userdata.get('hf_read')
write_access_token = userdata.get('hf_write')
Required modules
import importlib
import torch, transformers
if '2.3.0' not in torch.__version__:
!pip install torch==2.3.0
if transformers.__version__!='4.41.2':
!pip install transformers==4.41.2
if importlib.util.find_spec('datasets') is None:
!pip install datasets==2.18.0
!pip install evaluate==0.4.2
!pip install accelerate -U
!pip install rouge_score
If you have just installed accelerate, run Runtime > Restart session and run all from the Colab menu.
Data
def alter_sentence(example):
original_sentence = example["part"]
altered_sentence = "A " + original_sentence
return {"original_sentence": original_sentence, "altered_sentence": altered_sentence}
import textwrap
def split_paragraph(example):
return {"part": [part for foo in example["page"] for part in textwrap.wrap(foo, 100)]}
# load the data
from datasets import load_dataset, Dataset, DatasetDict, concatenate_datasets
wiki_dataset = load_dataset('Olimpiada-AI/ro_wiki', token=read_access_token)
# merge splits into a single dataset
wiki_dataset = concatenate_datasets([wiki_dataset["validation"], wiki_dataset["test"]])
# split into smaller chunks
wiki_dataset = wiki_dataset.map(split_paragraph, batched=True, remove_columns="page")
# alter sentences
wiki_dataset = wiki_dataset.map(alter_sentence, batched=False, remove_columns="part")
# split into train and validation
wiki_dataset= wiki_dataset.train_test_split(test_size=0.05)
Baseline
# load the pre-trained tokenizer and use it to process the data
from transformers import AutoTokenizer
from transformers import DataCollatorWithPadding, DataCollatorForSeq2Seq
tokenizer = AutoTokenizer.from_pretrained("google/mt5-base")
max_length = 256
train_data = wiki_dataset['train'].map(lambda x: {'input_ids': tokenizer(x['altered_sentence'], truncation=True, max_length=max_length)["input_ids"], 'label': tokenizer(x['original_sentence'], truncation=True, max_length=max_length)["input_ids"]}, remove_columns=["original_sentence", "altered_sentence"])
val_data = wiki_dataset['test'].map(lambda x: {'input_ids': tokenizer(x['altered_sentence'], truncation=True, max_length=max_length)["input_ids"], 'label': tokenizer(x['original_sentence'], truncation=True, max_length=max_length)["input_ids"]}, remove_columns=["original_sentence", "altered_sentence"])
data_collator = DataCollatorForSeq2Seq(tokenizer=tokenizer)
from transformers import AutoModelForSequenceClassification, TrainingArguments, AutoModelForSeq2SeqLM, Seq2SeqTrainer, Seq2SeqTrainingArguments
model = AutoModelForSeq2SeqLM.from_pretrained("google/mt5-base")
# define the evaluation metric
import evaluate
import numpy as np
import sys
bleu = evaluate.load("bleu")
meteor = evaluate.load('meteor')
rouge = evaluate.load('rouge')
def compute_metrics(eval_pred):
raw_predictions, raw_labels = eval_pred
predictions = []
labels = []
for pred in raw_predictions:
pred = list(filter(lambda x: x != -100, pred))
text_predictions = tokenizer.decode(pred, skip_special_tokens=True)
predictions.append(text_predictions)
for label in raw_labels:
label = list(filter(lambda x: x != -100, label))
text_labels = tokenizer.decode(label, skip_special_tokens=True)
labels.append([text_labels])
res_bleu = bleu.compute(predictions=predictions, references=labels)["bleu"]
res_meteor = meteor.compute(predictions=predictions, references=labels)["meteor"]
res_rouge = rouge.compute(predictions=predictions, references=labels)["rougeL"]
return {"bleu": res_bleu, "meteor": res_meteor, "rouge-L": res_rouge}
# define the model and the training configuration
from transformers import AutoModelForSequenceClassification, TrainingArguments, AutoModelForSeq2SeqLM, Seq2SeqTrainer, Seq2SeqTrainingArguments
model.generation_config.max_new_tokens = 256
training_args = Seq2SeqTrainingArguments(
output_dir="baseline_model",
per_device_train_batch_size=32,
per_device_eval_batch_size=32,
predict_with_generate=True,
do_train=True,
do_eval=True,
eval_strategy="epoch",
save_strategy="epoch",
num_train_epochs=1,
logging_steps = 1,
learning_rate=5e-5,
warmup_steps=5,
overwrite_output_dir=True,
save_total_limit=3,
bf16=True,
load_best_model_at_end=True,
push_to_hub=False, #
hub_strategy="checkpoint",
hub_token=write_access_token,
hub_private_repo=True,
hub_model_id='baseline_model'
)
trainer = Seq2SeqTrainer(
model=model,
args=training_args,
train_dataset=train_data,
eval_dataset=val_data,
tokenizer=tokenizer,
data_collator=data_collator,
compute_metrics=compute_metrics,
)
torch.cuda.empty_cache()
# execute the model training
trainer.train()
Inference
# run the trained model on validation split
eval_out = trainer.predict(val_data)
metrics = eval_out.metrics
print(metrics)
# run the trained model on custom data
test_data = [["Acest este o propizite greșita", "Aceasta este o propoziție corectă"], # "This is an incorrect sentence" -> "This is a correct sentence"
["A Ce fdci?", "Ce faci?"], # "Ce faci?" = "How are you?"
["A un test scurt.", "un test scurt."]] # "un test scurt." = "a short test."
import pandas as pd
df = pd.DataFrame(test_data)
test_data = Dataset.from_pandas(df.rename(columns={0: "input_sentence", 1: "output_sentence"}))
test_data = test_data.map(lambda x: {'input_ids': tokenizer(x['input_sentence'], truncation=True, max_length=max_length)["input_ids"], 'label': tokenizer(x['output_sentence'], truncation=True, max_length=max_length)["input_ids"]}, remove_columns=["input_sentence", "output_sentence"])
eval_out = trainer.predict(test_data)
metrics = eval_out.metrics
print(metrics)
Report of at most 300 words
Translated by SOTA. The Romanian original is the official version and wins wherever the two differ. The task is Romanian grammatical error correction, so the example sentences stay in Romanian with English glosses. The notebook asks for a technical report of at most two pages in one cell and at most 300 words in its last heading, as in the original. Published with the permission of Asociația Nitro, which runs ROAI (17 September 2026). If you organise this olympiad and would like the translation removed, email [email protected] and we will take it down.
At a glance
- You get
- The Hugging Face dataset Olimpiada-AI/
ro_wiki(access on request) and a baseline notebook that corrupts sentences trivially, fine-tunes google/mt5-base and reports BLEU, METEOR and ROUGE. - You submit
- A model pushed to the Hugging Face Hub and a technical report of at most two pages (Romanian or English); the notebook ends with a section for a report of at most 300 words.
- Scoring
- The final evaluation metric is not stated in the notebook; the baseline computes BLEU, METEOR and ROUGE on a validation split.
- Rules
- Encoder–decoder architectures only, 'base' size where available
- No third-party GEC data or GEC-fine-tuned models
- Only the provided Wikipedia texts may be used for training
- Google Colab with Hugging Face read/write tokens stored as Colab secrets
- Format
- ROAI 2024 national stage, 12 July 2024; 4-hour test in which each student chose either the NLP or the CV problem.