Skip to content

Latest commit

 

History

History
231 lines (177 loc) · 10.4 KB

File metadata and controls

231 lines (177 loc) · 10.4 KB

ProtonDB Game Compatibility Prediction

ML pipeline that predicts game compatibility with Proton/Wine on Linux using ProtonDB community reports.

Two-stage cascade LightGBM classifier with IRT-based label denoising, trained on 350K+ user reports with 123 features.

Results

Evaluation 3-class F1 Binary F1 Accuracy
Per-report 0.780 0.906 0.828
Per-game (production) 0.871 0.943 0.934

Per-game predictions aggregate individual report predictions via majority vote — individual errors cancel out.

Architecture

ProtonDB dump → Worker → SQLite DB → Preprocessing → ML Training → Prediction
                                          │
                          ┌────────────────┼────────────────┐
                          │                │                │
                    Enrichment      Normalization     LLM extraction
                  (Steam, PCGW,    (GPU/CPU/driver    (launch options,
                   ProtonDB API,    heuristic)         text analysis)
                   Steam PICS,
                   AWACY, GitHub)

Stack: Python 3.12, LightGBM, FastAPI, SQLite, SHAP, Click CLI.

Key components

  • Worker fetches ProtonDB data dumps and imports reports into SQLite
  • Preprocessing enriches data from Steam Store, Steam PICS (bulk CM protocol), PCGamingWiki, AreWeAntiCheatYet, ProtonDB API (contributor data); normalizes GPU/CPU strings; extracts structured data from text via LLM
  • ML trains a cascade classifier with IRT denoising, SVD embeddings, and per-game aggregation
  • IRT (Item Response Theory) decomposes subjective tinkering/oob labels into per-annotator strictness and per-game difficulty — the key innovation (+0.030 F1)

Key innovations

  1. IRT label denoising — 1PL Item Response Theory separates annotator bias (θ) from game difficulty (d). Resolves the subjective tinkering↔works_oob boundary that causes 15-20% label noise. (+0.030 F1)
  2. Contributor-aware relabeling — replaces Cleanlab + rule-based heuristics with IRT-informed relabeling based on annotator strictness. (+0.017 F1)
  3. Per-game majority vote — aggregates per-report predictions at inference time. Errors cancel out, boosting F1 from 0.780 to 0.871. (+0.091 F1)

Setup

git clone https://github.com/getjump/protondb-game-compatibility-prediction
cd protondb-game-compatibility-prediction

python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"

cp .env.example .env
# Edit .env — configure LLM backend (local ollama, OpenRouter, or Claude CLI)

Usage

1. Import data

# Download and import the latest ProtonDB dump
protondb-settings worker check
protondb-settings worker sync

2. Preprocess

# Cleaning + heuristic normalization + enrichment (no LLM required)
protondb-settings preprocess run

# Enrichment from additional sources
protondb-settings preprocess run --step enrichment --source protondb_reports
protondb-settings preprocess run --step enrichment --source steam_pics

# LLM-based preprocessing (requires local or cloud LLM)
protondb-settings preprocess llm normalize-gpu
protondb-settings preprocess llm normalize-cpu
protondb-settings preprocess llm parse-launch-options
protondb-settings preprocess llm extract

# LLM backends: --backend openai (default), openrouter, or claude-cli
protondb-settings preprocess llm --backend openrouter extract

3. Train

protondb-settings ml train-cascade
# Reuse Stage 1 for faster iteration on Stage 2:
protondb-settings ml train-cascade --reuse-stage1 data/model_stage1.pkl

4. Serve (WIP)

protondb-settings serve

How it works

Model

Two-stage cascade with IRT denoising:

  1. Stage 1: borked (0) vs works (1) — catches broken games (F1=0.846)
  2. Stage 2: tinkering (0) vs works_oob (1) — for non-broken games, distinguishes "needs tweaking" from "works out of the box" (F1=0.880 tinkering, 0.614 oob)
  3. IRT fitting: decomposes annotator strictness (θ) and game difficulty (d) from contributor data
  4. Contributor-aware relabeling: corrects labels from strict annotators
  5. Per-game aggregation: majority vote at inference time

Features (123 total)

Group Features Description
Hardware GPU family, driver versions, APU/iGPU flags From report system info
Temporal Report age Days since report submission
Game metadata Engine, genre, DRM, anticheat, Deck status From Steam, PCGamingWiki, AWACY
SVD embeddings GPU (16-20d), Game (16-20d) From co-occurrence matrices
Text Keywords, sentiment, note lengths From user notes
Text embeddings Sentence-transformer SVD (32d) From verdict notes
Game aggregates Customization rates, fault rates Per-game community signals
IRT features Game difficulty, contributor strictness From Item Response Theory
Error features Contributor consistency, game agreement Per-annotator and per-game stats
Proton variant official/ge/experimental/native Runner type (top Stage 2 feature)

Label noise and IRT

The main challenge is subjective labeling on the tinkering/works_oob boundary (~15-20% noise). Different users have different standards — choosing GE-Proton is "tinkering" for strict users but "works_oob" for lenient ones.

IRT solution: Fit a 1PL Item Response Theory model on contributor×game interactions:

  • θ (theta): per-contributor strictness — how likely they are to say "tinkering"
  • d (difficulty): per-(game, GPU family) objective difficulty
  • P(tinkering) = σ(θ - d): separates annotator bias from game reality

IRT features are the #2 and #4 most important features in Stage 2. Combined with contributor-aware relabeling, IRT provides +0.047 F1 improvement.

Experiment history

50+ experiments across Phases 1-22. See docs/ for detailed plans and results.

Foundation (Phases 1-8)

Phase Approach Result
1-3 Python foundation, worker, preprocessing pipeline Infrastructure
4 Single LightGBM model → cascade (borked/works → tinkering/oob) F1=0.593 → cascade +0.009
5 SVD embeddings (GPU×Game, CPU×Game co-occurrence) +0.010 F1
6 Game metadata enrichment (Steam, PCGamingWiki, AWACY, GitHub) +0.024 F1
7 Text features (keyword regex, sentiment, note lengths) +0.008 F1
8 Rule-based relabeling (tinkering→oob if no effort markers) +0.010 F1

Noise reduction (Phases 9-10)

Phase Approach ΔF1 Outcome
9.1 Label smoothing α=0.15 (cross_entropy objective) +0.008 Noise-robust training
9.2 Per-game aggregate features (26 features) +0.024 Community signals
9.3 Cleanlab noise removal (3% of train) +0.021 Confident learning
9.4 Ordinal classification, distillation, focal loss 0.000 All negative (6 experiments)
9.5 Feature combinations, target encoding −0.002 All negative (8 experiments)
10 Text embeddings (sentence-transformers SVD 32d) +0.005 Partial text coverage (32%)

IRT breakthrough (Phases 11-13) — main contribution

Phase Approach ΔF1 Outcome
11.2 Alternative models (CatBoost, XGBoost, HistGBM) −0.002 LightGBM optimal
12.8 IRT features (game difficulty + annotator strictness) +0.030 Key innovation
12.1-12.3 Contributor features, sample weighting −0.002 IRT dominates
13.1 IRT-only relabeling (replaces Phase 8 + Cleanlab) +0.013 Simpler, better
13.2 Contributor-aware relabeling by annotator θ +0.017 Graduated relabeling
13.3 Hybrid pipeline, confidence weighting −0.001 Weighting hurts
13.4-13.5 Iterative IRT, annotator SVD embeddings +0.001 Marginal

Optimization attempts (Phases 14-19) — mostly negative

Phase Approach ΔF1 Outcome
14 Steam PICS features (runtime, deck tests, review) 0.000 Redundant with existing
15 Temporal features, Proton×Game SVD, Factorization Machines −0.003..−0.017 report_age_days sufficient
16 Class weight 1.8x oob + error features +0.006 Compensate temporal shift
17 HP tuning (reg=1.0) + ensemble +0.004 Stronger regularization
18 Threshold optimization, focal loss, adaptive soft labels 0.000 Already calibrated
19 LLM verdict inference (OpenRouter), data filtering −0.001 IRT already optimal

Task reformulation (Phases 20-22) — production framing

Phase Approach ΔF1 Outcome
20 Per-(game, gpu) aggregated evaluation F1 0.780→0.829 per-pair
21 Per-game majority vote aggregation +0.091 Production metric: F1=0.871
21.5 Aggregated model (trained on pairs) Leakage confirmed
21.7 Cold-start model (metadata only) Binary F1=0.601
22 Text embeddings upgrade, Stage 1 tuning 0.000 Pipeline saturated

Project structure

protondb_settings/
  api/          FastAPI server and routes
  db/           SQLite connection, migrations
  ml/
    irt.py            IRT fitting, features, contributor-aware relabeling
    aggregate.py      Per-game aggregated prediction
    models/           Cascade classifier (Stage 1 + Stage 2)
    features/         Feature engineering (embeddings, encoding, game aggregates)
    train.py          Training pipeline
    predict.py        Single-sample prediction
    noise.py          Cleanlab noise detection (legacy, replaced by IRT)
    relabeling.py     Rule-based relabeling (legacy, replaced by IRT)
  preprocessing/
    cleaning.py         Data cleaning
    normalize/          GPU/CPU/driver normalization (heuristic + LLM)
    enrichment/         External API data (Steam, Steam PICS, PCGamingWiki,
                        ProtonDB, ProtonDB Reports, AWACY, GitHub)
    llm/                LLM client (OpenAI, OpenRouter, Claude CLI backends)
    extract/            Structured text extraction + verdict inference
  worker/       ProtonDB dump fetcher and importer
  cli.py        Click CLI entry point
  config.py     Environment-based configuration
scripts/        Experiment scripts (50+ experiments, reproducibility)
docs/           Architecture plans, ML experiment logs, research notes

License

MIT