POISS BGE-M3 retriever

BAAI/bge-m3 fine-tuned on POISS for point-of-interest retrieval over the full Overture Maps Places corpus (72M+ POIs). It is a bi-encoder: queries and POIs are encoded independently, so POI embeddings can be built offline and searched with an ANN index.

The distinguishing feature of this task is that a query carries the coordinate it was issued from, and relevance depends jointly on semantics and geography. The coordinate is given to the model as text, at four-decimal precision, in both the query and the POI representation.

Companion cross-encoder: amazon/poiss-bge-m3-reranker.

Results

Full-corpus retrieval on the POISS test split (42,018 queries, percentages):

System R@10 R@100 R@1k N@5 N@20
BM25 17.8 29.9 42.8 16.0 18.0
BGE-M3 zero-shot 15.2 26.4 40.0 13.7 15.3
this model 45.2 83.2 95.6 58.3 60.3

Per-language R@100: EN 82.4, ES 83.9, FR 85.1, PT 80.5, DE 86.1, IT 84.7, NL 79.8. Fine-tuning shrinks the cross-language spread from 2.2× to 1.08×.

Per-intent R@100: Search 89.1, Detail 88.0, Recommend 77.3, Things-to-do 74.0. Open-ended intents remain the harder half of the benchmark.

Used as a reranker over its own top-1000 (the setting the cross-encoder is compared against): P@5 57.6, P@20 35.5, MRR 85.5, nDCG@5 61.5, nDCG@20 62.6.

Usage

Embeddings are the CLS token of the last layer, L2-normalized; score with a dot product. Both sides must follow the templates below, including the coordinate formatting, or accuracy drops sharply.

With sentence-transformers, pooling and normalization are already configured:

from sentence_transformers import SentenceTransformer

model = SentenceTransformer("amazon/poiss-bge-m3-retriever")
query = "User Query: pizzeria near the colosseum; Coordinate: 41.8902N 12.4922E;"
pois = [
    "Location Name: Pizzeria Da Enzo; Address: Via dei Fori Imperiali 1; Position: Rome, "
    "Lazio, IT, 00184; Coordinate: 41.8925N 12.4853E; Categories: pizza restaurant, restaurant;",
    "Location Name: Oslo Dental Clinic; Address: Storgata 10; "
    "Position: Oslo, Oslo, NO, 0155; Coordinate: 59.9139N 10.7522E; Categories: dentist;",
]
print(model.similarity(model.encode([query]), model.encode(pois)))

Or with transformers directly, which is what the formatting helpers below are for:

import torch
from transformers import AutoModel, AutoTokenizer

model_id = "amazon/poiss-bge-m3-retriever"
tokenizer = AutoTokenizer.from_pretrained(model_id)
# add_pooling_layer=False: the checkpoint has no pooler, and pooled output is unused
model = AutoModel.from_pretrained(model_id, add_pooling_layer=False).eval()


def encode(texts, max_length):
    batch = tokenizer(texts, padding=True, truncation=True,
                      max_length=max_length, return_tensors="pt")
    with torch.no_grad():
        hidden = model(**batch).last_hidden_state[:, 0, :]
    return torch.nn.functional.normalize(hidden, dim=1)


def format_query(text, lat, lng):
    lat_dir, lng_dir = "N" if lat >= 0 else "S", "E" if lng >= 0 else "W"
    return (f"User Query: {text}; "
            f"Coordinate: {abs(lat):.4f}{lat_dir} {abs(lng):.4f}{lng_dir};").strip()


def format_poi(name, street, city, state, country, postcode, lat, lng, categories):
    parts = ["Location "]
    if name:
        parts.append(f"Name: {name}; ")
    if street:
        parts.append(f"Address: {street}; ")
    position = [v for v in (city, state, country, postcode) if v]
    parts.append("Position: " + ", ".join(position) + "; ")
    lat_dir, lng_dir = "N" if lat >= 0 else "S", "E" if lng >= 0 else "W"
    parts.append(f"Coordinate: {abs(lat):.4f}{lat_dir} {abs(lng):.4f}{lng_dir}; ")
    if categories:
        parts.append(f"Categories: {categories}; ")
    return "".join(parts).strip()


query = format_query("pizzeria near the colosseum", 41.8902, 12.4922)
pois = [
    format_poi("Pizzeria Da Enzo", "Via dei Fori Imperiali 1", "Rome", "Lazio", "IT",
               "00184", 41.8925, 12.4853, "pizza restaurant, restaurant"),
    format_poi("Oslo Dental Clinic", "Storgata 10", "Oslo", "Oslo", "NO",
               "0155", 59.9139, 10.7522, "dentist"),
]

query_vec = encode([query], max_length=128)
poi_vecs = encode(pois, max_length=160)
print((query_vec @ poi_vecs.T).squeeze(0))

Notes on the templates:

  • Categories is the Overture primary category followed by the alternates, comma-separated, with underscores and slashes replaced by spaces.
  • Truncation lengths at training and evaluation time were 128 tokens for queries and 160 for POIs.
  • Strip the final string. Training inputs were stripped, so a trailing space after the last ; adds a token and shifts the embedding. sentence-transformers strips for you; with plain transformers do it yourself, as the helpers above do.
  • No rating, review-count or quality signal appears in either input: this model is purely semantic + geographic. The cross-encoder is the component that consumes the quality score.

For corpus-scale use, embed POIs offline and index them (the paper used FAISS).

Training

Fine-tuned from BAAI/bge-m3 on the POISS train split: 254,089 training records and 13,374 validation records, with POI content resolved from Overture Places release 2026-03-18.

Objective InfoNCE with in-batch negatives, learnable temperature initialized at 0.01
Hard negatives 100 mined per query, 4 sampled per step
Optimizer AdamW, lr 2e-5, linear schedule, gradient clipping 1.0
Batch size 16 per GPU × 8 GPUs
Precision / hardware bf16 mixed precision, 8× NVIDIA A100 40GB
Schedule up to 20 epochs, early stopping on validation MRR
Selected checkpoint epoch 19, validation MRR 0.8828

Negatives are mined by taking the candidates the LLM judge marked non-relevant and padding from the cross-encoder's reranked top-300 starting at rank 30 — semantically related POIs that are nonetheless wrong, typically differing in quality score, distance or prominence.

Training supervision is derived from POISS labels: positives are the graded relevant POIs.

Reproducibility note

The published weights are numerically identical to the ones used for the paper: they were converted from the training checkpoint by re-expressing a fused-QKV flash-attention layout in the standard XLMRobertaModel layout, and the conversion round-trips bit-exactly through the original remapping function.

Metrics reproduce to about 1e-3 rather than exactly, because the reported numbers were computed in bf16 with flash attention. Near-tied candidates around rank 20 can swap order across precisions, which moves nDCG@20 slightly while leaving the top of the ranking unchanged.

Intended use and limitations

Intended for research on POI search and geospatial retrieval. Coverage is limited to 7 languages concentrated in the Americas and Europe, so behaviour outside those locales is untested.

Citation

@inproceedings{maritan-etal-2026-poiss,
    title = "{POISS}: A Large-Scale Multilingual Dataset for Point-of-Interest Search",
    author = "Maritan, Nicola  and
      Moschitti, Alessandro  and
      Borazio, Federico  and
      Zhou, Xiaokun  and
      Bai, Zhengwei",
    booktitle = "Proceedings of the 2026 Conference on Empirical Methods in Natural Language Processing",
    month = oct,
    year = "2026",
    address = "Budapest, Hungary",
    publisher = "Association for Computational Linguistics",
    note = "To appear",
}

License

These weights are a derivative work of BAAI/bge-m3, distributed by BAAI under the MIT License, and are released under the same license; see LICENSE. The base model derives from XLM-RoBERTa, also MIT.

Modification with respect to the base model: all weights were modified by supervised fine-tuning on the POISS train split with an InfoNCE objective. Architecture, configuration, vocabulary and tokenizer are unchanged.

The training data, POISS, is Apache-2.0 and places no additional restriction on these weights.

Downloads last month
133
Safetensors
Model size
0.6B params
Tensor type
F32
·
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Model tree for amazon/poiss-bge-m3-retriever

Base model

BAAI/bge-m3
Finetuned
(582)
this model

Dataset used to train amazon/poiss-bge-m3-retriever