mehedihasanbijoy/BanglaSEC
BanglaSEC A 1.18M-pair parallel corpus for Bangla spelling error correction, with character-level error masks across 14 error types. BanglaSEC is the corpus introduced in A transformer based spelling error correction framework for Bangla and resource scarce Indic languages (Bijoy, Hossain, Islam & Shatabda, Computer Speech & Language 89:101703, 2025). Each row pairs a correct Bangla word with an erroneous form, labelled by error type and annotated with a binary mask marking… See the full description on the dataset page: https://huggingface.co/datasets/mehedihasanbijoy/BanglaSEC.
BanglaSEC
A 1.18M-pair parallel corpus for Bangla spelling error correction, with character-level error masks across 14 error types.
BanglaSEC is the corpus introduced in *A transformer based spelling error correction framework for Bangla and resource scarce Indic languages* (Bijoy, Hossain, Islam & Shatabda, Computer Speech & Language 89:101703, 2025). Each row pairs a correct Bangla word with an erroneous form, labelled by error type and annotated with a binary mask marking exactly which characters are wrong.
This repository provides the official train / validation / test splits used by DPCSpell.
Quick start
from datasets import load_dataset
ds = load_dataset("mehedihasanbijoy/BanglaSEC")
print(ds)
# DatasetDict({
# train: Dataset({num_rows: 957746, ...})
# validation: Dataset({num_rows: 50414, ...})
# test: Dataset({num_rows: 177919, ...})
# })
print(ds["test"][0])
# {'Word': 'ঠানকা', 'Error': 'ঠনকা', 'ErrorType': 'Typo Deletion', 'Flag': 0,
# 'Mask': '[0, 1, 0, 0]', 'ErrorBlanks': "['ঠ', '_', 'ক', 'া']", 'MaskFlag': 1}Stream it instead of downloading the full 184 MB:
ds = load_dataset("mehedihasanbijoy/BanglaSEC", split="train", streaming=True)
for row in ds.take(3):
print(row["Error"], "→", row["Word"])Dataset structure
Splits
Fields
Mask and ErrorBlanks are stored as strings for CSV round-tripping. Parse them with ast.literal_eval:
import ast
row = ds["train"][0]
mask = ast.literal_eval(row["Mask"]) # [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0]
blanks = ast.literal_eval(row["ErrorBlanks"]) # ['ন', 'ি', 'ত', '্', 'য', 'ন', 'ৈ', '_', ...]
wrong_positions = [i for i, m in enumerate(mask) if m == 1]Error types
Every error type appears in all three splits in proportion to its corpus share (within 0.01 percentage points).
Configurations
default — the full corpus
All seven columns, one row per error instance. Use this for almost everything.
ds = load_dataset("mehedihasanbijoy/BanglaSEC")detector — DPCSpell detector-network format
Exactly what DPCSpell's detector.py writes to ./Dataset/{train,valid,test}.csv: two columns, space-separated at the character level, ready to drop into the detector's TabularDataset.
ds = load_dataset("mehedihasanbijoy/BanglaSEC", "detector")
print(ds["test"][0])
# {'Error': 'ঠ ন ক া', 'ErrorBlanks': 'ঠ _ ক া'}The DPCSpell purificator and corrector networks train ondetector_preds.csvandpurificator_preds.csv, which are generated by running each preceding stage's trained model over the corpus. They cannot be derived from the corpus alone and are not included here — produce them by runningdetector.pyandpurificator.pyfrom the DPCSpell repo.
Example usage
Fine-tune a seq2seq corrector (ByT5)
Character-level models suit this task well, since errors are character edits.
from datasets import load_dataset
from transformers import AutoTokenizer, AutoModelForSeq2SeqLM
ds = load_dataset("mehedihasanbijoy/BanglaSEC")
tok = AutoTokenizer.from_pretrained("google/byt5-small")
model = AutoModelForSeq2SeqLM.from_pretrained("google/byt5-small")
def preprocess(batch):
enc = tok(batch["Error"], max_length=64, truncation=True)
enc["labels"] = tok(batch["Word"], max_length=64, truncation=True)["input_ids"]
return enc
tokenized = ds.map(preprocess, batched=True, remove_columns=ds["train"].column_names)Evaluate per error type
The headline number hides a lot — split-word and run-on errors are much harder than single-character typos.
test = load_dataset("mehedihasanbijoy/BanglaSEC", split="test")
for etype in sorted(set(test["ErrorType"])):
subset = test.filter(lambda r: r["ErrorType"] == etype)
preds = my_model.correct(subset["Error"])
acc = sum(p == g for p, g in zip(preds, subset["Word"])) / len(subset)
print(f"{etype:<36} n={len(subset):>6} exact-match={acc:.4f}")Train on a single error type
typos = ds.filter(lambda r: r["ErrorType"].startswith("Typo"))
print(len(typos["train"])) # 487,124Use the character masks for error detection
Treat the task as token classification — predict which characters are wrong — rather than generation.
import ast
def to_tagging(row):
return {
"chars": list(row["Word"]),
"labels": ast.literal_eval(row["Mask"]), # 1 = character is misspelled
}
tagging = ds["train"].map(to_tagging)Few-shot prompting with an LLM
train = load_dataset("mehedihasanbijoy/BanglaSEC", split="train")
shots = train.shuffle(seed=0).select(range(8))
prompt = "Correct the Bangla spelling error.\n\n"
for s in shots:
prompt += f"Input: {s['Error']}\nOutput: {s['Word']}\n\n"
prompt += f"Input: {test[0]['Error']}\nOutput:"How the splits were made
Splitting follows train_valid_test_df in DPCSpell's utils.py, as called from detector.py:
train_valid_test_df(df, test_size=0.15, valid_size=0.05)Within each ErrorType independently — so the split is stratified:
train_tmp, test = train_test_split(etype_df, test_size=0.15)
train, valid = train_test_split(train_tmp, test_size=0.05)giving 0.85 × 0.95 = 80.75% train, 0.85 × 0.05 = 4.25% validation, 15% test.
The original code leaves train_test_split unseeded, so the exact row assignment of the published runs is unrecoverable. These files were generated with random_state=1234 (the seed DPCSpell sets for torch) so the split is reproducible from here on. Proportions and stratification match the paper exactly; row-level membership is a fresh draw. Regenerate with the included make_splits.py.
Limitations and caveats
Please read these before reporting numbers on this dataset.
- Duplicate pairs straddle splits. The corpus contains 51,732 duplicate
(Word, Error)rows, because different error-generation processes can land on the same surface form. The paper's procedure splits rows, not unique pairs, so 12,511 test rows (7.0% of the test split) have an identical pair somewhere in train. This is preserved for comparability with published results — deduplicate yourself if you want a stricter evaluation. - Word-level overlap is by design. Each correct word appears with many different errors, so 97,712 of the 98,090 distinct test words also occur in train. This benchmarks error correction, not generalisation to unseen vocabulary.
- Split-word and run-on errors contain spaces. 214,570
Errorstrings contain a space. DPCSpell'sword2char+ whitespace tokenizer silently drops it, so thedetectorconfig does not preserve the word boundary. Thedefaultconfig keeps raw strings intact — prefer it if boundaries matter to you. - `Homonym Error` is tiny — 123 rows, splitting to 98 / 6 / 19. Per-type metrics for this category are very noisy; don't read much into them.
- Errors are synthetically generated, not harvested from human writing. The generation processes are modelled on real Bangla error patterns (Avro/Bijoy keyboard layouts, visual confusability, cognitive substitution), but the distribution is not a natural error distribution.
- `Flag` and `MaskFlag` are constant (
0and1). They are retained for schema compatibility with the DPCSpell codebase and carry no information.
Citation
@article{hossain2024panini,
title={Panini: a transformer-based grammatical error correction method for bangla},
author={Hossain, Nahid and Bijoy, Mehedi Hasan and Islam, Salekul and Shatabda, Swakkhar},
journal={Neural Computing and Applications},
volume={36},
number={7},
pages={3463--3477},
year={2024},
publisher={Springer}
}License
MIT, matching the DPCSpell reference implementation.
