Skip to content

feat(decon): opcjonalny pass near-duplicate (MinHash/Jaccard) - #36

Open
natiixnt wants to merge 3 commits into
slayerlabs:mainfrom
natiixnt:feat/decon-neardup-minhash
Open

feat(decon): opcjonalny pass near-duplicate (MinHash/Jaccard)#36
natiixnt wants to merge 3 commits into
slayerlabs:mainfrom
natiixnt:feat/decon-neardup-minhash

Conversation

@natiixnt

Copy link
Copy Markdown
Contributor

Rozszerza bench/decon_audit.py o opcjonalny pass near-duplicate. Powiązane z #4 (skrypt dekontaminacji).

Dlaczego

Verbatim n-gram łapie tylko dokładny wspólny ciąg >= N słów, więc mija: itemy krótsze niż N słów (zero n-gramów) oraz lekko przeredagowane kopie. To realna luka w bramce anty-kontaminacji.

Co

  • nowy, opcjonalny pass --near-dup: MinHash + LSH banding, estymata Jaccarda, rekord-do-rekordu. Pure-python, zero nowych zależności.
  • domyślne zachowanie (sam verbatim) niezmienione - near-dup tylko pod flagą.
  • flagi: --near-dup --jaccard 0.7 --minhash-k 4 --perms 128 --bands 32.
  • raport zyskuje neardup_hits / hits (addytywnie; verbatim_hits zachowane).
  • fix przy okazji: os.makedirs("results", exist_ok=True) przed zapisem historii - bez tego skrypt crashował FileNotFoundError na świeżym checkoucie.

Test

bench/test_decon_neardup.py - samowystarczalny (bez sieci i slayer-data/): rdzeń MinHash, retrieval LSH, komplementarność (8-gram ślepy na krótki item, near-dup łapie).

Ograniczenie

Pełnego audytu na realnym korpusie nie odpalono (dane slayer-data/ poza repo). Weryfikacja na syntetycznym fixture; ktoś z dostępem do danych powinien odpalić --near-dup na realnych shardach.

@vercel

vercel Bot commented Jun 17, 2026

Copy link
Copy Markdown

@natiixnt is attempting to deploy a commit to the kwikiel's projects Team on Vercel.

A member of the Team first needs to authorize it.

…_audit

Verbatim n-gram łapie tylko dokładny wspólny ciąg >= N słów, więc mija
itemy krótsze niż N oraz lekko przeredagowane kopie. Dodaje opcjonalny
pass --near-dup (MinHash + LSH banding, estymata Jaccarda) jako
uzupełniający, rekord-do-rekordu sygnał podobieństwa. Pure-python, zero
nowych zależności; domyślne zachowanie (sam verbatim) niezmienione.

Przy okazji: os.makedirs("results", exist_ok=True) przed zapisem historii
audytu - bez tego decon_audit.py crashował FileNotFoundError gdy katalog
results/ nie istniał (np. świeży checkout).

Test: bench/test_decon_neardup.py (samowystarczalny, bez sieci i danych).
@natiixnt
natiixnt force-pushed the feat/decon-neardup-minhash branch from b898355 to 2fc30fc Compare June 18, 2026 11:37
@dzienisz
dzienisz requested a review from kwikiel June 18, 2026 11:49
@xfaang-ci

Copy link
Copy Markdown
Contributor

🤖 Automated code review (Xavier / Xfaang), requested by @zientara in #tasks. Read-only analysis — findings for you to weigh.

Verdict: APPROVE WITH NITS — implementation is correct, deterministic, well-tested, zero new dependencies. Strongly prefer this over the parallel #29.

Findings:

  • [minor] decon_audit.py (NearDupIndex/lsh_keys) — banding is fixed but the threshold is a CLI knob. With defaults (perms=128, bands=32, rows=4) recall at the default --jaccard 0.7 is ~0.9998. But the band/row split isn't re-derived from --jaccard; running --jaccard 0.5 drops candidate recall to ~0.87, i.e. ~13% of true near-dups at J=0.5 are never retrieved — the dangerous direction for a contamination gate. Fix: auto-pick bands from the threshold (or datasketch-style optimal_param), or at minimum warn when threshold < ~0.65. Document the coupling in --help.
  • [nit] minhash_signaturesh = set(shingles(ws,k)) or {" ".join(ws)} is never empty, so the if not hs: return tuple([0]*…) branch is unreachable. Remove it (it misleads readers into expecting a null-signature path).
  • [nit] degenerate empty/sub-k docs estimate Jaccard 1.0, but the len(ws) < ndup.k: continue query guard and the >= k index guard contain it. Worth a one-line comment so a future refactor doesn't reintroduce false positives.
  • [nit] performance — single-threaded pure-Python, ~1.3 ms/60-word doc (~22 min per 1M scanned records). Fine behind the opt-in flag; note the O(N·perms·shingles) ceiling in the docstring.

Verified good (empirically, not just read): determinism (make_perms(seed=1234) reproducible), ctor rejects perms % bands != 0, standard unbiased MinHash estimator, band index in bucket keys (no cross-band collisions), verbatim-vs-neardup accounting partitions cleanly, tests pass and are network/data-free, zero new deps, and the drive-by os.makedirs("results", exist_ok=True) fixes a real FileNotFoundError on fresh checkout.

Comparison vs #29 (same feature, ~90% overlap — don't land both): #29 adds a separate bench/decon_neardup.py (so git-mergeable, but ships two parallel near-dup tools with different thresholds 0.8 vs 0.7 and different report schemas — ambiguous "which is the gate?"). #36 is the better base: stdlib-only (no datasketch), integrated into the existing audited flow / single exit code, verified LSH banding, real tests. #29 uniquely contributes a diacritics-folding tier (Polish ASCII-fold n-grams, catches diacritic-stripped copies) that #36 lacks. Recommendation: land #36, then port #29's diacritics tier on top as an additive --fold-diacritics pass.

kamilkaczmareksolutions added a commit that referenced this pull request Jun 20, 2026
usunieto datasketch/minhash (silnik w #36), naprawiono asymetrie fold_shingles
(bez fallbacku calotekstowego), --strip tylko verbatim, --strip-diacritics
jawnie dla tieru diakrytykow + ostrzezenie over-collapse, report -> public/results/
natiixnt added 2 commits June 21, 2026 18:05
- ostrzeżenie gdy near-dup próg Jaccard < 0.65 (banding LSH ma tam
  obniżony recall) + doc sprzężenia w --jaccard --help
- usunięcie nieosiągalnej gałęzi `if not hs` w minhash_signature
  (sh nigdy puste przez fallback) + docstring o trywialnej sygnaturze
- notka O(N * perms * shingli) i kosztu w komentarzu near-dup

Logika add/query/estimate_jaccard/lsh_keys nietknięta; domyślne
zachowanie (próg 0.7) bez zmian.
…layerlabs#29)

Near-dup MinHash przepuszczał kopie pozbawione polskich znaków
(zażółć -> zazolc). Flaga --fold-diacritics zwija polskie diakrytyki do
ASCII przed shinglingiem, symetrycznie dla indeksu i zapytań, więc łapie
też takie kopie - łącząc MinHash (przestawienia, krótkie itemy) z foldem
diakrytyków z slayerlabs#29 w jednym, zintegrowanym passie (stdlib-only).

Domyślnie OFF (fold=False) - zachowanie bez zmian. Over-collapse
(łoś->los) akceptowalny dla bramki kontaminacji: podnosi recall, a
weryfikacja Jaccardem >= próg i tak filtruje. Test pokrywa fold on/off.
@natiixnt

Copy link
Copy Markdown
Contributor Author

Dołożyłem opcjonalny --fold-diacritics (port pomysłu z #29): near-dup zwija polskie diakrytyki do ASCII przed shinglingiem, więc łapie też kopie pozbawione polskich znaków (zażółć -> zazolc). Teraz ten PR jest nadzbiorem #29: MinHash + LSH (przestawienia, krótkie itemy, near-dup) PLUS warstwa diakrytyków, w jednym zintegrowanym passie ze wspólnym exit code, stdlib-only (bez datasketch). Fold domyślnie OFF (zero regresji), symetryczny dla indeksu i zapytań, z testem on/off. Over-collapse (łoś->los) udokumentowany i bezpieczny dla bramki (recall-faworyzujący, filtr Jaccard >= próg odsiewa).

kamilkaczmareksolutions added a commit that referenced this pull request Jun 28, 2026
* feat: decon near-dup layer (diacritics + MinHash)

decon_audit.py wykrywa tylko kontaminacje doslowna (n-gramy slow). Ten modul
dokłada warstwe near-duplicate na tych samych EVAL_SOURCES i lapie przecieki,
ktore warstwa doslowna przepuszcza: kopie bez polskich znakow oraz lekko
przeredagowane itemy (MinHash/Jaccard). CPU-only, datasketch opcjonalny,
decon_audit bez zmian.

* refactor(decon): tier diakrytykow only — minhash do #36

usunieto datasketch/minhash (silnik w #36), naprawiono asymetrie fold_shingles
(bez fallbacku calotekstowego), --strip tylko verbatim, --strip-diacritics
jawnie dla tieru diakrytykow + ostrzezenie over-collapse, report -> public/results/
@natiixnt

Copy link
Copy Markdown
Contributor Author

Kontekst po ostatnich merge'ach (dla review @kwikiel): #29 (diakrytyki) jest już w main i celowo zostawia MinHash tutaj (cytat z mergowanego decon_neardup.py: "Near-dup MinHash/Jaccard jest w #36 (decon_audit)"). Ten PR dostarcza więc nie-zdublowany rdzeń: near-dup MinHash + LSH/Jaccard w decon_audit (main go nie ma), opcjonalny --fold-diacritics, oraz fix os.makedirs('results') (FileNotFoundError przy świeżym checkoucie - też nieobecny w main). Sprawdziłem: gałąź merguje się czysto z aktualnym origin/main, bez konfliktów.

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