Busca semântica com NumPy
Crie um sistema de busca semântica totalmente em Python usando NumPy para calcular a similaridade de cosseno entre o embedding de uma consulta e uma coleção de embeddings de documentos.
Busca semântica com NumPy é uma aula grátis de AI Engineering Academy no CoddyKit. Esta é a aula 3 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de AI Engineering Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de AI Engineering Academy inclui 4 aulas no total.
Busca semântica sem banco de dados
A busca semântica encontra os documentos mais relevantes para uma consulta com base no significado, e não na sobreposição de palavras-chave. A implementação mais simples usa NumPy para calcular a similaridade de cosseno entre a incorporação de uma consulta e todas as incorporações de documentos na memória — sem exigir um banco de dados externo.
Essa abordagem funciona bem para até dezenas de milhares de documentos e é ideal para prototipagem antes de investir em um banco de dados vetorial.
Construindo o corpus de documentos
Comece reunindo seus documentos e gerando uma incorporação por documento usando a API da OpenAI. Armazene as incorporações como uma matriz 2D do NumPy, em que cada linha representa o vetor de um documento. Mantenha uma lista paralela com os textos dos documentos para poder recuperar o conteúdo original depois de encontrar as melhores correspondências.
import numpy as np
from openai import OpenAI
client = OpenAI()
documents = [
'Python is a high-level programming language.',
'NumPy provides fast numerical computing for Python.',
'Embeddings represent text as dense vectors.',
'Cosine similarity measures angle between vectors.',
'RAG combines retrieval with language generation.'
]
response = client.embeddings.create(
model='text-embedding-3-small',
input=documents
)
corpus_embeddings = np.array([item.embedding for item in response.data])
print(f'Corpus shape: {corpus_embeddings.shape}') # (5, 1536)Gerando a incorporação da consulta do usuário
Quando um usuário enviar uma consulta de busca, gere a incorporação usando o mesmo modelo usado para gerar as incorporações dos documentos. Misturar modelos — por exemplo, usar text-embedding-3-small para os documentos e text-embedding-3-large para as consultas — produzirá vetores em espaços diferentes e fornecerá pontuações de similaridade sem significado.
from openai import OpenAI
import numpy as np
client = OpenAI()
query = 'How do I compute similarity between text?'
response = client.embeddings.create(
model='text-embedding-3-small', # must match corpus model
input=query
)
query_embedding = np.array(response.data[0].embedding)
print(f'Query vector shape: {query_embedding.shape}') # (1536,)Calculando a similaridade de cosseno com NumPy
Para encontrar a similaridade entre a consulta e cada documento em uma única operação, calcule o produto escalar do vetor da consulta com a matriz de vetores dos documentos. Como ambas as incorporações da OpenAI são normalizadas em L2, isso equivale à similaridade de cosseno para todos os documentos de uma só vez — O(n * d), em que n é o número de documentos e d é a dimensão.
import numpy as np
# corpus_embeddings: (n_docs, 1536)
# query_embedding: (1536,)
def semantic_search(query_vec, corpus_vecs):
# Matrix-vector dot product: shape (n_docs,)
similarities = corpus_vecs @ query_vec
return similarities
# Example call (assuming pre-computed embeddings)
# sims = semantic_search(query_embedding, corpus_embeddings)
# print(sims) # array of similarity scores, one per documentClassificando e recuperando os resultados principais
Use np.argsort para classificar os documentos pela pontuação de similaridade em ordem decrescente e, em seguida, selecione os índices dos k primeiros resultados. Isso fornece os índices dos documentos mais relevantes, que você usa para consultar o texto original na lista paralela.
import numpy as np
def get_top_k(query_vec, corpus_vecs, documents, k=3):
similarities = corpus_vecs @ query_vec
# argsort gives ascending order; [::-1] reverses to descending
ranked_indices = np.argsort(similarities)[::-1]
top_k_indices = ranked_indices[:k]
return [
{'text': documents[i], 'score': float(similarities[i])}
for i in top_k_indices
]
# results = get_top_k(query_embedding, corpus_embeddings, documents, k=3)
# for r in results:
# print(f'{r["score"]:.4f}: {r["text"]}')Exemplo completo de busca semântica
Reunindo tudo: gere a incorporação do corpus, gere a incorporação da consulta, calcule as similaridades e retorne os resultados classificados. Esse padrão completo é a base de toda etapa de recuperação de RAG, mesmo quando um banco de dados vetorial substitui o NumPy internamente.
import numpy as np
from openai import OpenAI
client = OpenAI()
docs = [
'Embeddings map text to numerical vectors.',
'Python lists store ordered collections.',
'Cosine similarity compares vector directions.',
'RAG retrieves documents to ground LLM answers.',
'Dictionaries store key-value pairs in Python.'
]
corpus_resp = client.embeddings.create(model='text-embedding-3-small', input=docs)
corpus = np.array([d.embedding for d in corpus_resp.data])
query = 'finding similar text using angles'
q_resp = client.embeddings.create(model='text-embedding-3-small', input=query)
q_vec = np.array(q_resp.data[0].embedding)
scores = corpus @ q_vec
for i in np.argsort(scores)[::-1][:3]:
print(f'{scores[i]:.3f}: {docs[i]}')Definindo um limite para a pontuação
Nem todos os resultados principais são realmente relevantes — às vezes, a melhor correspondência ainda é semanticamente inadequada. Adicione um limite de pontuação para filtrar resultados com baixa similaridade. Um limite comum é de 0,70 a 0,80 para a similaridade de cosseno, mas você deve calibrá-lo para o seu domínio específico usando consultas reais.
import numpy as np
def search_with_threshold(query_vec, corpus_vecs, documents, k=5, threshold=0.75):
similarities = corpus_vecs @ query_vec
ranked = np.argsort(similarities)[::-1][:k]
results = []
for i in ranked:
if similarities[i] >= threshold:
results.append({'text': documents[i], 'score': float(similarities[i])})
return results
# Only returns documents above the minimum similarity thresholdCaracterísticas de desempenho da busca com NumPy
A busca por similaridade com NumPy tem complexidade temporal O(n * d) por consulta, em que n é o número de documentos e d é a dimensão da incorporação. Para incorporações de 1536 dimensões:
- 10.000 documentos: aproximadamente 5 ms por consulta em uma CPU moderna
- 100.000 documentos: aproximadamente 50 ms por consulta
- 1.000.000 de documentos: aproximadamente 500 ms — muito lento; mude para um banco de dados vetorial
O NumPy é excelente para prototipagem, mas não oferece busca aproximada, filtragem ou persistência integradas.
Persistindo incorporações no disco
Recalcular as incorporações a cada execução desperdiça chamadas à API e dinheiro. Salve no disco as incorporações do corpus e os textos dos documentos para gerar novas incorporações somente quando o corpus mudar.
np.save armazena a matriz de incorporações de forma eficiente, e você pode salvar a lista de documentos como JSON. Na inicialização, carregue os dois arquivos em vez de chamar a API.
import numpy as np
import json
# Save
np.save('/tmp/corpus_embeddings.npy', corpus_embeddings)
with open('/tmp/corpus_docs.json', 'w') as f:
json.dump(documents, f)
# Load
corpus_embeddings = np.load('/tmp/corpus_embeddings.npy')
with open('/tmp/corpus_docs.json') as f:
documents = json.load(f)
print(f'Loaded {len(documents)} docs, shape {corpus_embeddings.shape}')Processando novos documentos de forma incremental
Quando novos documentos chegarem, você não precisará gerar novamente as incorporações de todo o corpus. Gere incorporações apenas para os documentos novos e use np.vstack para anexar os vetores à matriz existente. Lembre-se de anexar os novos textos à sua lista de documentos na mesma ordem.
import numpy as np
from openai import OpenAI
client = OpenAI()
# Assume these exist from a previous session:
# corpus_embeddings: (n, 1536)
# documents: list of strings
new_docs = ['New document about vector search.']
resp = client.embeddings.create(model='text-embedding-3-small', input=new_docs)
new_vecs = np.array([item.embedding for item in resp.data])
corpus_embeddings = np.vstack([corpus_embeddings, new_vecs])
documents.extend(new_docs)
print(f'Corpus now has {len(documents)} documents')Limitações da busca em memória
A busca semântica com NumPy tem limitações significativas em comparação com um banco de dados vetorial criado especificamente para essa finalidade:
- Sem persistência — tudo fica na RAM e é perdido ao reiniciar
- Sem filtragem por metadados — você não pode filtrar os resultados por data, categoria ou autor
- Apenas varredura linear — não há indexação aproximada de vizinhos mais próximos
- Sem acesso simultâneo — não é adequada para implantações de produção com vários usuários
Essas limitações motivam o uso de um banco de dados vetorial dedicado em sistemas RAG de produção.
Verificação rápida
Teste sua compreensão dos conceitos de Engenharia de IA desta lição.
Recapitulação da lição
Nesta lição, você aprendeu que: produtos escalares de matrizes com incorporações normalizadas em L2 calculam a similaridade de cosseno de todo o corpus em uma única operação, np.argsort com inversão recupera os k documentos mais semelhantes e a busca com NumPy é ideal para protótipos, mas não oferece persistência nem filtragem por metadados. A seguir, usaremos agrupamento e UMAP para descobrir a estrutura temática de uma coleção de incorporações.
Aprenda Python com um tutor de IA — grátis
Escreva e execute código real no seu navegador, obtenha ajuda instantânea de um tutor de IA 24/7 e continue de onde parou na web ou no app.
- Cursos
- 30
- Aulas
- 120
Perguntas Frequentes
A aula “Busca semântica com NumPy” é grátis?
Sim — o texto completo de “Busca semântica com NumPy” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de AI Engineering Academy, atualize para CoddyKit PRO. O curso de AI Engineering Academy inclui 4 aulas no total.
O que vou aprender em “Busca semântica com NumPy”?
Crie um sistema de busca semântica totalmente em Python usando NumPy para calcular a similaridade de cosseno entre o embedding de uma consulta e uma coleção de embeddings de documentos. Você pratica AI Engineering Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar AI Engineering Academy?
Nenhuma experiência prévia é necessária. AI Engineering Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 3 de 4.
Quanto tempo leva a aula “Busca semântica com NumPy”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de AI Engineering Academy?
Sim. Cada aula de AI Engineering Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- O que são embeddings vetoriais?
- Gerando embeddings com a OpenAI
- Busca semântica com NumPy
- Agrupando e visualizando embeddings