0Pricing
AI Engineering Academy · บทเรียน

การค้นหาเชิงความหมายด้วย NumPy

สร้างระบบค้นหาเชิงความหมายด้วย Python ล้วน โดยใช้ NumPy คำนวณความคล้ายคลึงแบบโคไซน์ระหว่างเวกเตอร์ฝังตัวของคำค้นกับกลุ่มเวกเตอร์ฝังตัวของเอกสาร

การค้นหาเชิงความหมายด้วย NumPy เป็นบทเรียน AI Engineering Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน AI Engineering Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส AI Engineering Academy มีบทเรียนทั้งหมด 4 บทเรียน

การค้นหาเชิงความหมายโดยไม่ใช้ฐานข้อมูล

การค้นหาเชิงความหมายค้นหาเอกสารที่เกี่ยวข้องกับคำค้นมากที่สุดโดยพิจารณาจากความหมาย ไม่ใช่เพียงการมีคำสำคัญตรงกัน การใช้งานที่ง่ายที่สุดคือใช้ NumPy คำนวณความคล้ายคลึงแบบโคไซน์ระหว่างเวกเตอร์ฝังตัวของคำค้นกับเวกเตอร์ฝังตัวของเอกสารทั้งหมดในหน่วยความจำ โดยไม่ต้องใช้ฐานข้อมูลภายนอก

แนวทางนี้ทำงานได้ดีสำหรับเอกสารจำนวนไม่เกินหลักหมื่นรายการ และเหมาะอย่างยิ่งสำหรับการสร้างต้นแบบก่อนลงทุนใช้ฐานข้อมูลเวกเตอร์

การสร้างคลังเอกสาร

เริ่มด้วยการรวบรวมเอกสารและสร้างเวกเตอร์ฝังตัวหนึ่งรายการต่อเอกสารโดยใช้ OpenAI API จัดเก็บเวกเตอร์ฝังตัวเป็นอาร์เรย์ NumPy สองมิติ โดยแต่ละแถวแทนเวกเตอร์ของเอกสารหนึ่งรายการ เก็บรายการข้อความเอกสารแยกไว้อีกชุดหนึ่งในลำดับเดียวกัน เพื่อให้เรียกคืนเนื้อหาต้นฉบับได้หลังจากพบรายการที่ตรงกันมากที่สุด

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)

การสร้างเวกเตอร์ฝังตัวจากคำค้นของผู้ใช้

เมื่อผู้ใช้ส่งคำค้น ให้สร้างเวกเตอร์ฝังตัวโดยใช้โมเดลเดียวกันกับที่ใช้สร้างเวกเตอร์ฝังตัวของเอกสาร การผสมโมเดลเข้าด้วยกัน เช่น ใช้ text-embedding-3-small กับเอกสารและใช้ text-embedding-3-large กับคำค้น จะทำให้เวกเตอร์อยู่คนละปริภูมิและทำให้ค่าความคล้ายคลึงไม่มีความหมาย

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

การคำนวณความคล้ายคลึงแบบโคไซน์ด้วย NumPy

หากต้องการหาความคล้ายคลึงระหว่างคำค้นกับเอกสารทุกฉบับในขั้นตอนเดียว ให้คำนวณผลคูณจุดของเวกเตอร์คำค้นกับเมทริกซ์เวกเตอร์เอกสาร เนื่องจากเวกเตอร์ฝังตัวของ OpenAI ทั้งสองแบบผ่านการทำให้เป็นมาตรฐานแบบ L2 แล้ว ค่านี้จึงเท่ากับความคล้ายคลึงแบบโคไซน์ของเอกสารทั้งหมดในครั้งเดียว ใช้เวลา O(n * d) เมื่อ n คือจำนวนเอกสาร และ d คือมิติ

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

การจัดอันดับและเรียกคืนผลลัพธ์ k อันดับแรก

ใช้ np.argsort เพื่อจัดอันดับเอกสารตามค่าความคล้ายคลึงจากมากไปน้อย จากนั้นตัดเลือกดัชนี k รายการแรก วิธีนี้จะให้ดัชนีของเอกสารที่เกี่ยวข้องมากที่สุด ซึ่งคุณสามารถใช้ค้นหาข้อความต้นฉบับจากรายการที่จัดเก็บไว้คู่กัน

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

ตัวอย่างการค้นหาเชิงความหมายแบบครบถ้วน

เมื่อนำทุกอย่างมารวมกัน ให้สร้างเวกเตอร์ฝังตัวของคลังเอกสาร สร้างเวกเตอร์ฝังตัวของคำค้น คำนวณค่าความคล้ายคลึง และส่งคืนผลลัพธ์ที่จัดอันดับแล้ว รูปแบบที่ครบถ้วนนี้เป็นแกนหลักของขั้นตอนการค้นคืน RAG ทุกแบบ แม้ภายใต้การทำงานจริงจะใช้ฐานข้อมูลเวกเตอร์แทน NumPy ก็ตาม

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

การกำหนดเกณฑ์ค่าคะแนน

ผลลัพธ์ k อันดับแรกไม่ได้เกี่ยวข้องเสมอไป บางครั้งผลลัพธ์ที่ตรงกันมากที่สุดก็ยังไม่สอดคล้องทางความหมายมากพอ ให้เพิ่มเกณฑ์ค่าคะแนนเพื่อกรองผลลัพธ์ที่มีความคล้ายคลึงต่ำออกไป โดยทั่วไปเกณฑ์สำหรับความคล้ายคลึงแบบโคไซน์จะอยู่ที่ 0.70–0.80 แต่คุณควรปรับเทียบค่าให้เหมาะกับโดเมนเฉพาะของคุณโดยใช้คำค้นจริง

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

ลักษณะด้านประสิทธิภาพของการค้นหาด้วย NumPy

การค้นหาความคล้ายคลึงด้วย NumPy มีความซับซ้อนด้านเวลา O(n * d) ต่อคำค้น โดย n คือจำนวนเอกสาร และ d คือมิติของเวกเตอร์ฝังตัว สำหรับเวกเตอร์ฝังตัวขนาด 1536 มิติ:

  • เอกสาร 10,000 รายการ: ประมาณ 5 มิลลิวินาทีต่อคำค้นบน CPU รุ่นใหม่
  • เอกสาร 100,000 รายการ: ประมาณ 50 มิลลิวินาทีต่อคำค้น
  • เอกสาร 1,000,000 รายการ: ประมาณ 500 มิลลิวินาที ซึ่งช้าเกินไป ควรเปลี่ยนไปใช้ฐานข้อมูลเวกเตอร์

NumPy เหมาะอย่างยิ่งสำหรับการสร้างต้นแบบ แต่ไม่มีการค้นหาโดยประมาณ การกรอง หรือการคงอยู่ของข้อมูลในตัว

การบันทึกเวกเตอร์ฝังตัวลงดิสก์

การคำนวณเวกเตอร์ฝังตัวใหม่ทุกครั้งที่เรียกใช้โปรแกรมทำให้เสียทั้งการเรียก API และเงิน ให้บันทึกเวกเตอร์ฝังตัวของคลังเอกสารและข้อความเอกสารลงดิสก์ เพื่อสร้างเวกเตอร์ฝังตัวใหม่เฉพาะเมื่อคลังเอกสารเปลี่ยนแปลง

np.save จัดเก็บเมทริกซ์เวกเตอร์ฝังตัวได้อย่างมีประสิทธิภาพ และคุณสามารถบันทึกรายการเอกสารเป็น JSON ได้ เมื่อเริ่มโปรแกรม ให้โหลดไฟล์ทั้งสองแทนการเรียก 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}')

การจัดการเอกสารใหม่แบบเพิ่มทีละส่วน

เมื่อมีเอกสารใหม่เข้ามา คุณไม่จำเป็นต้องสร้างเวกเตอร์ฝังตัวของคลังเอกสารทั้งหมดใหม่ ให้สร้างเวกเตอร์ฝังตัวเฉพาะเอกสารใหม่ แล้วใช้ np.vstack ต่อเวกเตอร์เหล่านั้นเข้ากับเมทริกซ์เดิม อย่าลืมต่อข้อความใหม่เข้ากับรายการเอกสารตามลำดับเดียวกัน

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

ข้อจำกัดของการค้นหาในหน่วยความจำ

การค้นหาเชิงความหมายด้วย NumPy มีข้อจำกัดสำคัญเมื่อเทียบกับฐานข้อมูลเวกเตอร์ที่สร้างมาโดยเฉพาะ:

  • ไม่มีการคงอยู่ของข้อมูล — ทุกอย่างอยู่ใน RAM และสูญหายเมื่อเริ่มระบบใหม่
  • ไม่มีการกรองข้อมูลกำกับ — ไม่สามารถกรองผลลัพธ์ตามวันที่ หมวดหมู่ หรือผู้เขียนได้
  • ค้นหาแบบไล่ตรวจตามลำดับเท่านั้น — ไม่มีดัชนีค้นหาเพื่อนบ้านใกล้เคียงโดยประมาณ
  • ไม่รองรับการเข้าถึงพร้อมกัน — ไม่เหมาะสำหรับระบบใช้งานจริงที่มีผู้ใช้หลายคน

ข้อจำกัดเหล่านี้เป็นเหตุผลให้ควรใช้ฐานข้อมูลเวกเตอร์โดยเฉพาะสำหรับระบบ RAG ที่ใช้งานจริง

ตรวจสอบความเข้าใจอย่างรวดเร็ว

ทดสอบความเข้าใจแนวคิดด้านวิศวกรรม AI จากบทเรียนนี้

สรุปบทเรียน

ในบทเรียนนี้ คุณได้เรียนรู้ว่า การคำนวณผลคูณจุดของเมทริกซ์กับเวกเตอร์ฝังตัวที่ทำให้เป็นมาตรฐานแบบ L2 จะคำนวณความคล้ายคลึงแบบโคไซน์ของคลังเอกสารทั้งหมดได้ในขั้นตอนเดียว การใช้ np.argsort ร่วมกับการย้อนลำดับจะเรียกคืนเอกสารที่คล้ายคลึงมากที่สุด k รายการ และ การค้นหาด้วย NumPy เหมาะสำหรับต้นแบบ แต่ไม่มีการคงอยู่ของข้อมูลและการกรองข้อมูลกำกับ บทถัดไป เราจะใช้การจัดกลุ่มและ UMAP เพื่อค้นหาโครงสร้างหัวข้อในชุดเวกเตอร์ฝังตัว

คำถามที่พบบ่อย

บทเรียน “การค้นหาเชิงความหมายด้วย NumPy” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “การค้นหาเชิงความหมายด้วย NumPy” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส AI Engineering Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส AI Engineering Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “การค้นหาเชิงความหมายด้วย NumPy”

สร้างระบบค้นหาเชิงความหมายด้วย Python ล้วน โดยใช้ NumPy คำนวณความคล้ายคลึงแบบโคไซน์ระหว่างเวกเตอร์ฝังตัวของคำค้นกับกลุ่มเวกเตอร์ฝังตัวของเอกสาร คุณปฏิบัติ AI Engineering Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน AI Engineering Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน AI Engineering Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน

บทเรียน “การค้นหาเชิงความหมายด้วย NumPy” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน AI Engineering Academy นี้ได้ไหม

ได้ บทเรียน AI Engineering Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. เวกเตอร์ฝังตัวคืออะไร
  2. การสร้างเวกเตอร์ฝังตัวด้วย OpenAI
  3. การค้นหาเชิงความหมายด้วย NumPy
  4. การจัดกลุ่มและแสดงภาพเวกเตอร์ฝังตัว
← กลับไปที่ AI Engineering Academy