Semantisch zoeken met NumPy
U bouwt een semantisch zoeksysteem in pure Python met NumPy om de cosine similarity tussen een query-embedding en een verzameling document-embeddings te berekenen.
Semantisch zoeken met NumPy is een gratis AI Engineering Academy-les op CoddyKit. Dit is les 3 van 4. Je kunt de volledige les hieronder gratis lezen en daarna in de browser praktisch oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is. Deze les maakt deel uit van het leertraject AI Engineering Academy. Je voortgang wordt gesynchroniseerd op het web en in de CoddyKit-app. De cursus AI Engineering Academy bevat in totaal 4 lessen.
Semantisch zoeken zonder database
Semantisch zoeken vindt de relevantste documenten voor een zoekopdracht op basis van betekenis in plaats van overlap tussen trefwoorden. De eenvoudigste implementatie gebruikt NumPy om de cosinusovereenkomst tussen een query-embedding en alle documentembeddings in het geheugen te berekenen — zonder externe database.
Deze aanpak werkt goed voor maximaal tienduizenden documenten en is ideaal voor prototyping voordat je in een vectordatabase investeert.
Het documentcorpus opbouwen
Begin met het verzamelen van je documenten en genereer met de OpenAI API één embedding per document. Sla de embeddings op als een tweedimensionale NumPy-array, waarbij elke rij één documentvector bevat. Houd een parallelle lijst met documentteksten bij, zodat je de oorspronkelijke inhoud kunt ophalen nadat je de beste overeenkomsten hebt gevonden.
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)De query van de gebruiker embedden
Wanneer een gebruiker een zoekopdracht indient, embed je die met hetzelfde model dat is gebruikt om de documenten te embedden. Het combineren van modellen — bijvoorbeeld text-embedding-3-small voor documenten en text-embedding-3-large voor query's — levert vectoren in verschillende ruimten op en geeft betekenisloze overeenkomstscores.
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,)Cosinusovereenkomst berekenen met NumPy
Bereken voor het vinden van de overeenkomst tussen de query en elk document in één bewerking het inwendig product van de queryvector met de matrix van documentvectoren. Omdat beide OpenAI-embeddings L2-genormaliseerd zijn, is dit voor alle documenten tegelijk gelijk aan de cosinusovereenkomst — O(n * d), waarbij n het aantal documenten is en d de dimensie.
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 documentDe beste K-resultaten rangschikken en ophalen
Gebruik np.argsort om documenten op aflopende overeenkomstreeks te rangschikken en selecteer daarna de indices van de beste k resultaten. Zo krijg je de indices van de relevantste documenten, waarmee je de oorspronkelijke tekst uit je parallelle lijst kunt ophalen.
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"]}')Volledig voorbeeld van semantisch zoeken
Alles bij elkaar: embed het corpus, embed de query, bereken de overeenkomsten en retourneer gerangschikte resultaten. Dit volledige patroon vormt de kern van elke RAG-ophaalstap, zelfs wanneer een vectordatabase NumPy onder de motorkap vervangt.
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]}')Drempelwaarden voor scores
Niet alle beste-k-resultaten zijn daadwerkelijk relevant — soms is zelfs de beste overeenkomst nog semantisch zwak. Voeg een score-drempelwaarde toe om resultaten met een lage overeenkomst eruit te filteren. Een gebruikelijke drempelwaarde voor cosinusovereenkomst is 0,70–0,80, maar je moet deze afstemmen op je specifieke domein met behulp van echte query's.
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 thresholdPrestatiekenmerken van zoeken met NumPy
Het zoeken naar overeenkomsten met NumPy heeft per query een tijdscomplexiteit van O(n * d), waarbij n het aantal documenten is en d de dimensie van de embedding. Voor embeddings met 1536 dimensies:
- 10.000 documenten: ongeveer 5 ms per query op een moderne CPU
- 100.000 documenten: ongeveer 50 ms per query
- 1.000.000 documenten: ongeveer 500 ms — te langzaam, stap over op een vectordatabase
NumPy is uitstekend geschikt voor prototyping, maar biedt standaard geen benaderd zoeken, filtering of persistentie.
Embeddings op schijf bewaren
Embeddings bij elke uitvoering opnieuw berekenen verspilt API-aanroepen en geld. Sla de embeddings van je corpus en de documentteksten op schijf op, zodat je alleen opnieuw hoeft te embedden wanneer het corpus verandert.
np.save slaat de embeddingmatrix efficiënt op en je kunt de documentenlijst als JSON opslaan. Laad beide bestanden bij het opstarten in plaats van de API aan te roepen.
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}')Nieuwe documenten incrementeel verwerken
Wanneer er nieuwe documenten binnenkomen, hoef je niet het hele corpus opnieuw te embedden. Embed alleen de nieuwe documenten en gebruik np.vstack om hun vectoren aan de bestaande matrix toe te voegen. Vergeet niet de nieuwe teksten in dezelfde volgorde aan je documentenlijst toe te voegen.
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')Beperkingen van zoeken in het geheugen
Semantisch zoeken met NumPy heeft aanzienlijke beperkingen vergeleken met een speciaal daarvoor gebouwde vectordatabase:
- Geen persistentie — alles staat in het RAM en gaat verloren bij een herstart
- Geen filtering op metagegevens — je kunt resultaten niet filteren op datum, categorie of auteur
- Alleen lineair scannen — geen indexering voor benaderde dichtstbijzijnde buren
- Geen gelijktijdige toegang — niet geschikt voor productie-implementaties met meerdere gebruikers
Deze beperkingen zijn een reden om voor productiesystemen met RAG een speciale vectordatabase te gebruiken.
Korte controle
Test je begrip van de AI-engineeringconcepten uit deze les.
Samenvatting van de les
In deze les heb je geleerd dat matrixproducten van L2-genormaliseerde embeddings in één bewerking de cosinusovereenkomst voor het volledige corpus berekenen, dat np.argsort met omkering de k meest vergelijkbare documenten ophaalt en dat zoeken met NumPy ideaal is voor prototypes, maar geen persistentie en filtering op metagegevens biedt. Hierna gebruiken we clustering en UMAP om de thematische structuur in een verzameling embeddings te ontdekken.
Leer Python met een AI-tutor — gratis
Schrijf echte code en voer die uit in je browser, krijg direct hulp van een AI-tutor die 24/7 beschikbaar is en ga verder waar je gebleven bent op het web of in de app.
- Cursussen
- 30
- Lessen
- 120
Veelgestelde vragen
Is de les “Semantisch zoeken met NumPy” gratis?
Ja — de volledige tekst van “Semantisch zoeken met NumPy” kun je hier gratis op het web lezen. Als je interactief wilt oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is, en de rest van de cursus AI Engineering Academy wilt ontgrendelen, kun je upgraden naar CoddyKit PRO. De cursus AI Engineering Academy bevat in totaal 4 lessen.
Wat leer ik in “Semantisch zoeken met NumPy”?
U bouwt een semantisch zoeksysteem in pure Python met NumPy om de cosine similarity tussen een query-embedding en een verzameling document-embeddings te berekenen. Je oefent met AI Engineering Academy door code rechtstreeks in de browser uit te voeren. Een AI-begeleider die 24/7 beschikbaar is beantwoordt je vragen terwijl je de les doorwerkt.
Heb ik ervaring nodig om met AI Engineering Academy te beginnen?
Ervaring vooraf is niet nodig. AI Engineering Academy op CoddyKit is opgebouwd voor beginners tot gevorderden, zodat je hier of bij het begin kunt starten en in je eigen tempo kunt leren. Dit is les 3 van 4.
Hoe lang duurt de les “Semantisch zoeken met NumPy”?
De meeste lessen van CoddyKit duren ongeveer 5–10 minuten. Elke les is kort en interactief, zodat je gestaag vooruitgaat en op het web en in de app precies verdergaat waar je was gebleven.
Kan ik code schrijven en uitvoeren in deze les over AI Engineering Academy?
Ja. Elke les over AI Engineering Academy bevat een ingebouwde code-editor, zodat je rechtstreeks in je browser echte code kunt schrijven en uitvoeren en direct feedback van AI krijgt — lokale installatie is niet nodig.
Alle lessen in deze cursus
- Wat zijn vector embeddings?
- Embeddings genereren met OpenAI
- Semantisch zoeken met NumPy
- Embeddings clusteren en visualiseren