项目文件夹

文件
wehub-resource-sync 542cfa195c
CI / Frontend build (push) Failing after 9m6s
CI / Plugin validate (push) Failing after 9m27s
CI / Python lint (push) Failing after 16m1s
CI / Tests (push) Successful in 18m0s
Deploy / deploy (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:33:27 +08:00

593 行
21 KiB
Markdown

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
# Reproducing Paper Results
> **Paper**: *PixelRAG: Retrieval and Generation in Pixel Space over Millions of Web Screenshots*
>
> This document maps every table and figure in the paper to the exact commands needed to reproduce the numbers.
## Prerequisites
### Infrastructure
| Component | Description | Where |
|-----------|-------------|-------|
| **Wikipedia tile index (base)** | 28M vectors, Qwen3-VL-Embedding-2B (pretrained) | `pixelrag-data/search_index/` (215 GB FAISS IVF, dim=2048) |
| **Wikipedia tile index (fine-tuned)** | 26M vectors, LoRA checkpoint-200 | `pixelrag-data/search_index_lora_vit_ckpt200_v2/` (202 GB) |
| **Wikipedia text index** | 15.7M text chunks (1024 tokens, Trafilatura) | `pixelrag-data/text_search_index_1024/` (121 GB) |
| **Article metadata** | URL↔tile mapping for 7.1M articles | `pixelrag-data/articles.json` (199 MB) |
| **Tile images** | ~30M PNG tiles (1024×1024) | Remote NFS or local SSD (~5.6 TB) |
| **News tile index** | 3.6M tiles (BBC/AP/CNN) for LiveVQA | S3: `s3://wiki-screenshot-tiles-backup/kiwix_tiles/news_image_search_index/` |
| **News text index** | 866K text chunks for news | S3: `s3://wiki-screenshot-tiles-backup/kiwix_tiles/news_text_search_index/` |
| **News tiles** | Raw PNG tiles for news articles | S3: `s3://wiki-screenshot-tiles-backup/kiwix_tiles/news_tiles/` |
| **LoRA adapter** | Fine-tuned embedding LoRA weights | S3: `s3://wiki-screenshot-tiles-backup/kiwix_tiles/adapters/lora_vit_ckpt200/` |
| **Kiwix ZIM** | Offline Wikipedia for HTML baselines | S3: `s3://wiki-screenshot-tiles-backup/kiwix_tiles/zim/` |
All S3 paths use AWS profile `leann` (`aws s3 --profile leann ...`).
### Services to Start
```bash
# 1. Screenshot search API (port 30888) — serves the pixel tile index
pixelrag-serve \
--index-dir pixelrag-data/search_index \ # or search_index_lora_vit_ckpt200_v2
--tiles-dir /path/to/wikipedia_tiles \
--articles-json pixelrag-data/articles.json \
--model Qwen/Qwen3-VL-Embedding-2B \
--device cuda --port 30888
# 2. Text search API (port 30889) — serves the text chunk index
pixelrag-serve \
--index-dir pixelrag-data/text_search_index_1024 \
--tiles-dir /path/to/text_chunks \
--articles-json pixelrag-data/articles.json \
--model Qwen/Qwen3-VL-Embedding-2B \
--device cuda --port 30889
# 3. Reader model (port 8000) — vLLM serving Qwen3.5-4B (default reader)
vllm serve Qwen/Qwen3.5-4B-Instruct \
--port 8000 --tensor-parallel-size 1 \
--max-model-len 32768
```
### Environment
```bash
cd ~/pixelrag/eval
# Install eval dependencies (one-time)
uv pip install pandas tqdm trafilatura openai aiohttp datasets huggingface-hub
# For grading
export OPENAI_API_KEY=sk-... # GPT-4.1 judge
```
---
## Table 1: Main Results (6 Benchmarks × 4 Methods)
**Reader**: Qwen3.5-4B, **k=3**, **Grader**: GPT-4.1 judge (except LiveVQA = exact match)
### No Retrieval (baseline)
```bash
# SimpleQA — no retrieval
python run_bench.py \
--task simpleqa --model Qwen/Qwen3.5-4B-Instruct \
--num-examples 1000 --no-think
# NQ — no retrieval
python run_bench.py \
--task nq --model Qwen/Qwen3.5-4B-Instruct \
--num-examples 1000 --no-think
# NQ-Tables — no retrieval
python run_bench.py \
--task nq_tables --model Qwen/Qwen3.5-4B-Instruct \
--num-examples 1000 --no-think
# MMSearch — no retrieval (300 examples)
python run_bench.py \
--task mmsearch --model Qwen/Qwen3.5-4B-Instruct \
--num-examples 300 --no-think
# EVQA — no retrieval (landmarks, automatic only, n=749)
python run_bench.py \
--task encyclopedic_vqa --model Qwen/Qwen3.5-4B-Instruct \
--evqa-dataset-filter landmarks --evqa-question-type-filter automatic \
--num-examples 749 --no-think
# LiveVQA — see "LiveVQA Separate Pipeline" section below
```
### Text Retrieval — Trafilatura (Text → Text)
Requires: text search API on port 30889 with Trafilatura-parsed text chunks.
```bash
# SimpleQA — Trafilatura text retrieval
python run_bench.py \
--task simpleqa --model Qwen/Qwen3.5-4B-Instruct \
--text-api --text-api-url http://localhost:30889/search \
--retrieval-top-k 3 --num-examples 1000 --no-think
# NQ — Trafilatura text retrieval
python run_bench.py \
--task nq --model Qwen/Qwen3.5-4B-Instruct \
--text-api --text-api-url http://localhost:30889/search \
--retrieval-top-k 3 --num-examples 1000 --no-think
# NQ-Tables
python run_bench.py \
--task nq_tables --model Qwen/Qwen3.5-4B-Instruct \
--text-api --text-api-url http://localhost:30889/search \
--retrieval-top-k 3 --num-examples 1000 --no-think
# MMSearch (multimodal query: text + image → text index)
python run_bench.py \
--task mmsearch --model Qwen/Qwen3.5-4B-Instruct \
--text-api --text-api-url http://localhost:30889/search \
--retrieval-top-k 3 --num-examples 300 --no-think
# EVQA
python run_bench.py \
--task encyclopedic_vqa --model Qwen/Qwen3.5-4B-Instruct \
--text-api --text-api-url http://localhost:30889/search \
--evqa-dataset-filter landmarks --evqa-question-type-filter automatic \
--retrieval-top-k 3 --num-examples 749 --no-think
# LiveVQA — see "LiveVQA Separate Pipeline" section below
```
### Text Retrieval — mwparserfromhell
Same as Trafilatura but requires a separate text index built with mwparserfromhell parser.
The text API must be started pointing to that index.
```bash
# Same commands as Trafilatura above, but --text-api-url points to
# the mwparserfromhell text index API (different port or index-dir).
# The parser choice is baked into the index at build time, not a runtime flag.
```
### PixelRAG (base) — Screenshot → Screenshot
Requires: screenshot search API on port 30888 with base (pretrained) embedding index.
```bash
# SimpleQA — pixel retrieval (base)
python run_bench.py \
--task simpleqa --model Qwen/Qwen3.5-4B-Instruct \
--local-api --local-api-url http://localhost:30888/search \
--retrieval-top-k 3 --num-examples 1000 --no-think
# NQ — pixel retrieval (base)
python run_bench.py \
--task nq --model Qwen/Qwen3.5-4B-Instruct \
--local-api --local-api-url http://localhost:30888/search \
--retrieval-top-k 3 --num-examples 1000 --no-think
# NQ-Tables
python run_bench.py \
--task nq_tables --model Qwen/Qwen3.5-4B-Instruct \
--local-api --local-api-url http://localhost:30888/search \
--retrieval-top-k 3 --num-examples 1000 --no-think
# MMSearch (multimodal: query image sent alongside text)
python run_bench.py \
--task mmsearch --model Qwen/Qwen3.5-4B-Instruct \
--local-api --local-api-url http://localhost:30888/search \
--retrieval-top-k 3 --num-examples 300 --no-think
# EVQA (multimodal: landmark photo + question text)
python run_bench.py \
--task encyclopedic_vqa --model Qwen/Qwen3.5-4B-Instruct \
--local-api --local-api-url http://localhost:30888/search \
--evqa-dataset-filter landmarks --evqa-question-type-filter automatic \
--retrieval-top-k 3 --num-examples 749 --no-think
# LiveVQA — see "LiveVQA Separate Pipeline" section below
```
### PixelRAG (fine-tuned) — Screenshot → Screenshot with LoRA embedding
Same commands as PixelRAG (base), but the search API must be started with the fine-tuned index:
```bash
# Start search API with fine-tuned index
pixelrag-serve \
--index-dir pixelrag-data/search_index_lora_vit_ckpt200_v2 \
--tiles-dir /path/to/wikipedia_tiles \
--articles-json pixelrag-data/articles.json \
--model Qwen/Qwen3-VL-Embedding-2B \
--peft-adapter /path/to/lora_checkpoint_200 \
--device cuda --port 30888
```
Then run the same `--local-api` commands above.
### Grading
```bash
cd ~/pixelrag/eval
# Grade with GPT-4.1 judge (Wikipedia QA tasks)
python grade.py simpleqa eval_output/simpleqa_*.jsonl
python grade.py encyclopedic_vqa eval_output/encyclopedic_vqa_*.jsonl
python grade.py mmsearch eval_output/mmsearch_*.jsonl
# For NQ/NQ-Tables (with LLM judge for paper numbers)
python grade.py nq eval_output/nq_*.jsonl --llm-judge
python grade.py nq_tables eval_output/nq_tables_*.jsonl --llm-judge
# For LiveVQA (exact letter match — handled by the LiveVQA pipeline scripts)
```
---
## Table 3: Retrieval–Reader Modality Ablation
**Task**: SimpleQA (1000) + LiveVQA (6632), **Reader**: Qwen3.5-4B, **k=3**,
**Embedding**: Qwen3-VL-Embedding-2B (base, no LoRA)
| Row | Retrieval | Reader Input | Flags |
|-----|-----------|-------------|-------|
| Screenshot → Screenshot | Pixel index | Raw tile images | `--local-api` |
| Screenshot → OCR text | Pixel index | OCR'd text from tiles | `--local-api --read-as-text-ocr` |
| Text → Rendered image | Text index | Text chunks rendered as PNG | `--text-api --render-as-image` |
| Text → Text | Text index | Raw text chunks | `--text-api` |
| Text → HTML | Text index | Raw HTML from kiwix | `--text-api --html-dom-lookup` |
```bash
# Screenshot → Screenshot (same as main results PixelRAG base)
python run_bench.py \
--task simpleqa --model Qwen/Qwen3.5-4B-Instruct \
--local-api --local-api-url http://localhost:30888/search \
--retrieval-top-k 3 --num-examples 1000 --no-think
# Screenshot → OCR text
python run_bench.py \
--task simpleqa --model Qwen/Qwen3.5-4B-Instruct \
--local-api --local-api-url http://localhost:30888/search \
--read-as-text-ocr --ocr-url http://localhost:8202/v1 \
--retrieval-top-k 3 --num-examples 1000 --no-think
# Text → Rendered image
python run_bench.py \
--task simpleqa --model Qwen/Qwen3.5-4B-Instruct \
--text-api --text-api-url http://localhost:30889/search \
--render-as-image \
--retrieval-top-k 3 --num-examples 1000 --no-think
# Text → Text (same as main results Trafilatura)
python run_bench.py \
--task simpleqa --model Qwen/Qwen3.5-4B-Instruct \
--text-api --text-api-url http://localhost:30889/search \
--retrieval-top-k 3 --num-examples 1000 --no-think
# Text → HTML (DOM lookup)
python run_bench.py \
--task simpleqa --model Qwen/Qwen3.5-4B-Instruct \
--text-api --text-api-url http://localhost:30889/search \
--html-dom-lookup \
--retrieval-top-k 3 --num-examples 1000 --no-think
```
For LiveVQA, use the separate pipeline (see "LiveVQA Separate Pipeline" section) with the corresponding ablation scripts.
---
## Table 4: Embedding Training Recipe Ablation
**Evaluated on mini-datastore** (400 queries, 7426 tiles).
This ablation uses `--prebuilt-tiles-dir` pointing to the pre-built mini-datastore, with different embedding checkpoints. Each row corresponds to a different embedding training recipe:
```bash
# Base model (no fine-tuning)
python run_bench.py \
--task simpleqa --model Qwen/Qwen3.5-4B-Instruct \
--use-tiled-retrieval --use-qwen3vl-embedding \
--qwen3vl-model Qwen/Qwen3-VL-Embedding-2B \
--embedding-backend hf \
--prebuilt-tiles-dir tiles-hard-mini/ \
--retrieval-top-k 3 --num-examples 400 --no-think
# With LoRA checkpoint (dynamic hard negatives + ViT unfrozen)
python run_bench.py \
--task simpleqa --model Qwen/Qwen3.5-4B-Instruct \
--use-tiled-retrieval --use-qwen3vl-embedding \
--qwen3vl-model Qwen/Qwen3-VL-Embedding-2B \
--embedding-backend biqwen3 \
--peft-adapter /path/to/checkpoint-200 \
--prebuilt-tiles-dir tiles-hard-mini/ \
--retrieval-top-k 3 --num-examples 400 --no-think
```
The intermediate checkpoints (in-batch negatives, naive hard negatives, dynamic hard negatives frozen) each have their own PEFT adapter path.
---
## Figure 2: Token Efficiency (SimpleQA, k=1,2,3, 4 readers)
**Task**: SimpleQA (1000), **Readers**: Qwen3.5-4B, Qwen3.5-9B, Qwen3.5-27B, Qwen3.6-35B-A3B
For each reader × k × retrieval method, run:
```bash
# Example: Qwen3.5-4B, k=1, PixelRAG (fine-tuned)
python run_bench.py \
--task simpleqa --model Qwen/Qwen3.5-4B-Instruct \
--local-api --local-api-url http://localhost:30888/search \
--retrieval-top-k 3 --reader-top-k 1 \
--num-examples 1000 --no-think
# Example: Qwen3.5-4B, k=2, PixelRAG (fine-tuned)
python run_bench.py \
--task simpleqa --model Qwen/Qwen3.5-4B-Instruct \
--local-api --local-api-url http://localhost:30888/search \
--retrieval-top-k 3 --reader-top-k 2 \
--num-examples 1000 --no-think
# Example: Qwen3.5-4B, k=3, PixelRAG (fine-tuned)
python run_bench.py \
--task simpleqa --model Qwen/Qwen3.5-4B-Instruct \
--local-api --local-api-url http://localhost:30888/search \
--retrieval-top-k 3 \
--num-examples 1000 --no-think
```
> **Optimization**: Use `--retrieval-top-k 3 --reader-top-k N` to retrieve once at k=3 and evaluate at k=1,2,3 from the same JSONL (the full retrieved set is stored in `retrieved_images`).
For each reader, change `--model` and start the appropriate vLLM server.
Repeat for text retrieval (Trafilatura: `--text-api`) and PixelRAG base (base index).
The plot script is at `arxiv/figures/plot_token_efficiency.py`.
---
## Figure 3: Agentic Multi-Hop QA (MoNaCo)
**Task**: MoNaCo (1315 questions), **Agent**: GPT-5 ReAct, **k=5 per search**
Uses `eval/run_monaco.py` — a ReAct agent that issues search tool calls.
```bash
cd ~/pixelrag/eval
# PixelRAG backend
python run_monaco.py \
--reader gpt-5 \
--retrieval pixel \
--pixel-api http://localhost:30888/search \
--default-top-k 5
# Text retrieval backend (Trafilatura)
python run_monaco.py \
--reader gpt-5 \
--retrieval text \
--text-api http://localhost:30889/search \
--default-top-k 5
# Grade (token F1 computed inline; add --judge for LLM judge F1)
python run_monaco.py \
--reader gpt-5 \
--retrieval pixel \
--judge --judge-model gpt-4.1-2025-04-14
# Or grade existing predictions:
python grade.py monaco eval_output/monaco/<run_tag>
```
The dataset (`monaco_version_1_release.jsonl`) should be placed at
`eval/data/monaco/` or passed via `--data-path`.
---
## Figure 4: Image Compression Curve
**Task**: SimpleQA (1000), **Reader**: Qwen3.5-4B (base + SFT), k=1..5, compression c=1×/2×/3×
```bash
# No compression (c=1×), k=3
python run_bench.py \
--task simpleqa --model Qwen/Qwen3.5-4B-Instruct \
--local-api --local-api-url http://localhost:30888/search \
--retrieval-top-k 5 --reader-top-k 3 \
--num-examples 1000 --no-think
# 2× compression, k=3
python run_bench.py \
--task simpleqa --model Qwen/Qwen3.5-4B-Instruct \
--local-api --local-api-url http://localhost:30888/search \
--retrieval-top-k 5 --reader-top-k 3 \
--pixel-compress-ratio 2.0 \
--num-examples 1000 --no-think
# 3× compression, k=3
python run_bench.py \
--task simpleqa --model Qwen/Qwen3.5-4B-Instruct \
--local-api --local-api-url http://localhost:30888/search \
--retrieval-top-k 5 --reader-top-k 3 \
--pixel-compress-ratio 3.0 \
--num-examples 1000 --no-think
```
For the SFT reader, replace `--model` with the SFT checkpoint path and serve it via vLLM.
The plot script is at `arxiv/figures/plot_sft_compression_curve.py`.
---
## Table 8: Full Reader-Model Sweep (31 VLMs)
**Task**: SimpleQA (1000), **k=3**, pixel retrieval (base) vs text retrieval (Trafilatura)
For each of the 31 reader models, run two jobs:
```bash
# Pixel retrieval
python run_bench.py \
--task simpleqa --model <MODEL_NAME> \
--local-api --local-api-url http://localhost:30888/search \
--retrieval-top-k 3 --num-examples 1000 --no-think
# Text retrieval
python run_bench.py \
--task simpleqa --model <MODEL_NAME> \
--text-api --text-api-url http://localhost:30889/search \
--retrieval-top-k 3 --num-examples 1000 --no-think
```
where `<MODEL_NAME>` is one of:
- `liuhaotian/llava-v1.5-7b`
- `meta-llama/Llama-3.2-11B-Vision-Instruct` (k=1 for pixel due to architecture limit)
- `meta-llama/Llama-3.2-90B-Vision-Instruct` (k=1 for pixel)
- `meta-llama/Llama-4-Scout-17B-16E-Instruct`
- `meta-llama/Llama-4-Maverick-17B-128E-Instruct`
- `Qwen/Qwen2-VL-2B-Instruct` through `Qwen/Qwen2-VL-72B-Instruct`
- `Qwen/Qwen2.5-VL-3B-Instruct` through `Qwen/Qwen2.5-VL-72B-Instruct`
- `Qwen/Qwen3-VL-2B` through `Qwen/Qwen3-VL-235B-A22B`
- `Qwen/Qwen3.5-0.8B` through `Qwen/Qwen3.5-35B-A3B`
- `Qwen/Qwen3.6-27B`, `Qwen/Qwen3.6-35B-A3B`
For reasoning-mode models, omit `--no-think`.
Each model requires its own vLLM instance (or OpenRouter/Commonstack for API models).
---
## LiveVQA (Table 1 + Table 3)
LiveVQA uses `eval/run_livevqa.py` — a dedicated script for the news corpus.
**Requires**: News pixel search API (port 30890), news text search API (port 30892),
LiveVQA v4 JSON dataset, vLLM reader.
```bash
cd ~/pixelrag/eval
# No retrieval
python run_livevqa.py --mode naive \
--model Qwen/Qwen3.5-4B-Instruct \
--output eval_output/livevqa_naive.jsonl
# PixelRAG (screenshot → screenshot)
python run_livevqa.py --mode pixel \
--pixel-api http://localhost:30890/search \
--model Qwen/Qwen3.5-4B-Instruct \
--output eval_output/livevqa_pixel.jsonl
# Text retrieval (Trafilatura)
python run_livevqa.py --mode text \
--text-api http://localhost:30892/search \
--model Qwen/Qwen3.5-4B-Instruct \
--output eval_output/livevqa_text.jsonl
# Hybrid (pixel + text)
python run_livevqa.py --mode hybrid \
--pixel-api http://localhost:30890/search \
--text-api http://localhost:30892/search \
--model Qwen/Qwen3.5-4B-Instruct \
--output eval_output/livevqa_hybrid.jsonl
```
Grading is automatic (5-option MC exact letter match) — printed at the end of each run.
---
## Known Issues (Blockers for Reproduction)
### ~~0. Missing simpleqa modules~~ (FIXED)
`screenshot.py` and `pixel_query.py` have been copied into `eval/lib/`.
Selenium import is deferred so it doesn't block `--local-api` users.
### ~~1. `dr_agent` not importable~~ (FIXED)
Dataset loaders extracted into `eval/lib/benchmarks.py`. The `run_bench.py`
import now reads from `simpleqa.datasets_loader` instead of `dr_agent`.
### ~~2. Grading script not in this repo~~ (FIXED)
`eval/grade.py` implements GPT-4.1 3-way grading (CORRECT/INCORRECT/NOT_ATTEMPTED) using
the same prompt template as the paper. No dependency on the old repo's evaluation framework.
For the legacy full evaluation framework (per-example HTML reports, etc.), the original
is still at `~/pixelrag-src/Vis-RAG/agent/scripts/evaluate.py`.
### 3. Hardcoded paths in retrieval.py
`eval/lib/retrieval.py` lines 84–88 have placeholder paths (`/path/to/project`, `/path/to/data`) for the local kiwix tile store. These are only used by `LocalWikiTiledScreenshotRetriever` (ground-truth screenshot mode), not by the production `--local-api` mode.
### ~~4. LiveVQA uses separate pipeline~~ (FIXED)
`eval/run_livevqa.py` handles all LiveVQA modes (naive, pixel, text, hybrid).
### ~~5. MoNaCo runs from old repo~~ (FIXED)
`eval/run_monaco.py` implements the full ReAct agent loop with pixel/text retrieval backends.
### 6. mwparserfromhell text index
The paper's second text baseline uses mwparserfromhell parser. The text index must be built separately with this parser — the parser choice is embedded at index build time, not at query time. The build pipeline for this variant needs to be documented.
### 7. News corpus indexes
LiveVQA requires separate tile and text indexes built over the news corpus (BBC/AP/CNN). These indexes are on a different machine/path and need their own `pixelrag-serve` instances.
---
## Grading Protocol Summary
| Benchmark | Metric | Grader |
|-----------|--------|--------|
| SimpleQA | CORRECT/INCORRECT/NOT_ATTEMPTED → accuracy | GPT-4.1 (temp=0, seed=42) |
| NQ | Same 3-way judge | GPT-4.1 (temp=0, seed=42) |
| NQ-Tables | Same 3-way judge (up to 10 gold aliases joined with OR) | GPT-4.1 |
| MMSearch | Same 3-way judge | GPT-4.1 |
| EVQA | Same 3-way judge (reference_list → "Any of: ref1 \| ref2") | GPT-4.1 |
| LiveVQA | 5-option multiple-choice exact letter match | No LLM |
| MoNaCo | Token-level F1 (primary), LLM judge F1 (secondary) | GPT-4.1 |
---
## Quick Smoke Test (Verify Pipeline Works)
Run a single example end-to-end before committing to full runs:
```bash
# 1. Verify search API is responding
curl -s http://localhost:30888/status | python -m json.tool
# 2. Run 5 examples, no retrieval
python run_bench.py \
--task simpleqa --model Qwen/Qwen3.5-4B-Instruct \
--num-examples 5 --no-think --force
# 3. Run 5 examples, pixel retrieval
python run_bench.py \
--task simpleqa --model Qwen/Qwen3.5-4B-Instruct \
--local-api --local-api-url http://localhost:30888/search \
--retrieval-top-k 3 --num-examples 5 --no-think --force
# 4. Grade
cd ~/pixelrag-src/Vis-RAG/agent
python scripts/evaluate.py simpleqa ~/pixelrag/eval/eval_output/<output>.jsonl
```
---
## Output File Convention
All outputs go to `eval_output/` with auto-generated filenames:
```
eval_output/{task}_{mode}_{model_safe}_{n}.jsonl
```
Examples:
- `eval_output/simpleqa_naive_qwen_qwen3.5_4b_instruct_1000.jsonl`
- `eval_output/simpleqa_local_api_qwen_qwen3.5_4b_instruct_1000.jsonl`
- `eval_output/nq_text_api_qwen_qwen3.5_4b_instruct_1000.jsonl`
Grading results are saved alongside as `*_eval_results.json`.