Tutorial FAISS: Pencarian Kemiripan Vektor dalam Skala Besar

# 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 apl...

By Ruby Abdullah · · tutorial
FAISSVector SearchSimilarity SearchEmbeddingsANNPython

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:

  • FAISS mengharapkan array float32 yang kontigu. Jika Anda memberi float64 akan muncul error.
  • Dimensi 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)
    

    indexip.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 sekitar sqrt(n) sampai 4 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 butuh nprobe lebih besar untuk menjaga recall.
    • Data pelatihan: targetkan minimal 30 nlist sampai 256 nlist vektor 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 = 1 memberi kecepatan maksimum dan recall minimum; nprobe = nlist merosot 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:

    • d harus habis dibagi m. Untuk d = 384, nilai m yang valid mencakup 8, 12, 16, 24, 32, 48, 96.
    • m lebih besar mempertahankan lebih banyak detail (recall lebih tinggi) tetapi memakai lebih banyak memori dan lebih lambat.
    • nbits = 8 adalah yang paling umum; ia menjaga lookup codebook ramah-cache.
    • Untuk dorongan akurasi tambahan Anda dapat me-rerank kandidat PQ dengan jarak eksak memakai IndexIVFPQR atau pembungkus IndexRefineFlat.

    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. M lebih tinggi memperbaiki recall dan mempercepat pencarian tetapi menambah memori (tiap vektor menyimpan M tautan) 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 dengan nprobe pada 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:

    • IndexIDMap menyimpan pemetaan di tabel sampingan. Ia mendukung removeids, cara standar menghapus vektor di FAISS.
    • IndexIDMap2 tambahan mendukung reconstruct(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, dan IndexIVFPQ didukung. 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 indexgputocpu sebelum memanggil writeindex.

    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: IVFFlat atau HNSW. 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 float32 kontigu 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) dan efSearch (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 IndexIDMap2 jika 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; nlist membentuk partisi dan nprobe menukar 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.
    • IndexIDMap memetakan hasil ke ID Anda sendiri dan memungkinkan penghapusan; writeindex/readindex mempersistensikan seluruh index; indexcputo_gpu mengoffload 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.

    Artikel Terkait

    Tutorial Semantic Search Engine dari Nol: Embeddings dan Vector Search

    Membangun Mesin Pencari Semantik dari Nol Daftar Isi Pendahuluan Prasyarat Memahami Pencarian Semantik [Text Embedding.....

    Tutorial BERTopic: Topic Modeling Modern dengan Embeddings

    BERTopic: Pemodelan Topik Modern dengan Embedding BERTopic adalah library pemodelan topik yang menggabungkan embedding t...

    Tutorial Sentence Transformers: Embeddings, Similarity, dan Reranker

    Sentence Transformers: Embedding, Kemiripan Semantik, dan Reranker Sentence Transformers (sering disebut SBERT) adalah p...

    Tutorial Milvus: Distributed Vector Database untuk AI

    Tutorial 10: Milvus - Database Vektor Terdistribusi untuk AI Daftar Isi Pendahuluan Prasyarat Arsitektur Milvus [Instala...