classifs / README.md
anshy's picture
Upload folder using huggingface_hub
7f390fb verified
|
Raw History Blame Contribute Delete
11.9 kB
# PR quality/multilingual/tags fastText classifiers
Four fastText classifiers that score or tag a GitHub PR from its diff + touched-file
content, distilled from Jev (`typesafe/jev-1.13`) annotations on mined GitHub Archive
PRs. Each subfolder is self-contained: `model.bin` (weights), `train.txt`/`val.txt`
(the exact fastText training data), `train.py` (the exact training call — rerun in
place to reproduce `model.bin` from `train.txt`), and `infer.py` (standalone scoring
script — fetches a live PR via the GitHub API and classifies it, no dependency on the
original mining pipeline).
## Usage
```bash
pip install fasttext==0.9.3 "numpy<2" requests # numpy<2 is required -- see note below
export GITHUB_PAT=... # any GitHub token (read-only is fine)
python quality/infer.py https://github.com/owner/repo/pull/123
python multilingual/infer.py https://github.com/owner/repo/pull/123
python research_novelty/infer.py https://github.com/owner/repo/pull/123
python tags/infer.py https://github.com/owner/repo/pull/123 # multi-label
python tags/infer.py --threshold 0.3 https://github.com/owner/repo/pull/123
```
To retrain a model from its own `train.txt` in place: `cd <subfolder> && python train.py`.
`quality`/`multilingual`/`research_novelty` each print a `soft` score (probability-
weighted expected value, the better-correlating of the two — use this) and a `hard`
score (argmax level), both on a 0-10 scale. `tags` instead prints every tag (of 424)
whose predicted probability clears `--threshold` (default 0.5), sorted by confidence —
see its own section below for why it's one multi-label model, not 424 separate ones.
**Note on fastText + NumPy 2.x:** the prebuilt `fasttext-wheel` can silently corrupt
batch-`predict()` probabilities under NumPy 2.x, and even a from-source build's Python
wrapper crashes outright on `np.array(..., copy=False)`. Pin `numpy<2`.
## `quality/` — Jev's strict 0-10 quality rubric
Rates the PR's diff on a strict, sensibility-gated 0-10 scale: 0-5 is gated on whether
the codebase/diff is coherent and credible (a simple-but-clean change tops out at 5); 6+
requires genuine engineering or conceptual complexity on top of that. See
`annotate_pr_quality.py`'s `QUALITY_LEVELS` for the full rubric text.
- **Hyperparameters**: dim=64, epoch=10, lr=0.3, wordNgrams=2, minCount=3, bucket=2,000,000
- **Train**: 254,143 PRs (`train.txt`)
- **Val**: 28,238 PRs, 1,766 at level≥6 (`val.txt`)
**This is the plain dim64/epoch10 baseline** — every variant tried (2-4x capacity, class-
rebalanced train sets, a ~1.55x-larger combined corpus with a separately-mined
higher-credibility PR pool) came out flat or slightly worse on a held-out 10,000-PR
tracking set (685 at quality≥6). More data/capacity does not fix this classifier;
treat it as a known ceiling, not a bug to chase.
**Metrics** (10,000-PR tracking holdout, rule = `quality >= 6`):
| metric | value |
|---|---|
| rule accuracy | 93.73% |
| precision | 74.17% |
| **recall** | **12.99%** |
| exact-level accuracy | 78.01% |
| MAE | 0.531 |
| soft Pearson / Spearman | 0.7252 / 0.6339 |
**Read this before using it as a filter**: it misses ~87% of genuinely good PRs
(quality≥6) to keep false positives to 0.33% of negatives, and its own predictions never
exceed ~6.8-7 on any PR we've tested — it is a high-precision, very-low-recall filter,
not a general-purpose quality scorer. It also gave near-identical predictions (4.4-5.3)
to real, high-quality ML/research PRs (e.g. lucidrains' `x-transformers`, `imagen-pytorch`)
as to trivial personal-repo PRs — it is not sensitive to genuine technical depth. Lowering
the decision threshold to `>=5` makes this *worse*, not better (accuracy drops to 37%,
since ~80% of real PRs are already `>=5` and the model's own predicted mass sits below
that line).
## `multilingual/` — non-English comment/string content
Purely descriptive 0-10 scale: how much non-English natural-language text (comments,
docstrings, string literals) appears in the diff and touched files, independent of the
programming language itself. See `annotate_pr_quality.py`'s `MULTILINGUAL_LEVELS`.
- **Hyperparameters**: dim=128, epoch=25, lr=0.3, wordNgrams=2, minCount=3, bucket=2,000,000
(best of a capacity sweep vs dim64/epoch10 baseline and dim256/epoch25 — dim128 won on
every axis, dim256 added nothing further for 2x the model size)
- **Train**: 254,143 PRs (`train.txt`)
- **Val**: 28,238 PRs (`val.txt`)
**Metrics** (100-PR live holdout, 79 true positives, rule = `multilingual <= 1`):
| metric | value |
|---|---|
| rule accuracy | 96.00% |
| precision | 96.30% |
| **recall** | **98.73%** |
| MAE | 0.586 |
| Pearson / Spearman | 0.906 / 0.721 |
This one is genuinely reliable as a pre-filter — high recall *and* precision. If you only
trust one of these two classifiers for a filtering decision, trust this one.
## `research_novelty/` — research/IP content (⚠️ still recall≈0 at the `>=6` rule)
0-10 scale rating the extent to which the PR implements technical algorithms, ideas from
research papers, or original design/IP, as opposed to routine engineering — independent
of how difficult or high-quality the engineering itself is. See `annotate_pr_quality.py`'s
`RESEARCH_NOVELTY_LEVELS`.
- **Hyperparameters**: dim=64, epoch=10 (same baseline as `quality`/`multilingual`)
- **Train**: 96,634 PRs, 297 at level≥6 (`train.txt`)
- **Val**: 10,772 PRs, 35 at level≥6 (`val.txt`)
- **Same train/val PR ids as `tags/`** (both come from the same annotation batch; see
Provenance) — comparable across the two.
**Updated 2026-09-30**: retrained on a new 107,406-PR annotation batch (up from the
original 39,081-PR/98-positive pass), joined back to `quality`/`multilingual`'s existing
`text` field so the same 254K+-PR pool's diff/file content is reused rather than
re-mined. Labels use the field's own fixed 0-10 scale (int truncation), same as
`quality`/`multilingual` — not sample quantiles.
**Metrics** (own val.txt, 10,772 PRs):
| metric | value |
|---|---|
| exact-level accuracy | 82.45% |
| within-1-level accuracy | 94.06% |
| MAE | 0.2745 |
| hard Pearson / Spearman | 0.5679 / 0.5069 |
| soft Pearson / Spearman | 0.6662 / 0.5470 |
| rule (`>=6`) accuracy | 99.68% |
| rule (`>=6`) precision | undefined (0 predicted positive) |
| **rule (`>=6`) recall** | **0.00%** (0/35) |
**Read this before using it as a filter**: correlation improved modestly over the old
diagnostic pass (soft Pearson 0.60→0.67) and the accuracy/MAE numbers look good, but that's
almost entirely the near-100%-easy "correctly predict low novelty" majority class — at the
actual decision boundary that matters (`>=6`), it now has enough val data to say
confidently (35 true positives, not 2) that it **misses every single one**, the same
recall-collapse pattern as `quality`. Its own predictions rarely if ever clear ~6-7 (see
the Megatron-LM spot-check below). Treat it as a continuous novelty *signal* (the soft
score correlates reasonably, 0.67) rather than a working `>=6` binary classifier.
**Spot-check**: `NVIDIA/Megatron-LM#7762` (an Engram/CUDA synchronization change) scores
soft=2.46, hard=1 — plausible as "some genuine ML-systems depth but not paper-derived,"
though still likely underrating it given the classifier's known insensitivity to real
technical depth (see `quality`'s note above; the same failure mode shows up here).
## `tags/` — multi-label topic tagging (424 tags)
Multi-label classification over the topic vocabulary in `/ansh/harbor-tags.txt` (424
distinct tags after dedup — e.g. `python`, `machine-learning`, `web-security`,
`gpu`, `formal methods`) — any number of tags can apply to one PR, independently.
Each tag is an independent Jev "noul" (boolean-probability) claim ("this PR's diff or
touched files involve code/content related to topic X"), bundled into one Jev call per
PR alongside `research_novelty` (see `annotate_tags_research.py`).
**One joint multi-label model, not 424 separate binary classifiers.** fastText's native
multi-label mode (`loss="ova"`, one-vs-all) shares a single embedding/vocabulary across
all 424 tags and trains in one pass — dramatically cheaper than 424x the training
time/storage for what is fundamentally the same text signal, at the cost of slightly
less per-tag threshold flexibility (mitigated by tuning `--threshold` in `infer.py`
per use case; default 0.5).
- **Hyperparameters**: dim=64, epoch=10, lr=0.3, wordNgrams=2, minCount=3,
bucket=2,000,000, **loss=ova**
- **Train**: 96,604 PRs (`train.txt`) / **Val**: 10,769 PRs (`val.txt`) — same PR ids as
`research_novelty/`'s updated split (both from the same annotation batch)
- 33 PRs with zero tags above threshold were dropped (fastText needs ≥1 label/line)
**Metrics** (10,769-PR val set, pooled across all 10,769×424 PR-tag pairs, threshold=0.5):
| metric | value |
|---|---|
| **precision** | **81.95%** |
| **recall** | **57.17%** |
| f1 | 67.35% |
| pooled Pearson r (vs. Jev's raw `tag_probs`) | **0.8055** |
| pooled Spearman ρ | **0.7145** |
Precision stays high across common tags (0.79-0.99) with recall degrading gracefully as
per-tag support drops — normal multi-label behavior, not a red flag. This is the
best-behaved of the four classifiers relative to its task difficulty (a 424-way
multi-label problem vs. a single 0-10 scale).
**Known data-hygiene issue**: `harbor-tags.txt` has a couple of near-duplicate tag pairs
that survived as distinct labels (`data processing`/`data-processing`, `data
engineering`/`data_engineering` — the latter pair also collided post-slugification and
one was suffixed `_dup` in `tag_vocab.json` to disambiguate). Worth deduping the source
file; not fixed here since it would invalidate the current label vocabulary/model.
**Spot-check**: `vercel/next.js#99499` (a tag-revalidation fix) → 30 tags incl.
`typescript`, `react`, `nextjs`, `frontend`, `routing`, `concurrency`, `web-server`.
`NVIDIA/Megatron-LM#7762` → 36 tags incl. `machine-learning`, `pytorch`, `cuda`, `gpu`,
`distributed-systems`, `performance-optimization`, `deeply-technical-ip`. Both match
what a human skim of the PRs would tag.
## Provenance
- Distilled from `typesafe/jev-1.13` (via OpenRouter) on GitHub Archive-mined PRs.
- Full annotation/training pipeline: `/ansh/prism-datagen/annotate_pr_quality.py`
(rubric + Jev calls, incl. `HARBOR_TAGS`/`TAG_CLAIMS`), `/ansh/fasttext-stuff/run.py`/
`train_sweep.py` (quality/multilingual training — each subfolder's `train.py` is the
relevant one of these, trimmed to a single model), `/ansh/fasttext-stuff/evaluate.py`/
`evaluate_sweep.py`/`compare_pre_post.py` (eval).
- `quality`'s known ceiling was diagnosed, not guessed: a dim/epoch capacity sweep
(64→128→256), a class-rebalancing ablation (10%/15% minority oversampling), and a
~1.55x larger combined training corpus (adding a separately GitHub-credibility-filtered
PR pool skewed toward higher quality) were all tried and none moved recall past ~14%.
- `research_novelty`/`tags` come from a separate, lighter-weight annotation pass:
`/ansh/prism-datagen/annotate_tags_research.py` re-streams the same S3 source
`quality`/`multilingual` were mined from, filtered to PR ids already in that
254K+-PR corpus, and asks Jev only for `research_novelty` + the 424 tag claims
(bundled into one call per PR, not 424 separate calls) — reusing that corpus's
existing `text` field rather than re-annotating everything from scratch. Dataset
build: `/ansh/fasttext-stuff/build_research_novelty_v2_dataset.py` and
`build_tags_dataset.py` (both replicate `quality`/`multilingual`'s exact train/val
split — same seed, same 10%, same PR ids — for cross-classifier comparability).
Training/eval: each subfolder's own `train.py`/`evaluate.py`.