Skip to content

docs: add devnote for evaluation rewrite - #241

Open
memadi-nv wants to merge 3 commits into
mainfrom
memadi/docs/evaluation-anonymizer-rewrite-devnote
Open

docs: add devnote for evaluation rewrite#241
memadi-nv wants to merge 3 commits into
mainfrom
memadi/docs/evaluation-anonymizer-rewrite-devnote

Conversation

@memadi-nv

Copy link
Copy Markdown
Contributor

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

  • Bug fix
  • New feature
  • Breaking change
  • Documentation update
  • Refactoring
  • CI, release, or contributor workflow update

Contributor Checklist

  • PR title follows Conventional Commits, for example fix: handle empty entity list
  • Related issue is linked, or a maintainer-owned no-issue reason is documented above
  • For non-trivial changes, a plan document is linked above, or the no-plan reason is documented above
  • Public API impact checked; skills/anonymizer/SKILL.md updated if needed
  • No real PII added to tests, docs, notebooks, fixtures, or artifacts
  • No API keys, service tokens, private keys, credentials, or real endpoint secrets added

Validation

  • Commands run:
  • Skipped checks or known failures:

Documentation and Artifacts

  • Docs updated, or not needed
  • If docs changed: make docs-build passes locally
  • If tutorial sources changed: notebooks regenerated with make convert-notebooks
  • If e2e, benchmark, or model-provider behavior changed: relevant validation is listed above

Signed-off-by: memadi <memadi@nvidia.com>
@memadi-nv
memadi-nv force-pushed the memadi/docs/evaluation-anonymizer-rewrite-devnote branch from a586797 to 898c419 Compare August 13, 2026 00:34
Signed-off-by: memadi <memadi@nvidia.com>
Signed-off-by: memadi <memadi@nvidia.com>
@memadi-nv
memadi-nv marked this pull request as ready for review August 13, 2026 23:52
@memadi-nv
memadi-nv requested review from a team as code owners August 13, 2026 23:52
@greptile-apps

greptile-apps Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Adds a Rewrite-mode evaluation developer note and registers it in the documentation navigation.

  • Explains the evaluate–repair loop, utility and leakage metrics, repair thresholds, and human-review behavior.
  • Documents optional post-hoc judges, output columns, persistence, and record display.
  • Adds a screenshot illustrating the Rewrite evaluation report.

Confidence Score: 4/5

The 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

Security Review

The persistence example should warn that pickle.load is safe only for trusted files, because loading a substituted result can execute arbitrary code. How this was verified: The example passes the saved file directly to pickle.load without any trust check or warning.

Important Files Changed

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

Comment on lines +71 to +72
saved_result = pickle.load(f)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 security 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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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`.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should uncomment this.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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”

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 lipikaramaswamy left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants