docs: add devnote for evaluation rewrite - #241
Conversation
Signed-off-by: memadi <memadi@nvidia.com>
a586797 to
898c419
Compare
Greptile SummaryAdds a Rewrite-mode evaluation developer note and registers it in the documentation navigation.
Confidence Score: 4/5The PR appears safe to merge after considering a non-blocking documentation hardening note for the pickle reload example. The documented evaluation behavior matches the current implementation, navigation and links resolve, and the only accepted concern is the missing trusted-input warning around pickle deserialization. Files Needing Attention: docs/devnotes/posts/evaluation-anonymizer-rewrite.md
|
| Filename | Overview |
|---|---|
| docs/devnotes/posts/evaluation-anonymizer-rewrite.md | The evaluation semantics and examples match the implementation, but the pickle reload example needs an explicit trusted-files warning. |
| mkdocs.yml | Adds the new developer note to the existing Developer Notes navigation using the established structure. |
| docs/devnotes/posts/assets/evaluate-rewrite-display-record.png | Adds the report screenshot referenced by the new article. |
Reviews (1): Last reviewed commit: "nit" | Re-trigger Greptile
| saved_result = pickle.load(f) | ||
|
|
There was a problem hiding this comment.
Warn that pickle requires trust
If the saved result comes from or can be replaced through an untrusted location, pickle.load() can execute attacker-controlled code with the user's privileges. Add an explicit warning that this workflow must only load trusted files.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| <!-- SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. --> | ||
| <!-- SPDX-License-Identifier: Apache-2.0 --> | ||
|
|
||
| Let's return to the customer biographies from Part 1. Replace mode changed the explicit identifiers. Now suppose the data must meet a stricter privacy requirement: even after names and addresses are replaced, a rare occupation, an exact sequence of life events, or a distinctive combination of hometown and employer may still identify someone. |
There was a problem hiding this comment.
Can we link the Part 1 devnote from "Part 1"?
| <!-- SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. --> | ||
| <!-- SPDX-License-Identifier: Apache-2.0 --> | ||
|
|
||
| Let's return to the customer biographies from Part 1. Replace mode changed the explicit identifiers. Now suppose the data must meet a stricter privacy requirement: even after names and addresses are replaced, a rare occupation, an exact sequence of life events, or a distinctive combination of hometown and employer may still identify someone. |
There was a problem hiding this comment.
Rare occupation and hometown / employer seem like things that Replace mode can detect and change I would think? If so maybe those aren't the best examples. Exact sequence of life events seems like a good one! Maybe we can come up with others more clearly in the latent entity realm?
|
|
||
| During `run()` or `preview()`, Rewrite checks each generated record for privacy leakage and meaning preservation. Leakage results determine whether a record enters the repair loop, while both leakage and utility contribute to the final human-review flag. After anonymization, an optional `evaluate()` call reviews entity coverage and the rewrite's privacy, quality, and style. Detection validity is separately opt-in. | ||
|
|
||
| This is Part 2 of a two-part series on evaluation in Anonymizer. Part 1 covers Replace mode; this article explains the two evaluation layers used by **Rewrite mode**, what each score means, and how to inspect the results. |
There was a problem hiding this comment.
Maybe link Part 1 dev note again here
| ### Repair Loop: Can a Failing Rewrite Be Improved? | ||
|
|
||
| After the initial checks, rows above the repair threshold—or rows with a high-sensitivity leak when the selected risk tolerance requires it—enter the repair loop. Only failing rows are repaired and checked again. The loop stops when they pass or reach `max_repair_iterations`. | ||
|
|
There was a problem hiding this comment.
Should we update this diagram to include the check on whether max repair iterations has been reached, and if so, what happens?
|
|
||
| ### Entity Coverage: Were All In-Scope Entities Detected? | ||
|
|
||
| Entity coverage works the same way in Rewrite and Replace modes: an independent judge identifies in-scope candidates in the original text and measures how many Anonymizer detected. See [Part 1: Entity Coverage](evaluation-anonymizer-replace.md#entity-coverage-were-all-in-scope-entities-detected) for the calculation, output columns, and interpretation guidance. |
There was a problem hiding this comment.
Can the candidates include latent entities or no? Might be worth mentioning!
| rewrite_scores = result.dataframe[rewrite_metric_columns] | ||
|
|
||
| # To run the optional post-hoc evaluation immediately, use: | ||
| # evaluated = anonymizer.evaluate(result) |
There was a problem hiding this comment.
We should uncomment this.
There was a problem hiding this comment.
Also this runs the default post-hoc judges, but does not create detection_valid, as referenced in L240. IMO instead of a comment there we should just make it explicit here. Otherwise for anyone copy pasting code snippets, this will lead to errors.
| - **Leakage metrics** test known protected values and inferences; they do not model every possible attacker or external dataset. | ||
| - **Entity coverage** depends on one judge's candidate extraction and is not ground truth. | ||
| - **Detection validity** measures precision of detected entities, not whether all sensitive information was found. | ||
| - **Privacy, quality, and style** are coarse holistic judgments, not guarantees. |
There was a problem hiding this comment.
Coarse holistic sounds odd to me. Are we trying to say something like Privacy, quality, and style are broad, model-based assessments, not guarantees?
|
|
||
| ## Evaluate–Repair Loop: Did the Rewrite Balance Privacy and Utility? | ||
|
|
||
| During `run()` or `preview()`, Anonymizer creates quality questions from the original record and privacy questions from the detected entities and their sensitivity dispositions. It answers those questions against the rewritten text, computes per-record metrics, and repairs rows that exceed the configured privacy threshold. |
There was a problem hiding this comment.
We talk about entities and their sensitivity dispositions here, and then later shorten to entity dispositions - which means little to skimmers. Maybe we just consistently use entities and their sensitivity dispositions ? Or to be even clearer, entities and their assigned sensitivity and protection dispositions
There was a problem hiding this comment.
And in a similar vein, it might help to follow the principle On first use, give the plain-language meaning followed by the technical term. After that, use the technical term consistently. especially for rewrite specific terms. Codex wrote up this list of potential candidates for further clarification
| Term | Why it may be unclear | Clearer first-use wording |
|---|---|---|
| latent entities | Sounds like hidden named entities rather than information inferred from context | “Sensitive information implied by the record rather than explicitly named, called latent entities” |
| privacy leakage | May sound like data leaving the system | “Identifying information that remains recoverable from the rewrite” |
| utility | Broad ML term with no obvious meaning here | “The useful meaning preserved by the rewrite” |
| repair loop | Does not reveal what is being repaired or how | “The automatic rewrite-and-recheck cycle, called the repair loop” |
| meaning units | Defined in the article but still abstract | “Individual facts or relationships that the rewrite should preserve” |
| protected value | Could imply encrypted or access-controlled data | “A sensitive value that the rewrite is expected to conceal” |
| sensitivity weight | Does not initially explain the number’s purpose | “A numeric weight reflecting how sensitive the entity is” |
| leakage mass | The metric name is not intuitive by itself | “The total weighted amount of identifying information that remains” |
| weighted leakage rate | Sounds very similar to leakage mass | “The remaining leakage as a fraction of the record’s maximum possible leakage” |
| risk tolerance | May sound like a general organizational policy | “The preset controlling how aggressively Anonymizer repairs and flags rewrites” |
| in-scope candidates | “Candidate” does not identify what is being considered | “Sensitive values covered by the configured entity types” |
| linkage risk | Privacy-specialist terminology | “The risk that remaining clues can connect the rewrite to a specific person or original record” |
| quasi-identifiers | Important but specialist terminology | “Details that may not identify someone alone but can do so in combination” |
There was a problem hiding this comment.
Also, the sentence saying Anonymizer “repairs rows that exceed the configured privacy threshold” is incomplete - some risk presets also repair any row with a high-sensitivity leak, even when its total leakage mass is below the threshold.
lipikaramaswamy
left a comment
There was a problem hiding this comment.
Awesome! This is technically strong, well organized, and easy to understand. A few optional editorial refinements could make it even more engaging:
- A little trimming of repeated terms such as “post-hoc evaluation” and “Rewrite mode” could make the article flow more naturally
- Readers may benefit from tracing one example through the utility, leakage and repair sections
|
|
||
| ## Evaluate–Repair Loop: Did the Rewrite Balance Privacy and Utility? | ||
|
|
||
| During `run()` or `preview()`, Anonymizer creates quality questions from the original record and privacy questions from the detected entities and their sensitivity dispositions. It answers those questions against the rewritten text, computes per-record metrics, and repairs rows that exceed the configured privacy threshold. |
There was a problem hiding this comment.
And in a similar vein, it might help to follow the principle On first use, give the plain-language meaning followed by the technical term. After that, use the technical term consistently. especially for rewrite specific terms. Codex wrote up this list of potential candidates for further clarification
| Term | Why it may be unclear | Clearer first-use wording |
|---|---|---|
| latent entities | Sounds like hidden named entities rather than information inferred from context | “Sensitive information implied by the record rather than explicitly named, called latent entities” |
| privacy leakage | May sound like data leaving the system | “Identifying information that remains recoverable from the rewrite” |
| utility | Broad ML term with no obvious meaning here | “The useful meaning preserved by the rewrite” |
| repair loop | Does not reveal what is being repaired or how | “The automatic rewrite-and-recheck cycle, called the repair loop” |
| meaning units | Defined in the article but still abstract | “Individual facts or relationships that the rewrite should preserve” |
| protected value | Could imply encrypted or access-controlled data | “A sensitive value that the rewrite is expected to conceal” |
| sensitivity weight | Does not initially explain the number’s purpose | “A numeric weight reflecting how sensitive the entity is” |
| leakage mass | The metric name is not intuitive by itself | “The total weighted amount of identifying information that remains” |
| weighted leakage rate | Sounds very similar to leakage mass | “The remaining leakage as a fraction of the record’s maximum possible leakage” |
| risk tolerance | May sound like a general organizational policy | “The preset controlling how aggressively Anonymizer repairs and flags rewrites” |
| in-scope candidates | “Candidate” does not identify what is being considered | “Sensitive values covered by the configured entity types” |
| linkage risk | Privacy-specialist terminology | “The risk that remaining clues can connect the rewrite to a specific person or original record” |
| quasi-identifiers | Important but specialist terminology | “Details that may not identify someone alone but can do so in combination” |
|
|
||
| ### Entity Coverage: Were All In-Scope Entities Detected? | ||
|
|
||
| Entity coverage works the same way in Rewrite and Replace modes: an independent judge identifies in-scope candidates in the original text and measures how many Anonymizer detected. See [Part 1: Entity Coverage](evaluation-anonymizer-replace.md#entity-coverage-were-all-in-scope-entities-detected) for the calculation, output columns, and interpretation guidance. |
There was a problem hiding this comment.
Can we clarify that entity coverage checks literal entity spans, not latent or inferred identifying clues. That distinction matters especially in an article about Rewrite mode.
|
|
||
| ## Evaluate–Repair Loop: Did the Rewrite Balance Privacy and Utility? | ||
|
|
||
| During `run()` or `preview()`, Anonymizer creates quality questions from the original record and privacy questions from the detected entities and their sensitivity dispositions. It answers those questions against the rewritten text, computes per-record metrics, and repairs rows that exceed the configured privacy threshold. |
There was a problem hiding this comment.
Also, the sentence saying Anonymizer “repairs rows that exceed the configured privacy threshold” is incomplete - some risk presets also repair any row with a high-sensitivity leak, even when its total leakage mass is below the threshold.
Summary
Adds a developer note explaining rewrite-mode evaluation, its strategy-specific scores, output columns, usage examples, and display_record() report. Includes a report screenshot.
Type of Change
Contributor Checklist
fix: handle empty entity listskills/anonymizer/SKILL.mdupdated if neededValidation
Documentation and Artifacts
make docs-buildpasses locallymake convert-notebooks