Inilah momen ketika pencarian teks biasa tidak memadai. Berikut contoh nyatanya:
- Portal dukungan pelanggan tidak mengembalikan hasil saat pengguna mengetik "tidak mau menyala" alih-alih frasa persis yang digunakan di artikel bantuan.
- Platform e-commerce tidak menemukan apa-apa saat pembeli mencari "sesuatu yang hangat untuk musim dingin" karena tidak ada deskripsi produk yang memuat kata-kata persis itu.
- Basis pengetahuan melewatkan dokumen yang tepat karena pengguna merumuskan pertanyaan secara berbeda dari cara dokumen ditulis.
Pencarian semantik mengatasi hal ini. Ia memahami makna dan maksud di balik kueri, bukan sekadar mencocokkan kata yang sama persis.
Tutorial ini menunjukkan cara mengimplementasikan pencarian semantik di MongoDB menggunakan Python dan model embedding gratis nomic-embed-text-v1.
Catatan: nomic-embed-text-v1 berukuran sekitar 0,27 GB saat dimuat ke memori, jadi pastikan mesin Anda memiliki setidaknya 1 GB RAM yang tersedia sebelum menjalankan skrip apa pun di tutorial ini. Pada percobaan pertama, model akan diunduh dari Hugging Face, jadi luangkan waktu tambahan bergantung pada koneksi internet Anda. Semua percobaan berikutnya memuat model dari cache lokal Anda. Inferensi di CPU jauh lebih lambat daripada di GPU. Pada mesin tanpa GPU khusus, model berjalan di CPU dan menggunakan RAM alih-alih VRAM, sehingga pembuatan embedding mungkin memakan waktu lebih lama.
Dalam tutorial ini, Anda akan mempelajari hal-hal berikut:
- Apa itu vektor embedding dan bagaimana ia merepresentasikan makna
- Cara menghasilkan dan menyimpan embedding di MongoDB
- Cara membuat indeks MongoDB Vector Search
- Cara mengonversi kueri pengguna menjadi vektor
- Cara menjalankan pipeline agregasi
$vectorSearchdan menafsirkan hasilnya
Anda dapat menemukan semua contoh kode untuk tutorial ini di repositori GitHub.
Pencarian Teks vs Pencarian Semantik
Pencarian teks mencocokkan dokumen yang memuat kata-kata persis seperti dalam kueri Anda. Pencarian semantik mencocokkan dokumen yang memiliki makna yang sama dengan kueri Anda, meskipun menggunakan kata yang sama sekali berbeda.
Tabel berikut menunjukkan apa yang dikembalikan masing-masing pendekatan untuk kueri pencarian "masalah jantung":
| Teks dokumen | Pencarian teks | Pencarian semantik | Alasan pencarian teks mengembalikannya |
|---|---|---|---|
| "...masalah jantung pada orang dewasa..." | Ya | Ya | Memuat kata-kata persis "masalah jantung" |
| "...kondisi dan gejala kardiak..." | Tidak | Ya | Tidak memuat kata-kata persis "masalah jantung" |
| "...faktor risiko penyakit jantung..." | Tidak | Ya | Tidak memuat kata-kata persis "masalah jantung" |
| "...nyeri dada dan sesak napas..." | Tidak | Ya | Tidak memuat kata-kata persis "masalah jantung" |
Konsep Pencarian Semantik
Tiga konsep berikut menjadi inti pencarian semantik di MongoDB. Ini akan membantu Anda memahami mengapa setiap langkah dalam tutorial ini bekerja seperti adanya.
Vektor embedding
Model embedding mengonversi teks menjadi daftar angka berdimensi tetap yang disebut vektor. Setiap angka dalam vektor merepresentasikan dimensi makna.
Dua teks dengan makna serupa menghasilkan vektor yang secara numerik berdekatan satu sama lain. "kondisi kardiak" dan "masalah jantung" berada saling berdekatan dalam ruang vektor, meskipun tidak berbagi kata apa pun. Kedekatan itulah yang memungkinkan pencarian semantik.
Tutorial ini menggunakan model embedding nomic-embed-text-v1, yang gratis, sumber terbuka, dan berjalan sepenuhnya di mesin lokal Anda. Model ini otomatis diunduh dari Hugging Face saat Anda pertama kali menjalankan skrip dan disimpan secara lokal untuk semua percobaan berikutnya. Model menghasilkan 768 angka per masukan.
Indeks pencarian vektor
Indeks pencarian vektor memberi tahu MongoDB field mana yang menyimpan embedding, berapa jumlah dimensi yang diharapkan, dan fungsi kemiripan mana yang digunakan untuk perbandingan. Anda harus membuat indeks ini sebelum dapat menjalankan kueri $vectorSearch apa pun.
Tahap agregasi $vectorSearch
$vectorSearch adalah tahap agregasi yang menjalankan pencarian. Tahap ini menerima vektor kueri, menelusuri field yang diindeks, dan mengembalikan dokumen yang diurutkan berdasarkan kemiripan semantik. Anda dapat merantaikannya dengan $project dan tahap lainnya seperti pipeline agregasi pada umumnya.
Sekarang mari kita mulai.
Prasyarat
Sebelum memulai, pastikan Anda telah menyiapkan hal-hal berikut:
- Python 3.8 atau yang lebih baru terpasang
- Akun MongoDB Atlas dengan klaster M0 (tingkat Gratis) sudah disiapkan
pymongo4.7 atau yang lebih baru, paket Pythonsentence-transformers, daneinopsterpasang- Pemahaman dasar tentang Python dan koleksi MongoDB
- String koneksi Atlas Anda, tersedia di UI Atlas di **Database > Connect > Drivers**
- Alamat IP Anda sudah diizinkan di Atlas sebelum menjalankan skrip apa pun. Buka **Security > Network Access** di UI Atlas dan tambahkan alamat IP Anda saat ini.
Menyiapkan Proyek Anda
Buat folder untuk proyek Anda dan navigasikan ke dalamnya di terminal:
mkdir mongodb-semantic-search
cd mongodb-semantic-search
Buat dan aktifkan virtual environment untuk mengisolasi dependensi proyek Anda:
python -m venv venv
source venv/bin/activate
Di Windows, aktifkan virtual environment dengan:
venv\Scripts\activate
Sekarang pasang paket yang diperlukan:
pip install pymongo sentence-transformers einops
Anda akan membuat satu file Python untuk setiap langkah dalam tutorial ini. Semua file ditempatkan di folder mongodb-semantic-search.
Buat File Utilitas Embedding
Buat file bernama embedding_utils.py. File ini menyimpan kedua fungsi embedding yang digunakan dalam tutorial ini:
from sentence_transformers import SentenceTransformer
# Load the free, open-source embedding model.
# The model downloads from Hugging Face on first run and saves locally.
# trust_remote_code=True is required by this model:
model = SentenceTransformer("nomic-ai/nomic-embed-text-v1", trust_remote_code=True)
def get_embedding(text, precision="float32"):
# Use this function when embedding text you plan to store in MongoDB:
return model.encode(text, precision=precision).tolist()
def get_query_embedding(text, precision="float32"):
# Use this function when embedding a user's search query:
return model.encode(text, precision=precision).tolist()
Peringatan: trust_remote_code=True memungkinkan model mengeksekusi kode Python spesifik model yang diunduh dari Hugging Face ke mesin Anda. Jika kode sumber model di Hugging Face disusupi atau diperbarui dengan perubahan berbahaya, kode tersebut akan berjalan otomatis di lingkungan Anda. Berhati-hatilah sebelum menggunakan parameter ini dalam penerapan produksi.
Hasilkan dan Simpan Vektor Embedding
Buat file bernama generate_embeddings.py. File ini mengimpor fungsi embedding dari embedding_utils.py, menghasilkan vektor untuk setiap field teks dokumen, dan menyimpan dokumen lengkap, termasuk embedding, di MongoDB:
from embedding_utils import get_embedding
from pymongo import MongoClient
# Replace the placeholder with your Atlas connection string:
mongodb_client = MongoClient(
"mongodb+srv://<USERNAME>:<PASSWORD>@<HOST>/",
appname="devrel-tutorial-python-semantic-search"
)
collection = mongodb_client["sample_db"]["documents"]
# Sample data:
sample_documents = [
{
"title": "MongoDB Atlas",
"text": "MongoDB Atlas is a fully managed cloud database."
},
{
"title": "Vector Search",
"text": "Vector search finds results based on semantic meaning."
},
{
"title": "Nomic AI",
"text": "nomic-embed-text-v1 is a free, open-source embedding model."
},
]
docs_to_insert = []
for doc in sample_documents:
embedding = get_embedding(doc["text"])
docs_to_insert.append({
"title": doc["title"],
"text": doc["text"],
# The vector lives alongside your original data in the same document:
"embedding": embedding
})
# Drop the collection before each run to avoid inserting duplicate documents:
collection.drop()
result = collection.insert_many(docs_to_insert)
print(f"Inserted {len(result.inserted_ids)} documents with embeddings.")
mongodb_client.close()
Pada kode di atas, collection.drop() menghapus semua dokumen dan indeks pada koleksi sebelum setiap percobaan. Menjalankan ulang generate_embeddings.py tanpa baris ini akan menyisipkan dokumen duplikat, yang menyebabkan $vectorSearch mengembalikan dokumen yang sama beberapa kali. Menghapus koleksi juga menghapus indeks pencarian vektor, sehingga Anda harus menjalankan ulang create_vector_index.py setiap kali Anda menjalankan ulang generate_embeddings.py.
Jalankan skrip:
python generate_embeddings.py
Anda akan mendapatkan keluaran berikut di terminal:
<All keys matched successfully>
Inserted 3 documents with embeddings.
Setiap dokumen di MongoDB kini memuat teks asli dan vektor berdimensi 768 di field embedding. Vektor disimpan berdampingan dengan data Anda dalam dokumen yang sama, sehingga tidak diperlukan penyimpanan terpisah atau pencarian tambahan saat waktu kueri.
Buat Indeks MongoDB Vector Search
Buat file bernama create_vector_index.py. File ini mendefinisikan dan membuat indeks pencarian vektor pada field embedding agar MongoDB dapat menjalankan kueri $vectorSearch terhadap koleksi Anda.
Definisi indeks membutuhkan tiga field:
path: field yang menyimpan embedding (embeddingdalam tutorial ini)numDimensions: harus cocok dengan ukuran keluaran model (768untuknomic-embed-text-v1)similarity: fungsi perbandingan (cosineadalah yang benar untuk model ini)
Nilai numDimensions harus persis sama dengan model embedding Anda. Ketidaksesuaian akan menyebabkan pembuatan indeks gagal:
from pymongo.mongo_client import MongoClient
from pymongo.operations import SearchIndexModel
import time
# Replace the placeholder with your Atlas connection string:
mongodb_client = MongoClient(
"mongodb+srv://<USERNAME>:<PASSWORD>@<HOST>/",
appname="devrel-tutorial-python-semantic-search"
)
# Point to the same database and collection you used in the previous step:
database = mongodb_client["sample_db"]
collection = database["documents"]
# Define the vector search index.
# The three required fields tell MongoDB what to index and how to compare vectors:
search_index_model = SearchIndexModel(
definition={
"fields": [
{
"type": "vector",
"path": "embedding", # The field name from generate_embeddings.py
"numDimensions": 768, # nomic-embed-text-v1 always outputs 768 dimensions
"similarity": "cosine" # Recommended similarity function for this model
}
]
},
name="vector_index",
type="vectorSearch"
)
result = collection.create_search_index(model=search_index_model)
print("New search index named " + result + " is building.")
# Poll every five seconds until the index is ready to accept queries:
print("Polling to check if the index is ready. This may take up to a minute.")
predicate = lambda index: index.get("queryable") is True
while True:
indices = list(collection.list_search_indexes(result))
if len(indices) and predicate(indices[0]):
break
time.sleep(5)
print(result + " is ready for querying.")
mongodb_client.close()
Jalankan skrip:
python create_vector_index.py
Anda akan mendapatkan keluaran berikut di terminal:
New search index named vector_index is building.
Polling to check if the index is ready. This may take up to a minute.
vector_index is ready for querying.
Pembuatan indeks memakan waktu hingga satu menit. Loop polling memeriksa setiap 5 detik dan hanya keluar ketika MongoDB mengonfirmasi bahwa indeks dapat dikueri. Jangan lanjut ke langkah berikutnya sampai Anda melihat "vector_index is ready for querying."
Konversikan Kueri Pencarian Anda Menjadi Vektor
Buat file bernama generate_query_vector.py. File ini mengimpor fungsi get_query_embedding() dari file embedding_utils.py. Jalankan file generate_query_vector.py untuk memverifikasi bahwa model embedding dimuat dengan benar dan menghasilkan vektor berdimensi 768:
- File ini mendefinisikan fungsi
get_query_embedding()yang diimpor pada langkah berikutnya. - Anda dapat menjalankannya secara mandiri untuk memverifikasi bahwa model berfungsi dengan benar.
Anda harus menggunakan model yang sama di sini seperti pada langkah pertama. Model yang berbeda menghasilkan vektor yang berada pada ruang numerik berbeda, sehingga perbandingan menjadi tidak bermakna:
from embedding_utils import get_query_embedding
user_query = "I need an automated, scalable system for serious information storage"
query_vector = get_query_embedding(user_query)
print(f"Query: '{user_query}'")
print(f"Vector dimensions: {len(query_vector)}")
print(f"First 5 values: {query_vector[:5]}")
Jalankan skrip:
python generate_query_vector.py
Anda akan mendapatkan keluaran serupa berikut:
<All keys matched successfully>
Query: 'I need an automated, scalable system for serious information storage'
Vector dimensions: 768
First 5 values: [0.0025914530269801617, 0.09862980246543884, -0.023092379793524742, -0.0171672236174345, -0.05548065900802612]
Pada keluaran di atas, Vector dimensions: 768 mengonfirmasi bahwa keluaran model cocok dengan embedding yang Anda simpan.
Jalankan Kueri $vectorSearch
Buat file bernama run_vector_search.py. File ini mengimpor fungsi get_query_embedding() dari file embedding_utils.py dan mengonversi istilah pencarian pengguna menjadi vektor. run_vector_search.py menjalankan pipeline agregasi $vectorSearch, dan mencetak hasil yang diurutkan berdasarkan kemiripan semantik:
from embedding_utils import get_query_embedding
from pymongo import MongoClient
# Replace the placeholder with your Atlas connection string:
mongodb_client = MongoClient(
"mongodb+srv://<USERNAME>:<PASSWORD>@<HOST>/",
appname="devrel-tutorial-python-semantic-search"
)
collection = mongodb_client["sample_db"]["documents"]
# Generate a query vector from the user's search input:
user_query = "I need an automated, scalable system for serious information storage"
query_vector = get_query_embedding(user_query)
# Define the $vectorSearch aggregation pipeline:
pipeline = [
{
"$vectorSearch": {
"index": "vector_index", # The index created in create_vector_index.py
"path": "embedding", # The field that holds your stored vectors
"queryVector": query_vector, # The vector generated from the user's query
"numCandidates": 150, # How many neighbors MongoDB considers
"limit": 3 # How many results to return
}
},
{
"$project": {
"_id": 0,
"title": 1,
"text": 1,
"score": {
"$meta": "vectorSearchScore" # Relevance score for each result
}
}
}
]
results = collection.aggregate(pipeline)
print(f"\nTop results for query: '{user_query}'\n")
for doc in results:
print(f"Title: {doc['title']}")
print(f"Text: {doc['text']}")
print(f"Score: {doc['score']:.4f}")
print()
mongodb_client.close()
Pada kode di atas, dua parameter mengendalikan perilaku pencarian:
numCandidatesmenetapkan ukuran kumpulan pencarian awal yang diperiksa MongoDB sebelum mengerucut ke hasil akhir. Nilai yang lebih besar meningkatkan recall namun sedikit lebih lama.limitmenetapkan berapa banyak hasil yang Anda terima. Dalam tutorial ini,limitdisetel ke 3, dan numCandidates disetel ke 150. Titik awal yang umum adalah menyetelnumCandidatesmenjadi 10–15 kali nilai limit Anda.
Sekarang jalankan skrip:
python run_vector_search.py
Anda akan mendapatkan keluaran serupa berikut:
<All keys matched successfully>
Top results for query: 'I need an automated, scalable system for serious information storage.'
Title: MongoDB Atlas
Text: MongoDB Atlas is a fully managed cloud database.
Score: 0.7211
Title: Nomic AI
Text: nomic-embed-text-v1 is a free, open-source embedding model.
Score: 0.6818
Title: Vector Search
Text: Vector search finds results based on semantic meaning.
Score: 0.6642
"MongoDB Atlas" mendapatkan skor tertinggi meskipun kueri "I need an automated, scalable system for serious information storage" tidak berbagi kata dengan "MongoDB Atlas is a fully managed cloud database." Pencarian mengembalikannya karena vektornya berdekatan secara makna: "...sistem terotomasi dan dapat diskalakan untuk penyimpanan informasi" dipetakan secara semantik ke "...basis data cloud yang sepenuhnya dikelola". Pencarian teks untuk kueri yang sama akan mengembalikan nol hasil, karena tidak ada kata persis dari kueri yang muncul di dokumen mana pun. Itulah pencarian semantik bekerja sebagaimana mestinya.
Hal-Hal Penting
- MongoDB Vector Search berjalan pada klaster M0 tingkat Gratis. Tidak memerlukan paket berbayar.
- Anda harus menggunakan model embedding yang sama untuk menghasilkan embedding yang disimpan dan menghasilkan vektor kueri. Mencampur model menghasilkan hasil yang tidak bermakna.
- Nilai
numDimensionsdalam definisi indeks Anda harus persis sama dengan ukuran keluaran model embedding Anda.nomic-embed-text-v1selalu menghasilkan 768 dimensi. input_type="document"mengoptimalkan embedding untuk penyimpanan.input_type="query"mengoptimalkannya untuk pengambilan. Gunakan tipe yang benar pada setiap tahap.numCandidatesmengontrol ukuran jaring pencarian yang ditebarkan MongoDB sebelum mengerucut ke hasil akhirlimit. Nilai yang lebih besar meningkatkan recall dengan konsekuensi waktu kueri.vectorSearchScoremengurutkan hasil berdasarkan kemiripan semantik. Hasil tidak perlu memuat kata-kata persis dari kueri. Rentang skor bervariasi menurut model dan dataset, tetapi batas berikut berguna sebagai titik awal untuknomic-embed-text-v1dengan cosine similarity:- 0,9 ke atas: Makna hampir identik. Dokumen dan kueri secara semantik hampir sama.
- 0,7 hingga 0,9: Relevansi kuat. Dokumen jelas berkaitan dengan maksud kueri.
- 0,5 hingga 0,7: Relevansi sedang. Dokumen terkait topik namun menggunakan bingkai atau konteks berbeda.
- Di bawah 0,5: Relevansi lemah. Keterkaitan longgar dan hasilnya mungkin tidak berguna.
Anda dapat menemukan semua contoh kode untuk tutorial ini di repositori GitHub.
Bacaan Lanjutan
- Gambaran umum MongoDB Vector Search membahas kapabilitas lengkap MongoDB Vector Search, termasuk pemfilteran dan kuantisasi.
- Cara melakukan pencarian hibrida menunjukkan cara menggabungkan pencarian vektor dan pencarian teks penuh dalam satu kueri.
- Cara Membuat Vector Embeddings menjelaskan cara menghasilkan vektor embedding untuk data teks di koleksi Anda menggunakan model embedding dari Voyage AI, OpenAI, dan penyedia model sumber terbuka lainnya.
- Retrieval-Augmented Generation (RAG) dengan MongoDB menunjukkan cara menggunakan pencarian semantik sebagai lapisan pengambilan dalam aplikasi retrieval-augmented generation.
FAQ
Apakah saya memerlukan paket Atlas berbayar untuk mengimplementasikan pencarian semantik?
Tidak. Keempat langkah dalam tutorial ini berjalan pada klaster M0 tingkat Gratis, yang gratis.
Apa yang terjadi jika saya menggunakan model embedding yang berbeda untuk kueri dibandingkan dengan dokumen yang saya simpan?
Hasil Anda tidak akan bermakna. Vektor tidak dapat dibandingkan karena model yang berbeda memetakan teks ke ruang numerik yang berbeda. Selalu gunakan model yang sama untuk pengindeksan dan pengkuerian.
Apa perbedaan antara `numCandidates` dan `limit`?
numCandidates adalah jumlah vektor yang diperiksa MongoDB selama pencarian. limit adalah jumlah hasil teratas yang dikembalikan kepada Anda. Nilai numCandidates yang lebih tinggi meningkatkan kualitas hasil dengan konsekuensi kueri sedikit lebih lambat. Titik awal umum adalah menyetel numCandidates menjadi 10 hingga 15 kali limit Anda.
Bisakah saya menggunakan pencarian semantik dan pencarian teks bersama-sama?
Ya. MongoDB mendukung pencarian hibrida, yang menggabungkan $vectorSearch dan $search dalam satu pipeline.
Apakah pencarian semantik berfungsi untuk bahasa selain bahasa Inggris?
Tergantung pada model embedding Anda. nomic-embed-text-v1 dilatih terutama pada teks berbahasa Inggris. Untuk kasus penggunaan multibahasa, pilih model embedding multibahasa yang dilatih pada bahasa-bahasa yang terdapat dalam data Anda.
