AI Engineering Academy · leksjon

Semantisk søk med NumPy

Bygg et rent Python-basert system for semantisk søk ved hjelp av NumPy for å beregne cosinuslikhet mellom en spørringsvektor og en samling dokumentvektorer.

Leksjon 3 av 413 trinn

Semantisk søk med NumPy er en gratis leksjon i AI Engineering Academy på CoddyKit. Dette er leksjon 3 av 4. Du kan lese hele leksjonen gratis nedenfor – og deretter øve praktisk i nettleseren med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Den er en del av læringsløpet i AI Engineering Academy, og fremdriften din synkroniseres mellom nettet og CoddyKit-appen. Kurset i AI Engineering Academy inneholder totalt 4 leksjoner.

Semantisk søk uten database

Semantisk søk finner de mest relevante dokumentene for et søk basert på betydning, ikke på overlappende nøkkelord. Den enkleste implementasjonen bruker NumPy til å beregne cosinuslikhet mellom en søkeembedding og alle dokumentembeddingene i minnet – uten behov for en ekstern database.

Denne tilnærmingen fungerer godt for opptil titusenvis av dokumenter og er ideell for prototyping før De investerer i en vektordatabase.

Bygge dokumentsamlingen

Begynn med å samle dokumentene Deres og generere én embedding per dokument ved hjelp av OpenAI API. Lagre embeddingene som en todimensjonal NumPy-array der hver rad er én dokumentvektor. Behold en parallell liste over dokumenttekstene, slik at De kan hente det opprinnelige innholdet etter å ha funnet de beste treffene.

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)

Embedding av brukersøket

Når en bruker sender inn et søk, lager De embedding av det med den samme modellen som ble brukt til å lage embedding av dokumentene. Hvis De blander modeller – for eksempel bruker text-embedding-3-small for dokumenter og text-embedding-3-large for søk – havner vektorene i forskjellige rom, og likhetsskårene blir meningsløse.

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,)

Beregne cosinuslikhet med NumPy

For å finne likheten mellom søket og hvert dokument i én operasjon beregner De skalarproduktet av søkevektoren med matrisen av dokumentvektorer. Siden begge OpenAI-embeddingene er L2-normaliserte, tilsvarer dette cosinuslikhet for alle dokumentene samtidig – O(n * d), der n er antallet dokumenter og d er dimensjonen.

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 document

Rangere og hente de beste K-resultatene

Bruk np.argsort til å rangere dokumentene etter likhetsskår i synkende rekkefølge, og hent deretter ut indeksene for de k beste resultatene. Da får De indeksene til de mest relevante dokumentene, som De bruker til å slå opp den opprinnelige teksten i den parallelle listen.

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"]}')

Fullstendig eksempel på semantisk søk

Sett alt sammen: lag embedding av dokumentsamlingen, lag embedding av søket, beregn likheter og returner rangerte resultater. Dette komplette mønsteret er kjernen i hvert RAG-gjenfinningstrinn, selv når en vektordatabase erstatter NumPy under overflaten.

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]}')

Grenseverdi for skår

Ikke alle av de k beste resultatene er faktisk relevante – noen ganger er selv det beste treffet et dårlig semantisk samsvar. Legg til en skårgrense for å filtrere bort resultater med lav likhet. En vanlig grenseverdi er 0,70–0,80 for cosinuslikhet, men De bør kalibrere den mot Deres spesifikke domene ved hjelp av virkelige søk.

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 threshold

Ytelsesegenskaper ved NumPy-søk

NumPy-likhetssøk har tidskompleksiteten O(n * d) per søk, der n er antallet dokumenter og d er embedding-dimensjonen. For embedding med 1536 dimensjoner:

  • 10 000 dokumenter: ~5 ms per søk på en moderne CPU
  • 100 000 dokumenter: ~50 ms per søk
  • 1 000 000 dokumenter: ~500 ms – for tregt, bytt til en vektordatabase

NumPy er utmerket for prototyping, men har ingen innebygd støtte for omtrentlig søk, filtrering eller persistens.

Lagre embedding på disk

Det er sløsing med API-kall og penger å beregne embeddingene på nytt ved hver kjøring. Lagre embeddingene for dokumentsamlingen og dokumenttekstene på disk, slik at De bare trenger å lage embedding på nytt når samlingen endres.

np.save lagrer embedding-matrisen effektivt, og De kan lagre dokumentlisten som JSON. Ved oppstart laster De inn begge filene i stedet for å kalle API-et.

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}')

Håndtere nye dokumenter trinnvis

Når nye dokumenter kommer til, trenger De ikke å lage embedding av hele samlingen på nytt. Lag bare embedding av de nye dokumentene, og bruk np.vstack til å legge vektorene til den eksisterende matrisen. Husk å legge de nye tekstene til i dokumentlisten i samme rekkefølge.

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')

Begrensninger ved søk i minnet

Semantisk søk med NumPy har betydelige begrensninger sammenlignet med en spesialutviklet vektordatabase:

  • Ingen persistens – alt ligger i RAM og går tapt ved omstart
  • Ingen filtrering på metadata – De kan ikke filtrere resultater etter dato, kategori eller forfatter
  • Bare lineært søk – ingen indeks for omtrentlig nærmeste nabo
  • Ingen samtidig tilgang – ikke egnet for produksjonsdistribusjoner med flere brukere

Disse begrensningene gjør det nødvendig å bruke en dedikert vektordatabase for RAG-systemer i produksjon.

Rask kontroll

Test forståelsen Deres av konseptene innen AI Engineering fra denne leksjonen.

Oppsummering av leksjonen

I denne leksjonen har De lært at matrise-skalarprodukter med L2-normaliserte embedding beregner cosinuslikhet for hele dokumentsamlingen i én operasjon, at np.argsort med reversering henter de k mest like dokumentene, og at NumPy-søk er ideelt for prototyper, men mangler persistens og metadatafiltrering. Neste steg er å bruke klynging og UMAP til å oppdage temastrukturen i en samling embedding.

Gratis å komme i gang

Lær deg Python med en AI-veileder – gratis

Skriv og kjør ekte kode i nettleseren, få umiddelbar hjelp fra en AI-veileder som er tilgjengelig døgnet rundt, og fortsett der du slapp – på nettet eller i appen.

Kurs
30
Leksjoner
120

Ofte stilte spørsmål

Er leksjonen «Semantisk søk med NumPy» gratis?

Ja – hele teksten i «Semantisk søk med NumPy» er gratis å lese her på nettet. For å øve interaktivt med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt, og for å låse opp resten av AI Engineering Academy-kurset, kan du oppgradere til CoddyKit PRO. Kurset i AI Engineering Academy inneholder totalt 4 leksjoner.

Hva lærer jeg i «Semantisk søk med NumPy»?

Bygg et rent Python-basert system for semantisk søk ved hjelp av NumPy for å beregne cosinuslikhet mellom en spørringsvektor og en samling dokumentvektorer. Du øver på AI Engineering Academy med praktisk kode som du kjører direkte i nettleseren, mens en AI-veileder som er tilgjengelig døgnet rundt, svarer på spørsmålene dine mens du jobber deg gjennom leksjonen.

Trenger jeg erfaring for å begynne med AI Engineering Academy?

Ingen tidligere erfaring er nødvendig. AI Engineering Academy på CoddyKit er lagt opp for både nybegynnere og viderekomne, så De kan begynne her eller helt fra start og lære i Deres eget tempo. Dette er leksjon 3 av 4.

Hvor lang tid tar leksjonen «Semantisk søk med NumPy»?

De fleste CoddyKit-leksjoner tar omtrent 5–10 minutter. Hver leksjon er kort og interaktiv, slik at De gjør jevne fremskritt og kan fortsette akkurat der De slapp – både på nettet og i appen.

Kan jeg skrive og kjøre kode i denne AI Engineering Academy-leksjonen?

Ja. Alle AI Engineering Academy-leksjoner har en innebygd kodeeditor, slik at De kan skrive og kjøre ekte kode direkte i nettleseren og få umiddelbar tilbakemelding fra AI – uten lokal konfigurering.

Alle leksjonene i dette kurset

  1. Hva er vektorembeddinger?
  2. Generere embeddinger med OpenAI
  3. Semantisk søk med NumPy
  4. Klynging og visualisering av embeddinger
← Tilbake til AI Engineering Academy