A Turkish dataset for finding personal data in documents — and for working out who each piece of data belongs to.
5,780 synthetic Turkish documents (contracts, medical records, HR forms, court filings, invoices and more) annotated with 28,761 personal-data mentions across 118 types, plus 15,535 relations linking each value to the person it describes. The label set follows the Turkish data-protection law KVKK (Law No. 6698).
Every document is fabricated. The people do not exist and the identifiers are invented, so the data can be shared and worked on freely.
from datasets import load_dataset
ds = load_dataset("newmindai/nm-kvkk-pii-6K", "spans") # add token=... while the repo is private
print(ds)
# DatasetDict({train: 5178, test: 602, test_hard: 241, test_easy: 361})
doc = ds["test"][0]
for e in doc["entities"]:
print(e["label"], "->", doc["text"][e["start"]:e["end"]])
Hasta biyometrik kimlik kaydı, İlke Tutun adına düzenlenmiştir. Kimlik doğrulama amacıyla T.C. kimlik numarası 84720154472 ve doğum tarihi 02.01.1965 kayda alınmıştır. Biyometrik doğrulama için … biyometrik yüz ve parmak izi kaydı kullanılmış …
{"id": "nmkvkk-00374",
"text": "Hasta biyometrik kimlik kaydı, İlke Tutun adına düzenlenmiştir. …",
"entities": [
{"id": "e1", "label": "full_name", "span": "İlke Tutun", "start": 31, "end": 41},
{"id": "e2", "label": "national_id_number", "span": "84720154472", "start": 111, "end": 122},
{"id": "e4", "label": "date_of_birth", "span": "02.01.1965", "start": 139, "end": 149},
{"id": "w1", "label": "biometric_data_reference", "span": "biyometrik yüz ve parmak izi kaydı", "start": 237, "end": 271}],
"relations": [
{"head": "e2", "relation": "national_id_of", "tail": "e1"},
{"head": "e4", "relation": "date_of_birth_of", "tail": "e1"},
{"head": "w1", "relation": "biometric_of", "tail": "e1"}],
"relations_negative": [],
"meta": {"persons": 1, "hard": false, "zero_pii": false,
"domain": "Healthcare", "document_type": "patient biometric ID record", "madde6": true}}
Relations point from the value to the person: national_id_of reads "this ID number belongs
to this person". That direction is fixed throughout.
Pick a format with the name argument of load_dataset. All four contain the same documents under
the same ids and the same train/test split — only the shape differs.
| name | shape | use it for |
|---|---|---|
spans | character offsets, entity ids, relations as a graph | anything: NER, relation extraction, your own tokenizer. The reference format. |
bio | one token/tag sequence per document | token classification off the shelf |
gliner2-tr | entities as text strings, Turkish label names | training GLiNER2 |
gliner2-en | same, English label names | the same, in English |
spans — id, text, entities (id, label, span, start, end), relations and
relations_negative (head, relation, tail, referring to entity ids), meta, source_id.
bio — id, text, tokens, ner_tags (B-full_name, I-full_name, O, …),
token_starts, token_ends. The offsets index into text, so the tokenisation can be redone with
any tokenizer. Relations are not part of this format.
gliner2-tr / gliner2-en — id, input (the document), output.entities (every label with
its list of matching strings, empty where the label is absent) and output.relations.
Labels are stable ids like national_id_number in spans and bio. labels.json maps each id to
a Turkish and an English display name and gives every relation a one-line description.
| train | test | test_hard | test_easy | |
|---|---|---|---|---|
| documents | 5,178 | 602 | 241 | 361 |
| of those, containing no personal data | 426 | 0 | 0 | 0 |
| naming more than one person | 943 | 241 | 241 | 0 |
test_hard and test_easy are the test set split in two, not extra data. 241 + 361 = 602.
Use them to report scores separately for the hard case; do not add all four splits together, or the
602 test documents will be counted twice. The corpus is 5,178 + 602 = 5,780 documents.
A document is hard when it names more than one person — a relative, a doctor, a lawyer, a witness, a guarantor. Those are the documents where a model has to decide whose phone number it just found, and they are where errors concentrate.
Train and test share no documents and no document types: documents were grouped by the brief they were written from and whole groups were assigned to one side (seed 42), so a model cannot pass the test set by recognising a template it has already seen.
118 entity types from the KVKK PII taxonomy, grouped into names, identity numbers, contact details, financial data, digital identifiers, health data, location, vehicles, employment records, registry records and special categories. 113 of them appear in the corpus.
| group | mentions | most frequent labels | mentions | documents | |
|---|---|---|---|---|---|
| names | 12,061 | full_name | 11,521 | 5,354 | |
| identity numbers | 2,834 | national_id_number | 2,098 | 1,971 | |
| financial | 2,042 | company_name | 733 | 489 | |
| digital identifiers | 1,956 | insurance_policy_number | 497 | 426 | |
| health | 1,798 | username | 461 | 361 | |
| contact | 1,593 | phone_number | 432 | 383 | |
| legal and customer records | 1,592 | email_address | 426 | 376 | |
| birth and civil status | 1,198 | customer_number | 415 | 382 | |
| organisations | 1,183 | full_address | 389 | 368 | |
| employment | 903 | health_report_id | 384 | 333 | |
| vehicles 680 · registry 474 · location 345 · special categories 102 |
113 relation types, 108 of which occur — 15,535 instances, 2.7 per document.
| relation | means | instances |
|---|---|---|
national_id_of | ID number → its holder | 2,011 |
account_of | username → its owner | 566 |
email_of | e-mail address → its owner | 514 |
phone_of | phone number → its owner | 436 |
insurance_policy_of | policy number → the insured person | 424 |
customer_number_of | customer number → the customer | 380 |
iban_of | bank account → its holder | 358 |
employer_of | company → the person it employs | 354 |
position_of | job title → the person holding it | 328 |
health_file_of | file number → the patient | 328 |
Full per-label and per-relation counts, with the train/test breakdown, are in label_stats.json.
Alongside the 15,535 true relations, spans marks 796 relations that do not hold, in 638
documents that name more than one person. Each one is a plausible mistake: the same value attached
to the wrong person, with the correct pairing sitting next to it.
document names two people — Arzu Arat and Kamer Değirmenci
true employee id 5752 → Arzu Arat
false employee id 5752 → Kamer Değirmenci
Attaching a value to the wrong person is the characteristic error in documents that name several people, and it is hard to measure from precision alone — a false pairing and a hallucinated one look the same in the score.
These are provided for evaluation. They give you a labelled set of the plausible wrong answers,
so you can report how often a model commits exactly that error rather than inferring it. They are
part of the spans format only: the gliner2-tr and gliner2-en formats can express that a
relation type does not occur in a document, but not that a particular pairing is false.
The documents were generated and annotated by the kvkk_synth pipeline in
newmindai/nm-kvkk, over two runs producing 885 and 4,469
documents. That repository documents the method and the quality checks.
nmkvkk-00001 … nmkvkk-05780 and are the same in all four formats.
source_id and id_map.json link them back to the generation runs.008 in SIP-008 or
a URL followed by a full stop. The tag still covers the token; only the exact boundary differs.
Use spans when boundaries must be exact. spans_report.json lists the eight.full_name is 40 % of all mentions, and the special-category types are rare by
nature. Per-label results on rare types rest on few examples.Apache License 2.0 — see LICENSE. The documents are synthetic and describe no real person, so the
dataset carries no personal data of its own.
spans/ train · test · test_hard · test_easy the reference format
bio/ the same four splits tokens and tags
tr/ en/ the same four splits GLiNER2 training format
labels.json label id → Turkish / English name, relation descriptions
label_stats.json per-label and per-relation counts
id_map.json document id ↔ generation-run id
split_report.json how the split was made
spans_report.json verification of the offsets and the BIO conversion
LICENSE Apache License 2.0
A Turkish dataset for finding personal data in documents — and for working out who each piece of data belongs to.
5,780 synthetic Turkish documents (contracts, medical records, HR forms, court filings, invoices and more) annotated with 28,761 personal-data mentions across 118 types, plus 15,535 relations linking each value to the person it describes. The label set follows the Turkish data-protection law KVKK (Law No. 6698).
Every document is fabricated. The people do not exist and the identifiers are invented, so the data can be shared and worked on freely.
from datasets import load_dataset
ds = load_dataset("newmindai/nm-kvkk-pii-6K", "spans") # add token=... while the repo is private
print(ds)
# DatasetDict({train: 5178, test: 602, test_hard: 241, test_easy: 361})
doc = ds["test"][0]
for e in doc["entities"]:
print(e["label"], "->", doc["text"][e["start"]:e["end"]])
Hasta biyometrik kimlik kaydı, İlke Tutun adına düzenlenmiştir. Kimlik doğrulama amacıyla T.C. kimlik numarası 84720154472 ve doğum tarihi 02.01.1965 kayda alınmıştır. Biyometrik doğrulama için … biyometrik yüz ve parmak izi kaydı kullanılmış …
{"id": "nmkvkk-00374",
"text": "Hasta biyometrik kimlik kaydı, İlke Tutun adına düzenlenmiştir. …",
"entities": [
{"id": "e1", "label": "full_name", "span": "İlke Tutun", "start": 31, "end": 41},
{"id": "e2", "label": "national_id_number", "span": "84720154472", "start": 111, "end": 122},
{"id": "e4", "label": "date_of_birth", "span": "02.01.1965", "start": 139, "end": 149},
{"id": "w1", "label": "biometric_data_reference", "span": "biyometrik yüz ve parmak izi kaydı", "start": 237, "end": 271}],
"relations": [
{"head": "e2", "relation": "national_id_of", "tail": "e1"},
{"head": "e4", "relation": "date_of_birth_of", "tail": "e1"},
{"head": "w1", "relation": "biometric_of", "tail": "e1"}],
"relations_negative": [],
"meta": {"persons": 1, "hard": false, "zero_pii": false,
"domain": "Healthcare", "document_type": "patient biometric ID record", "madde6": true}}
Relations point from the value to the person: national_id_of reads "this ID number belongs
to this person". That direction is fixed throughout.
Pick a format with the name argument of load_dataset. All four contain the same documents under
the same ids and the same train/test split — only the shape differs.
| name | shape | use it for |
|---|---|---|
spans | character offsets, entity ids, relations as a graph | anything: NER, relation extraction, your own tokenizer. The reference format. |
bio | one token/tag sequence per document | token classification off the shelf |
gliner2-tr | entities as text strings, Turkish label names | training GLiNER2 |
gliner2-en | same, English label names | the same, in English |
spans — id, text, entities (id, label, span, start, end), relations and
relations_negative (head, relation, tail, referring to entity ids), meta, source_id.
bio — id, text, tokens, ner_tags (B-full_name, I-full_name, O, …),
token_starts, token_ends. The offsets index into text, so the tokenisation can be redone with
any tokenizer. Relations are not part of this format.
gliner2-tr / gliner2-en — id, input (the document), output.entities (every label with
its list of matching strings, empty where the label is absent) and output.relations.
Labels are stable ids like national_id_number in spans and bio. labels.json maps each id to
a Turkish and an English display name and gives every relation a one-line description.
| train | test | test_hard | test_easy | |
|---|---|---|---|---|
| documents | 5,178 | 602 | 241 | 361 |
| of those, containing no personal data | 426 | 0 | 0 | 0 |
| naming more than one person | 943 | 241 | 241 | 0 |
test_hard and test_easy are the test set split in two, not extra data. 241 + 361 = 602.
Use them to report scores separately for the hard case; do not add all four splits together, or the
602 test documents will be counted twice. The corpus is 5,178 + 602 = 5,780 documents.
A document is hard when it names more than one person — a relative, a doctor, a lawyer, a witness, a guarantor. Those are the documents where a model has to decide whose phone number it just found, and they are where errors concentrate.
Train and test share no documents and no document types: documents were grouped by the brief they were written from and whole groups were assigned to one side (seed 42), so a model cannot pass the test set by recognising a template it has already seen.
118 entity types from the KVKK PII taxonomy, grouped into names, identity numbers, contact details, financial data, digital identifiers, health data, location, vehicles, employment records, registry records and special categories. 113 of them appear in the corpus.
| group | mentions | most frequent labels | mentions | documents | |
|---|---|---|---|---|---|
| names | 12,061 | full_name | 11,521 | 5,354 | |
| identity numbers | 2,834 | national_id_number | 2,098 | 1,971 | |
| financial | 2,042 | company_name | 733 | 489 | |
| digital identifiers | 1,956 | insurance_policy_number | 497 | 426 | |
| health | 1,798 | username | 461 | 361 | |
| contact | 1,593 | phone_number | 432 | 383 | |
| legal and customer records | 1,592 | email_address | 426 | 376 | |
| birth and civil status | 1,198 | customer_number | 415 | 382 | |
| organisations | 1,183 | full_address | 389 | 368 | |
| employment | 903 | health_report_id | 384 | 333 | |
| vehicles 680 · registry 474 · location 345 · special categories 102 |
113 relation types, 108 of which occur — 15,535 instances, 2.7 per document.
| relation | means | instances |
|---|---|---|
national_id_of | ID number → its holder | 2,011 |
account_of | username → its owner | 566 |
email_of | e-mail address → its owner | 514 |
phone_of | phone number → its owner | 436 |
insurance_policy_of | policy number → the insured person | 424 |
customer_number_of | customer number → the customer | 380 |
iban_of | bank account → its holder | 358 |
employer_of | company → the person it employs | 354 |
position_of | job title → the person holding it | 328 |
health_file_of | file number → the patient | 328 |
Full per-label and per-relation counts, with the train/test breakdown, are in label_stats.json.
Alongside the 15,535 true relations, spans marks 796 relations that do not hold, in 638
documents that name more than one person. Each one is a plausible mistake: the same value attached
to the wrong person, with the correct pairing sitting next to it.
document names two people — Arzu Arat and Kamer Değirmenci
true employee id 5752 → Arzu Arat
false employee id 5752 → Kamer Değirmenci
Attaching a value to the wrong person is the characteristic error in documents that name several people, and it is hard to measure from precision alone — a false pairing and a hallucinated one look the same in the score.
These are provided for evaluation. They give you a labelled set of the plausible wrong answers,
so you can report how often a model commits exactly that error rather than inferring it. They are
part of the spans format only: the gliner2-tr and gliner2-en formats can express that a
relation type does not occur in a document, but not that a particular pairing is false.
The documents were generated and annotated by the kvkk_synth pipeline in
newmindai/nm-kvkk, over two runs producing 885 and 4,469
documents. That repository documents the method and the quality checks.
nmkvkk-00001 … nmkvkk-05780 and are the same in all four formats.
source_id and id_map.json link them back to the generation runs.008 in SIP-008 or
a URL followed by a full stop. The tag still covers the token; only the exact boundary differs.
Use spans when boundaries must be exact. spans_report.json lists the eight.full_name is 40 % of all mentions, and the special-category types are rare by
nature. Per-label results on rare types rest on few examples.Apache License 2.0 — see LICENSE. The documents are synthetic and describe no real person, so the
dataset carries no personal data of its own.
spans/ train · test · test_hard · test_easy the reference format
bio/ the same four splits tokens and tags
tr/ en/ the same four splits GLiNER2 training format
labels.json label id → Turkish / English name, relation descriptions
label_stats.json per-label and per-relation counts
id_map.json document id ↔ generation-run id
split_report.json how the split was made
spans_report.json verification of the offsets and the BIO conversion
LICENSE Apache License 2.0