Discord

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.

  • NLP
  • Sequence-to-sequence text correction
  • Romanian original · English translation

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 575 words and 12 code cells

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:

  1. Go to the HuggingFace page and request access to the data.

  2. In the settings, create Access Tokens, one for "read" and one for "write", and save them in Colab Secrets as hf_read and hf_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.

Details

Year
2024, Romania (UB, UBB, UAIC, UVT)
Round
National Stage · NLP task
Language
Romanian; English translation by SOTA
License
Not stated by the source