261 lines
9.4 KiB
Markdown
261 lines
9.4 KiB
Markdown
---
|
||
license: apache-2.0
|
||
language:
|
||
- en
|
||
tags:
|
||
- ecommerce
|
||
- e-commerce
|
||
- retail
|
||
- marketplace
|
||
- shopping
|
||
- amazon
|
||
- ebay
|
||
- alibaba
|
||
- google
|
||
- rakuten
|
||
- bestbuy
|
||
- walmart
|
||
- flipkart
|
||
- wayfair
|
||
- shein
|
||
- target
|
||
- etsy
|
||
- shopify
|
||
- taobao
|
||
- asos
|
||
- carrefour
|
||
- costco
|
||
- overstock
|
||
- pretraining
|
||
- decoder
|
||
- language-modeling
|
||
- foundation-model
|
||
library_name: transformers
|
||
base_model:
|
||
- Qwen/Qwen3-Reranker-0.6B
|
||
pipeline_tag: text-ranking
|
||
datasets:
|
||
- thebajajra/Amazebay-Relevance
|
||
model-index:
|
||
- name: RexReranker-0.6B
|
||
results:
|
||
- task:
|
||
type: text-ranking
|
||
name: Reranking (query–product relevance)
|
||
dataset:
|
||
name: ERESS (E-commerce Relevance Evaluation Scoring Suite)
|
||
type: thebajajra/eress
|
||
metrics:
|
||
- name: nDCG@5
|
||
type: ndcg_at_5
|
||
value: 0.9794
|
||
- name: nDCG@10
|
||
type: ndcg_at_10
|
||
value: 0.9722
|
||
---
|
||
|
||
<p align="center">
|
||
<img src="https://cdn-uploads.huggingface.co/production/uploads/6893dd21467f7d2f5f358a95/apOIbl5PdJuRk-tQMdDc8.png" alt="RexReranker">
|
||
</p>
|
||
<p align="center">
|
||
</p>
|
||
|
||
[](https://huggingface.co/collections/thebajajra/rexreranker)
|
||
[](https://huggingface.co/datasets/thebajajra/Amazebay-Relevance)
|
||
[](https://huggingface.co/datasets/thebajajra/eress)
|
||
[](https://github.com/bajajra/RexRerankers)
|
||
[](https://huggingface.co/blog/thebajajra/rexrerankers)
|
||
|
||
# RexReranker-0.6B
|
||
|
||
## Model Summary
|
||
**RexReranker-0.6B** is state-of-the-art decoder-based **generation reranker** for e-commerce product discovery. Given a user query and a candidate product (title + optional description/attributes), it outputs a relevance score derived from the model’s **token-level probability of a binary judgment (“yes” vs “no”)**.
|
||
|
||
|
||
## Intended Use
|
||
**Primary use cases**
|
||
- Second-stage reranking for **product search** (high-recall retrieval → top-k rerank).
|
||
- Shopping/commerce assistants: selecting and ordering candidate products given natural-language constraints (size, compatibility, color, etc.).
|
||
- Offline evaluation / benchmarking of reranking approaches for product discovery.
|
||
|
||
## Model Details
|
||
|
||
### Model type
|
||
- **Text Ranking / Reranking** model
|
||
- **Decoder LM architecture** (`Qwen3ForCausalLM`)
|
||
- **Parameters:** ~0.6B
|
||
|
||
|
||
## Training Data
|
||
This model is trained for e-commerce relevance reranking using the project’s released relevance datasets:
|
||
|
||
- **Amazebay-Relevance**: **6.33M rows** of query–product pairs (train split ~6.29M, validation ~38k)
|
||
|
||
## Evaluation
|
||
|
||
Pareto Comparison:
|
||
<center><img src="https://cdn-uploads.huggingface.co/production/uploads/6893dd21467f7d2f5f358a95/RAlkM57sjxKTyoLiWyhqL.png" width="675"></center>
|
||
|
||
Performance on various Query Types:
|
||

|
||
|
||
## How to Use
|
||
|
||
#### Using vLLM
|
||
|
||
```python
|
||
# Requires vllm>=0.8.5
|
||
import logging
|
||
from typing import Dict, Optional, List
|
||
|
||
import json
|
||
import logging
|
||
|
||
import torch
|
||
|
||
from transformers import AutoTokenizer, is_torch_npu_available
|
||
from vllm import LLM, SamplingParams
|
||
from vllm.distributed.parallel_state import destroy_model_parallel
|
||
import gc
|
||
import math
|
||
from vllm.inputs.data import TokensPrompt
|
||
|
||
|
||
|
||
def format_instruction(instruction, query, doc):
|
||
text = [
|
||
{"role": "system", "content": "Judge whether the Document meets the requirements based on the Query and the Instruct provided. Note that the answer can only be \"yes\" or \"no\"."},
|
||
{"role": "user", "content": f"<Instruct>: {instruction}\n\n<Query>: {query}\n\n<Document>: {doc}"}
|
||
]
|
||
return text
|
||
|
||
def process_inputs(pairs, instruction, max_length, suffix_tokens):
|
||
messages = [format_instruction(instruction, query, doc) for query, doc in pairs]
|
||
messages = tokenizer.apply_chat_template(
|
||
messages, tokenize=True, add_generation_prompt=False, enable_thinking=False
|
||
)
|
||
messages = [ele[:max_length] + suffix_tokens for ele in messages]
|
||
messages = [TokensPrompt(prompt_token_ids=ele) for ele in messages]
|
||
return messages
|
||
|
||
def compute_logits(model, messages, sampling_params, true_token, false_token):
|
||
outputs = model.generate(messages, sampling_params, use_tqdm=False)
|
||
scores = []
|
||
for i in range(len(outputs)):
|
||
final_logits = outputs[i].outputs[0].logprobs[-1]
|
||
token_count = len(outputs[i].outputs[0].token_ids)
|
||
if true_token not in final_logits:
|
||
true_logit = -10
|
||
else:
|
||
true_logit = final_logits[true_token].logprob
|
||
if false_token not in final_logits:
|
||
false_logit = -10
|
||
else:
|
||
false_logit = final_logits[false_token].logprob
|
||
true_score = math.exp(true_logit)
|
||
false_score = math.exp(false_logit)
|
||
score = true_score / (true_score + false_score)
|
||
scores.append(score)
|
||
return scores
|
||
|
||
number_of_gpu = torch.cuda.device_count()
|
||
tokenizer = AutoTokenizer.from_pretrained('thebajajra/RexReranker-0.6B')
|
||
model = LLM(model='thebajajra/RexReranker-0.6B', tensor_parallel_size=number_of_gpu, max_model_len=10000, enable_prefix_caching=True, gpu_memory_utilization=0.8)
|
||
tokenizer.padding_side = "left"
|
||
tokenizer.pad_token = tokenizer.eos_token
|
||
suffix = "<|im_end|>\n<|im_start|>assistant\n<think>\n\n</think>\n\n"
|
||
max_length=8192
|
||
suffix_tokens = tokenizer.encode(suffix, add_special_tokens=False)
|
||
true_token = tokenizer("yes", add_special_tokens=False).input_ids[0]
|
||
false_token = tokenizer("no", add_special_tokens=False).input_ids[0]
|
||
sampling_params = SamplingParams(temperature=0,
|
||
max_tokens=1,
|
||
logprobs=20,
|
||
allowed_token_ids=[true_token, false_token],
|
||
)
|
||
|
||
|
||
task = 'Given a web search query, retrieve relevant passages that answer the query'
|
||
queries = ["visual fractions workbooks for children",
|
||
"replacement motor mount for 2008 focus",
|
||
]
|
||
documents = [
|
||
"Fractions and Decimals Workbook for Grades 4 to 5",
|
||
"3pcs Set - Motor Mounts Kit Compatible with 08-11 Ford Focus 2.0L Auto Automatic and Manual Trans Transmission AT MT - Engine Mounts",
|
||
]
|
||
|
||
pairs = list(zip(queries, documents))
|
||
inputs = process_inputs(pairs, task, max_length-len(suffix_tokens), suffix_tokens)
|
||
scores = compute_logits(model, inputs, sampling_params, true_token, false_token)
|
||
print('scores', scores)
|
||
|
||
destroy_model_parallel()
|
||
```
|
||
|
||
#### Using HF Transformers
|
||
|
||
```python
|
||
# Requires transformers>=4.51.0
|
||
import torch
|
||
from transformers import AutoModel, AutoTokenizer, AutoModelForCausalLM
|
||
|
||
def format_instruction(instruction, query, doc):
|
||
if instruction is None:
|
||
instruction = 'Given a web search query, retrieve relevant passages that answer the query'
|
||
output = "<Instruct>: {instruction}\n<Query>: {query}\n<Document>: {doc}".format(instruction=instruction,query=query, doc=doc)
|
||
return output
|
||
|
||
def process_inputs(pairs):
|
||
inputs = tokenizer(
|
||
pairs, padding=False, truncation='longest_first',
|
||
return_attention_mask=False, max_length=max_length - len(prefix_tokens) - len(suffix_tokens)
|
||
)
|
||
for i, ele in enumerate(inputs['input_ids']):
|
||
inputs['input_ids'][i] = prefix_tokens + ele + suffix_tokens
|
||
inputs = tokenizer.pad(inputs, padding=True, return_tensors="pt", max_length=max_length)
|
||
for key in inputs:
|
||
inputs[key] = inputs[key].to(model.device)
|
||
return inputs
|
||
|
||
@torch.no_grad()
|
||
def compute_logits(inputs, **kwargs):
|
||
batch_scores = model(**inputs).logits[:, -1, :]
|
||
true_vector = batch_scores[:, token_true_id]
|
||
false_vector = batch_scores[:, token_false_id]
|
||
batch_scores = torch.stack([false_vector, true_vector], dim=1)
|
||
batch_scores = torch.nn.functional.log_softmax(batch_scores, dim=1)
|
||
scores = batch_scores[:, 1].exp().tolist()
|
||
return scores
|
||
|
||
tokenizer = AutoTokenizer.from_pretrained("thebajajra/RexReranker-0.6B", padding_side='left')
|
||
model = AutoModelForCausalLM.from_pretrained("thebajajra/RexReranker-0.6B").eval()
|
||
# We recommend enabling flash_attention_2 for better acceleration and memory saving.
|
||
# model = AutoModelForCausalLM.from_pretrained("thebajajra/RexReranker-0.6B", torch_dtype=torch.float16, attn_implementation="flash_attention_2").cuda().eval()
|
||
token_false_id = tokenizer.convert_tokens_to_ids("no")
|
||
token_true_id = tokenizer.convert_tokens_to_ids("yes")
|
||
max_length = 8192
|
||
|
||
prefix = "<|im_start|>system\nJudge whether the Document meets the requirements based on the Query and the Instruct provided. Note that the answer can only be \"yes\" or \"no\".<|im_end|>\n<|im_start|>user\n"
|
||
suffix = "<|im_end|>\n<|im_start|>assistant\n<think>\n\n</think>\n\n"
|
||
prefix_tokens = tokenizer.encode(prefix, add_special_tokens=False)
|
||
suffix_tokens = tokenizer.encode(suffix, add_special_tokens=False)
|
||
|
||
task = 'Given a web search query, retrieve relevant passages that answer the query'
|
||
|
||
queries = ["visual fractions workbooks for children",
|
||
"replacement motor mount for 2008 focus",
|
||
]
|
||
documents = [
|
||
"Fractions and Decimals Workbook for Grades 4 to 5",
|
||
"3pcs Set - Motor Mounts Kit Compatible with 08-11 Ford Focus 2.0L Auto Automatic and Manual Trans Transmission AT MT - Engine Mounts",
|
||
]
|
||
|
||
pairs = [format_instruction(task, query, doc) for query, doc in zip(queries, documents)]
|
||
|
||
# Tokenize the input texts
|
||
inputs = process_inputs(pairs)
|
||
scores = compute_logits(inputs)
|
||
|
||
print("scores: ", scores)
|
||
|
||
``` |