newmindai/nm-kvkk-pii-6K

Dataset

nm-kvkk-pii-6K

0

6 commits

2 linked in READMEs

updated Sep 17, 2026

See the code

README

nm-kvkk-pii-6K

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.

Quick start

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"]])

What a document looks like

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.

Four formats of the same data

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.

nameshapeuse it for
spanscharacter offsets, entity ids, relations as a graphanything: NER, relation extraction, your own tokenizer. The reference format.
bioone token/tag sequence per documenttoken classification off the shelf
gliner2-trentities as text strings, Turkish label namestraining GLiNER2
gliner2-ensame, English label namesthe same, in English

Fields

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.

Splits

traintesttest_hardtest_easy
documents5,178602241361
of those, containing no personal data426000
naming more than one person9432412410

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.

What is labelled

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.

groupmentionsmost frequent labelsmentionsdocuments
names12,061full_name11,5215,354
identity numbers2,834national_id_number2,0981,971
financial2,042company_name733489
digital identifiers1,956insurance_policy_number497426
health1,798username461361
contact1,593phone_number432383
legal and customer records1,592email_address426376
birth and civil status1,198customer_number415382
organisations1,183full_address389368
employment903health_report_id384333
vehicles 680 · registry 474 · location 345 · special categories 102

113 relation types, 108 of which occur — 15,535 instances, 2.7 per document.

relationmeansinstances
national_id_ofID number → its holder2,011
account_ofusername → its owner566
email_ofe-mail address → its owner514
phone_ofphone number → its owner436
insurance_policy_ofpolicy number → the insured person424
customer_number_ofcustomer number → the customer380
iban_ofbank account → its holder358
employer_ofcompany → the person it employs354
position_ofjob title → the person holding it328
health_file_offile number → the patient328

Full per-label and per-relation counts, with the train/test breakdown, are in label_stats.json.

Relations that are false

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.

How it was made

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.

Notes

  • Document ids run 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.
  • BIO boundaries. 28,753 of the 28,761 mentions map exactly onto token boundaries. The other 8 do not — a nested mention, and values sitting inside a larger token such as 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.
  • Distribution. 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.
  • 426 documents contain no personal data at all, as negative examples.

Licence

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.

Files

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
kvkk
named-entity-recognition
relation-extraction
synthetic

newmindai/nm-kvkk-pii-6K

Dataset

nm-kvkk-pii-6K

0

6 commits

2 linked in READMEs

updated Sep 17, 2026

See the code

README

nm-kvkk-pii-6K

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.

Quick start

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"]])

What a document looks like

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.

Four formats of the same data

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.

nameshapeuse it for
spanscharacter offsets, entity ids, relations as a graphanything: NER, relation extraction, your own tokenizer. The reference format.
bioone token/tag sequence per documenttoken classification off the shelf
gliner2-trentities as text strings, Turkish label namestraining GLiNER2
gliner2-ensame, English label namesthe same, in English

Fields

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.

Splits

traintesttest_hardtest_easy
documents5,178602241361
of those, containing no personal data426000
naming more than one person9432412410

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.

What is labelled

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.

groupmentionsmost frequent labelsmentionsdocuments
names12,061full_name11,5215,354
identity numbers2,834national_id_number2,0981,971
financial2,042company_name733489
digital identifiers1,956insurance_policy_number497426
health1,798username461361
contact1,593phone_number432383
legal and customer records1,592email_address426376
birth and civil status1,198customer_number415382
organisations1,183full_address389368
employment903health_report_id384333
vehicles 680 · registry 474 · location 345 · special categories 102

113 relation types, 108 of which occur — 15,535 instances, 2.7 per document.

relationmeansinstances
national_id_ofID number → its holder2,011
account_ofusername → its owner566
email_ofe-mail address → its owner514
phone_ofphone number → its owner436
insurance_policy_ofpolicy number → the insured person424
customer_number_ofcustomer number → the customer380
iban_ofbank account → its holder358
employer_ofcompany → the person it employs354
position_ofjob title → the person holding it328
health_file_offile number → the patient328

Full per-label and per-relation counts, with the train/test breakdown, are in label_stats.json.

Relations that are false

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.

How it was made

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.

Notes

  • Document ids run 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.
  • BIO boundaries. 28,753 of the 28,761 mentions map exactly onto token boundaries. The other 8 do not — a nested mention, and values sitting inside a larger token such as 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.
  • Distribution. 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.
  • 426 documents contain no personal data at all, as negative examples.

Licence

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.

Files

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
kvkk
named-entity-recognition
relation-extraction
synthetic