Basis data vektor (vector database) lahir dari satu masalah konkret, yaitu mencari dokumen yang relevan tanpa bergantung pada kata yang persis sama. Topik ini membahas mengapa pencarian kata kunci gagal ketika pertanyaan dinyatakan dengan kata yang berbeda, bagaimana representasi vektor mengubah setiap kalimat menjadi titik pada ruang berdimensi tinggi, dan bagaimana primitif mencari k tetangga terdekat menjadi inti operasi basis data vektor. Peringkat kemiripan dihitung sendiri dengan NumPy pada bagian akhir, sehingga mekanismenya terlihat dalam angka nyata.
Keterbatasan pencarian kata kunci#
Pencarian kata kunci bekerja dengan mencocokkan kata yang muncul di dalam dokumen dan kata yang muncul di dalam pertanyaan. Metode ini cepat dan tepat selama kedua pihak menggunakan kata yang sama. Masalah muncul ketika satu makna dapat dinyatakan dengan banyak kata, sesuatu yang lazim dalam bahasa alami. Setiap permintaan pencarian disebut query, yakni teks pendek yang menyatakan hal yang dicari, dan query yang dirumuskan ulang sering tidak berbagi satu kata pun dengan dokumen yang dimaksud.
Masalah ini nyata pada aplikasi berbasis LLM (large language model, model bahasa besar). Untuk menjawab pertanyaan pengguna, model bahasa membutuhkan dokumen relevan dari koleksi, karena pengetahuan yang tersimpan di dalam bobot model tidak memuat dokumen internal yang dimaksud. Pola yang menyerahkan pertanyaan ke tahap pencarian lebih dahulu, lalu menempelkan hasil pencarian ke prompt model, dikenal sebagai retrieval-augmented generation (RAG). Apabila tahap pencarian melewatkan dokumen yang tepat, jawaban model ikut meleset meskipun modelnya bermutu tinggi.
Contoh kecilnya sudah terlihat pada arsip dokumentasi internal. Kalimat “menampilkan daftar berkas dalam direktori” tidak berbagi satu kata pun dengan query “melihat isi folder”, padahal keduanya menyangkut hal yang sama. Mesin pencari dapat menambal kesenjangan semacam ini dengan daftar sinonim, tetapi daftar tersebut harus disusun dan dirawat secara manual, dan tidak ada jaminan cara menyatakan makna berikutnya sudah tercakup. Yang dibutuhkan adalah cara mencocokkan makna, bukan kata.
Dokumen sebagai titik#
Jalan keluarnya adalah mengubah teks menjadi angka. Model embedding (embedding model) adalah model yang dilatih untuk menyajikan makna teks dalam bentuk vektor, dan keluarannya disebut embedding, yakni deret bilangan berpanjang tetap dengan satu bilangan untuk setiap dimensi. Panjangnya bervariasi antar model, misalnya 384 dimensi pada all-MiniLM-L6-v2, 1536 dimensi pada text-embedding-3-small, dan 1024 dimensi pada bge-m3 yang bersifat multibahasa.
Yang penting dari penyajian ini adalah sifat geometrisnya. Model dilatih sehingga dua teks yang maknanya dekat menghasilkan vektor yang posisinya berdekatan, sedangkan teks yang berbeda makna terpisah jauh. Dokumen dan query sama-sama berada di ruang yang sama, sehingga pertanyaan “dokumen mana yang mirip dengan query ini” berubah menjadi pertanyaan geometris “titik mana yang dekat dengan titik ini”. Diagram berikut menggambarkan gagasan tersebut pada bidang dua dimensi.
Kemiripan kosinus#
Kedekatan dua vektor diukur dengan beberapa metrik, dan yang paling banyak digunakan untuk teks adalah kemiripan kosinus (cosine similarity). Metrik ini membandingkan arah dua vektor, bukan panjangnya, dengan menghitung kosinus sudut di antara keduanya:
Nilainya 1 apabila kedua vektor searah, mendekati 0 apabila keduanya tegak lurus, dan negatif apabila berlawanan arah. Panjang vektor tidak berpengaruh karena hanya arahnya yang masuk perhitungan. Banyak sistem melaporkan hasilnya sebagai jarak kosinus, yaitu satu dikurangi kemiripan kosinus, sehingga nilai yang lebih kecil berarti lebih mirip. Basis data vektor umumnya menawarkan beberapa metrik sekaligus, dan pilihannya perlu mengikuti model embedding yang digunakan untuk menghasilkan vektor tersebut.
Satu hal yang perlu dibiasakan sejak awal, yaitu skor kemiripan bukan probabilitas. Nilai 0.90 tidak berarti 90 persen benar, melainkan hanya derajat kesesuaian arah antara dua vektor pada ruang model tersebut. Skor dari model yang berbeda pun tidak dapat dibandingkan langsung karena tiap model menata ruang maknanya sendiri.
Primitif k tetangga terdekat#
Dengan dokumen sebagai titik dan query sebagai titik, seluruh operasi pencarian berangkat dari satu primitif, yaitu temukan k vektor yang paling mirip dengan vektor query. Pola ini disebut pencarian k-nearest neighbors (kNN), yakni pencarian k objek dengan kedekatan terbaik, dan nilai k kecil seperti 5 atau 10 dipilih sesuai kebutuhan aplikasi. Hasilnya berupa daftar terperingkat dari yang paling mirip, biasanya disertai skor kemiripannya.
Cara paling lugas adalah membandingkan query dengan setiap vektor di koleksi, satu per satu. Biaya perhitungannya bertumbuh linear terhadap jumlah vektor, dinyatakan sebagai , sehingga menggandakan jumlah dokumen juga menggandakan jumlah operasi. Pada koleksi ratusan ribu hingga jutaan vektor berdimensi 384 atau 1536, pemindaian penuh menjadi lambat untuk aplikasi yang responsif. Basis data vektor mengatasi hal ini dengan indeks approximate nearest neighbor (ANN), yaitu struktur data yang mempersempit kandidat yang diperiksa sehingga jawaban diperoleh jauh lebih cepat dengan risiko melewatkan sedikit kandidat terbaik. Keseimbangan antara kecepatan dan ketepatan inilah wilayah kerja utama basis data vektor.
Simulasi peringkat dengan NumPy#
Contoh berikut mereplikasi primitif tersebut dengan angka yang dapat dihitung ulang. Dua kalimat arsip dokumentasi dan tiga query diwakili vektor dua dimensi yang ditulis sebagai konstanta eksplisit, bukan keluaran model embedding. Skrip menghitung kemiripan kosinus antara setiap query dan kedua dokumen, mengurutkan hasilnya, lalu mencetak peringkat. Peringkat ini dapat direproduksi dengan menjalankan skrip yang sama di komputer mana pun, karena seluruh vektornya konstanta eksplisit tanpa pemanggilan model dan tanpa angka acak.
import numpy as np
# dua kalimat arsip dokumentasi dan tiga query, diwakili vektor 2D buatan
# (konstanta eksplisit untuk latihan, bukan keluaran model embedding)
dokumen = {
"A": (np.array([0.9, 0.1]), "menampilkan daftar berkas dalam direktori"),
"B": (np.array([0.2, 0.9]), "menghapus arsip kedaluwarsa"),
}
query = [
("melihat isi folder", np.array([0.80, 0.20])),
("daftar berkas dalam satu folder", np.array([0.85, 0.15])),
("pembersihan arsip lama", np.array([0.15, 0.85])),
]
def kemiripan_cosine(a, b):
return float(np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b)))
for d, (v, teks) in dokumen.items():
print(f"dokumen {d} : {teks}")
for teks, v in query:
print(f'query : "{teks}"')
skor = [(kemiripan_cosine(v, dv), d) for d, (dv, _) in dokumen.items()]
for s, d in sorted(skor, reverse=True):
print(f" {s:.4f} dokumen {d}")dokumen A : menampilkan daftar berkas dalam direktori
dokumen B : menghapus arsip kedaluwarsa
query : "melihat isi folder"
0.9910 dokumen A
0.4472 dokumen B
query : "daftar berkas dalam satu folder"
0.9980 dokumen A
0.3833 dokumen B
query : "pembersihan arsip lama"
0.9990 dokumen B
0.2815 dokumen APola pada keluaran sesuai dengan yang diharapkan. Query “melihat isi folder” tidak berbagi satu kata pun dengan dokumen A, tetapi tetap menempati peringkat pertama karena arah vektornya berdekatan dengan arah vektor dokumen A. Query “daftar berkas dalam satu folder” berbagi kata dengan dokumen A sehingga pencarian kata kunci pun akan berhasil, sedangkan “pembersihan arsip lama” berpindah ke dokumen B. Seluruh perbedaan skor dihasilkan hanya dari enam operasi kosinus, dan setiap dokumen tambahan menambah satu operasi kosinus untuk setiap query, sesuai perilaku yang dijelaskan sebelumnya.
Kesalahan umum#
Membandingkan embedding dari model yang berbeda. Dua vektor yang dihasilkan model berbeda tidak dapat dibandingkan secara bermakna meskipun panjangnya kebetulan sama, karena setiap model menata ruang maknanya sendiri. Perbaikannya, gunakan satu model untuk satu koleksi dan catat nama model tersebut bersama data.
Menyimpan vektor tanpa identitas dokumennya. Vektor yang disimpan tanpa rujukan ke teks asalnya tidak dapat ditampilkan kembali setelah ditemukan, sehingga hasil pencarian tidak berguna. Perbaikannya, simpan kunci atau tautan ke dokumen asal bersama setiap vektor, misalnya sebagai kolom terpisah pada tabel yang sama.
Menilai mutu pencarian dari satu contoh. Satu query yang berhasil tidak membuktikan apa-apa tentang koleksi yang besar, karena keberhasilan tunggal dapat pula berasal dari kebetulan. Penyebabnya, penilaian dilakukan sebelum kumpulan query uji disiapkan. Perbaikannya, siapkan sekumpulan query dengan jawaban yang sudah diketahui, lalu ukur seberapa sering dokumen yang tepat muncul di peringkat atas.
Ringkasan#
- Pencarian kata kunci mencocokkan kata, sehingga gagal pada parafrase yang tidak berbagi kata dengan dokumen.
- Model embedding mengubah teks menjadi vektor berpanjang tetap, dan kemiripan makna menjadi kedekatan geometris antar titik.
- Kemiripan kosinus membandingkan arah dua vektor dan tidak dipengaruhi panjang vektornya.
- Primitif inti adalah pencarian k tetangga terdekat yang pemindaian penuhnya berbiaya , sehingga indeks ANN dibutuhkan pada koleksi besar.
Referensi#
- ANN-Benchmarks — GitHub — Benchmark publik untuk membandingkan algoritma k nearest neighbor pada berbagai dataset dan metrik.
- Pencarian — Qdrant — Penjelasan tahapan pencarian kemiripan vektor beserta parameter yang memengaruhinya.