Spaces:
Running on Zero
Running on Zero
| """ | |
| Does this Space read a PDF the way the corpus was read? | |
| This is the test the whole design rests on. Part A's index and Part B's F1 of 0.945 were measured | |
| on `skeleton_masked` and `payload_window` strings produced by the EDA notebook's extractor. If | |
| `corpus_text.py` produces even slightly different text, those numbers stop describing this app and | |
| become decoration. | |
| So it pulls real PDFs out of the generation repo, runs them through `corpus_text.py`, and compares | |
| the result **character by character** against the published parquet. Not "close enough" — identical. | |
| The same argument applies to the family-naming model, for the same reason and with less margin: it | |
| was fitted on seventeen numeric columns of that parquet, and `doc_features.py` recomputes them from | |
| an uploaded file. A feature that drifts does not throw - it shifts the model's input distribution | |
| and the model keeps answering confidently. So the numeric block is checked against the published | |
| columns too, and the assembled feature row is checked for width against the fitted estimator. | |
| python test_fidelity.py [n_files] | |
| """ | |
| import sys | |
| import numpy as np | |
| import pandas as pd | |
| import corpus_text | |
| import doc_features | |
| CORPUS = ("https://huggingface.co/datasets/Cyber-security-final-project/" | |
| "HARMLESS_Synthetic_Injected_PDFs_EDA/resolve/main/Datasets/" | |
| "synthetic_corpus_part2_clustered.parquet") | |
| GENERATION_REPO = "Cyber-security-final-project/Generated_Injected_PDFs_HARMLESS" | |
| def main(n=8): | |
| from huggingface_hub import hf_hub_download | |
| corpus = pd.read_parquet(CORPUS).set_index("file_id") | |
| # Injected and clean both, and not all from one family: masking and binary-stream handling | |
| # differ between them, and a test that only saw one would pass on a broken extractor. | |
| ids = list(corpus.index) | |
| sample = ids[::max(1, len(ids) // n)][:n] | |
| failures = 0 | |
| for fid in sample: | |
| row = corpus.loc[fid] | |
| try: | |
| path = hf_hub_download(GENERATION_REPO, f"Output_PDFs/{fid}", repo_type="dataset") | |
| except Exception as e: | |
| print(f" ? {fid}: could not fetch ({type(e).__name__})") | |
| continue | |
| data = open(path, "rb").read() | |
| skeleton, truncated, dropped = corpus_text.build_skeleton(data) | |
| masked = corpus_text.mask_leaks(skeleton) | |
| window = corpus_text.payload_window(skeleton) | |
| checks = {"skeleton_masked": (masked, row["skeleton_masked"]), | |
| "payload_window": (window, row["payload_window"])} | |
| # The single-window path must also be what the triage returns first for a marked file, | |
| # or the model reads a different string here than the corpus was scored on. | |
| top = corpus_text.candidate_windows(skeleton, cover_all=False)[0] | |
| bad = [k for k, (got, want) in checks.items() if got != want] | |
| # The family model's document-shape block, against the columns it was fitted on. Compared | |
| # with a relative tolerance rather than exactly: the entropies and the compression ratio | |
| # are floats that went through a parquet round-trip, and demanding bit-equality of those | |
| # would fail on storage rather than on a real drift. | |
| stats = doc_features.describe(path, data, skeleton, truncated, dropped) | |
| drifted = [k for k in doc_features.NUMERIC | |
| if k in row.index | |
| and not np.isclose(float(stats[k]), float(row[k]), rtol=1e-4, atol=1e-6)] | |
| if drifted: | |
| bad += [f"feature {k}" for k in drifted] | |
| for k in drifted: | |
| print(f" {k}: computed {stats[k]}, corpus has {row[k]}") | |
| if bad: | |
| failures += 1 | |
| print(f" x {fid}: differs in {', '.join(bad)}") | |
| for k in [b for b in bad if not b.startswith("feature ")]: | |
| got, want = checks[k] | |
| print(f" {k}: got {len(got):,} chars, corpus has {len(want):,}") | |
| else: | |
| note = "head" if top["is_head"] else f"{len(top['families'])} family marker(s)" | |
| print(f" . {fid}: identical (triage top = {note}, " | |
| f"{len(doc_features.NUMERIC)} features match)") | |
| print(f"\n{len(sample) - failures}/{len(sample)} identical to the published corpus") | |
| # Width of the assembled row against the fitted estimator. Cheap, and it is the check that | |
| # catches a retrain that added a feature block without anyone updating `build_features`. | |
| try: | |
| import family_model | |
| if family_model.AVAILABLE: | |
| row = family_model.build_features( | |
| skeleton, {"pred_family": "none", "pred_injected": 0, "parse_ok": 1}, | |
| stats, np.zeros(len(family_model.CLASSES), dtype=np.float32)) | |
| expected = family_model.load_model().n_features_in_ | |
| ok = row.shape[0] == expected | |
| print(f"{'.' if ok else 'x'} feature row is {row.shape[0]} wide, " | |
| f"the fitted model expects {expected}") | |
| failures += 0 if ok else 1 | |
| except Exception as e: | |
| print(f"? family model not checked: {type(e).__name__}: {e}") | |
| return 1 if failures else 0 | |
| if __name__ == "__main__": | |
| sys.exit(main(int(sys.argv[1]) if len(sys.argv) > 1 else 8)) | |