Instructions to use Kurdzik/flisak-anon with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use Kurdzik/flisak-anon with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("token-classification", model="Kurdzik/flisak-anon")# pip install -U transformers accelerate # Load model directly from transformers import AutoTokenizer, AutoModelForTokenClassification tokenizer = AutoTokenizer.from_pretrained("Kurdzik/flisak-anon") model = AutoModelForTokenClassification.from_pretrained("Kurdzik/flisak-anon", device_map="auto") - Notebooks
- Google Colab
- Kaggle
flisak-anon
flisak-anon 0.1.0 is clarin-pl/FastPDN, a Polish named-entity recognition model built on HerBERT, fine-tuned to find people's names in Polish e-mail text so that they can be replaced with placeholders.
FastPDN finds names in well-formed prose but misses many of the forms names take in mail. Vocatives and other inflected forms lose their endings ("Krzysztofie" comes back as "Krzyszto", "pani Krygier" as "Kry"). Lowercase first names and diminutives ("hej aniu") are almost never found, and uppercase signatures lose pieces. It also cuts one name into fragments ("Mate | uszem"), so a single person gets two placeholders. flisak-anon was trained on synthetic mail written to contain those forms, with KPWr-NER replayed alongside it to keep FastPDN's general behaviour.
On a synthetic mail test set, the share of name characters left readable drops from 2.31% to 0.003%. On the KPWr-NER test set, person F1 rises from 0.682 to 0.932 (details under Evaluation).
The architecture, the 165 labels, the config and the tokenizer are FastPDN's, byte for byte; only the weights differ. It is a drop-in replacement for FastPDN.
It was developed for the anonymisation feature of Flisak, a document-management system.
Model details
| Developed by | Mateusz Kurdziel |
| Version | 0.1.0 |
| Architecture | BertForTokenClassification (HerBERT base: 12 layers, hidden size 768), 165 labels |
| Base model | clarin-pl/FastPDN at 0fcce46a197decff51f11a327a6509b9a5e4c95f, itself fine-tuned from allegro/herbert-base-cased |
| Language | Polish (a quarter of the synthetic training mail is in English) |
| Licence | CC BY 4.0 |
Intended use
The model detects person names in Polish business e-mail and similar short Polish texts, so that the names can be replaced before the text is shown, shared or processed further. It finds names only. It was built as the second stage of a two-stage anonymisation pipeline:
- Regex stage. Pattern rules, with checksum validation where the number has a checksum, find e-mail
addresses, phone numbers (Polish formats, with or without +48 or 0048), PESEL numbers, Polish bank account
numbers and IBANs, ID card numbers and IP addresses. Each match is replaced by an indexed placeholder such as
<EMAIL_1>or<PHONE_1>. - Name stage (this model). The text with placeholders is run through the transformers token-classification
pipeline with
aggregation_strategy="simple"andstride=64. Entities with one of the four person labels (see Labels) and a score above 0.4 are kept. A span that cuts into a placeholder is widened to cover it, and where two spans overlap the rightmost one wins. Each name becomes<PERSON_n>.
Placeholders are numbered per request, and the same value gets the same number, so a reader can tell one person from two across a greeting and a signature. Names are compared by their case-folded form.
Every training text went through the regex stage first, so the model has learnt to find a name next to a
placeholder such as <EMAIL_1>. It also runs on plain text: the KPWr-NER test set below is ordinary Polish
prose. The regex stage itself is not part of this repository.
Companies, streets, institutions and brands named after people ("Kowalski Sp. z o.o.", "ul. Jana Pawła II", "Szkoła Podstawowa nr 12 im. Marii Konopnickiej", "Makita") are meant to stay readable, and the model was trained to leave them alone.
Out-of-scope use
- Anything other than person names. E-mail addresses, phone numbers, identity and account numbers are the
regex stage's job. The model still carries all of FastPDN's labels, but only the person labels were evaluated.
The other labels (places, organisations, the
nam_num_phonephone label and the rest) were trained only through KPWr-NER replay and, for four of them, the synthetic hard negatives. Their accuracy is unknown. - Home addresses. A private person's street and house number are not detected.
- Names inside e-mail addresses. The model is not meant to find them; the regex stage replaces the whole address.
- A guarantee of anonymity. Automatic anonymisation can miss names (see Limitations). Check the output before relying on it where a missed name matters.
- Identifying, profiling or tracking people.
- Languages other than Polish and English. They were not tested.
How to use
pip install "transformers==5.12.0" "tokenizers==0.22.2" torch
The evaluation below used transformers 5.12.0, tokenizers 0.22.2 and torch 2.11.0 on CPU. Pin tokenizers: see the notes under the snippet.
from transformers import pipeline
ner = pipeline("token-classification", model="Kurdzik/flisak-anon", aggregation_strategy="simple")
# Some tokenizers releases drop all but the start of a long text, which would leave its names unmasked
windows = ner.tokenizer("a " * 2000, truncation=True, max_length=512, stride=64, return_overflowing_tokens=True)
assert len(windows["input_ids"]) > 2, "this tokenizers version cuts long texts: install tokenizers 0.22.2"
# The KPWr labels the anonymisation pipeline treats as a person, and its score threshold
PERSON_LABELS = {"nam_liv_person", "nam_liv_character", "nam_liv_god", "nam_adj_person"}
THRESHOLD = 0.4
def mask_names(text: str) -> str:
"""Replace person names with <PERSON_n>; the same name, ignoring case, gets the same number."""
found = sorted(
(e["start"], e["end"])
for e in ner(text, stride=64)
if e["entity_group"] in PERSON_LABELS and e["score"] > THRESHOLD
)
spans, limit = [], len(text)
for start, end in reversed(found): # where two spans overlap, the rightmost one wins
if end <= limit:
spans.insert(0, (start, end))
limit = start
numbers, out, pos = {}, [], 0
for start, end in spans:
key = " ".join(text[start:end].casefold().split())
out += [text[pos:start], f"<PERSON_{numbers.setdefault(key, len(numbers) + 1)}>"]
pos = end
return "".join(out) + text[pos:]
text = "Cześć Krzysztofie,\nprzesyłam umowę od pani Krygier. hej aniu, możesz ją podpisać?\n\nPozdrawiam\nJAN KOWALSKI"
print(mask_names(text))
With the versions above, this prints:
Cześć <PERSON_1>,
przesyłam umowę od pani <PERSON_2>. hej <PERSON_3>, możesz ją podpisać?
Pozdrawiam
<PERSON_4>
Notes:
- The snippet is the name stage only. In the full pipeline, run your own regex stage first and pass the text with its placeholders.
- Keep
aggregation_strategy="simple". Word-level strategies such as"first"find no word boundaries in HerBERT's tokenizer; on an earlier version of the synthetic test set, FastPDN with"first"left 11.2% of name characters readable. stride=64splits a text longer than the model's 512 tokens into overlapping windows, but only with a tokenizers release that returns every window. tokenizers 0.23.2 (transformers 5.16.1 needs 0.23.1 or later) returns the first window and a few tokens more, so the model never sees the rest of a long text and the names there stay readable. In one test, a 5,439-character text was read only up to character 2,051. The assert under the pipeline stops the snippet on such a version; tokenizers 0.22.2 passes it.- Replacing FastPDN. Code that loads
clarin-pl/FastPDNcan loadKurdzik/flisak-anoninstead with no other change: the labels, config and tokenizer files are identical. FastPDN also ships atokenizer.json; the tokenizer built fromvocab.jsonandmerges.txtgives the same tokens and offsets as that file on every text of both test sets. A setup that reads onlypytorch_model.bin, as FastPDN ships it, gets the same weights from that file.
Labels
165 BIO labels: O, plus B- and I- for each of the 82 categories of KPWr-NER n82. The id2label map is
FastPDN's, unchanged. The anonymisation pipeline treats four categories as a person:
| Label | What it marks |
|---|---|
nam_liv_person |
a person's name |
nam_liv_character |
a character's name |
nam_liv_god |
the name of a god |
nam_adj_person |
an adjective formed from a person's name ("gierkowskie") |
The original pipeline also maps nam_num_phone to a phone placeholder. That label was not evaluated (see
Out-of-scope use).
Training data
Training used no real e-mail. The only real-world data were public: the KPWr-NER corpus (published Polish texts, which name real people) and the PESEL name-frequency lists.
Synthetic mail
A seeded generator wrote 20,000 training mails (136,268 person spans) and 1,000 validation mails (6,710 spans), each with exact character offsets for every name.
Shape. A greeting, one to three sentences naming people, up to two sentences with hard negatives, an optional filler sentence, a closing and a signature. Half the mails quote an earlier message under Gmail, Thunderbird or Outlook style headers in Polish or English, with
>lines.Language. 75% Polish and 25% English; half of the English mails use Polish names.
Polish names. These come from the first-name and surname lists of the PESEL register (the Polish national population register), published on dane.gov.pl as datasets 1667 and 1681 (state of 2026-01-20, CC0 1.0). Names seen at least five times are kept and drawn in proportion to their count.
English names. These come from Faker's
en_USlists.Declension. A rule table declines Polish names in all seven cases. For an ending it does not cover, it returns nothing, so no guessed form is ever written and names it cannot decline are left out. It covers 95.5% of men's and 95.8% of women's first-name occurrences and 90.9% and 99.6% of their surname occurrences.
Forms. The generator writes:
- full names in all seven cases, and in capitals (
JAN KOWALSKI); - first names in the nominative, accusative, instrumental and vocative, and in lowercase;
- diminutives in the nominative, genitive, dative and vocative, and lowercase vocatives ("aniu");
- surnames alone in the nominative, genitive and vocative ("pani Nowak");
- an initial with a surname ("J. Kowalski") and a first name with a surname in capitals ("Jan KOWALSKI").
Titles (Pan/Pani, mgr inż., Mr/Ms) stay outside the name span, as in KPWr.
- full names in all seven cases, and in capitals (
Hard negatives. Companies, streets, institutions and brands named after people carry the KPWr tag they would have (
nam_org_company,nam_fac_road,nam_org_institution,nam_pro_brand), so the model learns to leave them.Casing. A tenth of the training mails were lowercased or uppercased as a whole.
Held out. Validation and test draw surnames, street patrons, brands and English streets and institutions only from a slice that training never sees: 10% of the surname list and 30% of each of the short lists. One fifth of each template pool is used only in the test set. First names are shared by all splits.
KPWr-NER replay
The train-tune split of clarin-pl/kpwr-ner (n82, revision
a20247f8787670ef987fa61e1c9139227c186105) has 900 documents. A seeded draw of 90 of them was held out as a
validation guard, and the other 810 were replayed twice per epoch. Each document's tokens were joined into running
text, and all of its KPWr tags were kept as labels.
Preprocessing and labelling
- Every text, synthetic or KPWr, went through the regex stage before training, with the gold spans moved to match.
- Every subword of an entity is labelled:
B-on the token holding its first character,I-on all the others. Withsimpleaggregation the model then returns whole names. FastPDN's fragments came from continuation subwords it taggedB-or dropped. - Texts were cut into windows of at most 256 tokens, with overlapping windows sharing 32 tokens. An epoch held 23,037 windows, the KPWr ones counted twice.
Training procedure
| Setting | Value |
|---|---|
| Starting point | FastPDN's weights, with its tokenizer built from vocab.json and merges.txt |
| Optimiser | AdamW, learning rate 3e-5, weight decay 0.01 |
| Schedule | linear warm-up over 6% of the steps, then linear decay |
| Batch size | 32 windows |
| Maximum length | 256 tokens |
| Precision | bf16 autocast |
| Gradient clipping | norm 1.0 |
| Planned length | 3 epochs (2,160 steps) |
| Evaluation | every 250 steps |
| Early stopping | after 4 evaluations without a better checkpoint |
| Seed | 0 |
| Steps run | 1,250 |
| Released checkpoint | step 250 (about a third of an epoch) |
| Hardware | one NVIDIA RTX PRO 4500 Blackwell GPU; 164 s for training and evaluations together |
| Software | torch 2.13.0+cu130, transformers 5.12.0 |
Checkpoint selection. Every 250 steps the full pipeline (regex stage, then the model) ran over the 1,000 synthetic validation mails and the 90 held-out KPWr documents. Only checkpoints whose KPWr-validation person F1 was at most 1 point below FastPDN's (0.671) were eligible. Among those, the kept checkpoint had the lowest synthetic-validation leak rate, then the fewest mails with a leak, then the highest F1. A tie kept the earlier step. Synthetic validation saturated at the first evaluation (no leak, F1 1.000), the next four evaluations tied, and training stopped at step 1,250. The tie rule kept step 250, the earliest and so the least changed from FastPDN. Its KPWr-validation person F1 was 0.988.
FastPDN was itself trained on KPWr's train-tune split, so those 90 documents are not unseen by it. They served as a guard against regressions, not as a test.
Retraining. The model was trained twice with the same data and settings. The first run's checkpoint was lost, and the released weights come from the second run. GPU training is not bit-reproducible, so the two runs differ slightly. Person F1 on the synthetic test was 0.9996 for the first run and 0.9995 for the second; both scored 0.932 on KPWr-NER. The KPWr leak rate was 1.46% for the first run and 1.34% for the second.
Evaluation
Setup. Both models ran through the same pipeline: regex stage, then the model with simple aggregation,
stride=64, batch size 8 and score above 0.4. Each mail was a request of its own. The runs used CPU in fp32
with 6 threads, torch 2.11.0 and transformers 5.12.0. The baseline is clarin-pl/FastPDN at the revision above.
Test sets.
- Synthetic mail, test v2. 2,000 mails, 723,800 characters, 10,314 person names and 2,887 hard negatives, generated as described above, with held-out surnames and test-only phrasing. It is not included in this repository.
- KPWr-NER test (n82). 304 documents, 4,323 sentences and 1,002 person names. Each document is read as one text, with tokens joined by spaces except around punctuation. The hard negatives are its 3,428 entities of other categories.
Metrics.
- P / R / F1. Precision, recall and F1 of person spans on exact character offsets.
- Leak rate. The share of gold person-name characters (whitespace aside) that no replaced span covers, whether regex or model. This is the privacy measure.
- Mails with a leak. Texts (mails, or KPWr documents) in which at least one person character stays readable.
- Fragmented. Gold names missed only because the prediction cut them into pieces inside the name. Nothing leaks, but each piece gets its own placeholder and number.
- Hard negatives touched. Non-person entities that a predicted person span overlaps. On the synthetic set these are companies, streets, institutions and brands named after people; on KPWr, every other entity.
Results.
| Test set | Model | P | R | F1 | Leak rate | Mails with a leak | Fragmented | Hard negatives touched |
|---|---|---|---|---|---|---|---|---|
| Synthetic mail v2 | FastPDN | 0.780 | 0.852 | 0.814 | 2.31% (2,527 / 109,567 chars) | 392 / 2,000 (19.6%) | 943 (9.1%) | 19 / 2,887 (0.66%) |
| Synthetic mail v2 | flisak-anon | 0.9994 | 0.9996 | 0.9995 | 0.003% (3 / 109,567 chars) | 1 / 2,000 (0.05%) | 0 | 2 / 2,887 (0.07%) |
| KPWr-NER test | FastPDN | 0.624 | 0.754 | 0.682 | 1.92% (200 / 10,443 chars) | 24 / 304 | 141 (14.1%) | 41 / 3,428 (1.20%) |
| KPWr-NER test | flisak-anon | 0.907 | 0.958 | 0.932 | 1.34% (140 / 10,443 chars) | 16 / 304 | 2 (0.2%) | 45 / 3,428 (1.31%) |
By form, synthetic test. These are the forms FastPDN handled worst:
| Form | Names | FastPDN exact recall | flisak-anon found |
|---|---|---|---|
| lowercase first names and diminutives ("hej aniu", "hi benjamin") | 154 | 0.06 | 154 / 154 |
uppercase signature (JAN KOWALSKI) |
272 | 0.59 | 272 / 272 |
| surname alone after a title ("pani Nowak", "Ms Goc") | 439 | 0.68 | 438 / 439 |
| first name alone, nominative | 1,689 | 0.81 | 1,688 / 1,689 |
| first name, vocative | 1,270 | 0.87 | 1,270 / 1,270 |
| full name, nominative | 3,260 | 0.92 | 3,258 / 3,260 |
flisak-anon finds every name in every other form.
What is still missed.
- Synthetic set. One leak ("Amy," alone on a greeting line, 3 characters). Two signatures whose span ran on into the job title on the line below. One surname taken together with its title ("Ms Socińska"). The brand Makita, touched twice.
- KPWr. Person adjectives (
nam_adj_person, such as "gierkowskie"): 8 of 18 found, with 49% of their characters readable (FastPDN: 8 and 55%). Lowercase or context-free names ("redaktor jaworowicz", "raportu Gartnera", "lalki Bobo"). Boundary errors: "Don Pedro" taken with its title where KPWr marks only "Pedro", and "Ha" found for "Habermasa".
Speed. The architecture and tokenizer are FastPDN's, so the compute per character is the same by construction. On a shared CPU host with 6 threads, flisak-anon read 1,000 characters of synthetic mail in 174 ms of wall time (952 ms of CPU time), against FastPDN's 224 ms (1,113 ms). On KPWr it took 165 ms (858 ms), against FastPDN's 146 ms (788 ms). The differences come from the host's load, not from the model.
Limitations and biases
The synthetic test is close to the training data. It shares the training set's first names and four fifths of its phrasing; only surnames, some patrons and brands, and a fifth of the templates are held out. Its scores show that the model learnt the mail forms FastPDN missed. They do not show that real mail is solved. The KPWr-NER test, made of unrelated texts, is the independent check.
There is no evaluation on real e-mail yet. Real mail has layouts, signatures, languages and noise that the generator does not produce. It is also longer: every synthetic test mail fits in one 512-token window (the longest has 247 tokens), so only the 38 KPWr test documents above 512 tokens exercise the overlapping windows.
Person names only. Everything else that identifies a person (contact details, ID numbers, account numbers) has to come from the regex stage, and home addresses from nowhere yet.
More over-masking on general text. On KPWr-NER, hard negatives touched rose from 41 to 45: sports teams +3, TV titles +2, an animal +1, a country +1, organisations -2, bands -1. On the synthetic set they fell from 19 to
Person adjectives ("gierkowskie") are still found only about half the time.
Name coverage. Polish names were drawn in proportion to their frequency in the PESEL register, so common names dominate the training mail and rare ones appear seldom. Names the declension table does not cover never appear in the synthetic data, including:
- men's surnames with a mobile e or an ó/o alternation (Wróbel, Stępień, Kopeć, Kozioł, Mróz, Król);
- names ending in -o, -e or -u;
- names with letters outside the Polish alphabet (María);
- transliterated first names (Andrii, Serhii, Olha);
- soft -ia given names (Sonia, Kasia).
For these, the model relies on what FastPDN and the KPWr replay already taught it. English names come only from Faker's US lists, and names from other languages are rare in the training mail.
Numbering is by surface form. In the pipeline above, inflected forms of one name ("Jan Kowalski", "Jana Kowalskiego") get different placeholder numbers.
Settings. Only
simpleaggregation, a score threshold of 0.4, transformers 5.12.0 and tokenizers 0.22.2 were evaluated. Other settings or versions may give different results; with tokenizers 0.23.2, long texts are cut (see How to use).
Files
| File | Contents |
|---|---|
model.safetensors |
the fine-tuned weights, the same tensors as pytorch_model.bin |
pytorch_model.bin |
the fine-tuned weights, saved with torch.save under FastPDN's own keys, as FastPDN ships them (495,986,947 bytes, sha256 2d6a49abb154962fce281c8cf5c9ead73d3a45a5ea089d20cb5c3b65caa99e05) |
config.json |
FastPDN's, byte-identical (architecture and the 165 labels) |
tokenizer_config.json, special_tokens_map.json, vocab.json, merges.txt |
FastPDN's tokenizer files, byte-identical |
LICENSE |
CC BY 4.0, with the changes made |
ATTRIBUTION.md |
credits for every model and dataset used |
When both weight files are present, transformers loads model.safetensors. FastPDN's tokenizer.json is not
included, so the tokenizer is built from vocab.json and merges.txt, as it was in training and evaluation.
Licence and attribution
flisak-anon is licensed under CC BY 4.0. It is an adaptation of these works:
- FastPDN, clarin-pl/FastPDN at
0fcce46a197decff51f11a327a6509b9a5e4c95f, CC BY 4.0. Grupa Wieszcze CLARIN-PL, Wiktor Walentynowicz. Source: https://gitlab.clarin-pl.eu/grupa-wieszcz/ner/fast-pdn - HerBERT base cased, allegro/herbert-base-cased at
50e33e0567be0c0b313832314c586e3df0dc2297, CC BY 4.0, FastPDN's pretrained encoder and tokenizer. Machine Learning Research Team at Allegro and Linguistic Engineering Group, Institute of Computer Science, Polish Academy of Sciences. Mroczkowski R., Rybak P., Wróblewska A., Gawlik I., "HerBERT: Efficiently Pretrained Transformer-based Language Model for Polish", BSNLP 2021.
Changes made. The weights were fine-tuned on synthetic Polish/English business mail and on KPWr-NER replay.
The architecture, labels, config and tokenizer are FastPDN's, unchanged. model.safetensors holds the same
tensors as pytorch_model.bin.
The original authors do not endorse this model.
Data used in training (none of it is redistributed here):
- KPWr-NER n82, clarin-pl/kpwr-ner at
a20247f8787670ef987fa61e1c9139227c186105, CC BY 3.0. Broda B., Marcińczuk M., Maziarz M., Radziszewski A., Wardyński A., "KPWr: Towards a Free Corpus of Polish", LREC 2012; CLARIN-PL. Changes made: tokens joined into text; for fine-tuning, regex matches (e-mail addresses, phone numbers and the like) replaced by placeholders. Its test split was used for evaluation. - PESEL first names and surnames, dane.gov.pl datasets 1667 and 1681, state of 2026-01-20, CC0 1.0. Ministerstwo Cyfryzacji.
- Faker (MIT), joke2k and contributors, https://github.com/joke2k/faker: English names.
Citation
FastPDN's model card gives no citation, and this model has no paper; credit FastPDN as above. If you use this model, please also cite HerBERT and KPWr:
@inproceedings{mroczkowski-etal-2021-herbert,
title = "{H}er{BERT}: Efficiently Pretrained Transformer-based Language Model for {P}olish",
author = "Mroczkowski, Robert and
Rybak, Piotr and
Wr{\'o}blewska, Alina and
Gawlik, Ireneusz",
booktitle = "Proceedings of the 8th Workshop on Balto-Slavic Natural Language Processing",
month = apr,
year = "2021",
address = "Kiyv, Ukraine",
publisher = "Association for Computational Linguistics",
url = "https://www.aclweb.org/anthology/2021.bsnlp-1.1",
pages = "1--10",
}
@inproceedings{broda-etal-2012-kpwr,
title = "{KPW}r: Towards a Free Corpus of {P}olish",
author = "Broda, Bartosz and
Marci{\'n}czuk, Micha{\l} and
Maziarz, Marek and
Radziszewski, Adam and
Wardy{\'n}ski, Adam",
booktitle = "Proceedings of the Eighth International Conference on Language Resources and Evaluation ({LREC}'12)",
month = may,
year = "2012",
address = "Istanbul, Turkey",
publisher = "European Language Resources Association (ELRA)",
url = "http://www.lrec-conf.org/proceedings/lrec2012/pdf/965_Paper.pdf",
pages = "3218--3222",
}
- Downloads last month
- 15
Model tree for Kurdzik/flisak-anon
Base model
clarin-pl/FastPDNDataset used to train Kurdzik/flisak-anon
Evaluation results
- Person precision (exact span) on Synthetic Polish/English business mail, test v2test set self-reported0.999
- Person recall (exact span) on Synthetic Polish/English business mail, test v2test set self-reported1.000
- Person F1 (exact span) on Synthetic Polish/English business mail, test v2test set self-reported1.000
- Person precision (exact span) on KPWr-NER (n82)test set self-reported0.907
- Person recall (exact span) on KPWr-NER (n82)test set self-reported0.958
- Person F1 (exact span) on KPWr-NER (n82)test set self-reported0.932