Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
cdaf034
rewrote passage chunking
Dec 10, 2025
1536517
added logic for left padding
Dec 10, 2025
dccfaf8
added search
Dec 11, 2025
1769715
changed the tokenizer logic
Dec 14, 2025
16d604a
added train collator debug
Dec 14, 2025
61dbf6e
traincollator is done
Dec 14, 2025
6ec22a9
fixed some comments
Dec 14, 2025
40eddf8
modified chunkedencoder
Dec 14, 2025
0ebcf37
Modified forward and model
Dec 14, 2025
e7e3bc3
Modified inference on chunked passage, in progress
Dec 14, 2025
2d9939f
fixed a chunk size not passed to model
Dec 14, 2025
ed3e302
changed eos to sep
Dec 15, 2025
add3832
added logs
Dec 16, 2025
249dd9d
added some scripts
Dec 19, 2025
9efb43b
added tests
Dec 22, 2025
9c37e29
added tests
Dec 22, 2025
a18c578
added log
Dec 22, 2025
f0ee786
review
Dec 23, 2025
3c1752d
added collator helper functions
Dec 23, 2025
1fa3c1e
added padding helper
Dec 23, 2025
224e185
Tested the collator
Dec 23, 2025
a60b20b
Reviewed forward and maxsim
Dec 23, 2025
32a1976
dataset uses random negative for cases have less negatives
Dec 24, 2025
1b4163e
added some prints
Dec 24, 2025
fc16311
removed one breakpoint
Dec 24, 2025
22868d2
Added random chunks
Dec 29, 2025
d389bc6
Added full randomization
Dec 29, 2025
338fe92
removed useless variables
Dec 29, 2025
190f937
Refactored the randomization
Dec 29, 2025
0a9626f
added search time passage encoding with chunks
Jan 6, 2026
b88e5c1
Added random chunk size for eval, and tests for prechunked passages a…
Jan 10, 2026
cd858d1
Move loss compute to trainer
Jan 26, 2026
417a201
add logs
Jan 26, 2026
fa5362d
try to fix masks
Jan 26, 2026
fe01725
removed some logs
kevin-zkc Jan 26, 2026
6793707
Removed many logs
Jan 26, 2026
e853354
removed emb printing
Jan 26, 2026
5338771
one more fix for chunk size
Jan 27, 2026
5f48c8a
add debug log
Jan 27, 2026
493ed71
fix hash
Jan 27, 2026
3e50628
added script generator
Jan 29, 2026
69840a2
updated the scripts
Jan 29, 2026
d6dbff8
modified script
Jan 29, 2026
82efea9
changed the scripts and log
Jan 29, 2026
8bbcf87
report to wandb
Jan 29, 2026
cc26a6b
reevaluated the code
Jan 30, 2026
6056cc5
added the chunking
Feb 6, 2026
ac92ddb
add a script
Feb 6, 2026
6df28f6
add train
Feb 6, 2026
ca544cf
Added diff
Feb 13, 2026
23b06aa
added tests/test_chunking.py
Feb 13, 2026
da49386
Added mpe
Feb 27, 2026
b1c28ed
deleted some scripts
Feb 27, 2026
19e0519
deleted some scripts-2
Feb 27, 2026
ee39a3e
deleted non-necessary code
Feb 27, 2026
9d31443
deleted more scripts
Feb 27, 2026
376d1da
dataset package
Feb 27, 2026
54913b0
added req
Feb 27, 2026
c1bd75c
uses sdpa for longembed
Feb 27, 2026
dc6a4fb
changed readme
Feb 28, 2026
1c1ec6d
chore: gitignore decrypted BrowseComp-Plus queries.tsv
Jul 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ __pycache__/
examples/**/models/
examples/**/results/
examples/**/wandb/
# Decrypted BrowseComp-Plus queries — regenerate locally via decrypt_dataset.py --generate-tsv; never commit
examples/BrowseComp-Plus/topics-qrels/queries.tsv
379 changes: 379 additions & 0 deletions examples/mpe/00_prepare_data.py

Large diffs are not rendered by default.

129 changes: 129 additions & 0 deletions examples/mpe/01_train.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
#!/bin/bash
set -euo pipefail
# ══════════════════════════════════════════════════════════════════════════════
# Click 1 of 4 — Train All Models
#
# Trains 4 models on MLDR-EN (Shitao/MLDR, en split) with 1 epoch:
#
# Model │ Chunk args
# ────────────────────┼──────────────────────────────────────
# nochunk-epoch1 │ --passage_chunk_size 0
# maxp-train-epoch1 │ --passage_chunk_size 64 --passage_chunk_independent
# fixed-64-epoch1 │ --passage_chunk_size 64
# prand-32to1024-epoch1│ --passage_chunk_size_range 32,1024
#
# These 4 models support the 5 evaluation configs in 02–04_eval_*.sh:
# Single-Vector → nochunk-epoch1 (eval: no chunking)
# MaxP → nochunk-epoch1 (eval: independent chunk 64)
# MaxP-Train → maxp-train-epoch1 (eval: independent chunk 64)
# MPE-fixed64 → fixed-64-epoch1 (eval: chunk 64)
# MPE-rand → prand-32to1024-epoch1 (eval: chunk 64)
#
# Usage:
# bash 01_train.sh
# ══════════════════════════════════════════════════════════════════════════════

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
EXP_ROOT="${EXP_ROOT:-${REPO_ROOT}}"
MODEL_ROOT="${EXP_ROOT}/models"
LOG_DIR="${EXP_ROOT}/logs/repro"
NUM_GPUS=8
export OMP_NUM_THREADS=1

mkdir -p "${LOG_DIR}"

# ── Helpers ───────────────────────────────────────────────────────────────────
run_cmd() {
echo ""
echo "[CMD] $*"
echo ""
if [[ "$1" == *=* ]]; then
env "$@"
else
"$@"
fi
}

train_model() {
local train_name="$1"
shift
local extra_args="$@"
local model_dir="${MODEL_ROOT}/${train_name}"
local log_file="${LOG_DIR}/train_${train_name}.log"

echo ""
echo "================================================================"
echo " Training: ${train_name}"
echo " Model dir: ${model_dir}"
echo " Extra args: ${extra_args}"
echo " Started: $(date)"
echo "================================================================"

if [ -f "${model_dir}/adapter_config.json" ]; then
echo "=== Skipping training (checkpoint exists at ${model_dir}) ==="
return
fi

mkdir -p "${model_dir}"

run_cmd \
CUDA_VISIBLE_DEVICES=$(seq -s, 0 $((NUM_GPUS-1))) \
torchrun --nproc_per_node ${NUM_GPUS} --master_port 60001 \
-m tevatron.retriever.driver.train \
--output_dir "${model_dir}" \
--model_name_or_path Qwen/Qwen3-Embedding-0.6B \
--bf16 --pooling last --padding_side right --normalize \
--attn_implementation sdpa \
--do_train --lora \
--lora_target_modules q_proj,k_proj,v_proj,o_proj,down_proj,up_proj,gate_proj \
--save_steps 5000 \
--dataset_name Shitao/MLDR --dataset_config en --dataset_split train \
--query_prefix "Instruct: Given a question, retrieve documents that answer the question.\nQuery:" \
--passage_prefix "" \
--temperature 0.03 \
--per_device_train_batch_size 2 --train_group_size 4 \
--learning_rate 1e-4 \
--query_max_len 512 --passage_max_len 8192 \
${extra_args} \
--num_train_epochs 1 \
--logging_steps 10 \
--overwrite_output_dir \
--gradient_checkpointing \
--gradient_accumulation_steps 1 \
--run_name "${train_name}" \
2>&1 | tee -a "${log_file}"

echo ""
echo "=== Training done: ${train_name} at $(date) ==="
}

# ══════════════════════════════════════════════════════════════════════════════
echo "╔══════════════════════════════════════════════════════════════════╗"
echo "║ Paper Reproduction — Step 1/4: Train All Models ║"
echo "║ Started: $(date) ║"
echo "╚══════════════════════════════════════════════════════════════════╝"

# 1. nochunk (for Single-Vector and MaxP eval)
train_model "nochunk-epoch1" \
--passage_chunk_size 0

# 2. maxp-train (for MaxP-Train eval: independent chunking during training)
train_model "maxp-train-epoch1" \
--passage_chunk_size 64 --passage_chunk_independent

# 3. fixed-64 (for MPE-fixed64 eval)
train_model "fixed-64-epoch1" \
--passage_chunk_size 64

# 4. prand-32to1024 (for MPE-rand eval: random chunk range, eval at 64)
train_model "prand-32to1024-epoch1" \
--passage_chunk_size_range 32,1024

echo ""
echo "╔══════════════════════════════════════════════════════════════════╗"
echo "║ Step 1/4 complete — all 4 models trained. ║"
echo "║ Finished: $(date) ║"
echo "║ ║"
echo "║ Next: bash 02_eval_mldr_en.sh ║"
echo "╚══════════════════════════════════════════════════════════════════╝"
220 changes: 220 additions & 0 deletions examples/mpe/02_eval_mldr_en.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
#!/bin/bash
set -euo pipefail
# ══════════════════════════════════════════════════════════════════════════════
# Click 2 of 4 — Evaluate All Configs on MLDR-EN
#
# 5 evaluation configurations:
#
# Config │ Model │ Eval chunk │ Independent
# ─────────────────┼────────────────────┼────────────┼────────────
# single-vector │ nochunk-epoch1 │ 0 (none) │ —
# maxp │ nochunk-epoch1 │ 64 │ yes
# maxp-train │ maxp-train-epoch1 │ 64 │ yes
# mpe-fixed64 │ fixed-64-epoch1 │ 64 │ no
# mpe-rand-32to1024│ prand-32to1024-epoch1│ 64 │ no
#
# Benchmark settings:
# bf16, pooling last, padding_side right, passage_max_len 8192
# Metrics: ndcg_cut.10, recall.100
#
# Prerequisites:
# - Models trained via 01_train.sh
# - MLDR-EN data at data/{corpus.jsonl, queries.jsonl, qrels.tsv}
#
# Usage:
# bash 02_eval_mldr_en.sh [num_gpus] (default: 8)
# ══════════════════════════════════════════════════════════════════════════════

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
EXP_ROOT="${EXP_ROOT:-${REPO_ROOT}}"
MODEL_ROOT="${EXP_ROOT}/models"
NUM_GPUS=8
BASE_MODEL="Qwen/Qwen3-Embedding-0.6B"
LOG_DIR="${EXP_ROOT}/logs/repro"
export OMP_NUM_THREADS=1
mkdir -p "${LOG_DIR}"

# ── Helpers ───────────────────────────────────────────────────────────────────
run_cmd() {
echo ""
echo "[CMD] $*"
echo ""
if [[ "$1" == *=* ]]; then
env "$@"
else
"$@"
fi
}

log_shard_cmd() {
echo ""
echo "[CMD] (x${NUM_GPUS} shards) CUDA_VISIBLE_DEVICES={gpu} $*"
echo ""
}

# ══════════════════════════════════════════════════════════════════════════════
# MLDR-EN evaluation function
# ══════════════════════════════════════════════════════════════════════════════
eval_mldr_en() {
local config_name="$1"
local model_dir="$2"
local chunk_size="$3" # 0 = no chunking
local independent="$4" # "true" or "false"

local benchmark="mldr-en"
local encode_dir="${EXP_ROOT}/encode/${benchmark}/${config_name}"
local results_dir="${EXP_ROOT}/results/${benchmark}/${config_name}"
local log_file="${LOG_DIR}/eval_${benchmark}_${config_name}.log"
mkdir -p "${encode_dir}" "${results_dir}"

local model_args="--model_name_or_path ${BASE_MODEL} \
--bf16 --pooling last --padding_side right --normalize --attn_implementation sdpa"
local lora_args="--lora_name_or_path ${model_dir}"
local query_prefix="Instruct: Given a question, retrieve documents that answer the question.\nQuery:"
local passage_max_len=8192
local corpus_path="${EXP_ROOT}/data/corpus.jsonl"
local query_path="${EXP_ROOT}/data/queries.jsonl"
local qrels="${EXP_ROOT}/data/qrels.tsv"

# Build chunk / search args
local chunk_args="" search_args="" indep_args=""
if [ "${chunk_size}" -gt 0 ]; then
chunk_args="--passage_chunk_size ${chunk_size}"
search_args="--chunked --chunk_multiplier 10"
if [ "${independent}" = "true" ]; then
indep_args="--passage_chunk_independent"
fi
fi

echo ""
echo "================================================================"
echo " [${benchmark}] ${config_name}"
echo " Model: ${model_dir}"
echo " Chunk size: ${chunk_size} Independent: ${independent}"
echo " Started: $(date)"
echo "================================================================"

# ── Encode queries ────────────────────────────────────────────────────────
run_cmd CUDA_VISIBLE_DEVICES=0 python -m tevatron.retriever.driver.encode \
--output_dir temp \
${model_args} \
${lora_args} \
--per_device_eval_batch_size 16 \
--query_prefix "${query_prefix}" --query_max_len 512 \
--dataset_name json \
--dataset_path "${query_path}" \
--encode_is_query \
--encode_output_path "${encode_dir}/queries.pkl"

# ── Encode corpus (sharded) ──────────────────────────────────────────────
log_shard_cmd python -m tevatron.retriever.driver.encode \
--output_dir temp \
${model_args} ${lora_args} \
--per_device_eval_batch_size 4 \
--passage_prefix "''" --passage_max_len ${passage_max_len} \
${chunk_args} ${indep_args} \
--dataset_name json --dataset_path "${corpus_path}" \
--dataset_number_of_shards ${NUM_GPUS} \
--dataset_shard_index '{s}' \
--encode_output_path "${encode_dir}/corpus.{s}.pkl"
pids=()
for s in $(seq 0 $((NUM_GPUS-1))); do
CUDA_VISIBLE_DEVICES=${s} python -m tevatron.retriever.driver.encode \
--output_dir temp \
${model_args} \
${lora_args} \
--per_device_eval_batch_size 4 \
--passage_prefix "" --passage_max_len ${passage_max_len} \
${chunk_args} ${indep_args} \
--dataset_name json \
--dataset_path "${corpus_path}" \
--dataset_number_of_shards ${NUM_GPUS} \
--dataset_shard_index ${s} \
--encode_output_path "${encode_dir}/corpus.${s}.pkl" &
pids+=($!)
done
for pid in "${pids[@]}"; do
wait "${pid}"
done
echo " All ${NUM_GPUS} shards encoded."

# ── Search ──────────────────────────────────────────────────────────────
local rank_file="${results_dir}/ranking.txt"
local trec_file="${results_dir}/ranking.trec"
run_cmd python -m tevatron.retriever.driver.search \
--query_reps "${encode_dir}/queries.pkl" \
--passage_reps "${encode_dir}/corpus.*.pkl" \
--depth 100 --batch_size 64 --save_text \
${search_args} \
--save_ranking_to "${rank_file}"

# ── Evaluate ────────────────────────────────────────────────────────────
run_cmd python -m tevatron.utils.format.convert_result_to_trec \
--input "${rank_file}" \
--output "${trec_file}" \
--remove_query

echo ""
echo "MLDR-EN Results [${config_name}]:"
run_cmd python -m pyserini.eval.trec_eval \
-m ndcg_cut.10 -m recall.100 \
"${qrels}" "${trec_file}"

echo ""
echo "=== [${benchmark}] ${config_name} done at $(date) ==="
}

# ══════════════════════════════════════════════════════════════════════════════
# Main — run all 5 configs on MLDR-EN
# ══════════════════════════════════════════════════════════════════════════════
# Format: CONFIG_NAME | MODEL_NAME | EVAL_CHUNK_SIZE | INDEPENDENT
EVAL_CONFIGS=(
"single-vector|nochunk-epoch1|0|false"
"maxp|nochunk-epoch1|64|true"
"maxp-train|maxp-train-epoch1|64|true"
"mpe-fixed64|fixed-64-epoch1|64|false"
"mpe-rand-32to1024|prand-32to1024-epoch1|64|false"
)

echo "╔══════════════════════════════════════════════════════════════════╗"
echo "║ Paper Reproduction — MLDR-EN Evaluation ║"
echo "║ 5 configs on MLDR-EN ║"
echo "║ Started: $(date) ║"
echo "║ Num GPUs: ${NUM_GPUS} ║"
echo "╚══════════════════════════════════════════════════════════════════╝"
echo ""
echo "Configs:"
for entry in "${EVAL_CONFIGS[@]}"; do
IFS='|' read -r cfg model chunk indep <<< "${entry}"
printf " %-22s model=%-25s chunk=%-4s indep=%s\n" "${cfg}" "${model}" "${chunk}" "${indep}"
done
echo ""

TOTAL=${#EVAL_CONFIGS[@]}
IDX=0

for entry in "${EVAL_CONFIGS[@]}"; do
IFS='|' read -r config_name model_name chunk_size independent <<< "${entry}"
IDX=$((IDX + 1))
model_dir="${MODEL_ROOT}/${model_name}"

if [ ! -d "${model_dir}" ]; then
echo "WARNING: Model not found at ${model_dir}, skipping ${config_name}"
continue
fi

echo ""
echo "────────────────────────────────────────────────────────────────"
echo " [${IDX}/${TOTAL}] ${config_name}"
echo "────────────────────────────────────────────────────────────────"

eval_mldr_en "${config_name}" "${model_dir}" "${chunk_size}" "${independent}"
done

echo ""
echo "╔══════════════════════════════════════════════════════════════════╗"
echo "║ MLDR-EN evaluation complete. ║"
echo "║ Finished: $(date) ║"
echo "║ Results in: ${EXP_ROOT}/results/mldr-en/ ║"
echo "╚══════════════════════════════════════════════════════════════════╝"
Loading