~/projects

Product search on a small catalogue

This was an exploratory study in preparation for a possible master's thesis in search. I tested the leading open-source models from different benchmarks, hybrid setups, and pipelines with rerankers and with query and product expansion. All models were used out of the box, but every setup was tested thoroughly on the key ranking metrics and on latency for production use.

githubllm-product-search ↗ licenseMIT python3.12 toolinguv · ruff · mypy datasetWANDS

~/results

dev (241 queries) test (239 queries) first-stage budget total budget

Best setting at each stage, chosen on dev (hollow) and checked once on test (filled). Figure 2 in notebook 08.

StagenDCG@10 (test)Changep99 latencyKept
BM250.710–1.3 msbaseline
+ denseinf-retriever-v1-1.5b0.786+7.6 vs BM2528 mskept
+ hybridBM25 + 2 dense models, RRF0.804+1.9 vs dense37 mskept
+ rerankerzerank-2dev only+0.9 on dev~470 mstoo slow
+ LLM expansion0.808+0.3, not significant467 msnot kept
  • Hybrid search gave the reliable gain: +1.9 nDCG@10 on test, 37 ms.
  • Rerankers and LLM expansion each cost ~10× the latency for at most one point, and the expansion gain did not hold on test.
  • LLM expansion helps plain BM25 a lot: 151 of 161 combinations still significantly better on test.

nDCG@10: quality of the top 10 results (0–1). Rcap@100: share of the right products found in the top 100. Change: test-set difference in points against the stage above (paired permutation test).

~/how it was measured

data

WANDS (Wayfair): 42,994 products, 480 real queries, 233,448 human labels

split

241 dev queries for every choice, 239 test queries used once at the end

rule

Keep a change only if significantly better (p < 0.05; q-values when many settings are compared)

hardware

One RTX 4070 (12 GB), one query at a time. Budget: p99 ≤ 50 ms first stage, ≤ 500 ms in total

~/steps

BM25 notebook 03 ↗

  • 420 settings tried. Stemming (+4.5 Rcap@100) and the description field (+5.6) matter.
  • Tuning k1 and b does not help (+1.6, not significant), so we use the default.

Rcap@100 for every k1 and b. Every setting lands between 0.69 and 0.72, so we use the default (outlined). Figure 2 in notebook 03.

Change from the best grid settingRcap@100 changepDecision
Stemming off−4.5< 0.001keep stemming
Description field removed−5.60.001keep description
Attributes field removed−1.50.120leave out
k1 and b reset to default−0.60.215use default

Change in points when one part is removed or reset. p99 latency 1.3 ms. Table 1 in notebook 03.

~/limits

  • One dataset, built around its 480 queries.
  • Latency for one user; expansion pieces timed separately, not on one shared GPU.
  • No fine-tuning.

~/next

  • Fine-tune an embedding model or reranker on synthetic queries.
  • Move the expansion gain offline, into the product index.
  • Check on a larger catalogue (TREC Product Search 2023, 1.66M products).