HyunJin Kim, Jaejun Shim, Young Jin Kim, JinYeong Bak ICML 2026
TPOUR (Temporal Preference Optimization for Unsupervised Retrieval) is a framework for learning temporally-aware dense retrievers without requiring explicit timestamp supervision solely based on corpus-level temporal signal (i.e., data collected at a specific time).
Traditional unsupervised retrievers (e.g., contrastive learning–based models) focus purely on semantic similarity, often retrieving documents that are temporally misaligned with the query. TPOUR addresses this limitation by introducing temporal preference learning into the retrieval objective.
- Queries often contain explicit (e.g., "in 2019") or implicit (e.g., "this year") temporal intent
- Standard retrievers ignore this signal → temporal misalignment
- Supervised temporal retrieval requires labeled timestamps → not scalable
Figure: Comparison between TPOUR aligned at 2019 and a time-unaware retriever for queries with explicit (e.g., in 2019) or implicit (e.g., this year) temporal information. Left: A mixed-timestamp document collection containing (i) semantically and temporally aligned documents (green), (ii) semantically relevant but temporally misaligned documents (yellow), and (iii) irrelevant documents (red). Right: Ranked retrieval results. The time-unaware retriever, trained solely for semantic similarity, struggles to rank the temporally aligned document (green) over the misaligned (yellow). In contrast, the TPOUR-trained retriever prioritizes the temporally aligned document.
TPOUR integrates contrastive learning with a preference optimization objective:
- Contrastive loss → semantic similarity
- TRPO loss → temporal alignment based on preference learning
The model is trained to:
- Prefer aligned document
$D^t$ - Over misaligned document
$D^{t'}$
Figure: Overview of TPOUR. Given a query
-
Encoder
$\pi_\theta$ : learns joint semantic + temporal representations -
Reference encoder
$\pi_{\text{ref}}$ : momentum-updated (MoCo-style) -
Preference pairs: constructed from documents across time periods
-
Loss function:
-
$L_{CE} = -\log \frac{e^{S_\theta(y_i^w)}}{e^{S_\theta(y_i^w)} + \sum_{j<i} (e^{S_{\mathrm{ref}}(y_j^w)} + e^{S_{\mathrm{ref}}(y_j^l)})}$ : contrastive learning -
$L_{\mathrm{TRPO}} = -\log \sigma\big(\beta [S_\theta(y_i^w) - S_\theta(y_i^l) - (S_{\mathrm{ref}}(y_i^w) - S_{\mathrm{ref}}(y_i^l))]\big)$ : temporal preference alignment $L_{total} = \lambda L_{CE} + (1 - \lambda)L_{TRPO}$
-
TPOUR introduces time vector interpolation (1) to enable smooth adaptation to intermediate time periods and (2) without training:
-
Extract temporal shift:
$\tau_t = \theta_t - \theta_{\text{base}}$ -
Interpolate between time periods:
$\theta_{mid} = \theta_{\text{base}} + (1-\alpha)\tau_{t_1} + \alpha\tau_{t_2}$
- Install dependencies
conda env create --name tpour --file environment.yml
- Activate the environment
conda activate tpour
- Run finetuning via
finetune.shscript
sh finetune.sh
python -m wikiextractor.WikiExtractor <Wikipedia dump file> [--templates <extracted template file>]
python -m wikiextractor.WikiExtractor -o ./20181220_json --json enwiki-20181220-pages-articles-multistream.xml.bz2
python build_preprocessed.py --num-workers 10 ./20181220_json_timestamp ./20181220_json_timestamp_preprocessed.json
python split_paragraph.py --input_path 20181220_json_timestamp_preprocessed_13717022.json --output_path 20181220_json_timestamp_splitted_13717022.json
python preprocessing/compare_documents_various.py \
--input_path 20171220_preprocessed.json 20181220_preprocessed.json 20211220_preprocessed.json 20231220_preprocessed.json \
--block_size 100 \
--output_path ./preprocessing/data/
for i in 2018 2019; do
d=preprocessing/${i}_filtered
CUDA_VISIBLE_DEVICES=0 python preprocessing/contriever_embedding_and_indexing.py \
--ctx_encoder_model_name facebook/contriever \
--json_path ${d}.json \
--output_dir ${d} \
--year ${i}
done
for type in combined; do
for year_q in 20231220 20211220 20181220; do # if two years
for year_d in 20231220 20211220 20181220; do
path=preprocessing/data
CUDA_VISIBLE_DEVICES=0 python preprocessing/contriever_mine_pos_neg_ctx.py \
--input_query ${path}/${year_q}_${type} \
--database_path ${path}/${year_d}_${type} \
--index_path ${path}/${year_d}_${type}/gpu_indexivfpq.faiss \
--n_docs 5 \
--output_positive_path ${path}/q_${year_q}_d_${year_d}_${type}_positive_ctx.json \
--output_negative_path ${path}/q_${year_q}_d_${year_d}_${type}_negative_ctx.json \
--save_or_load_index
done
done
done
for year_q in 20231220; do
python preprocessing/stream_create_finetuning_data.py \
--input_path ./preprocessing/data/q_${year_q}_d_20181220_combined_positive_ctx.json \
./preprocessing/data/q_${year_q}_d_20211220_combined_positive_ctx.json \
./preprocessing/data/q_${year_q}_d_20231220_combined_positive_ctx.json \
--output ./preprocessing/data/q_${year_q}_finetuning_data.jsonl
done
- While the current repo is already designed to train on a large dataset, due to limiited size for upload, we provide a small dataset (2018_sample_train_trimmed.jsonl) for testing and debugging.
- For the wikipedia dump, we recommend you to find relevant dump data in (https://dumps.wikimedia.org/)
- Please refer to './src/moco.py' for the implementation of TPOUR (it's implemented in TPOUR class).
- I need time to re-run & verify all process are correct, if you have any trouble, just let me know (email: khyunjin1993@gmail.com).

