Pasangan Metrik dan Model

Menggunakan jarak yang menjadi dasar pelatihan model embedding sebagai metrik pencarian.

Daftar isi

Metrik pencarian bukan pilihan yang bebas diganti-ganti. Model embedding, yaitu model yang mengubah teks atau objek menjadi vektor, dilatih dengan satu fungsi jarak atau skor tertentu, dan fungsi itulah yang menjadi pasangan alami pencariannya. Topik ini membahas alasan kesesuaian tersebut, memperlihatkan bagaimana peringkat menyimpang ketika metrik salah pasang, dan memetakan nama metrik pada pgvector, Qdrant, dan Milvus.

Metrik mengikuti pelatihan#

Fungsi yang digunakan saat pelatihan menentukan konfigurasi ruang vektor yang dihasilkan model. Banyak model teks umum dioptimalkan dengan cosine similarity, sehingga kemiripan maknanya dinyatakan sebagai kesamaan arah vektor. Sebagian model rekomendasi dioptimalkan dengan inner product, sehingga panjang vektor memang dimaksudkan untuk membawa informasi skor. Menjalankan pencarian dengan metrik yang berbeda dari fungsi pelatihannya berarti membaca ruang vektor dengan aturan yang tidak pernah dipelajari modelnya, dan peringkat yang dihasilkan tidak dapat dianggap benar menurut maksud model.

Konsekuensinya bersifat praktis. Qdrant dan Milvus menetapkan metrik pada saat koleksi atau indeks dibuat, sehingga keputusan diambil sekali dan berlaku untuk seluruh pencarian pada koleksi tersebut. pgvector menetapkannya melalui operator yang dipilih pada setiap query, sehingga keputusan yang sama berulang pada setiap permintaan. Dalam dua bentuk ini, sumber keputusannya sama, yaitu fungsi pelatihan model embedding yang digunakan.

Kesesuaian ini juga menjadi cara membaca dokumentasi model. Apabila halaman model menyebutkan fungsi jarak atau skor yang dioptimalkan, nilai itu digunakan langsung sebagai metrik pencarian. Apabila dokumentasi tidak menyebutkannya, pencarian dengan cosine similarity pada vektor ternormalisasi menjadi titik awal yang aman dan mudah diuji.

Ketidaksesuaian juga sulit dideteksi dari tampilan hasilnya. Daftar jawaban tetap muncul, skor tetap terisi, dan tidak ada pesan yang menyela, sehingga satu-satunya pemeriksaan yang dapat diandalkan adalah membandingkan peringkat dengan ukuran yang benar pada sampel kecil. Pemeriksaan kecil semacam ini juga berguna setelah model embedding diganti, karena model baru dapat menggunakan fungsi pelatihan yang berbeda dari model lamanya.

Ketika peringkat saling menyimpang#

Penyimpangan dapat diamati langsung dari hubungan dot product dengan cosine. Dot product sama dengan hasil kali panjang kedua vektor dikalikan cosine sudutnya, sehingga panjang vektor yang lebih besar menaikkan dot product meskipun sudutnya lebih lebar. Penyimpangan ini paling mudah muncul ketika dokumen yang dibandingkan memiliki panjang yang sangat tidak seimbang, misalnya catatan singkat bersanding dengan arsip panjang.

a⋅b=∥a∥ ∥b∥cos⁡(θab) a \cdot b = \lVert a \rVert \, \lVert b \rVert \cos(\theta_{ab})

Skrip berikut menyusun satu vektor query dan tiga vektor dokumen dengan panjang yang berbeda, mengurutkan dokumen dengan dot product, mengurutkannya dengan cosine similarity, lalu mengulangi pengurutan dot product setelah seluruh vektor dinormalisasi.

python simpangan-peringkat.py
import numpy as np

q = np.array([1.0, 0.0])
dokumen = {
    "d1": np.array([0.9, 0.4]),
    "d2": np.array([0.85, 0.1]),
    "d3": np.array([0.7, 0.7]),
}


def cosine(u, v):
    return np.dot(u, v) / (np.linalg.norm(u) * np.linalg.norm(v))


id_dok = list(dokumen)
matriks = np.vstack([dokumen[nama] for nama in id_dok])

skor_dot = matriks @ q
skor_cos = np.array([cosine(q, d) for d in matriks])

print(f"{'dokumen':<9} {'dot':>7}   {'cosine':>7}")
for i, nama in enumerate(id_dok):
    print(f"{nama:<9} {skor_dot[i]:7.4f}   {skor_cos[i]:7.4f}")

urutan_dot = list(np.argsort(-skor_dot))
urutan_cos = list(np.argsort(-skor_cos))
print("peringkat dot    :", ", ".join(id_dok[i] for i in urutan_dot))
print("peringkat cosine :", ", ".join(id_dok[i] for i in urutan_cos))

matriks_n = matriks / np.linalg.norm(matriks, axis=1, keepdims=True)
qn = q / np.linalg.norm(q)
skor_dot_n = matriks_n @ qn
urutan_dot_n = list(np.argsort(-skor_dot_n))
print("peringkat dot ternormalisasi :", ", ".join(id_dok[i] for i in urutan_dot_n))
text Keluaran
dokumen       dot    cosine
d1         0.9000    0.9138
d2         0.8500    0.9932
d3         0.7000    0.7071
peringkat dot    : d1, d2, d3
peringkat cosine : d2, d1, d3
peringkat dot ternormalisasi : d2, d1, d3

Peringkat kedua ukuran tidak sama. Cosine similarity menempatkan d2 pada posisi pertama karena arahnya paling sejajar dengan query, sedangkan dot product menempatkan d1 pada posisi pertama karena vektornya lebih panjang. Setelah seluruh vektor dinormalisasi, peringkat dot product bergeser menjadi sama dengan peringkat cosine similarity karena hasil kali panjang pada pembaginya sudah bernilai satu.

Contoh ini menunjukkan mekanisme penyimpangan, bukan kesalahan salah satu ukuran. Keduanya benar menurut definisinya, tetapi keduanya menjawab pertanyaan yang berbeda. Normalisasi menjadi pilihan yang umum untuk menyelaraskan keduanya, dan banyak sistem menyimpan vektor ternormalisasi agar pencarian inner product menghasilkan peringkat cosine. Pilihan itu tetap ditinjau terhadap modelnya, karena apabila panjang vektor memang sinyal yang dimaksudkan model inner product, normalisasi justru membuang sinyal tersebut.

Nama metrik di beberapa mesin#

Tiga mesin yang digunakan sepanjang jalur ini memberi nama berbeda untuk tiga ukuran yang sama, dan perbedaan itu perlu dikenali sebelum membaca dokumentasi atau menulis query.

MesinCosineL2Inner product
pgvector<=> (cosine distance)<-><#> (inner product negatif)
QdrantCOSINEL2IP
MilvusCOSINEL2IP

Perbedaan penamaan menuntun pada dua perangkap pembacaan hasil. Pertama, operator <=> pada pgvector mengembalikan cosine distance yang bernilai 1 dikurangi cosine similarity, sehingga hasilnya mengikuti konvensi jarak dan diurutkan menaik. Kedua, operator <#> pada pgvector mengembalikan inner product yang ditandai negatif, yaitu skor dikalikan -1, sehingga nilai kolom hasil yang makin besar justru berarti skor inner product yang makin kecil. Qdrant dan Milvus menyimpan konvensi skor pada COSINE dan IP serta konvensi jarak pada L2.

Tabel tersebut juga berguna ketika aplikasi berpindah mesin. Pencarian dengan COSINE pada Qdrant dan pencarian dengan <=> pada pgvector menghasilkan peringkat yang sama selama konvensi arah peringkatnya diterjemahkan dengan benar, sehingga perpindahan antar mesin tidak mengubah urutan jawaban.

Ringkasan#

  • Metrik pencarian mengikuti fungsi pelatihan model embedding, yaitu cosine untuk banyak model teks umum dan inner product untuk sebagian model rekomendasi.
  • Dot product menyertakan panjang vektor melalui hubungan a⋅b=∥a∥ ∥b∥cos⁡(θab)a \cdot b = \lVert a \rVert \, \lVert b \rVert \cos(\theta_{ab}), sehingga peringkatnya dapat menyimpang dari cosine pada vektor berbeda panjang.
  • Normalisasi membuat peringkat dot product dan cosine similarity menjadi sejajar, kecuali panjang vektor memang sinyal yang dimaksudkan model.
  • pgvector menamai operatornya <=>, <->, dan <#>, sedangkan Qdrant dan Milvus menamai COSINE, L2, dan IP.

Referensi#