-
Notifications
You must be signed in to change notification settings - Fork 436
(diversity) attribute diversity implementation with post process and adaptive L #1250
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
narendatha
wants to merge
15
commits into
main
Choose a base branch
from
u/narendatha/diverse-postprocess-adaptive
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
04443be
disk: attribute-bucket diversity via post-processing (Design A)
narendatha 52323d1
disk: adaptive-L over-fetch for attribute diversity (Design B)
narendatha f68d8d2
style: rustfmt fixups for Design B diverse search files
narendatha 50eb734
disk: reuse cached distances in attribute-diversity post-processing
narendatha fa25d9f
docs: add YFCC dataset results, charts; remove cache-impl section
narendatha 85aeaa0
docs: replace mermaid charts with per-dataset QPS-vs-recall plots
narendatha 8de28b0
Add caselaw court_jurisdiction (distance-correlated) diversity regime
narendatha 28b1c51
Normalize diverse adaptive-L yield by achievable diversity
narendatha b531568
Rerun all diverse-search benchmarks and add chart scripts
narendatha 0297890
Fall back to sample-size yield denominator when num_buckets is unavai…
narendatha 7f59ed2
Merge remote-tracking branch 'origin/main' into u/narendatha/diverse-…
narendatha 3328e5d
diverse-adaptive-search: decouple post-process candidate pool from L-…
narendatha fcbc15c
diverse-search-results: update benchmarks for candidate-pool decoupling
narendatha 1a0b0b4
diverse-adaptive-search: apply rustfmt
narendatha 0ae8f2a
ci: drop removed experimental_diversity_search feature from clippy fe…
narendatha File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
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
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| { | ||
| "_comment": "QPS-vs-recall data at L=100 for the diverse-search benchmark charts. Recall is deterministic and authoritative; QPS is the best (least-contended) of several single-threaded runs (see diverse-search-results.md). Update these values when rerunning benchmarks, then regenerate the PNGs with plot_qps_vs_recall.py.", | ||
| "methods": ["Standard", "Queue", "Design A", "Design B"], | ||
| "colors": { | ||
| "Standard": "#7f7f7f", | ||
| "Queue": "#1f77b4", | ||
| "Design A": "#2ca02c", | ||
| "Design B": "#d62728" | ||
| }, | ||
| "charts": [ | ||
| { | ||
| "output": "enron-qps-vs-recall.png", | ||
| "title": "Enron (concentrated attribute)", | ||
| "points": { | ||
| "Standard": {"recall": 70.72, "qps": 142.6}, | ||
| "Queue": {"recall": 77.92, "qps": 100.0}, | ||
| "Design A": {"recall": 86.05, "qps": 159.2}, | ||
| "Design B": {"recall": 93.74, "qps": 53.0} | ||
| } | ||
| }, | ||
| { | ||
| "output": "caselaw-qps-vs-recall.png", | ||
| "title": "Caselaw (diffuse attribute \u2014 doc_id)", | ||
| "points": { | ||
| "Standard": {"recall": 96.91, "qps": 160.3}, | ||
| "Queue": {"recall": 98.32, "qps": 84.3}, | ||
| "Design A": {"recall": 99.25, "qps": 128.8}, | ||
| "Design B": {"recall": 99.26, "qps": 105.9} | ||
| } | ||
| }, | ||
| { | ||
| "output": "caselaw-jurisdiction-qps-vs-recall.png", | ||
| "title": "Caselaw (concentrated + distance-correlated \u2014 court_jurisdiction)", | ||
| "points": { | ||
| "Standard": {"recall": 35.91, "qps": 165.6}, | ||
| "Queue": {"recall": 79.12, "qps": 152.1}, | ||
| "Design A": {"recall": 72.59, "qps": 174.5}, | ||
| "Design B": {"recall": 90.19, "qps": 96.0} | ||
| } | ||
| }, | ||
| { | ||
| "output": "yfcc-qps-vs-recall.png", | ||
| "title": "YFCC (concentrated attribute \u2014 camera)", | ||
| "points": { | ||
| "Standard": {"recall": 49.73, "qps": 227.1}, | ||
| "Queue": {"recall": 81.78, "qps": 186.1}, | ||
| "Design A": {"recall": 93.98, "qps": 192.6}, | ||
| "Design B": {"recall": 97.49, "qps": 111.9} | ||
| } | ||
| } | ||
| ] | ||
| } |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| #!/usr/bin/env python3 | ||
| """Generate the QPS-vs-recall scatter charts for the diverse-search benchmark doc. | ||
|
|
||
| Reads chart_data.json (recall/QPS at L=100 for each method and dataset) and | ||
| writes one PNG per dataset next to this script. Kept in the repo so the charts | ||
| can be regenerated whenever the benchmark numbers change. | ||
|
|
||
| Usage: | ||
| python plot_qps_vs_recall.py | ||
|
|
||
| Requires matplotlib (pip install matplotlib). | ||
| """ | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import json | ||
| from pathlib import Path | ||
|
|
||
| import matplotlib.pyplot as plt | ||
|
|
||
| HERE = Path(__file__).resolve().parent | ||
| DATA_FILE = HERE / "chart_data.json" | ||
|
|
||
|
|
||
| def main() -> None: | ||
| data = json.loads(DATA_FILE.read_text(encoding="utf-8")) | ||
| methods = data["methods"] | ||
| colors = data["colors"] | ||
|
|
||
| for chart in data["charts"]: | ||
| fig, ax = plt.subplots(figsize=(9, 6)) | ||
|
|
||
| for method in methods: | ||
| point = chart["points"][method] | ||
| recall = point["recall"] | ||
| qps = point["qps"] | ||
| ax.scatter( | ||
| recall, | ||
| qps, | ||
| s=160, | ||
| color=colors[method], | ||
| label=method, | ||
| zorder=3, | ||
| ) | ||
| ax.annotate( | ||
| method, | ||
| (recall, qps), | ||
| textcoords="offset points", | ||
| xytext=(8, 8), | ||
| color=colors[method], | ||
| fontsize=10, | ||
| ) | ||
|
|
||
| ax.set_title(f"{chart['title']}\nQPS vs recall at L=100") | ||
| ax.set_xlabel("Recall @10 (%) - higher is better") | ||
| ax.set_ylabel("QPS - higher is better") | ||
| ax.grid(True, color="0.9", zorder=0) | ||
| ax.legend(title="Approach", loc="lower left") | ||
|
|
||
| out_path = HERE / chart["output"] | ||
| fig.tight_layout() | ||
| fig.savefig(out_path, dpi=100) | ||
| plt.close(fig) | ||
| print(f"wrote {out_path}") | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| main() |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,91 @@ | ||
| /* | ||
| * Copyright (c) Microsoft Corporation. | ||
| * Licensed under the MIT license. | ||
| */ | ||
|
|
||
| //! A file-backed [`AttributeValueProvider`] for diversity-aware benchmarks. | ||
|
|
||
| use std::path::Path; | ||
|
|
||
| use anyhow::Context; | ||
| use diskann::{neighbor::AttributeValueProvider, provider::HasId}; | ||
|
|
||
| /// The reserved attribute bucket assigned to graph navigation nodes (frozen start points) | ||
| /// that fall outside the range of loaded per-vector attributes. | ||
| /// | ||
| /// Greedy graph search seeds traversal from the index's start points. Those points must | ||
| /// therefore carry an attribute, otherwise the diverse queue would drop them and search | ||
| /// would never expand beyond the entry node. Assigning them a dedicated bucket keeps them | ||
| /// traversable without merging them into any real attribute group. | ||
| const NAVIGATION_BUCKET: u32 = u32::MAX; | ||
|
|
||
| /// An attribute value provider backed by a plaintext attribute file. | ||
| /// | ||
| /// The file is expected to contain one unsigned integer per line, where the value on the | ||
| /// `N`-th line (0-indexed) is the diversity attribute of the `N`-th vector. This matches the | ||
| /// on-disk layout produced by the labelling tools used elsewhere in the pipeline. | ||
| /// | ||
| /// Ids outside the range of loaded attributes (for example the graph's frozen start points) | ||
| /// are mapped to a reserved [`NAVIGATION_BUCKET`] so that greedy search can still traverse | ||
| /// the graph through them. | ||
| #[derive(Debug, Clone)] | ||
| pub(crate) struct FileAttributeProvider { | ||
| attributes: Vec<u32>, | ||
| num_buckets: usize, | ||
| } | ||
|
|
||
| impl FileAttributeProvider { | ||
| /// Load attributes from the plaintext file at `path`. | ||
| /// | ||
| /// # Errors | ||
| /// | ||
| /// Returns an error if the file cannot be read or if any line fails to parse as a `u32`. | ||
| pub(crate) fn load(path: &Path) -> anyhow::Result<Self> { | ||
| let contents = std::fs::read_to_string(path) | ||
| .with_context(|| format!("while reading attribute file {}", path.display()))?; | ||
|
|
||
| let attributes = contents | ||
| .lines() | ||
| .map(str::trim) | ||
| .filter(|line| !line.is_empty()) | ||
| .enumerate() | ||
| .map(|(line, value)| { | ||
| value.parse::<u32>().with_context(|| { | ||
| format!("invalid attribute value {value:?} on line {}", line + 1) | ||
| }) | ||
| }) | ||
| .collect::<anyhow::Result<Vec<u32>>>()?; | ||
|
|
||
| let num_buckets = attributes | ||
| .iter() | ||
| .copied() | ||
| .collect::<std::collections::HashSet<u32>>() | ||
| .len(); | ||
|
|
||
| Ok(Self { | ||
| attributes, | ||
| num_buckets, | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| impl HasId for FileAttributeProvider { | ||
| type Id = u32; | ||
| } | ||
|
|
||
| impl AttributeValueProvider for FileAttributeProvider { | ||
| type Value = u32; | ||
|
|
||
| fn get(&self, id: Self::Id) -> Option<Self::Value> { | ||
| Some( | ||
| self.attributes | ||
| .get(id as usize) | ||
| .copied() | ||
| .unwrap_or(NAVIGATION_BUCKET), | ||
| ) | ||
| } | ||
|
|
||
| fn num_buckets(&self) -> Option<usize> { | ||
| Some(self.num_buckets) | ||
| } | ||
| } |
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
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
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
Oops, something went wrong.
Oops, something went wrong.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When this goes into
benchmark, can you add generated diverse groundtruth for the two filter datasets intest_dataand enable an example benchmark json with them?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, of course, that is the plan. Will keep this comment open until that happens.