Repository navigation
feat: mask lower-case names with an uncased NER model beside the cased one - #19
Merged
Merged
Conversation
… news, and common words that are also names Twenty invented sentences, chosen before any candidate model was measured: names in lower case inside sentences that hold a capital, which the title-cased reading cannot reach; four news sentences, where a model that ignores case could lose what capitals tell; and eight sentences of words such as bill, rose and Will with no entity, where it could invent one. None of their names occurs in the published sample.
…in, and both models' names The redact task must load a second model directory, redact-uncased, and read the whole text with it as written, besides the cased model's reading and its title-cased second reading; a lower-case name in a sentence that holds a capital must be masked. Every model the fetcher pins needs a revision, a licence and SHA-256 hashes, and every directory the NER pass loads must be one the fetcher fills. The model smoke tests expect both models' names and a sentence only the uncased model can mask; the ONNX redact description must name it.
…lower-case name among capitals is masked The fetcher pins Xenova/bert-base-NER-uncased, the int8 conversion of dslim/bert-base-NER-uncased by the same converter as the cased model, into its own directory, redact-uncased; the two share a label order. The redact task keeps the cased model's reading and its title-cased reading of uncased sentences, and adds the uncased model's reading of the whole text as written, merged by confidence. Every result names both models. Chosen on the development set at 0.85 against the alternatives: the uncased model alone masked 0.76 and lost records to 0.54, where capitals carry the signal; the cased readings alone masked 0.69; together they mask 0.93 at the same precision as before, 0.89 against 0.90, and none of the common words that are also names became one. Locally the 2000-character redact job's cold median rose to 2.76 s; 8192 lower-case characters take 5.8 s.
…tory, and the smoke case to read all three labels A cache fetched before redact-uncased existed holds no directory for it, and every ONNX redact job would fail as task_failed with nothing logged; the worker must name what is missing and the fetcher instead, and check exactly the directories the fetcher fills. The uncased smoke sentence now holds a person, a place and an organisation, so a re-pinned conversion with another label order fails. The title-cased reading's test compares every span again.
…el, naming it and the fetcher A cache fetched before this branch has no redact-uncased directory, and every ONNX redact job that asked for an entity would have failed as task_failed with nothing logged. MODEL_DIRECTORIES lists what the backend loads, a test holds it equal to the fetcher's directories, and the worker checks for both files of each before it serves.
…cost, and the upgrade step The held-out sample, measured by the model workflow at e49e388 (run 36902794112, EPYC 7763): at 0.85, informal messages 0.80 masked (0.63 before) and records 1.00 (0.97), news unchanged, informal exact precision 0.72 (0.74). The redact row's median rose from 2.40 s to 3.40 s on that CPU type, 2.76 s locally, and the tool quotes that range. The protocol, the redact description, the README, the security notes and the testing guide name both models; the README says to fetch again after an upgrade that adds a model, which the worker now enforces; the protocol notes that the uncased model tags a domain name as an organisation.
# Conflicts: # docs/testing.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
PR #18 left three kinds of name visible, all written in lower case where the cased NER model cannot see them:
Regards, olumide), which the title-cased second reading never rewrites;@nadia);That PR named the deeper fix: a model that ignores case. This PR adds one beside the cased model rather than in its place, because the measurement showed the two fail on different sentences.
The model.
dslim/bert-base-NER-uncasedis the same author's model trained on the same data, CoNLL-2003 English news. It is used in the int8 ONNX conversion thatXenovapublished alongside the cased one:Xenova/bert-base-NER-uncasedat5e99d439.model_int8.onnxis 104.6 MiB, inside the fetcher's 128 MiB limit. It is pinned by revision and SHA-256 like the others, in its own directory,redact-uncased.bert-base-uncasedtokenizer.NER_LABELSserves both.elastic/distilbert-base-uncased-finetuned-conll03-english(63.8 MiB, inonnx-community's conversion). It is cheaper, but its precision was lower (0.77 against 0.93 alone), and it orders its labels differently, so it would have needed a second label table.How it was chosen. On the development set only; the published sample stayed unseen until CI measured it. Before measuring any candidate, I added 20 invented sentences to
tests/fixtures/ner-dev.json:pay the bill,a rose,Will the courier…), where an uncased model could invent a person.Configurations at 0.85, on the 51 development sentences:
What decided it:
K. Watanabe,OYELARAN, Bisi,Copperleaf Mining. Capitals carry real signal there.Cheers, rahimgo from 0.18 to 0.82 masked, and news stays at 1.00.Bill the client, is the cased model's, and main already has it.What changed:
scripts/fetch_model.py: theredact-uncasedpin.worker/ai_bridge/tasks.py:_nerloads both models. The cased model reads the text, then the title-cased copy of each run of uncased sentences. The uncased model reads the whole text as written. Every candidate goes through the existing merge, which keeps the more confident of two readings of the same words. Results reportXenova/bert-base-NER:int8+Xenova/bert-base-NER-uncased:int8.Results
On the published sample, measured by the model workflow at
e49e388(run 36902794112, AMD EPYC 7763), against PR #18's run on the same CPU type. Threshold 0.85:At 0.5, every register is now fully masked; informal was 0.87 before.
What still leaks at 0.85:
Regards, olumide);@nadia);porto,galway,fatima);Halvern).The uncased model also tags a bare domain name such as
example.comas an organisation. Inside an email address the EMAIL rule's span covers it; elsewhere it is masked when ORG is requested. The protocol now says so.What it costs. Each cold redact job loads and runs a second 105 MB model. The 2000-character benchmark row rose from 2.40 s to 3.40 s on the EPYC 7763; that run's rerank and embed rows were 5-8% slower than the earlier run's. Locally, on a Ryzen 9 9950X, the row takes 2.76 s. The worst case, 8192 lower-case characters that every reading covers, takes 5.8 s locally, inside the 30 s default deadline. The model cache grows from about 215 MB to about 330 MB.
The redact tool's description, the protocol, the README, the security notes and the testing guide name both models and quote these figures.
Review
A self-review at high effort produced 10 findings, all fixed, tests first where behaviour changed:
redact-uncaseddirectory. Every ONNX redact job asking for an entity would then fail astask_failed, with nothing logged. Now an ONNX worker refuses to start while any model directory lacks its two files, and names them and the fetcher command.MODEL_DIRECTORIESlists what the backend loads, and a test holds it equal to the fetcher's directories. The README tells operators to fetch again after an upgrade that adds a model.I spoke with esperanza in lisbon, she works for brightline insurance.A conversion that ordered its labels like the DistilBERT candidate would fail it.docs/mcp.mdanddocs/testing.mdwas re-measured. The description and the testing guide quote this PR's table.Visit example.com for details.scored 0.94) and documented in the protocol.Verification
Tests were written first and observed failing at each step: the uncased reading, the pin, both models' names, the start-up refusal, and the directory list. The smoke tests also failed against the old worker on the model name before the implementation, and passed after it, through both the PHP client and FrankenPHP.
python-tests: 120 passed, up from 109.mcp-tests: 119 passed.php-tests: 101 contract checks passed.e49e388: run 36902794112, whose table is the one published.001a6ed). Its checks then reran there, one ephemerallab-php-python-ai-bridgepod per job:deterministicpassed.