Reranking com modelos de embedding

Um código Python para reclassificação do RAG

Conteúdo da página

Reranking é a segunda etapa na Geração Aumentada por Recuperação
(RAG) sistemas,
diretamente entre a Recuperação e a Geração.

Cubos elétricos no espaço digital

O que está acima é como Flux-1 dev imagina Cubos elétricos no espaço digital.

Recuperação com reranking

Se armazenarmos os documentos na forma de embeddings na base de dados vetorial desde o início — a recuperação nos dará a lista de documentos semelhantes imediatamente.

Reranking autônomo

Mas se baixarmos os documentos da internet primeiro, a resposta do sistema de busca pode ser afetada por preferências/algoritmos do provedor de busca, conteúdo patrocinado, otimização para SEO, etc. Por isso, precisamos de um reranking pós-busca.

O que estava fazendo:

  • obtendo embeddings para a consulta de busca
  • obtendo embeddings para cada documento. o documento não era esperado para ter mais de 8k tokens de qualquer forma
  • calculando a similaridade entre a consulta e os embeddings de cada documento
  • ordenando os documentos com base nessa similaridade.

Nenhuma base de dados vetorial aqui, vamos seguir adiante.

Código de exemplo

Usando o Langchain para se conectar ao Ollama e à função cosine_similarity do langchain. Você pode filtrar com base na medida de similaridade, mas tenha em mente que para diferentes domínios e modelos LLM de embedding o limiar será diferente.

Ficarei feliz se este trecho de código for útil de alguma forma para você. Licença Copy/Paste/UseAnyWayYouWant. Cumprimentos.

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

Melhores Modelos de Embedding

Para as minhas tarefas, o melhor modelo de embedding atualmente é bge-large:335m-en-v1.5-fp16

O segundo lugar foi ocupado por nomic-embed-text:137m-v1.5-fp16 e jina/jina-embeddings-v2-base-en:latest.

Mas faça seus próprios testes para o seu próprio domínio e consultas.