Skip to content

Knn to ANN - #9

Merged
nevil-mathew merged 42 commits into
knn-to-annfrom
batch-clustering
Jul 24, 2026
Merged

Knn to ANN#9
nevil-mathew merged 42 commits into
knn-to-annfrom
batch-clustering

Conversation

@nevil-mathew

Copy link
Copy Markdown
Owner

No description provided.

nevil-mathew and others added 30 commits June 4, 2026 12:56
- Introduced CumulativeTriTopic class for cumulative, batch-wise clustering on top of TriTopic.
- Added configuration options for reclustering strategies, triggers, and memory management.
- Implemented batch processing with automatic reclustering based on novelty detection and manual triggers.
- Created StreamingCorpus utility for generating realistic streaming datasets with LSA embeddings.
- Developed evaluation metrics for comparing cumulative clustering against full-batch baselines, including ARI, NMI, and keyword overlap.
- Added pluggable recluster strategies: global_refit, coreset, and batch_merge.
- Enhanced metrics utility with functions for computing ARI, NMI, and Jaccard overlap for keyword stability.
- Introduced GPU acceleration for compute-intensive steps using PyTorch, FAISS, and RAPIDS cuML.
- Updated README.md to include GPU installation instructions and performance benefits.
- Added `min_cluster_fraction` parameter to TriTopicConfig for better scaling with corpus size.
- Refactored clustering methods to utilize effective min_cluster_size based on document count.
- Implemented GPU-accelerated cosine similarity and embedding refinement functions.
- Updated cumulative benchmark notebook to use new configuration options.
- Enhanced graph builder to select between FAISS and HNSW backends based on sample size.
- Introduced LLM-based topic alignment in `alignment.py` with functions `_build_align_prompt` and `_parse_alignment_response`.
- Added `llm_align_topics` function to facilitate LLM-driven topic alignment, allowing for flexible mapping of new topics to existing global topics.
- Updated `CumulativeTriTopic` to support LLM alignment methods, including "cosine", "llm", and "both", with appropriate configuration options.
- Implemented stratified coreset selection in `strategies.py` to ensure representation of rare topics, enhancing robustness against tail-collapse.
- Added metrics for evaluating rare topic recall in `evaluation.py`, providing insights into the preservation of small topics during cumulative modeling.
- Enhanced `LLMLabeler` to support OpenRouter as a provider, allowing for broader compatibility with LLM services.
- Implemented `llm_merge_topics` method in `TriTopic` and `CumulativeTriTopic` classes to semantically merge topics using a large language model (LLM).
- Introduced a new module `llm_merger.py` for handling LLM-driven topic merging, including prompt building and response parsing.
- Enhanced `LLMLabeler` with structured output capabilities for LLM calls, allowing for better integration with Google and OpenAI APIs.
- Added JSON schema enforcement for structured responses from the LLM, improving the reliability of the merging process.
- Updated documentation and added warnings for potential stale states in the cumulative model after merging.
- Implemented sensitivity weights for lightweight-coreset sampling, favoring outliers.
- Added tests for sensitivity sampling and micro-cluster coreset strategies.
- Enhanced README with details on new coreset selection methods: sensitivity and microcluster.
- Updated coreset configuration options to include `coreset_sampling` and `reserve_novel_docs`.
Weighted coresets (stratified_coreset/sensitivity_weights) previously
only reached small-cluster pruning, centroids, and keyword extraction —
the Leiden partition objective itself drew cluster boundaries as if
every sampled point represented one document, blunting the point of
the provably-bounded sampling distributions upstream.

ConsensusLeiden now switches to RBERVertexPartition with
node_sizes=node_weights whenever node weights are present, scoped only
to that branch so every unweighted fit (including global_refit) keeps
using RBConfigurationVertexPartition exactly as before, with zero
behavior change. An earlier attempt that scaled edge weights by w_i*w_j
instead of switching objectives was discarded after benchmarking showed
it amplifies kNN-graph noise near sparse regions and measurably hurt
real coreset fidelity (ARI vs. full-batch baseline dropped); the native
node_sizes mechanism avoids that failure mode and raised ARI from
~0.88-0.95 to ~0.92-0.99 across seeds on a realistic streaming benchmark.
Adds a third, opt-in path for choosing the Leiden resolution parameter
alongside modularity-maximization and binary-search-to-target-count.
Follows ClusterLLM (Zhang, Wang & Shang, EMNLP 2023): candidate
resolutions are scored by how well their partitions agree with LLM
judgments on sampled same/different-cluster document triplets.

- tritopic/labeling/llm_granularity.py: triplet sampling, prompt
  building, tiered-fallback response parsing, and candidate scoring
- TriTopic.tune_resolution_with_llm(): callable after fit(), reuses
  the existing graph/refresh helpers, never invoked automatically
- Unit + integration tests with a mocked labeler (no real API calls)
- README section documenting the method and its cost profile

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LaDUfD19BrzSkqbv1bfqbx
- Thread node_weights through llm_select_resolution/_partition_at_resolution
  so candidate scoring uses the same RBER+node_sizes objective as the final
  weighted consensus re-fit (previously always unweighted RBConfiguration)
- Guard tune_resolution_with_llm against models missing graph_/documents_/
  embeddings_ (e.g. after save()/load(), which never persists graph_) with
  a clear error instead of a deep leidenalg crash
- Validate n_candidates/batch_size are >= 1 in llm_select_resolution
- Drop unused NearestNeighbors distance outputs in _sample_triplets
- Laplace-smooth _triplet_agreement so a candidate informative on only one
  or two triplets can't outrank one informative across many at a slightly
  lower but more reliable agreement rate
- Replace np.argmax's implicit first-index tie-break with an explicit
  middle-candidate fallback for tied/all-zero scores
- Persist the calibrated resolution back to config.resolution and
  _clusterer.resolution so later resolution-dependent defaults
  (build_hierarchy, divide, _auto_resolve_topic_count) stay consistent

Adds 10 new tests covering each fix; all verified empirically before
being asserted (e.g. Leiden partition invariance for the tie-break test).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LaDUfD19BrzSkqbv1bfqbx
Verifies the node_weights forwarding fixed in the previous commit
(model.py:1871) actually reaches leidenalg end-to-end as the weighted
RBER objective across the full call chain (candidate scoring, per-run
consensus, and the co-occurrence re-clustering step), not just as a
passed-but-unused kwarg.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LaDUfD19BrzSkqbv1bfqbx
New section 5b (opt-in, off by default via TUNE_RESOLUTION_WITH_LLM)
demonstrates tune_resolution_with_llm() as an alternative to manually
hand-tuning RESOLUTION. Uses the existing OpenRouter labeler pattern
already established in this notebook (OPENROUTER_MODEL defaults to
google/gemini-2.5-flash-lite; DeepSeek is equally suitable) rather than
Claude, since B-or-C triplet judgments don't need a stronger model.

Outputs stripped before commit to match the other notebooks in this
repo and avoid committing real dataset content into git history.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LaDUfD19BrzSkqbv1bfqbx
…ation-9c6nrj

Add LLM-guided granularity calibration (ClusterLLM-style)
- Added a check for empty content in the LLM labeler, raising a ValueError with a descriptive message if the response is empty.
- Updated the LLM merger to issue a warning and return all topics as singletons when the LLM response is empty or consists only of whitespace.
- Introduced a minimum cluster size parameter to suppress small clusters during candidate partitioning, aligning with final fit behavior.
- Improved triplet sampling efficiency with a global k-NN query, reducing computational cost.
- Added diagnostics to capture detailed metrics during resolution selection, including triplet counts and unparsed responses.
- Implemented a two-stage resolution selection process: a coarse sweep followed by a fine grid search around the best candidate.
- Enhanced triplet response parsing to handle unparsed responses more robustly, avoiding bias in scoring.
- Updated documentation to reflect new features and improvements in the LLM granularity approach.
- Introduced a new feature to allow LLM-guided calibration of the RESOLUTION parameter.
- Added markdown explanation for the new feature, detailing its benefits and usage.
- Implemented code to utilize the LLM for tuning resolution based on boundary case judgments.
- Included diagnostics output to assess the effectiveness of the calibration.
…ation docs

New tritopic.adaptation subpackage implementing ClusterLLM-style triplet
fine-tuning: sample LLM-judged (anchor, positive, negative) triplets from a
fitted model, adapt the embedder to them via a pure-numpy linear transform
or a real sentence-transformers fine-tune, and refit TriTopic on the result.
Also includes the cheaper Few-Shot-Clustering extras (LLM keyphrase
expansion, low-confidence correction) and a compare_embedders evaluation
harness for judging whether adaptation actually helped on a given corpus.

- tritopic/adaptation/: config, triplet sampling/bank/cache, LinearAdapter +
  EmbeddingAdapter, evaluation harness, keyphrase/correction extras,
  adapt_and_refit() pipeline
- TriTopic.adapt_embeddings_with_llm(): in-place delegate mirroring
  tune_resolution_with_llm's ergonomics
- benchmarks/adaptation_quality_report.py: oracle-labeler POC report
  (synthetic + real 20 Newsgroups scenarios)
- notebooks/embedding_adaptation_demo.ipynb: executed end-to-end demo/test
  notebook with inline assertions, real 20NG data
- 34 new tests across tests/test_adaptation_*.py
- pyproject.toml: new `adaptation` extra (datasets, accelerate)
- README: document the new feature; expand tune_resolution_with_llm docs
  with two-stage/bias-mitigation details and a diagnostics example

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- run_benchmark.py: reproduces the README's TriTopic vs BERTopic/NMF/LDA
  benchmark table (20ng, BBC News, AG News, Arxiv) with a --quick synthetic
  smoke-test mode for CI. The file was previously referenced by the README
  but never committed because a blanket `run*` .gitignore rule silently
  swallowed it; added a `!run_benchmark.py` exception.
- Verify quoted phrases in LLM-generated report-theme narratives against
  the source documents shown to the LLM (tritopic/utils/quote_verification.py),
  flagging quotes that can't be traced back to a real document so a fabricated
  participant quote doesn't silently ship in a qualitative research report.
  Surfaced via ReportTheme.unverified_quotes and an inline warning in
  export_report()'s Markdown output.
- Add GitHub Actions CI: pytest on push/PR (py3.10/3.12), a fast benchmark
  harness smoke test, and a manually-triggered full benchmark reproduction job.
- Fix README code samples using the nonexistent `n_topics_target` kwarg;
  the actual TriTopic constructor param is `n_topics`.
…ules

CI (.github/workflows/ci.yml):
- persist-credentials: false on all checkout steps
- add a read-only top-level permissions block
- timeout-minutes on the full-benchmark job

run_benchmark.py:
- embedding cache key now includes sample_seed, so a rerun with a different
  subsampling seed can't silently reuse embeddings for a different set of docs

tritopic/adaptation/adapter.py:
- _resolve_mode now runs the same accelerate/datasets/sentence-transformers
  version check for explicit adapter_mode="finetune" that the "auto" branch
  already ran, raising the same actionable message instead of failing later
  with a raw ImportError out of _finetune_sentence_transformer
- EmbeddingAdapter.encode()'s normalize flag is now threaded through to
  LinearAdapter.transform() in linear mode instead of being ignored

tritopic/adaptation/correction.py:
- reassign_low_confidence drops any topic that a reassignment empties out
  entirely before recomputing centroids/probabilities, instead of averaging
  an empty embedding slice (was a real NaN-propagation crash, reproduced and
  confirmed fixed with a new regression test)
- clarify batch_size's docstring: it chunks iteration only, doesn't batch
  LLM requests (kept the signature as-is; a real multi-doc-per-call rewrite
  is a bigger change than this pass warrants)

tritopic/adaptation/keyphrase.py:
- generate_keyphrases' cache loader now skips malformed/partial JSONL lines
  instead of aborting the whole read
- keyphrase_expand_embeddings no longer blends in encode("") for documents
  with an empty keyphrase list; also merges the doc+keyphrase encode calls
  into one, halving request overhead for API-backed encoders

tritopic/adaptation/pipeline.py:
- adapt_and_refit carries the original model's explicit n_topics through to
  the refit model instead of silently resetting it to "auto"
- warns when use_metadata_view was enabled, since the fit-time metadata
  DataFrame isn't persisted on the model and can't be recovered for the refit

tritopic/core/model.py:
- AdaptationConfig is now imported under TYPE_CHECKING so the annotation on
  adapt_embeddings_with_llm resolves for static analysis (was a bare
  "name not defined" for mypy/Ruff)

Skipped (not still valid / not worth the risk-to-value tradeoff):
- Renaming/removing correction.py's batch_size outright — no call site or
  doc example depends on the "real batching" framing; docstring fix covers
  the misleading-cost-expectation concern without an API break.

All 232 tests pass (224 pre-existing + 8 new regression tests), including a
reproduction of the correction.py crash against the pre-fix code to confirm
the new test actually exercises the bug.
HF's datasets/huggingface_hub stopped resolving the legacy script-based
"ag_news" repo id (HfUriError: repo id must be namespace/name), breaking
the full benchmark CI job. Point at fancyzhx/ag_news, the parquet-based
mirror with the same text/label schema.
The manual full-benchmark job (4 datasets x 3 seeds x k-grid x 4 models,
including BERTopic) was hitting the 45-minute timeout and getting
cancelled before finishing, not failing outright.
…mark-error-juhs58

Update AG News dataset source to fancyzhx/ag_news
…teness-ipnli2

Add LLM-guided embedding adaptation module; polish granularity calibration docs
Mirrors the CSV_PATHS config pattern from challenges_clustering_kaggle.ipynb:
set CSV_PATHS (or the CSV_PATHS/CSV_PATH env var) to load your own documents
from CSV instead of the built-in 20 Newsgroups demo. An optional LABEL_COL
enables the ground-truth oracle self-test in Section 3; without it, that
section is skipped and the notebook points to the real-LLM path in Section 4.
…-input-kvq90t

Add LLM-guided embedding adaptation module; polish granularity calibration docs
- Added a new notebook `embedding_adaptation_kaggle.ipynb` for real fine-tuning on Kaggle using OpenRouter and `all-MiniLM-L6-v2`.
- Updated `README.md` to include links to the new notebook and clarify the adaptation process.
- Modified `adapt_and_refit` function to include the trained `EmbeddingAdapter` in the report, allowing for persistence of fine-tuned weights.
- Added a test to ensure the `EmbeddingAdapter` can be saved and loaded correctly after adaptation.
- Updated CHEAT_SHEET.md to reflect default memory-safe configuration for large datasets.
- Changed README.md to specify file locations in a clearer format.
- Adjusted README.md to correct the OOM crash mitigation advice.
- Modified __init__.py to reorder exports for better organization.
- Enhanced graphweave_full_demo.ipynb by clarifying LLM labeling options and comments.
- Improved run_benchmark.py to provide clearer warnings when BERTopic is not installed.
- Updated CHEAT_SHEET.md to clarify memory usage for default consensus_method="graph" and legacy hierarchical path.
- Revised README.md to emphasize the memory-safe nature of the default path and provide clearer installation instructions for optional features.
- Enhanced VISUAL_GUIDE.md to reflect changes in memory usage timelines and consensus steps for both paths.
- Adjusted integration tests to remove unnecessary low_memory=True settings when using the default graph consensus.
- Modified model configuration to clarify the effect of low_memory on hierarchical consensus only.
- Updated cumulative benchmark notebook to reflect changes in consensus method and memory management.
Introduce the GraphWeave brand and top-level API
… improve error handling in adaptation modules

- Updated CI to support Python 3.9 in addition to 3.10 and 3.12.
- Clarified installation instructions in README for LLM-guided embedding fine-tuning.
- Improved error handling in adapter.py and correction.py to provide more informative warnings.
- Enhanced keyphrase.py to include normalization options for embeddings.
- Updated triplet sampling logic for better performance and clarity.
- Added tests for quote verification and reasoning field handling in LLM labeler.
- Changed co-occurrence matrix accumulation from float32 to int16 to optimize memory usage.
- Updated documentation to reflect changes in data types and memory safety.
- Modified error handling in `adapt_and_refit` to raise ValueError when metadata is not passed, ensuring metadata view is preserved.
- Added tests to verify that metadata is correctly handled during adaptation and refitting processes.
- Adjusted installation instructions in notebooks to point to the correct repository branch.
Update embedding adaptation, metrics, CI, and GraphWeave metadata
Add LLM-guided embedding adaptation and report quote verification
@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 4008b993-0609-4be5-9148-d273353b153f

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch batch-clustering

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@nevil-mathew
nevil-mathew merged commit 70fc2df into knn-to-ann Jul 24, 2026
6 checks passed
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.

2 participants