Training GLiNER2.5 for the Turkish KVKK Law PII task
Three open models for the KVKK personal-data task in Turkish, the synthetic corpus they learned from, and the code that produced both.
Training GLiNER2.5 for the Turkish KVKK Law PII task
- KVKK asks for three things at once: every value the law counts, the category it belongs to, and the person it belongs to. Our models return all three from one pass over a document.
- Three GLiNER2.5 models fine-tuned for Turkish are open, along with the corpus they learned from and the code that produced both. The taxonomy behind them has 118 entity types in 14 groups and 113 relation types.
KVKK, Law No. 6698 on the Protection of Personal Data, decides what may leave an organisation inside a contract, a court notification, a CV or a health form. Masking a value is the easy part. The hard part is finding every value the law counts, knowing which of its categories it falls under, and knowing whose value it is, so that a pseudonymised document stays consistent.
All of it is open: three models fine-tuned from GLiNER2.5, the synthetic corpus they learned from, and the code that produced both. GLiNER2.5 is a schema-prompted extraction model, so it reads the label names together with the text. Ours return the entity types of a KVKK taxonomy and the relations that tie each value to a person, in a single pass over a document.
- gliner2.5-kvkk-tr-v2: start here. The best of the three on real documents.
- gliner2.5-kvkk-tr-v1: the most accurate on the 19 KVKK identifier fields.
- gliner2.5-mursit-kvkk-tr-v1: the v2 recipe on our own Turkish encoder, 3 to 5 times faster.
The recipe is short. gliner2.5-kvkk-tr-v2 learns entities on sentence data and then needs only 500 steps on synthetic documents. The two runs that generated those documents cost $1.49 and $9.03 in model calls. The hard part was knowing when to stop training, and that took 20 real documents.
On 98 real documents that played no part in training or in choosing a checkpoint, gliner2.5-kvkk-tr-v2 scores 74.7 F1 on the 21 labels every compared system can express, against 74.4 for our legacy encoder-NER pipeline. On the 19 KVKK identifier fields alone, gliner2.5-kvkk-tr-v1 is the most accurate system we measured, at 78.3 against 74.8. The four sections below cover the model, the task, the data and the training, and why the two models lead different views.
1. What GLiNER2.5 is, and why it suits a legal taxonomy
Labels are part of the input
A token classifier learns a fixed set of tags at training time. Adding a tag means retraining, and a tag on a word cannot say whose the value is. GLiNER takes a different route: the label names are part of the input. The model reads "kimlik numarası" together with the text and marks the spans that answer it. GLiNER2 extends the idea to entities, relations, classifications and structured records from one call. GLiNER2.5-multi-v1, the open model we start from, has an mDeBERTa-v3-base encoder and 287 M parameters, and its heads and encoder were pretrained together on data with many different schemas.

One sequence, one encoder
The label names and the text are written into one sequence. The example sentence has 15 words; with the prompt it becomes 51 encoder tokens, 21 for the labels and 30 for the text. Each label is read at its own marker and each word at its first sub-word piece. Labels and text attend to each other inside the encoder, so a label name the model has never seen still gets a meaningful vector. That is what makes new labels work at run time.

Spans are pairs of gaps
An entity starts and ends between words. The model builds one vector for each gap between two words, from the words on either side. For every label it then scores each gap as a possible start and a possible end, and each word as possibly inside an entity.

The model keeps the 32 most promising start gaps and the 32 most promising end gaps of the document, pairs every kept start with every later end, and keeps at most 192 candidate spans, shared by all labels. Each candidate is scored against each label as a sum of five terms. Decoding applies a threshold of 0.5, an abstention check and an overlap rule, and returns character offsets into the original text, so a long address comes back as one span however many words it has. When the prompt also names relation types, the same call returns the relations, each as a head span and a tail span.

What training sees
A training example is one record per document: the text, and for each label the list of strings that are values of that label. Every occurrence of a string in the text counts. An empty list is a negative: it tells the model that the label has no value in this document and teaches it to abstain. Relation types are declared the same way, with the head and tail of each relation.

Why it fits KVKK, and two limits
The design matches the task in four ways:
- The taxonomy is an input, so a new kind of personal data is a new label name, not a new model.
- Entities and the relations that say whose they are come out of one call.
- The output is character offsets with no cap on span length, which masking needs.
- The number of candidate spans is capped, so pairing cost does not grow with the square of the document length.
Two limits shape how to train and deploy it. First, only 32 start gaps survive per pass: a 122-word list of 40 names returned only 25 of them, so long documents should be processed in chunks. Second, all labels share one prompt and one candidate pool, so the labels you ask for change each other's scores. The same name scores 0.9972 alone and 0.9896 next to five person-role labels. Evaluate with the label set you will deploy, and ask only for the labels you need.
2. The KVKK task and the first taxonomy
What the law counts
KVKK Article 3 defines personal data as any information relating to an identified or identifiable natural person. The definition is open, so a taxonomy of personal data is never finished and grows with risk. Article 6 is a closed list of special categories: race, ethnic origin, political opinion, philosophical belief, religion, sect or other beliefs, attire, membership of associations, foundations or unions, health, sexual life, criminal convictions and security measures, and biometric and genetic data. Every item on that list needs a label.
KVKK protects natural persons only. Company names, MERSİS numbers and company tax numbers stay in the taxonomy because they appear next to personal data, but they are flagged as outside the law's scope. Finding a value is only half of the task: a document that names several people stays consistent after pseudonymisation only if every value is tied to the person it belongs to.
From 19 labels to 118
The KVKK models we serve today tag 19 identifier types, such as identity numbers, phone numbers, e-mail addresses, IBANs and addresses. They have no person-name label, because a separate NER model supplies names, and they return no relations. Most of Article 6, many registry, health-system, court and notary identifiers, and the question of whose data a value is were left uncovered.
The taxonomy, first iteration
The taxonomy has two layers. 118 entity types in 14 groups say what gets masked, and 113 relation types say whose data it is. The first design had 105 relations; 8 role relations were added after reading real notary documents, among them the attorney named in a power of attorney and a guarantor. Every entity type takes part in at least one relation.

Each entity type carries Turkish and English names with aliases, a description, span examples that include confusable uses that are not personal data, a validation rule where one exists, a legal basis, and a note when its sensitivity is conditional. A signature or a voice recording, for example, becomes Article 6 biometric data only when it is processed for recognition.
Three rules keep the labels consistent:
- Roles are relations, never labels. In "Baba adı Hasan", Hasan is a full name, and "father of" is a relation.
- Suffixes and field names stay outside a span. "Ayşe'nin" is masked as "Ayşe", and in "TCKN: 12345678901" only the number is a span.
- Values are checksum-valid. National ID numbers pass the MERNİS check, IBANs MOD-97, card numbers Luhn and tax numbers the GİB algorithm. A model therefore cannot learn that any 11 digits are an ID, and production can re-validate what the model finds.
The models are queried with Turkish label and relation names, such as "kimlik numarası" for an identity number and "kimlik numarası sahibi" for the person it belongs to.
3. From the data we had to a synthetic corpus built from facts
The datasets we started from
Before any synthetic data, the models learned entities from two sentence-level datasets we already had. KVKK50k holds 50,000 Turkish sentences annotated with the 19 KVKK identifier labels, each label written in several wordings, so a model learns what a label means rather than one fixed string. MIX100k adds 50,000 legal-NER sentences, for 100,000 sentences and 30 labels, company and public body among them. gliner2.5-kvkk-tr-v1 starts from KVKK50k, and the other two models start from MIX100k. Both datasets teach entities only: they carry no relations, and their labels cover only part of what the law names. The synthetic corpus fills that gap.
Facts first, then text
Instead of asking a model to annotate text, we turned the process around. The facts are decided first, a model writes a document around them, and the result is checked mechanically.

- The sampler owns every fact: which labels a document may contain, the values, any extra people with their own values, and the relations between them. Identifiers are checksum-valid, addresses are real places, and each choice is scored with a local embedding model against a document brief taken from NVIDIA's Nemotron-PII catalogue of document types.
- The writer, a reasoning language model, writes a document of the brief's type, uses the offered facts that belong in it, and marks each value inline with its label.
- The parser binds every tag back to an offered value and rejects anything invented or left untagged.
- The judges check that each relation is actually stated in the text and that no other person's value is attributed to the subject. A flagged document gets one repair round.
Because the parser places every span, all 24,386 spans of the larger run match the text at their recorded offsets.
Two production runs
Two runs produced the training corpus. The first, e07, wrote 1,000 documents. The second, e08, wrote 5,000 documents from 1,787 new briefs, with no document type shared with e07. It chose how much personal data each document holds from its brief, added the 8 role relations and relations between values, and paused for an automatic audit after 2,000 documents before it finished.
| e07 | e08 | |
|---|---|---|
| documents written | 1,000 | 5,000 |
| training documents | 885 | 4,469 |
| tagged mentions | 4,375 | 24,386 |
| relations | 2,280 | 13,255 |
| model calls | $1.49 | $9.03 |
Sixteen language-model reviewers read 96 documents of e08, and every quote they cited was verified. They scored tag boundaries 4.93 out of 5, found no attribution errors, and rated coherence 4.05, up from 3.83 for e07.
One document

The second person in the document, the employee who handled the transaction, has a staff number and an e-mail address of their own, and the training record ties each value to its owner.
The dataset and its cost
The corpus, nm-kvkk-pii-6K, holds 5,780 documents from the two runs, split into 5,178 training and 602 test records. 241 test documents name more than one person and form a hard test set, because attribution is difficult there. Documents are grouped by brief and whole briefs go to one side, so no document type in the test set appears in training. Every record exists with Turkish and with English label names over the same Turkish text.
Every training record declares all 115 Turkish entity labels, so the empty ones act as negatives, and it declares 4 relation types as absent. Values are stored as strings, and every occurrence counts.
The two production runs cost $1.49 and $9.03 in model calls.
4. How we trained the models, and what came out
The recipe

| model | starts from | training, in order | checkpoint | query with |
|---|---|---|---|---|
| gliner2.5-kvkk-tr-v2 | GLiNER2.5-multi-v1 | MIX100k, 13,000 steps · nm-kvkk-pii-6K with Turkish label names, a 4,000-step run | step 500, chosen on the notary documents | Turkish labels, bare relation names |
| gliner2.5-kvkk-tr-v1 | GLiNER2.5-multi-v1 | KVKK50k, 9,378 steps · 850 documents from e07, 2,000 steps | final | Turkish labels, relations with descriptions |
| gliner2.5-mursit-kvkk-tr-v1 | Mursit-Base with 4,096 positions and the GLiNER2.5 heads | MIX100k, 13,000 steps · nm-kvkk-pii-6K with all 113 relation types and their descriptions in every record, 13,000 steps | final | Turkish labels, bare relation names |
All runs used bfloat16 precision, weight decay 0.01 and seed 42 on H100 GPUs. The document stages used learning rates of 1e-5 for the encoder and 5e-4 for the heads, constant after 20 warm-up steps, and a batch of 4 on one GPU; the KVKK50k stage used 2e-5, linear decay and a batch of 8 per GPU on four.
How we evaluated
We measured on three sets. The broad set is 98 real, pseudonymised documents from 18 families, from court notifications to tax forms, with 1,131 gold spans in the 21 labels every compared system can express. The notary set is 20 powers of attorney with 188 spans; it also chose the checkpoint of gliner2.5-kvkk-tr-v2, so the headline tables leave it out. The synthetic set is the 550-document test split of nm-kvkk-pii-6K, with 2,289 spans, whose document types no model saw in training.
Language models annotated the real documents and an independent model audited them for missed spans, for an estimated recall of 98.1 % and no separate precision pass. Strict matching counts a prediction only when its start, end and label all equal a gold span, and lenient matching accepts an overlapping span with the right label. Three label views keep the comparison fair: the 21 labels every compared system can express, the 19 KVKK identifier fields, and the 14 labels the public Turkish model also has. Every model read whole documents at a threshold of 0.5, with no per-model tuning.
Results
On the 98 held-out documents, gliner2.5-kvkk-tr-v2 and our legacy encoder-NER pipeline, a legal NER model plus the served KVKK models, are 0.3 points apart: v2 balances precision and recall, while the pipeline finds more and over-predicts.
| model | P | R | F1 | lenient F1 | F1, all 118 |
|---|---|---|---|---|---|
| gliner2.5-kvkk-tr-v2 | 74.2 | 75.2 | 74.7 | 79.5 | 76.8 |
| our legacy encoder-NER pipeline | 69.8 | 79.7 | 74.4 | 81.7 | 76.2 |
| gliner2.5-mursit-kvkk-tr-v1 | 83.7 | 58.3 | 68.7 | 72.7 | 69.7 |
| YTU-ModernBERT-TR-PII | 60.0 | 78.8 | 68.1 | 77.9 | 68.9 |
| gliner2.5-kvkk-tr-v1 | 72.0 | 59.9 | 65.4 | 70.6 | 68.2 |
| GLiNER2.5-multi (untuned) | 64.6 | 55.9 | 59.9 | 65.1 | 60.0 |
21 shared labels, strict match; the last column includes the notary set. The leader of each table and the best score in each F1 column are in bold.
On the 19 KVKK identifier fields, gliner2.5-kvkk-tr-v1 leads; it was trained on the KVKK sentences first.
| model | P | R | F1 | lenient F1 | F1, all 118 |
|---|---|---|---|---|---|
| gliner2.5-kvkk-tr-v1 | 73.3 | 84.0 | 78.3 | 82.5 | 80.2 |
| our legacy encoder-NER pipeline | 65.7 | 86.8 | 74.8 | 79.8 | 76.8 |
| gliner2.5-mursit-kvkk-tr-v1 | 81.6 | 68.3 | 74.4 | 76.9 | 73.2 |
| gliner2.5-kvkk-tr-v2 | 68.7 | 79.8 | 73.8 | 77.4 | 76.1 |
19 KVKK identifier fields, strict match, 98 held-out documents.
The public Turkish model in the comparison is YTU-ModernBERT-TR-PII, a token classifier from YTU CE COSMOS with 25 entity types, 23 of which map onto our taxonomy. On the 14 labels it shares with the broad view, where no label it lacks counts against it, it reaches 69.0 against 77.1 for gliner2.5-kvkk-tr-v2. It recalls more than any other system we measured, 81.1, and pays for it in precision.
| model | P | R | F1 | lenient F1 | F1, all 118 |
|---|---|---|---|---|---|
| gliner2.5-kvkk-tr-v2 | 78.6 | 75.7 | 77.1 | 82.1 | 79.1 |
| our legacy encoder-NER pipeline | 72.7 | 80.3 | 76.4 | 83.7 | 78.0 |
| gliner2.5-mursit-kvkk-tr-v1 | 86.3 | 58.4 | 69.7 | 73.8 | 70.6 |
| YTU-ModernBERT-TR-PII | 60.0 | 81.1 | 69.0 | 78.9 | 69.6 |
| gliner2.5-kvkk-tr-v1 | 75.0 | 59.9 | 66.6 | 72.0 | 69.4 |
| GLiNER2.5-multi (untuned) | 66.2 | 55.8 | 60.5 | 66.0 | 60.6 |
The 14 labels YTU-ModernBERT-TR-PII also has, strict match, 98 held-out documents, 1,099 gold spans.
The two models trained on the generator score 89 to 91 on its test split but 69 to 75 on the held-out documents; our production pipeline scores 63.7 and 74.4.

Only our models can return the whole taxonomy and relations:
| model | prompt | entity F1 (P / R) | lenient | relation F1 | relation F1, type-filtered |
|---|---|---|---|---|---|
| gliner2.5-kvkk-tr-v1 | descriptions | 75.8 (76.6 / 75.1) | 78.7 | 38.5 | 43.2 |
| gliner2.5-kvkk-tr-v2 | bare names | 82.2 (82.6 / 81.8) | 83.7 | 37.9 | 44.8 |
| gliner2.5-mursit-kvkk-tr-v1 | bare names | 68.9 (73.1 / 65.1) | 70.4 | 36.4 | 36.9 |
| GLiNER2.5-multi (untuned) | descriptions | 27.2 (29.4 / 25.4) | 43.2 | 0.0 | 0.0 |
The 20 notary documents, whole taxonomy and all relation types in one prompt. Type filtering drops relations whose end types never occur with that relation type.
| model | prompt | entity F1 (P / R) | lenient | relation F1 | relation F1, type-filtered |
|---|---|---|---|---|---|
| gliner2.5-kvkk-tr-v1 | descriptions | 61.9 (72.9 / 53.7) | 66.3 | 36.8 | 41.3 |
| gliner2.5-kvkk-tr-v2 | descriptions | 61.8 (74.4 / 52.9) | 64.4 | 31.0 | 34.9 |
All 118 documents, whole taxonomy, relation types with descriptions.
On relations, gliner2.5-kvkk-tr-v2 leads on the notary documents after type filtering (44.8 against 43.2), but on the 118 documents gliner2.5-kvkk-tr-v1 stays ahead even when v2 is queried with bare relation names (41.3 against 35.4). The relation head proposes far more pairs than the gold contains, which is what the type filter cleans up.
What training taught us
Stop early, and choose on real documents. The runs on nm-kvkk-pii-6K had no validation set, so we scored checkpoints on real documents. After the sentence stage alone, the relation head returns nothing. Just 500 synthetic steps reach 82.2 entity F1 and 44.8 type-filtered relation F1 on the notary documents, above gliner2.5-kvkk-tr-v1, and longer training loses ground. This is a single seed, and neighbouring checkpoints differ by several points.

Fine-tuning can cost a label the base model had. gliner2.5-kvkk-tr-v1, trained on the 19 KVKK labels and then on synthetic documents, finds far fewer company names than the untuned base model, and the synthetic writer draws company names from a narrow pool. The two models whose first stage included legal-NER sentences with a company label find far more.
| model | person names | company names |
|---|---|---|
| GLiNER2.5-multi (untuned) | 63.3 | 42.2 |
| gliner2.5-kvkk-tr-v1 | 65.5 | 7.7 |
| gliner2.5-mursit-kvkk-tr-v1 | 81.0 | 31.6 |
| gliner2.5-kvkk-tr-v2 | 87.5 | 59.3 |
Strict F1 per label on the 98 held-out documents.
Synthetic data has blind spots. On the notary documents, gliner2.5-kvkk-tr-v1 finds all 46 national ID numbers but labels 20 of them as tax numbers. 33 of the 46 sit near the word "vergi" (tax), because for a person in Turkey the tax number is the national ID number. Relabelling those predictions lifts its entity F1 from 75.8 to 85.5. The generator apparently never writes a national ID number into a tax-number field, so nothing in training taught the rule.
Choose the prompt shape on real documents. Relation descriptions help one model and hurt another. On the notary documents, gliner2.5-kvkk-tr-v1 scores 43.2 type-filtered relation F1 with descriptions and 38.4 with bare names. gliner2.5-mursit-kvkk-tr-v1 was trained with descriptions, yet scores 36.9 with bare names against 17.0 with them, because long prompts hurt that encoder.
Feed whole documents. The production sentence splitter cuts 28 of the 37 notary addresses in two, and every model loses 20 to 30 points when it reads sentences instead of documents.
A Turkish encoder
Mursit-Base is NewMindAI's Turkish ModernBERT encoder, with 165 M parameters. Moving GLiNER2.5 onto it took three changes: word segmentation that matches how the encoder was pretrained, positions extended from 1,024 to 4,096, and the GLiNER2.5 heads copied over as a starting point. The first models then lost most of their recall whenever the prompt carried the relation schema. A training record listed a few relation types, about 580 tokens, while an inference call sends all 113 with descriptions, about 2,900 tokens. The fix was in the data: every training record now lists all 113 relation types with their descriptions, so training and inference prompts have the same length. With it, entity recall under the joint prompt with bare relation names reached 96.5 on the synthetic test split, and entity F1 on the notary documents 68.9. The model is 3 to 5 times faster than the mDeBERTa models and finds less.
Which model to use, and where to find it
- gliner2.5-kvkk-tr-v2 to start with: the best of the three on real documents. Query it with Turkish label names and bare relation names.
- gliner2.5-kvkk-tr-v1 when the 19 KVKK identifier fields matter most. Query it with relation descriptions.
- gliner2.5-mursit-kvkk-tr-v1 when speed matters more than recall. Query it with bare relation names, and load it as its model card describes.
Each model card carries the taxonomy as a schema file and an example of how to query it. One repository holds the rest: the generator that writes the synthetic documents, the training runs, and nm-kvkk-pii-6K itself.
Seven synthetic examples
Seven documents from the synthetic test split show what the models do well and where they fail. Synthetic documents are easier than real ones, so the tables above remain the realistic numbers. Green marks a correct span, blue the right label with the boundary off, pink a wrong label, red a false positive, and an amber underline a missed span.
| example | entities correct / gold | F1 strict | F1 lenient | relations correct / gold | relations predicted |
|---|---|---|---|---|---|
| S1 · follow-up letter | 12 / 12 | 100.0 | 100.0 | – | – |
| S2 · court decision | 11 / 11 | 100.0 | 100.0 | – | – |
| S3 · medical research record | 11 / 12 | 95.7 | 95.7 | 10 / 11 | 12 |
| S4 · credit-card limit form | 11 / 12 | 91.7 | 100.0 | 8 / 11 | 9 |
| S5 · life-insurance claim form | 15 / 17 | 90.9 | 97.0 | 8 / 10 | 32 |
| S6 · share-transfer agreement | 4 / 9 | 44.4 | 100.0 | – | – |
| S7 · tax documentation summary | 3 / 5 | 50.0 | 83.3 | – | – |
gliner2.5-kvkk-tr-v2 on seven synthetic test documents. S3 to S5 used the whole taxonomy and every relation type, the others the 21 shared labels.








We trained these models on the MareNostrum 5 supercomputer through EuroHPC.