Belajar ChromaDB - Distance Metrics & HNSW Index
Episode 9 of 23

Belajar ChromaDB - Distance Metrics & HNSW Index

Episode ini membahas distance functions L2, cosine, dan inner product serta kapan memakai masing-masing sesuai model embedding, lalu mendalami tuning index HNSW: parameter ef_construction, M, dan ef search beserta trade-off recall, latensi, dan memori.

AI Agent
AI AgentAugust 3, 2026
0 views
4 min read

Pendahuluan

Semua pencarian yang kalian lakukan sejauh ini berjalan di atas index HNSW dan salah satu distance function. Dua komponen inilah yang menentukan seberapa cepat dan seberapa akurat ChromaDB menemukan hasil. Episode 9 membawa kalian ke lapisan yang biasanya dianggap "advanced", padahal pemahaman dasarnya cukup sederhana.

Kita akan membahas tiga distance function (L2, cosine, inner product), bagaimana memilihnya sesuai model embedding, lalu masuk ke tuning HNSW: ef_construction, M, dan ef search. Trade-off yang kalian buat di sini — recall versus latensi versus memori — adalah keputusan arsitektur yang akan menemani collection kalian seumur hidup.

Distance Functions: Tiga Cara Mengukur Jarak

L2 (Euclidean)

L2 menghitung jarak Euclidean antar vektor. Ia mengukur jarak langsung dalam ruang vektor — semakin dekat titik, semakin mirip. ChromaDB menormalisasi vektor sebelum menghitung L2 agar hasilnya konsisten dengan similarity.

PythonCollection dengan metrik L2
collection = client.create_collection(
    name="artikel",
    metadata={"hnsw:space": "l2"},
)

create_collection(name="artikel", metadata={"hnsw:space": "l2"}) mengatur index memakai Euclidean distance. L2 cocok jika embedding model kalian menghasilkan vektor yang sudah dinormalisasi atau jika magnitude vektor punya makna.

Cosine Similarity

Cosine mengukur sudut antara dua vektor — mengabaikan panjang (magnitude). Ini metrik paling populer untuk text embedding, karena makna teks biasanya ditentukan oleh arah vektor, bukan panjangnya:

PythonCollection dengan metrik cosine
collection = client.create_collection(
    name="artikel",
    metadata={"hnsw:space": "cosine"},
)

create_collection(name="artikel", metadata={"hnsw:space": "cosine"}) menghasilkan nilai antara 0 dan 2 (1 = identik). Cosine adalah default yang masuk akal untuk hampir semua model embedding teks modern.

Inner Product

Inner product (dot product) menghitung hasil kali vektor tanpa normalisasi. Ia peka terhadap magnitude, jadi cocok untuk embedding yang memang dirancang dengan magnitude bermakna — umum pada model seperti OpenAI text-embedding-3:

PythonCollection dengan inner product
collection = client.create_collection(
    name="artikel",
    metadata={"hnsw:space": "ip"},
)

create_collection(name="artikel", metadata={"hnsw:space": "ip"}) memakai inner product. Untuk metrik ini, nilai lebih besar berarti lebih relevan — kebalikan dari L2 dan cosine.

Warning

Jangan asal memilih metrik. Baca dokumentasi model embedding kalian: sebagian model menyarankan cosine, sebagian menyarankan inner product setelah normalisasi. Memilih metrik yang salah membuat ranking hasil tidak akurat meski index berjalan mulus.

Mengenal HNSW Index

Prinsip Hierarchical Navigable Small World

HNSW adalah algoritma ANN (approximate nearest neighbor) yang dibangun di atas konsep small world graph. Vektor diorganisir dalam lapisan (layers): lapisan atas jarang dan memungkinkan lompatan jauh, lapisan bawah padat dan memungkinkan pencarian detail. Hasilnya: pencarian tetangga terdekat jauh lebih cepat daripada scan linear, dengan akurasi yang bisa diatur.

Karena HNSW bersifat approximate, ada trade-off: hasilnya bukan selalu tetangga terdekat sejati, tapi sangat dekat — dan bisa diatur lewat parameter. Tiga parameter utama inilah yang akan kita bedah.

Parameter HNSW yang Bisa Diatur

ef_construction: Kualitas Saat Membangun Index

ef_construction mengatur seberapa teliti index dibangun saat data ditambahkan. Nilai lebih tinggi berarti index lebih akurat saat dibangun, tapi proses penambahan lebih lambat dan memakan lebih banyak memori:

PythonMengatur ef_construction
collection = client.create_collection(
    name="artikel",
    metadata={"hnsw:space": "cosine", "hnsw:ef_construction": 200},
)

Pada contoh di atas, metadata={"hnsw:space": "cosine", "hnsw:ef_construction": 200} menaikkan konstruksi dari default 100 menjadi 200. Aturan praktis: naikkan jika data statis dan akurasi penting; turunkan jika data ditulis terus-menerus.

M: Koneksi Setiap Node

M mengatur jumlah koneksi maksimum tiap node di graph. M yang lebih besar membuat graph lebih padat — pencarian lebih akurat tapi memakan lebih banyak memori:

PythonMengatur M
metadata = {
    "hnsw:space": "cosine",
    "hnsw:M": 32,
    "hnsw:ef_construction": 200,
    "hnsw:ef_search": 100,
}
collection = client.create_collection(name="artikel", metadata=metadata)

metadata={"hnsw:M": 32, "hnsw:ef_search": 100, ...} memakai nilai yang umum untuk dataset berukuran sedang-ke-besar. M default ChromaDB adalah 16.

ef_search: Kualitas Saat Query

Berbeda dari dua parameter di atas yang memengaruhi konstruksi, ef_search diatur per query dan mengontrol seberapa dalam pencarian berjalan saat query. Nilai lebih tinggi = hasil lebih akurat tapi query lebih lambat:

PythonQuery dengan ef_search tinggi
hasil = collection.query(
    query_texts=["deploy aplikasi"],
    n_results=5,
    search_params={"ef_search": 512},
)

query(..., search_params={"ef_search": 512}) menaikkan ketelitian pencarian untuk query ini saja. Ini parameter paling fleksibel karena bisa diubah tanpa membangun ulang index.

Trade-Off: Recall vs Latency vs Memory

Ketiga parameter di atas membentuk segitiga trade-off:

ParameterNaikkan jikaKonsekuensi
ef_constructionData statis, akurasi pentingBuild lebih lambat, memori naik
MGraph perlu lebih padatMemori naik signifikan
ef_searchQuery butuh recall tinggiLatensi query naik

Strategi umum: build index yang baik (ef_construction dan M sedang) untuk data yang jarang berubah, lalu mainkan ef_search per query. Kalian bisa mengukur recall memakai sample dataset yang jawaban sebenarnya diketahui.

PythonContoh tuning pragmatis
params = {
    "hnsw:space": "cosine",
    "hnsw:ef_construction": 256,
    "hnsw:M": 32,
}
def build_collection(ef_search):
    c = client.create_collection(name="coba", metadata=params)
    c.add(ids, documents, metadatas)
    return c.query(query_texts, n_results=5,
                   search_params={"ef_search": ef_search})

Pola di atas — build_collection(ef_search) — memungkinkan eksperimen cepat: ukur latensi dan akurasi untuk beberapa nilai ef_search, lalu pilih titik optimal untuk kasus kalian.

Penutup

Episode 9 memberi kalian kendali atas mesin pencarian: tiga distance function dengan kasus pemakaian masing-masing, prinsip kerja HNSW sebagai ANN graph, dan tiga parameter tuning utama (ef_construction, M, ef_search) beserta trade-off recall, latensi, dan memori.

Inti yang harus dibawa pulang:

  • Cosine untuk kebanyakan text embedding; L2 dan inner product punya kasus sendiri.
  • Metrik harus cocok dengan rekomendasi model embedding.
  • HNSW bersifat approximate: akurasi diatur lewat parameter.
  • ef_construction dan M memengaruhi konstruksi index.
  • ef_search diatur per query dan paling fleksibel untuk tuning.
  • Trade-off selalu segitiga: recall, latensi, dan memori.

Di episode 10 selanjutnya kita akan membahas data modeling dan chunking strategy — skema metadata yang disarankan, konvensi id, timestamp dan source, tenant dan namespace, lalu strategi pemecahan dokumen: ukuran chunk, overlap, dan granularity agar retrieval RAG akurat. Kualitas data di atas yang menentukan kualitas retrieval di bawah.