RepoAmazon (Nova)Amazon (Nova)published Sep 1, 2026seen 1w

amazon-science/XplainRecommendation

Python

Open original ↗

Captured source

source ↗

amazon-science/XplainRecommendation

Description: Code accompanying "Pairwise Ranking Outperforms Single-Action RL for Offline Explanation Selection: A Practical Lesson" (Tanay Chowdhury, Saeideh Shahrokh Esfahani — RecSys 2026, Research & Practice Notes track).

Language: Python

License: NOASSERTION

Stars: 0

Forks: 0

Open issues: 0

Created: 2026-09-01T17:52:15Z

Pushed: 2026-09-01T18:34:29Z

Default branch: main

Fork: no

Archived: no

README:

XplainRecommendation

Code accompanying *"Pairwise Ranking Outperforms Single-Action RL for Offline Explanation Selection: A Practical Lesson"* (Tanay Chowdhury, Saeideh Shahrokh Esfahani — RecSys 2026, Research & Practice Notes track).

This repo reproduces every headline number in the paper from XRec's published data. It is standalone: the only external dependencies are XRec's data files (trn.pkl / tst.pkl), G-Refer's cached predictions (google_pred.jsonl), and a local review cache (iid_reviews.json).

If you already have the data-prep artefacts in results/ (the release bundle ships with them), jump to §4. If you want to regenerate everything from raw XRec data, start at §1.

> This code is being released solely for academic and scientific > reproducibility purposes, in support of the methods and findings described > in the associated publication. Pull requests are not being accepted in > order to maintain the code exactly as it was used in the paper.

---

0. What's in here

XplainRecommendation/
├── README.md ← this file
├── LICENSE ← CC-BY-NC-4.0
├── NOTICE
├── CONTRIBUTING.md
├── CODE_OF_CONDUCT.md
├── requirements.txt
├── docs/
│ ├── RESEARCH_SUMMARY.md ← problem statement, methods, theory, what didn't work
│ └── kg_path_selection_results.md
├── _paths.py ← shared path resolution helper
├── scripts/ ← pool-generation + reranker library modules
├── src/ ← PPO agent, Bedrock client, entropy scheduler
├── pipeline/ ← data-prep stages (verify → sample → pool → featurize → tables)
├── runners/ ← training scripts: PPO, GRPO, DPO, Distillation
├── movielens/ ← parallel pipeline for the MovieLens-1M cross-dataset check
└── results/ ← evaluation outputs (baselines, lambdarank, experiments)

By default, external datasets (XRec/, G-Refer/, data/) are expected as siblings of this repo's own top-level directories (i.e. inside this repo's root — see §1). Set the FINAL_RESULT_DATA env var if your datasets live somewhere else.

---

1. Prerequisites

Files that must exist under DATA_ROOT (the repo root, by default):

DATA_ROOT/
├── XRec/data/google/trn.pkl # 94,663 training pairs
├── XRec/data/google/val.pkl # 11,833 val pairs
├── XRec/data/google/tst.pkl # 3,000 test pairs
├── XRec/data/google/tst_pred.pkl # XRec's cached predictions (baseline)
├── G-Refer/gen_explanations/G-Refer/google_pred.jsonl # G-Refer's cached predictions (baseline)
├── G-Refer/evaluation/bart_score.py # for BARTScore
└── data/google_local/iid_reviews.json # same-business review cache

XRec and G-Refer are separate public research repos — clone them yourself; this repo does not vendor their code or data.

Python packages:

pip install -r requirements.txt

AWS credentials for Bedrock (Nova Lite + Claude 3 Haiku + Titan v2 embeddings) are required only if regenerating the candidate pool. If you're using the bundled results, no AWS access is needed.

---

2. Running the data-prep pipeline

The full pipeline is orchestrated by pipeline/prepare_data.py. It runs five stages sequentially; each stage caches its output, so re-runs skip anything already done.

python3 pipeline/prepare_data.py

| Stage | Script | Output | Wall time | Bedrock cost | |---|---|---|---|---| | 1. verify | verify_splits.py | (prints OK) | 1 min | $0 | | 2. sample | sample_training.py | results/trn_5k.pkl | 30 sec | $0 | | 3. pool | build_pool.py | results/pool/exp_pool_clean_xrec_{trn,tst}_*.pkl | ~3 h | ~$11 | | 4. featurize | featurize_and_label.py | results/features/features.pkl | ~4 h | ~$0.40 | | 5. tables | fit_lambdarank_and_build_tables.py | results/paper_ppo_tables.npz, results/lambdarank_*.{pkl,json,npz} | ~2 min | ~$0.20 |

Run individual stages:

python3 pipeline/prepare_data.py --stages verify,sample
python3 pipeline/prepare_data.py --force-stage pool

Each standalone script also runs on its own:

python3 pipeline/sample_training.py
python3 pipeline/featurize_and_label.py --max_workers 4

---

3. Evaluating baselines

python3 pipeline/eval_baselines.py

Reads XRec's tst_pred.pkl and G-Refer's google_pred.jsonl, scores them with the same BERTScore / BARTScore / USR metrics we use for our methods, writes results/baselines.json. No training, no Bedrock calls — just evaluation on cached predictions.

Expected output (on the shared 2,958-pair test subset):

  • XRec F1 = 0.3856, BART = −3.56, USR = 0.9993
  • G-Refer 8B F1 = 0.4592, BART = −3.31, USR = 0.60

---

4. Training and evaluating our methods

All four RL-family methods and LambdaRank are trained on the same results/paper_ppo_tables.npz state/reward tables produced by step 5. The 5,000-pair training set and 2,958-pair test set are baked into the tables; no data loading is required at training time.

4a. LambdaRank

Already computed in step 5. Read the result directly:

python3 -c "import json; d=json.load(open('results/lambdarank_result.json')); print(f\"LambdaRank F1 = {d['test_f1_mean']:.4f} ± {d['test_f1_std']:.4f} (n={d['n_test']})\")"
# → LambdaRank F1 = 0.5003 ± 0.0947 (n=2958)

4b. PPO (single seed, baseline reward)

python3 runners/run_ppo.py \
--tables results/paper_ppo_tables.npz \
--seed 42 --episodes 500 \
--reward_mode baseline --alpha 0.1 \
--result_out results/experiments/ppo/seed42.json \
--history_out results/experiments/ppo/seed42_history.json

--reward_mode can be baseline, zscore, alpha0, or median (see runners/run_ppo.py for the reward-shape ablation).

4c. GRPO

python3 runners/run_grpo.py \
--tables results/paper_ppo_tables.npz \
--seed 42 --episodes 500 \
--result_out results/experiments/grpo/seed42.json

4d. DPO

python3 runners/run_dpo.py \
--tables results/paper_ppo_tables.npz \
--seed 42 --episodes 500 \
--result_out results/experiments/dpo/seed42.json

4e. Distillation

python3 runners/run_distillation.py \
--tables results/paper_ppo_tables.npz \
--seed 42 \...

Excerpt shown — open the source for the full document.