Reranking met embeddingmodellen

Een Python-code voor het herschikken van RAG.

Inhoud

Reranking is een tweede stap in Retrieval Augmented Generation (RAG) systemen, direct tussen het ophalen (Retrieving) en het genereren (Generating).

Elektrische kubussen in digitale ruimte

Dit is hoe Flux-1 dev Elektrische kubussen in digitale ruimte voor zich ziet.

Voor een complete gids over het bouwen van RAG-systemen, zie de [Retrieval-Augmented Generation (RAG) Tutorial: Architectuur, Implementatie en Productie Gids](https://www.glukhov.org/nl/rag/ “Stap-voor-stap RAG-tutorial: bouw retrieval-augmented generatiesystemen met vectordatabases, hybride zoekopdrachten, reranking en webzoeken. Architectuur, implementatie en best practices voor productie).

Dit artikel behandelt reranking met embedding-modellen. Voor alternatieve benaderingen kun je ook kijken naar Reranking van teksten met Ollama en Qwen3 Embedding LLM - in Go en Reranking van documenten met Ollama en Qwen3 Reranker model - in Go.

Retrieval met reranking

Als we de documenten vanaf het begin in de vorm van embeddings opslaan in de vector database, zal de retrieval ons direct de lijst van vergelijkbare documenten opleveren.

Standalone reranking

Maar als we de documenten eerst van het internet downloaden, kan de respons van het zoeksystem beïnvloed worden door de voorkeuren/algoritmes van de zoekprovider, gesponsorde inhoud, SEO-optimalisatie, enzovoort, dus hebben we post-zoekopdracht reranking nodig.

Wat ik deed -

  • embeddings ophalen voor de zoekopdracht
  • embeddings ophalen voor elk document. Het document zou toch niet meer dan 8k tokens verwacht worden
  • gelijkaardigheid berekend tussen de query en de embeddings van elk document
  • documenten gesorteerd op basis van deze gelijkaardigheid.

Geen vector database hier, laten we gaan.

Voorbeeldcode

Het gebruik van Langchain om verbinding te maken met Ollama en de cosine_similarity-functie van langchain. Je kunt filteren op basis van de gelijkaardigheidsmaat, maar houd er rekening mee dat voor verschillende domeinen en embedding LLM’s de drempelwaarde anders zal zijn.

Ik zal blij zijn als dit stukje code je op enigerlei wijze van dienst is. Copy/Paste/GebruikHetOpWijzeDieJeWilt licentie. Proost.

from langchain_core.documents import Document
from langchain_community.embeddings import OllamaEmbeddings
from langchain_community.utils.math import cosine_similarity
import numpy as np


def cosine_distance(a: np.ndarray, b: np.ndarray) -> np.ndarray:
    return 1.0 - cosine_similarity(a, b)

def compute_score(vectors: np.ndarray) -> float:
    score = cosine_distance(vectors[0].reshape(1, -1), vectors[1].reshape(1, -1)).item()
    return score

def list_to_array(lst):
    return np.array(lst, dtype=float)   

def compute_scorel(lists) -> float:
    v1 = list_to_array(lists[0])
    v2 = list_to_array(lists[1])
    return compute_score([v1, v2])

def filter_docs(emb_model_name, docs, query, num_docs):
    content_arr = [doc.page_content for doc in docs]

    ollama_emb = OllamaEmbeddings(
        model=emb_model_name
    )

    docs_embs = ollama_emb.embed_documents(content_arr)
    query_embs = ollama_emb.embed_query(query)
    sims = []
    for i, emb in enumerate(docs_embs):
        idx = docs[i].id
        s = compute_scorel([query_embs, docs_embs[i]])
        simstr = str(round(s, 4))
        docs[i].metadata["sim"] = simstr
        sim = {
            "idx": idx,
            "i": i,
            "sim": s,
        }
        sims.append(sim)

    sims.sort(key=sortFn)

    sorted_docs = [docs[x["i"]] for x in sims]
    filtered_docs = sorted_docs[:num_docs]
    return filtered_docs

Beste Embedding Modellen

Voor mijn taken is het beste embedding model momenteel bge-large:335m-en-v1.5-fp16.

De tweede plaats werd ingenomen door nomic-embed-text:137m-v1.5-fp16 en jina/jina-embeddings-v2-base-en:latest.

Maar voer je eigen tests uit voor je eigen domein en query’s.