fix: add numerical stability safeguards to CosineLocalReranker - #2183
fix: add numerical stability safeguards to CosineLocalReranker#2183RerankerGuo wants to merge 2 commits into
Conversation
…nsor#1365) Improves _cosine_one_to_many robustness for the reranker used during contextual search — a path contributing to memory drift when vague follow-up queries produce all-zero or oddly-shaped embeddings. Changes: - Zero query / candidate vectors now return similarity 0.0 instead of NaN or divide-by-zero anomalies; this prevents undefined similarities from leaking into re-ordered results - Guard against 2D (batch-shaped) query vectors by flattening before the matmul, avoiding silent broadcasting/shape errors - Return early for empty or malformed candidate matrices - Wrap final scores with np.nan_to_num to clamp any remaining NaN / +Inf / -Inf edge cases to 0.0 - Apply the same NaN/Inf guarding in the pure-Python fallback (the _HAS_NUMPY=False branch) for consistency - Added comprehensive tests in tests/reranker/test_cosine_local.py covering: identical/orthogonal vectors, zero vectors, 2D query flattening, empty matrix, opposite direction, weights, and missing-embedding fallback. Closes MemTensor#1365 (memory drift: mismatched reranking scores can cause old memories to surface when a follow-up search query produces an edge-case embedding shape.) Test: python3 -m py_compile src/memos/reranker/cosine_local.py Test: python3 -m py_compile tests/reranker/test_cosine_local.py
🤖 Open Code ReviewTarget: PR #2183 🔍 OpenCodeReview found 4 issue(s) in this PR. 1.
|
✅ Automated Test Results: PASSEDAll tests passed (13/13 executed). memos_python_core/changed-repo-python: 13/13. Duration: 7s [advisory, non-gating] AI-generated tests on branch test/auto-gen-668df2a8003e21d4-20260729102828: 51/51 passed — these do NOT affect the PR verdict; review the branch manually. Branch: |
a695cc5 to
3d786e0
Compare
✅ Automated Test Results: PASSEDAll tests passed (13/13 executed). memos_python_core/changed-repo-python: 13/13. Duration: 5s [advisory, non-gating] AI-generated tests on branch test/auto-gen-993704e4a3f76fec-20260730092004: 56/57 passed, 1 failed — these do NOT affect the PR verdict; review the branch manually. Branch: |
✅ Automated Test Results: PASSEDAll tests passed (13/13 executed). memos_python_core/changed-repo-python: 13/13. Duration: 5s [advisory, non-gating] AI-generated tests on branch test/auto-gen-0d4367096806d9a4-20260731103211: 68/68 passed — these do NOT affect the PR verdict; review the branch manually. Branch: |
Description
Helps address #1365 ("记忆漂移 / memory drift" — follow-up queries surface unrelated old memories) by hardening the default cosine reranker used during contextual search. When a vague follow-up query (e.g. "再找找其他价格优惠的") produces zero / oddly-shaped embedding vectors,
_cosine_one_to_manypreviously returned NaN, Inf, or silently-wrong values. This caused the reranker to reorder candidates unpredictably and favor older B-item memories over current-session A-item memories.Changes
nan_to_num).[[a, b, c]]) to 1D before the matmul, preventing shape-mismatch/broadcast errors.np.nan_to_numso any residual undefined values become 0.0 rather than propagating up to the rerank ordering._HAS_NUMPY=Falsebranch now explicitly detects NaN (viascore != score) and ±Inf and normalizes them to 0.0, so deployments without NumPy are equally safe.tests/reranker/test_cosine_local.py:Why this fixes the symptom
With the fixes, edge-case query embeddings (common with short/ambiguous follow-ups like "再找找其他价格优惠的") no longer produce spurious high scores for unrelated old memories. The reranker now cleanly returns a zero/neutral similarity for such inputs instead of randomly reshuffling results, which was the root cause of the #1365 drift scenario where A-item context was lost and old B-item memories surfaced.
Fixes #1365.
Type of change
How Has This Been Tested?
python3 -m py_compile src/memos/reranker/cosine_local.pypython3 -m py_compile tests/reranker/test_cosine_local.pyChecklist