AI Engineering Academy · Pelajaran

Mengapa Pengambilan Dua Tahap Berhasil

Pahami keseimbangan recall-presisi dalam pengambilan satu tahap dan cara pengambil cepat yang bersifat umum, diikuti pemeringkat ulang yang lambat tetapi akurat, menghasilkan keunggulan dari kedua pendekatan.

Pelajaran 1 dari 413 langkah

Mengapa Pengambilan Dua Tahap Berhasil adalah pelajaran AI Engineering Academy gratis di CoddyKit. Ini adalah pelajaran 1 dari 4. Kamu bisa membaca pelajaran lengkapnya di bawah secara gratis — lalu praktikkan langsung di browser dengan editor kode bawaan dan tutor AI 24/7. Ini adalah bagian dari jalur belajar AI Engineering Academy, dan progresmu tersinkronisasi di web dan aplikasi CoddyKit. Kursus AI Engineering Academy mencakup 4 pelajaran total.

Kompromi Recall-Precision dalam Pengambilan

Setiap sistem pengambilan menghadapi kompromi mendasar: recall mengukur berapa banyak dokumen relevan yang Anda temukan (apakah ada yang terlewat?), sedangkan precision mengukur seberapa akurat hasil teratasnya (berapa banyak dokumen yang diambil benar-benar relevan?). Memaksimalkan keduanya secara bersamaan membutuhkan biaya komputasi yang besar. Retriever cepat mengorbankan precision demi recall; pemeringkat yang presisi mengorbankan kecepatan demi akurasi.

Bi-Encoder vs Cross-Encoder: Perbedaan Utama

Dua jenis model yang menjadi inti pengambilan dua tahap berbeda dalam hal cara keduanya memandang kueri dan dokumen. Bi-encoder menyandikan kueri dan setiap dokumen secara independen, lalu mengukur kemiripan antara vektornya—cepat, tetapi terbatas karena penyandian dilakukan secara terpisah. Cross-encoder melihat kueri dan dokumen yang digabungkan sebagai satu input, sehingga memungkinkan interaksi mendalam di antara keduanya—sangat akurat, tetapi memiliki kompleksitas O(n) terhadap himpunan kandidat.

# Bi-encoder: compute query embedding ONCE, compare to all doc embeddings
# O(1) query encoding + O(n) dot products via ANN index = fast
query_vec = embed(query)  # done once
results = vector_index.search(query_vec, top_k=100)  # fast ANN search

# Cross-encoder: re-scores (query, doc) pairs jointly
# O(k) forward passes for k candidate documents = slow but accurate
for doc in results[:100]:
    score = cross_encoder.score(query, doc.text)  # joint scoring

Tahap 1: Pengambilan Kasar yang Cepat

Tahap pertama adalah retriever cepattidak melewatkan dokumen yang relevan. Kita menjaring secara luas dan menerima beberapa positif palsu, karena tahap kedua akan membersihkannya.

from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings

# Stage 1: retrieve 100 candidates (high recall, modest precision)
vectorstore = FAISS.from_documents(documents, OpenAIEmbeddings())
coarse_retriever = vectorstore.as_retriever(
    search_kwargs={'k': 100}  # large candidate set
)

candidates = coarse_retriever.invoke(query)
print(f'Stage 1: retrieved {len(candidates)} candidate documents')

Tahap 2: Pemeringkatan Ulang Cross-Encoder yang Akurat

Tahap kedua mengambil himpunan kandidat dari tahap 1 dan menghitung ulang skor setiap pasangan (kueri, dokumen) menggunakan cross-encoder yang membaca keduanya secara bersamaan. Karena hanya memproses 50–200 kandidat (bukan seluruh korpus), tahap ini mampu menanggung biaya penyandian gabungan yang mahal. Perhatian mendalam cross-encoder terhadap input yang digabungkan membuatnya jauh lebih akurat dalam memperkirakan relevansi sebenarnya dibandingkan bi-encoder.

from sentence_transformers import CrossEncoder

reranker = CrossEncoder('cross-encoder/ms-marco-MiniLM-L-6-v2')

def rerank(query: str, candidates: list[str], top_k: int = 5) -> list[str]:
    # Score each (query, document) pair jointly
    pairs = [[query, doc] for doc in candidates]
    scores = reranker.predict(pairs)

    # Sort by score descending
    ranked = sorted(zip(candidates, scores), key=lambda x: x[1], reverse=True)
    return [doc for doc, _ in ranked[:top_k]]

candidate_texts = [doc.page_content for doc in candidates]
final_docs = rerank(query, candidate_texts, top_k=5)
print(f'Stage 2: selected top {len(final_docs)} documents after re-ranking')

Mengapa Kombinasi Ini Berhasil

Desain dua tahap memanfaatkan ketidaksimetrian penting: pencarian ANN cepat pada tahap pertama dapat diskalakan hingga jutaan dokumen dalam hitungan milidetik, sedangkan cross-encoder yang akurat pada tahap kedua hanya beroperasi pada kumpulan kandidat yang kecil. Anda memperoleh skalabilitas pencarian aproksimasi sekaligus akurasi penilaian gabungan yang tepat. Keseluruhan alur pemrosesan menjadi cepat dan sangat akurat—sesuatu yang tidak dapat dicapai oleh salah satu tahap jika berdiri sendiri.

Profil Latensi Pengambilan Dua Tahap

Dalam alur pemrosesan dua tahap yang umum: tahap 1 (pencarian ANN vektor pada 1 juta dokumen) memerlukan waktu 5–20 md; tahap 2 (cross-encoder pada 100 kandidat) memerlukan waktu 100–500 md, bergantung pada panjang dokumen dan perangkat keras. Total anggaran latensi adalah 150–600 md—masih dapat diterima untuk sebagian besar aplikasi. Akselerasi GPU pada tahap 2 dapat mengurangi pemeringkatan ulang menjadi kurang dari 30 md untuk dokumen pendek, sehingga alur ini mampu menyaingi pengambilan satu tahap dalam hal latensi pada aplikasi yang sensitif terhadap waktu respons.

import time

def two_stage_search(query, coarse_retriever, reranker, top_k=5):
    t0 = time.perf_counter()

    candidates = coarse_retriever.invoke(query)        # stage 1
    t1 = time.perf_counter()

    candidate_texts = [c.page_content for c in candidates]
    final_docs = rerank(query, candidate_texts, top_k)  # stage 2
    t2 = time.perf_counter()

    print(f'Stage 1 (retrieval): {(t1-t0)*1000:.1f}ms')
    print(f'Stage 2 (re-ranking): {(t2-t1)*1000:.1f}ms')
    print(f'Total: {(t2-t0)*1000:.1f}ms')
    return final_docs

Memilih Ukuran Himpunan Kandidat yang Tepat

Ukuran himpunan kandidat tahap pertama merupakan hiperparameter penting. Jika terlalu kecil (misalnya 10), dokumen relevan dapat terlewat sebelum pemeringkatan ulang dimulai. Jika terlalu besar (misalnya 500), latensi tahap 2 meningkat drastis. Kurva recall pada N

def recall_at_n(coarse_retriever, test_queries, golden_relevant, n_values):
    for n in n_values:
        recalls = []
        for query, relevant in zip(test_queries, golden_relevant):
            # Temporarily set k to n
            coarse_retriever.search_kwargs['k'] = n
            results = coarse_retriever.invoke(query)
            retrieved_ids = {r.metadata.get('id') for r in results}
            relevant_found = len(set(relevant) & retrieved_ids)
            recalls.append(relevant_found / len(relevant))
        avg = sum(recalls) / len(recalls)
        print(f'N={n}: recall={avg:.3f}')

Tahap Pertama Hibrida + Tahap Kedua Cross-Encoder

Konfigurasi dua tahap yang paling kuat memasangkan retriever hibrida (dense + BM25) sebagai tahap pertama dengan cross-encoder sebagai tahap kedua. Pengambilan hibrida memaksimalkan recall tahap pertama dengan menggabungkan pencocokan semantik dan kata kunci, kemudian cross-encoder memilih dokumen yang paling relevan secara akurat dari kumpulan kandidat gabungan. Konfigurasi ini secara konsisten mencapai kualitas pengambilan terbaik pada tolok ukur.

from langchain.retrievers import EnsembleRetriever

# Stage 1: hybrid retrieval for maximum recall
hybrid_retriever = EnsembleRetriever(
    retrievers=[bm25_retriever, vector_retriever],
    weights=[0.4, 0.6],
)

# Stage 2: cross-encoder re-ranking for high precision
from langchain.retrievers.document_compressors import CrossEncoderReranker
from langchain_community.cross_encoders import HuggingFaceCrossEncoder

cross_encoder_model = HuggingFaceCrossEncoder(model_name='cross-encoder/ms-marco-MiniLM-L-6-v2')
compressor = CrossEncoderReranker(model=cross_encoder_model, top_n=5)

from langchain.retrievers import ContextualCompressionRetriever
two_stage = ContextualCompressionRetriever(
    base_compressor=compressor,
    base_retriever=hybrid_retriever,
)

API Pemeringkatan Ulang Komersial

Jika Anda menginginkan akurasi cross-encoder tanpa mengelola model sendiri, Cohere Rerank dan Jina AI Reranker menyediakan API pemeringkatan ulang yang di-host di cloud. Anda mengirimkan kueri dan daftar teks dokumen, lalu menerima skor relevansinya. API ini menggunakan model cross-encoder besar (sering kali lebih dari 500 juta parameter) yang mengungguli cross-encoder kecil yang di-host sendiri, dengan biaya berupa latensi API tambahan (50–300 md) dan tarif untuk setiap dokumen yang diperingkat ulang.

import cohere

co = cohere.Client('YOUR_API_KEY')

def cohere_rerank(query: str, documents: list[str], top_k: int = 5):
    response = co.rerank(
        model='rerank-english-v3.0',
        query=query,
        documents=documents,
        top_n=top_k,
    )
    return [
        {'text': documents[r.index], 'score': r.relevance_score}
        for r in response.results
    ]

final = cohere_rerank(query, candidate_texts, top_k=5)
for doc in final:
    print(f'Score {doc["score"]:.3f}: {doc["text"][:80]}')

Kapan Pengambilan Dua Tahap Berlebihan

Pengambilan dua tahap menambah kompleksitas dan latensi dibandingkan pengambilan satu tahap. Pendekatan ini tidak selalu diperlukan. Untuk korpus kecil di bawah 10.000 dokumen, satu cross-encoder yang memproses seluruh himpunan mungkin cukup cepat. Untuk aplikasi yang sangat membutuhkan latensi di bawah 100 md dan memperoleh peningkatan akurasi yang kecil, pengambilan dense satu tahap mungkin lebih sesuai. Gunakan dua tahap jika Anda memiliki korpus besar, persyaratan akurasi tinggi, dan dapat menerima latensi pengambilan 200–500 md.

Pengambilan Tiga Tahap untuk Skala Ekstrem

Untuk korpus yang terdiri atas puluhan juta dokumen, terkadang digunakan pipeline tiga tahap: tahap pertama mengambil 10.000 kandidat dengan ANN, tahap kedua melakukan pemeringkatan ulang menjadi 100 kandidat menggunakan cross-encoder kecil yang cepat, dan tahap ketiga melakukan pemeringkatan ulang menjadi 5 kandidat menggunakan cross-encoder besar yang bertenaga. Setiap tahap menerapkan model yang lebih mahal dan akurat pada kumpulan yang lebih kecil. Arsitektur ini digunakan oleh mesin pencari berskala besar dan sistem tanya jawab dokumen.

Pemeriksaan Singkat

Uji pemahaman Anda tentang alasan pengambilan dua tahap dapat bekerja berdasarkan pelajaran ini.

Ringkasan Pelajaran

Dalam pelajaran ini, Anda telah mempelajari bahwa bi-encoder cepat, tetapi terbatas pada pengodean kueri dan dokumen secara terpisah; cross-encoder akurat karena melakukan pengodean bersama, tetapi terlalu lambat untuk pencarian di seluruh korpus; dan pengambilan dua tahap menggabungkan keduanya: tahap pertama yang cepat untuk memperoleh cakupan tinggi, diikuti tahap kedua yang akurat untuk memperoleh presisi tinggi. Tahap pertama mengambil jauh lebih banyak kandidat daripada yang diperlukan agar dokumen yang relevan tidak terlewat. Selanjutnya, kita akan mengimplementasikan pemeringkatan ulang dengan cross-encoder menggunakan Cohere dan BGE.

Gratis untuk memulai

Belajar Python dengan tutor AI — gratis

Tulis dan jalankan kode asli di browser kamu, dapatkan bantuan instan dari tutor AI 24/7, dan lanjutkan di mana kamu tinggalkan di web atau aplikasi.

Kursus
30
Pelajaran
120

Pertanyaan yang Sering Diajukan

Apakah pelajaran “Mengapa Pengambilan Dua Tahap Berhasil” gratis?

Ya — teks lengkap “Mengapa Pengambilan Dua Tahap Berhasil” gratis dibaca di sini di web. Untuk praktiknya secara interaktif (editor kode bawaan dan tutor AI 24/7) dan buka sisa kursus AI Engineering Academy, upgrade ke CoddyKit PRO. Kursus AI Engineering Academy mencakup 4 pelajaran total.

Apa yang akan aku pelajari di “Mengapa Pengambilan Dua Tahap Berhasil”?

Pahami keseimbangan recall-presisi dalam pengambilan satu tahap dan cara pengambil cepat yang bersifat umum, diikuti pemeringkat ulang yang lambat tetapi akurat, menghasilkan keunggulan dari kedua pe… Kamu berlatih AI Engineering Academy dengan kode praktik yang langsung kamu jalankan di browser, dan tutor AI 24/7 menjawab pertanyaanmu saat kamu mengerjakan pelajaran ini.

Apakah aku perlu pengalaman untuk memulai AI Engineering Academy?

Tidak diperlukan pengalaman sebelumnya. AI Engineering Academy di CoddyKit dirancang untuk pemula hingga pelajar tingkat lanjut, jadi kamu bisa memulai di sini atau dari awal dan belajar sesuai kecepatan kamu sendiri. Ini adalah pelajaran 1 dari 4.

Berapa lama pelajaran “Mengapa Pengambilan Dua Tahap Berhasil” memakan waktu?

Sebagian besar pelajaran CoddyKit memakan waktu sekitar 5–10 menit. Setiap pelajaran ringkas dan interaktif, jadi kamu membuat kemajuan stabil dan melanjutkan dari tempat kamu tinggalkan di web dan aplikasi.

Bisakah aku menulis dan menjalankan kode dalam pelajaran AI Engineering Academy ini?

Ya. Setiap pelajaran AI Engineering Academy menyertakan editor kode bawaan, jadi kamu menulis dan menjalankan kode nyata langsung di browser dan mendapatkan umpan balik AI instan — tidak diperlukan penyiapan lokal.

Semua pelajaran dalam kursus ini

  1. Mengapa Pengambilan Dua Tahap Berhasil
  2. Pemeringkatan Ulang Cross-Encoder dengan Cohere dan BGE
  3. Kompresi Kontekstual dan Penyaringan Relevansi
  4. Mengukur Dampak Pemeringkatan Ulang
← Kembali ke AI Engineering Academy