FAISS: Pencarian Kemiripan Vektor yang Efisien dalam Skala Besar
FAISS (Facebook AI Similarity Search) adalah library C++ dengan binding Python untuk mengindeks dan mencari vektor dense. Ketika aplikasi Anda perlu menemukan tetangga terdekat dari sebuah embedding di antara jutaan atau miliaran vektor, FAISS memberi kendali rinci atas trade-off kecepatan, memori, dan recall yang biasanya disembunyikan vector database terkelola di balik API. Tutorial ini berfokus pada library-nya sendiri: tipe-tipe index, cara kerja internalnya, dan cara menggunakannya di produksi.
Untuk siapa tutorial ini
Ini bukan sekadar panduan generik "membangun mesin pencari semantik". Kami berasumsi Anda sudah memahami embedding dan cosine similarity secara umum. Sebagai gantinya kita membahas mendalam ragam index FAISS: index flat eksak, index inverted-file (IVF), product quantization (PQ), graph HNSW, pemetaan ID, persistensi, dan offload ke GPU. Di akhir tutorial Anda akan mampu memilih index yang tepat sesuai ukuran dataset dan anggaran latensi, serta menyetelnya secara sengaja, bukan menebak-nebak.
Apa itu FAISS dan apa yang bukan
FAISS menyelesaikan satu masalah dengan sangat baik: diberikan sebuah vektor query, kembalikan k vektor paling mirip dari koleksi, menggunakan jarak L2 (Euclidean) atau inner product. Ini adalah library, bukan service. Tidak ada lapisan jaringan, autentikasi, mesin filter metadata, maupun persistensi bawaan selain membaca dan menulis satu file index.
Minimalisme itulah intinya. FAISS memungkinkan Anda:
- Menentukan dengan tepat cara vektor disimpan (presisi penuh, terkuantisasi, di disk).
- Menukar recall demi kecepatan dengan mengubah satu parameter saat query.
- Menjalankan index yang sama di CPU atau GPU dengan satu baris pemindahan.
- Menanamkan index langsung di dalam proses Anda sendiri, menghindari round-trip jaringan.
Kapan memakai FAISS vs vector database terkelola
Gunakan FAISS ketika:
- Anda ingin satu library tertanam di service tanpa infrastruktur tambahan.
- Anda butuh kendali presisi atas struktur index dan jejak memori.
- Vektor Anda sebagian besar statis, atau index dibangun ulang secara terjadwal.
- Anda melakukan riset atau komputasi kemiripan secara batch.
Gunakan vector database terkelola (Qdrant, Milvus, Weaviate, pgvector, Pinecone) ketika:
- Anda butuh filter metadata yang kaya dikombinasikan dengan pencarian vektor.
- Anda memerlukan insert, update, dan delete yang sering dengan jaminan durabilitas.
- Anda ingin penskalaan horizontal, replikasi, dan API HTTP/gRPC langsung pakai.
- Mengoperasikan service stateful dapat diterima dan Anda tidak ingin membangunnya sendiri.
Banyak database tersebut sebenarnya memakai algoritma mirip FAISS (IVF, HNSW, PQ) di belakang layar, jadi memahami FAISS membuat Anda lebih mahir menyetelnya juga.
Instalasi
FAISS hadir sebagai dua paket yang saling eksklusif. Pasang persis satu.
# Build CPU-only (jalan di mana saja, default yang baik)
pip install faiss-cpu
Build GPU (butuh GPU berkemampuan CUDA dan runtime CUDA yang cocok)
pip install faiss-gpu
Untuk contoh-contoh ini kita juga memakai sentence-transformers untuk menghasilkan embedding dan numpy untuk penanganan array.
pip install sentence-transformers numpy
Pemeriksaan cepat:
import faiss
import numpy as np
print("Versi FAISS:", faiss.version)
print("Jumlah GPU yang terlihat FAISS:", faiss.getnumgpus())
Jika getnumgpus() mengembalikan 0, Anda memakai build CPU, yang baik-baik saja untuk semuanya kecuali bagian GPU di akhir.
Membuat embedding
FAISS bekerja dengan array NumPy float32 berbentuk (nvektor, dimensi). FAISS tidak menghasilkan embedding sendiri; Anda membawa milik Anda. Di sini kita memakai model sentence-transformer kecil.
from sentencetransformers import SentenceTransformer
import numpy as np
model = SentenceTransformer("all-MiniLM-L6-v2") # output 384 dimensi
documents = [
"FAISS melakukan pencarian nearest-neighbour atas vektor dense.",
"Product quantization memampatkan vektor untuk menghemat memori.",
"Index inverted file mempartisi ruang vektor menjadi sel-sel.",
"HNSW membangun graph small-world yang mudah dinavigasi untuk pencarian cepat.",
"Cosine similarity adalah inner product pada vektor yang dinormalisasi.",
"Index GPU dapat mempercepat pencarian hingga satu orde besaran.",
]
embeddings = model.encode(documents, converttonumpy=True)
embeddings = embeddings.astype("float32") # FAISS mewajibkan float32
print(embeddings.shape) # (6, 384)
print(embeddings.dtype) # float32
Dua aturan yang perlu Anda hayati sejak awal:
float32 yang kontigu. Jika Anda memberi float64 akan muncul error.d yang Anda berikan saat membuat index harus sama persis dengan lebar embedding.d = embeddings.shape[1] # 384
Pencarian eksak: IndexFlatL2 dan IndexFlatIP
Index flat menyimpan setiap vektor apa adanya dan membandingkan query terhadap semuanya. Mereka memberi recall 100% (bersifat menyeluruh) tetapi penskalaannya linear terhadap jumlah vektor. Ini pilihan tepat untuk koleksi kecil dan menjadi baseline untuk mengukur index aproksimatif.
IndexFlatL2 memakai jarak Euclidean kuadrat. Jarak lebih kecil berarti lebih mirip.
import faiss
index = faiss.IndexFlatL2(d)
print("Sudah dilatih?", index.istrained) # True - index flat tak perlu pelatihan
index.add(embeddings) # simpan semua vektor
print("Vektor di index:", index.ntotal) # 6
Pencarian mengembalikan dua array: D (jarak) dan I (indeks ke dalam index), masing-masing berbentuk (nquery, k).
query = model.encode(["bagaimana memampatkan vektor"], converttonumpy=True).astype("float32")
k = 3
D, I = index.search(query, k)
print("Jarak:", D[0]) # menaik untuk L2
print("Indeks:", I[0])
for rank, idx in enumerate(I[0]):
print(rank, documents[idx])
IndexFlatIP memakai inner product (dot product) sebagai gantinya. Nilai lebih besar berarti lebih mirip, sehingga hasil dikembalikan berurutan menurun.
indexip = faiss.IndexFlatIP(d)
index
ip.add(embeddings)
D, I = indexip.search(query, k)
print(D[0]) # menurun: lebih tinggi lebih baik
Menormalisasi vektor untuk cosine similarity
Cosine similarity setara dengan inner product dari vektor yang dinormalisasi L2. FAISS tidak punya index cosine khusus, jadi Anda menormalisasi vektor database maupun query lalu memakai IndexFlatIP. Helper faiss.normalizeL2 melakukannya secara in-place.
import numpy as np
emb = embeddings.copy()
faiss.normalizeL2(emb) # normalisasi L2 in-place
cosindex = faiss.IndexFlatIP(d)
cosindex.add(emb)
q = query.copy()
faiss.normalizeL2(q) # normalisasi query dengan cara yang sama
D, I = cosindex.search(q, k)
print("Skor cosine:", D[0]) # kini di [-1, 1], lebih tinggi lebih mirip
Kesalahan umum adalah menormalisasi vektor database tetapi lupa query, yang diam-diam menghasilkan peringkat yang salah. Normalisasi semua hal yang menyentuh index inner-product.
Index factory
Membangun index kompleks secara manual itu bertele-tele. faiss.indexfactory membangunnya dari deskripsi string singkat, yang juga merupakan cara menyimpan resep index di file konfigurasi.
# Setara dengan IndexFlatL2(d)
index = faiss.indexfactory(d, "Flat")
IVF dengan 100 sel, penyimpanan flat, metrik L2
index = faiss.indexfactory(d, "IVF100,Flat")
IVF dengan kompresi PQ: 100 sel, 16 sub-quantizer masing-masing 8 bit
index = faiss.indexfactory(d, "IVF100,PQ16")
HNSW dengan 32 tetangga per node
index = faiss.indexfactory(d, "HNSW32")
Varian cosine similarity: berikan argumen metrik
index = faiss.indexfactory(d, "Flat", faiss.METRICINNERPRODUCT)
String factory adalah cara paling portabel untuk mendeskripsikan index, dan kita akan merujuk baik bentuk factory maupun bentuk kelas eksplisit di bawah ini.
Pencarian aproksimatif dengan IndexIVFFlat
Untuk koleksi besar, pencarian flat menyeluruh terlalu lambat. Index inverted file (IVF) mempartisi ruang vektor menjadi nlist sel Voronoi memakai klastering k-means. Saat query ia hanya mengunjungi nprobe sel terdekat dari query alih-alih semuanya. Inilah tuas inti kecepatan/recall di FAISS.
IVF membutuhkan quantizer (sebuah index flat untuk menetapkan vektor ke sel) dan harus dilatih pada sampel representatif sebelum Anda menambahkan data.
import numpy as np
Bangun dataset sintetis lebih besar agar IVF bermanfaat
np.random.seed(42)
n = 100000
xb = np.random.random((n, d)).astype("float32")
xq = np.random.random((5, d)).astype("float32")
nlist = 256 # jumlah sel Voronoi
quantizer = faiss.IndexFlatL2(d) # menetapkan vektor ke sel
index = faiss.IndexIVFFlat(quantizer, d, nlist, faiss.METRICL2)
print("Dilatih sebelum pelatihan?", index.istrained) # False
index.train(xb) # pelajari sentroid sel (k-means)
print("Dilatih setelah pelatihan?", index.istrained) # True
index.add(xb)
print("Vektor:", index.ntotal)
Secara default IVF memeriksa satu sel saja, yang cepat tetapi kehilangan recall karena tetangga sejati bisa berada di sel tetangga. Naikkan nprobe untuk mencari lebih banyak sel.
index.nprobe = 1
D, I = index.search(xq, k=5) # cepat, recall lebih rendah
index.nprobe = 16
D, I = index.search(xq, k=5) # lebih lambat, recall lebih tinggi
Memilih nlist dan nprobe
nlist: heuristik awal yang umum adalah sekitarsqrt(n)sampai4 sqrt(n). Lebih banyak sel berarti partisi lebih halus dan pemeriksaan per sel lebih cepat, tetapi tiap sel menampung lebih sedikit vektor dan biasanya Anda butuhnprobelebih besar untuk menjaga recall.- Data pelatihan: targetkan minimal
30 nlistsampai256 nlistvektor pelatihan agar k-means menghasilkan sentroid yang stabil. FAISS memberi peringatan jika Anda melatih dengan terlalu sedikit data. nprobe: knob saat query yang paling penting.nprobe = 1memberi kecepatan maksimum dan recall minimum;nprobe = nlistmerosot kembali menjadi pencarian menyeluruh. Sapu nilainya terhadap set ground-truth dan pilih nilai terkecil yang memenuhi target recall Anda.
Mengukur recall
Recall@k adalah proporsi tetangga top-k sejati yang dikembalikan index aproksimatif. Hitung ground truth sekali dengan index flat, lalu bandingkan.
# Ground truth dari pencarian eksak
flat = faiss.IndexFlatL2(d)
flat.add(xb)
, gt = flat.search(xq, 5)
def recallatk(approxI, truthI, k=5):
hits = 0
for arow, trow in zip(approxI, truthI):
hits += len(set(arow[:k]) & set(trow[:k]))
return hits / (len(truthI) k)
for nprobe in (1, 4, 16, 64):
index.nprobe = nprobe
, I = index.search(xq, 5)
print(f"nprobe={nprobe:3d} recall@5={recallatk(I, gt):.3f}")
Sapuan ini adalah alur kerja yang sebaiknya Anda jalankan pada setiap dataset: ia mengubah trade-off abstrak "kecepatan vs recall" menjadi angka konkret untuk data Anda.
Kompresi memori dengan IndexIVFPQ
Penyimpanan flat menjaga setiap vektor pada presisi penuh: n d 4 byte. Satu juta vektor 384-dim memakan sekitar 1,5 GB. Product quantization (PQ) memampatkan tiap vektor dengan membaginya menjadi m sub-vektor dan mengkodekan tiap sub-vektor dengan nbits bit, menggantinya dengan entri terdekat dalam codebook yang dipelajari.
Sebuah vektor lalu hanya menempati m * nbits / 8 byte terlepas dari d. Dengan m = 16 dan nbits = 8, tiap vektor berukuran 16 byte, kira-kira reduksi 24x dibanding float32 untuk 384 dimensi.
m = 16 # jumlah sub-quantizer; d harus habis dibagi m
nbits = 8 # bit per kode sub-quantizer (8 => codebook 256 entri)
nlist = 256
quantizer = faiss.IndexFlatL2(d)
index = faiss.IndexIVFPQ(quantizer, d, nlist, m, nbits)
index.train(xb) # melatih baik sentroid IVF maupun codebook PQ
index.add(xb)
index.nprobe = 16
D, I = index.search(xq, 5)
Biaya PQ adalah galat aproksimasi: jarak dihitung terhadap vektor yang direkonstruksi (lossy), jadi recall turun dibanding IVFFlat pada nprobe yang sama. Batasan dan penyetelan:
dharus habis dibagim. Untukd = 384, nilaimyang valid mencakup 8, 12, 16, 24, 32, 48, 96.mlebih besar mempertahankan lebih banyak detail (recall lebih tinggi) tetapi memakai lebih banyak memori dan lebih lambat.nbits = 8adalah yang paling umum; ia menjaga lookup codebook ramah-cache.- Untuk dorongan akurasi tambahan Anda dapat me-rerank kandidat PQ dengan jarak eksak memakai
IndexIVFPQRatau pembungkusIndexRefineFlat.
Gunakan IVFPQ ketika index presisi penuh tidak muat di RAM. Jika muat, IVFFlat akan memberi recall lebih baik untuk biaya query yang sama.
ANN berbasis graph dengan IndexHNSWFlat
HNSW (Hierarchical Navigable Small World) membangun graph berlapis di mana tiap vektor terhubung ke tetangga terdekatnya. Pencarian dimulai di lapisan atas dan berjalan secara greedy menuju query, menuruni lapisan saat konvergen. HNSW biasanya memberi recall-pada-latensi-rendah terbaik di antara index CPU dan, tidak seperti IVF, tidak butuh langkah pelatihan terpisah.
M = 32 # tetangga per node di lapisan dasar
index = faiss.IndexHNSWFlat(d, M) # juga: indexfactory(d, "HNSW32")
index.hnsw.efConstruction = 80 # lebar pencarian saat build (kualitas graph)
index.add(xb) # proses build adalah bagian yang mahal
index.hnsw.efSearch = 64 # lebar pencarian saat query (recall vs kecepatan)
D, I = index.search(xq, 5)
Tiga parameter yang penting:
M: jumlah edge per node.Mlebih tinggi memperbaiki recall dan mempercepat pencarian tetapi menambah memori (tiap vektor menyimpanMtautan) dan waktu build. Nilai khas 16 sampai 64.efConstruction: seberapa teliti graph dieksplorasi saat menyisipkan. Nilai lebih tinggi membangun graph lebih baik dengan biaya waktu indeks. Hanya memengaruhi build, bukan query.efSearch: berapa banyak kandidat yang dieksplorasi query. Ini adalah knob recall runtime HNSW, analog dengannprobepada IVF. Naikkan untuk recall lebih baik, turunkan untuk kecepatan.
HNSW menukar memori demi kualitas: ia menyimpan vektor penuh ditambah graph, sehingga memakai RAM lebih banyak daripada IVFPQ. Kekuatannya adalah tanpa kebutuhan pelatihan, recall sangat baik, dan latensi stabil. Kelemahannya adalah penggunaan memori tinggi dan konstruksi yang lambat serta append-only (penghapusan merepotkan).
Memetakan kembali ke ID Anda sendiri dengan IndexIDMap
Secara default FAISS mengembalikan posisi sekuensial (0, 1, 2, ...) di array I. Sistem nyata punya ID sendiri: primary key database, UUID dokumen, dan sebagainya. IndexIDMap membungkus index apa pun dan memungkinkan Anda menyuplai ID integer 64-bit dengan addwithids, yang lalu dikembalikan oleh search.
base = faiss.IndexFlatL2(d)
index = faiss.IndexIDMap(base)
ids = np.array([1001, 1002, 1003, 1004, 1005, 1006], dtype="int64")
small = embeddings[:6]
index.addwithids(small, ids)
D, I = index.search(query, 3)
print("ID Anda:", I[0]) # mis. [1002, 1004, 1001]
Ada dua varian:
IndexIDMapmenyimpan pemetaan di tabel sampingan. Ia mendukungremoveids, cara standar menghapus vektor di FAISS.IndexIDMap2tambahan mendukungreconstruct(id)untuk mengambil vektor asli berdasarkan ID-nya.
# Menghapus vektor berdasarkan ID
selector = faiss.IDSelectorArray(np.array([1002], dtype="int64"))
index.removeids(selector)
print("Setelah penghapusan:", index.ntotal)
Perhatikan bahwa index IVF menerima ID melalui addwithids secara langsung tanpa pembungkus eksplisit, tetapi IndexIDMap adalah mekanisme umum untuk keluarga flat dan HNSW.
Menyimpan dan memuat index
FAISS menserialisasi seluruh index, termasuk sentroid terlatih dan codebook, ke satu file. Tidak ada langkah "simpan model" terpisah; index itulah modelnya.
# Persistensikan
faiss.writeindex(index, "documents.faiss")
Pulihkan di proses atau run lain
index = faiss.readindex("documents.faiss")
print("Vektor yang dimuat ulang:", index.ntotal)
File ini mandiri tetapi tidak menyimpan teks mentah atau metadata Anda. Pola praktisnya adalah menyimpan index FAISS berdampingan dengan store terpisah (tabel SQLite, file Parquet, atau sidecar JSON) yang dikunci dengan ID integer yang sama yang Anda berikan ke addwithids.
Akselerasi GPU
Jika Anda memasang faiss-gpu, Anda dapat memindahkan index CPU ke GPU untuk lonjakan throughput besar pada batch besar. Bangun dan latih di CPU, lalu transfer.
res = faiss.StandardGpuResources() # mengelola memori GPU
cpuindex = faiss.IndexFlatL2(d)
cpuindex.add(xb)
gpuindex = faiss.indexcputogpu(res, 0, cpuindex) # device 0
D, I = gpuindex.search(xq, 5)
Kembalikan hasil ke CPU untuk disimpan (writeindex butuh index CPU)
backoncpu = faiss.indexgputocpu(gpuindex)
faiss.writeindex(backoncpu, "documents.faiss")
Untuk menyebar index ke setiap GPU yang terlihat, gunakan indexcputoallgpus.
gpuindex = faiss.indexcputoallgpus(cpuindex)
Catatan GPU yang perlu diingat:
- Tidak setiap tipe index didukung GPU;
IndexFlat,IndexIVFFlat, danIndexIVFPQdidukung. HNSW hanya CPU. - GPU bersinar pada batch query besar dan dataset besar. Untuk segelintir query, overhead transfer CPU/GPU bisa mendominasi.
- Anda harus memindahkan index GPU kembali ke CPU dengan
indexgputocpusebelum memanggilwriteindex.
Contoh menyeluruh: index dokumen yang dapat dicari
Bagian ini merangkai semuanya menjadi komponen kecil yang dapat dipakai ulang: meng-embed dokumen, membangun index IVFFlat dengan ID eksplisit, mempersistensikannya, memuatnya ulang, dan melakukan query. Kita menjaga teks dokumen dalam dictionary paralel yang dikunci dengan ID yang sama.
import faiss
import numpy as np
from sentencetransformers import SentenceTransformer
class DocumentIndex:
def init(self, modelname="all-MiniLM-L6-v2", nlist=64):
self.model = SentenceTransformer(modelname)
self.d = self.model.getsentenceembeddingdimension()
self.nlist = nlist
self.index = None
self.docs = {} # id -> teks asli
def embed(self, texts):
emb = self.model.encode(texts, converttonumpy=True).astype("float32")
faiss.normalizeL2(emb) # cosine similarity via inner product
return emb
def build(self, texts, ids):
emb = self.embed(texts)
quantizer = faiss.IndexFlatIP(self.d)
ivf = faiss.IndexIVFFlat(quantizer, self.d, self.nlist,
faiss.METRICINNERPRODUCT)
ivf.train(emb)
base = faiss.IndexIDMap(ivf)
base.addwithids(emb, np.array(ids, dtype="int64"))
self.index = base
self.docs = {int(i): t for i, t in zip(ids, texts)}
def search(self, query, k=5, nprobe=8):
# nprobe berada pada index IVF yang dibungkus
faiss.extractindexivf(self.index).nprobe = nprobe
q = self.embed([query])
D, I = self.index.search(q, k)
return [
{"id": int(i), "score": float(s), "text": self.docs.get(int(i), "")}
for s, i in zip(D[0], I[0]) if i != -1
]
def save(self, path):
faiss.writeindex(self.index, path)
def load(self, path):
self.index = faiss.readindex(path)
if name == "main":
corpus = [
"FAISS mempartisi vektor menjadi sel dengan index inverted file.",
"Product quantization memampatkan embedding untuk memangkas penggunaan memori.",
"HNSW adalah algoritma nearest-neighbour aproksimatif berbasis graph.",
"Normalisasi vektor untuk menghitung cosine similarity dengan inner product.",
"Pindahkan index ke GPU dengan indexcputogpu untuk throughput.",
"Recall diukur terhadap ground truth dari index flat eksak.",
]
ids = list(range(1, len(corpus) + 1))
store = DocumentIndex(nlist=4) # nlist kecil untuk korpus demo kecil
store.build(corpus, ids)
store.save("documents.faiss")
for hit in store.search("bagaimana mengurangi memori vektor saya", k=3):
print(f"{hit['score']:.3f} {hit['text']}")
Helper extractindexivf menjangkau melalui pembungkus IndexIDMap untuk menyetel nprobe pada index IVF di bawahnya, sebuah detail yang sering menjebak pengguna baru. Untuk korpus demo kecil kita memakai nlist kecil; pada data nyata ikuti heuristik sqrt(n) dan setel nprobe dengan sapuan recall yang ditunjukkan sebelumnya.
Membandingkan tipe index
Tabel di bawah merangkum trade-off praktisnya. "Recall" mengasumsikan parameter disetel secara wajar; semua index aproksimatif dapat mencapai recall tinggi jika Anda mengeluarkan cukup waktu query.
| Index | Tipe pencarian | Kecepatan | Memori | Recall | Pelatihan | Paling cocok untuk |
| --------------- | -------------- | --------------- | -------------- | ------------- | --------- | ------------------------------------------- |
| IndexFlatL2/IP | Eksak | Lambat (O(n)) | Tinggi | 100% | Tidak | Set kecil, ground truth, baseline |
| IndexIVFFlat | Aproksimatif | Cepat | Tinggi | Tinggi | Ya | Set besar yang muat di RAM |
| IndexIVFPQ | Aproksimatif | Cepat | Sangat rendah | Sedang | Ya | Set sangat besar, terbatas memori |
| IndexHNSWFlat | Aproksimatif | Sangat cepat | Sangat tinggi | Sangat tinggi | Tidak | Query latensi rendah, data statis |
Aturan praktis menurut ukuran dataset:
- Di bawah ~10rb vektor: gunakan
Flat. Pencarian eksak cukup cepat dan kesederhanaannya sepadan. - 10rb sampai beberapa juta, RAM melimpah:
IVFFlatatauHNSW. HNSW untuk latensi terendah, IVFFlat untuk penyetelan lebih sederhana dan dukungan GPU. - Puluhan juta ke atas, atau memori ketat:
IVFPQ, opsional dengan langkah refine untuk akurasi.
Praktik terbaik
- Selalu cast embedding ke
float32kontigu sebelum menambahkannya. Dtype yang tidak cocok adalah error paling umum. - Bangun set ground-truth dengan index flat dan ukur recall@k setiap kali Anda mengganti tipe index atau mengubah parameter. Jangan menyetel secara buta.
- Perlakukan
nprobe(IVF) danefSearch(HNSW) sebagai tuas runtime. Anda bisa mengubahnya per query untuk menyeimbangkan latensi dan kualitas tanpa membangun ulang. - Latih IVF dan PQ pada data dari distribusi yang sama dengan vektor yang akan Anda tambahkan. Melatih pada distribusi berbeda diam-diam merusak recall.
- Simpan teks dan metadata Anda di store terpisah yang dikunci dengan ID integer yang sama yang Anda berikan ke
addwithids. FAISS hanya menyimpan vektor. - Gunakan
IndexIDMap2jika Anda perlu merekonstruksi vektor asli atau menghapus berdasarkan ID. - Untuk penghapusan dan pembaruan yang sering, ingat FAISS paling nyaman sebagai index yang sebagian besar statis. Jika beban kerja Anda berat-tulis, vector database terkelola mungkin lebih melayani Anda.
- Sematkan versi FAISS Anda. Format file index stabil lintas versi minor, tetapi verifikasi sebelum meng-upgrade di produksi.
Kesimpulan dan poin penting
FAISS adalah library berfokus dan berkinerja tinggi untuk satu tugas: pencarian nearest-neighbour atas vektor dense. Nilainya adalah kendali eksplisit yang ia beri atas segitiga kecepatan, memori, dan recall.
- Index flat (
IndexFlatL2,IndexFlatIP) bersifat eksak, sederhana, dan menjadi baseline yang tepat. Normalisasi vektor dan pakai inner product untuk cosine similarity. - IVF (
IndexIVFFlat) mempercepat pencarian dengan hanya memeriksa sel terdekat;nlistmembentuk partisi dannprobemenukar kecepatan dengan recall saat query. - PQ (
IndexIVFPQ) memampatkan vektor secara drastis untuk deployment terbatas memori, dengan biaya sebagian recall. - HNSW (
IndexHNSWFlat) memberi recall sangat baik pada latensi rendah tanpa pelatihan, ditukar dengan memori tinggi dan konstruksi append-only. IndexIDMapmemetakan hasil ke ID Anda sendiri dan memungkinkan penghapusan;writeindex/readindexmempersistensikan seluruh index;indexcputo_gpumengoffload pencarian ke GPU.
Pilih index Anda dari ukuran dataset dan anggaran latensi, ukur recall terhadap baseline eksak, dan setel tuas runtime secara sengaja. Loop disiplin itulah yang membedakan deployment FAISS yang menskala dari yang sekadar berjalan.