- Di dalam Postgres, kita bisa membangun mesin pencari hibrida yang mencakup pencarian semantik, full-text, dan fuzzy sekaligus
- Pencarian adalah bagian penting di banyak aplikasi, tetapi tidak mudah untuk mengimplementasikannya dengan baik. Terutama pada pipeline RAG, kualitas pencarian dapat menentukan keberhasilan atau kegagalan seluruh proses
- Pencarian semantik memang sedang tren, tetapi pencarian tradisional berbasis leksikal tetap menjadi tulang punggung pencarian
- Teknik semantik dapat meningkatkan hasil, tetapi paling efektif bila berjalan di atas fondasi pencarian berbasis teks yang kokoh
Membangun mesin pencari dengan Postgres
- Menggabungkan tiga teknik:
- full-text search menggunakan
tsvector
- pencarian semantik menggunakan
pgvector
- fuzzy matching menggunakan
pg_trgm
- Pendekatan ini mungkin bukan yang mutlak terbaik untuk semua situasi, tetapi merupakan alternatif yang sangat baik dibanding membangun layanan pencarian terpisah
- Titik awal yang kokoh yang dapat diimplementasikan dan diskalakan di dalam database Postgres yang sudah ada
- Alasan menggunakan Postgres untuk segala hal: Gunakan saja Postgres untuk semua hal, PostgreSQL sudah cukup, Pakai saja Postgres
Menerapkan FTS dan pencarian semantik
- Supabase punya dokumentasi yang sangat baik tentang implementasi pencarian hibrida, jadi itu akan dijadikan titik awal
- Mengikuti panduan tersebut, FTS diimplementasikan dengan indeks GIN, dan pencarian semantik diimplementasikan dengan pgvector (juga disebut bi-encoder dense retrieval)
- Berdasarkan pengalaman pribadi, memilih embedding berdimensi 1536 bisa memberikan hasil yang jauh lebih baik
- Fungsi Supabase diganti dengan CTE dan query, lalu parameter diberi awalan
$
- Di sini, hasil digabungkan menggunakan RRF(Reciprocal Ranked Fusion)
- Metode ini memastikan item yang mendapat peringkat tinggi di beberapa daftar akan memperoleh peringkat tinggi di daftar akhir
- Metode ini juga memastikan item yang sangat tinggi di sebagian daftar tetapi rendah di daftar lain tidak akan otomatis mendapat peringkat tinggi di daftar akhir
- Menghitung skor dengan menempatkan peringkat di penyebut dapat memberi penalti pada record dengan peringkat lebih rendah
- Hal yang patut diperhatikan
$rrf_k: agar skor item peringkat pertama tidak menjadi terlalu ekstrem (karena dibagi dengan peringkat), biasanya konstanta k ditambahkan ke penyebut untuk menghaluskan skor
$ _weight: tiap metode dapat diberi bobot. Ini sangat berguna saat menyesuaikan hasil
Menerapkan pencarian fuzzy
- Metode-metode sebelumnya sudah bisa menyelesaikan banyak hal, tetapi typo pada named entity bisa langsung menimbulkan masalah
- Pencarian semantik menangkap kemiripan sehingga dapat mengurangi sebagian masalah ini, tetapi tetap kesulitan pada nama, singkatan, dan teks lain yang tidak mirip secara semantik
- Untuk mengatasinya, diperkenalkan ekstensi
pg_trgm agar pencarian fuzzy dimungkinkan
- Bekerja dengan trigram. Trigram memecah kata menjadi urutan 3 karakter, sehingga berguna untuk pencarian fuzzy
- Dengan ini, kata-kata serupa tetap bisa dicocokkan meski mengandung typo atau sedikit variasi
- Sebagai contoh, "hello" dan "helo" berbagi banyak trigram sehingga lebih mudah dicocokkan dalam pencarian fuzzy
- Buat indeks baru pada kolom yang diinginkan, lalu tambahkan ke query pencarian keseluruhan
- Ekstensi pg_trgm mengekspos operator
% untuk memfilter teks dengan kemiripan yang lebih besar dari pg_trgm.similarity_threshold (nilai default 0.3)
- Ada juga beberapa operator lain yang berguna
Menyetel full-text search
- Menyesuaikan bobot
tsvector: dokumen nyata tidak hanya berisi judul, tetapi juga isi
- Meski ada banyak kolom, tetap pertahankan hanya satu kolom embedding
- Secara pribadi, penulis menemukan bahwa mempertahankan
title dan body dalam embedding yang sama tidak memberi perbedaan performa besar dibanding menyimpan beberapa embedding
- Pada akhirnya,
title seharusnya menjadi representasi singkat dari isi. Sebaiknya eksperimen sesuai kebutuhan
title diharapkan pendek dan kaya kata kunci, sedangkan body akan lebih panjang dan memuat lebih banyak detail
- Karena itu, perlu menyesuaikan bagaimana bobot antar kolom full-text search diberikan
- Prioritas dapat diberikan berdasarkan posisi kata dalam dokumen atau tingkat kepentingannya
- A-weight: paling penting (misalnya judul, header). Default
1.0
- B-weight: penting (misalnya awal dokumen, ringkasan). Default
0.4
- C-weight: tingkat kepentingan standar (misalnya teks isi). Default
0.2
- D-weight: paling tidak penting (misalnya catatan kaki, anotasi). Default
0.1
- Sesuaikan bobot menurut struktur dokumen dan kebutuhan aplikasi untuk menyempurnakan relevansi
- Alasan memberi bobot lebih besar pada judul
- Karena judul biasanya menyatakan topik utama dokumen secara ringkas
- Pengguna cenderung terlebih dahulu menelusuri judul saat mencari, sehingga kecocokan kata kunci di judul umumnya lebih relevan dengan niat pengguna dibanding kecocokan di isi
Penyesuaian berdasarkan panjang
- Jika membaca dokumentasi
ts_rank_cd, kita akan melihat adanya parameter normalisasi
- Kedua fungsi ranking menggunakan opsi
normalization bertipe integer untuk menentukan apakah dan bagaimana panjang dokumen harus memengaruhi peringkat. Opsi integer ini adalah bit mask karena mengontrol beberapa perilaku: gunakan | untuk menentukan satu atau lebih perilaku (misalnya 2|4).
- Dengan berbagai opsi ini, kita dapat melakukan hal berikut
- Menyesuaikan bias panjang dokumen
- Menyeimbangkan relevansi di berbagai kumpulan dokumen
- Menyetel hasil ranking agar representasinya konsisten
- Menetapkan
0 (tanpa normalisasi) untuk judul, dan 1 (panjang dokumen log) untuk isi dapat memberi hasil yang baik
- Sekali lagi, sebaiknya bereksperimen dengan berbagai opsi untuk menemukan yang paling cocok bagi use case Anda
Reranking dengan cross-encoder
- Banyak sistem pencarian terdiri dari dua tahap
- Artinya, pertama mengambil N hasil awal dengan bi-encoder, lalu menggunakan cross-encoder untuk membandingkan hasil tersebut dengan query pencarian dan memberi peringkat
- bi-encoder: karena cepat, cocok untuk mengambil banyak dokumen
- cross-encoder
- Lebih lambat tetapi performanya lebih baik, sehingga cocok untuk melakukan reranking pada hasil yang sudah diambil
- Memproses query dan dokumen bersama-sama sehingga memungkinkan pemahaman yang lebih bernuansa tentang hubungan keduanya
- Ini memberikan akurasi ranking yang lebih baik dengan konsekuensi waktu komputasi dan skalabilitas
- Ada berbagai alat untuk melakukan ini
- Salah satu yang terbaik adalah Rerank dari Cohere
- Cara lain adalah membangunnya sendiri menggunakan GPT dari OpenAI
- Cross-encoder dapat meningkatkan akurasi hasil pencarian dengan memahami hubungan antara query dan dokumen dengan lebih baik
- Namun, karena biaya komputasinya besar, ada keterbatasan dari sisi skalabilitas
- Karena itu, pendekatan dua tahap yang menggunakan bi-encoder untuk pencarian awal, lalu menerapkan cross-encoder hanya pada sedikit dokumen yang diambil, menjadi efektif
Kapan perlu mencari solusi alternatif
- PostgreSQL adalah pilihan yang cocok untuk banyak skenario pencarian, tetapi bukan tanpa batasan
- Ketiadaan algoritme lanjutan seperti BM25 bisa terasa saat menangani panjang dokumen yang sangat bervariasi
- Full-text search di PostgreSQL bergantung pada TF-IDF, sehingga bisa kesulitan dengan dokumen yang sangat panjang dan istilah langka dalam koleksi besar
- Sebelum mencari solusi alternatif, Anda harus mengukurnya terlebih dahulu. Mungkin saja itu tidak sepadan
Kesimpulan
- Artikel ini membahas banyak hal, mulai dari full-text search dasar hingga teknik lanjutan seperti fuzzy matching, pencarian semantik, dan boosting hasil
- Dengan memanfaatkan kemampuan kuat Postgres, kita bisa membuat mesin pencari yang kuat dan fleksibel sesuai kebutuhan spesifik
- Postgres mungkin bukan alat pertama yang terlintas untuk pencarian, tetapi kemampuannya bisa melangkah sangat jauh
- Kunci menuju pengalaman pencarian yang hebat
- Iterasi dan penyetelan yang berkelanjutan
- Gunakan teknik debugging yang dibahas untuk memahami performa pencarian, dan jangan ragu menyesuaikan bobot serta parameter berdasarkan umpan balik dan perilaku pengguna
- PostgreSQL mungkin kekurangan fitur pencarian lanjutan, tetapi dalam kebanyakan kasus tetap cukup kuat untuk membangun mesin pencari yang memadai
- Sebelum mencari solusi alternatif, sebaiknya maksimalkan dulu kemampuan Postgres dan ukur performanya; jika masih kurang, barulah pertimbangkan solusi lain
2 komentar
Saya penasaran apakah pencarian dalam bahasa Korea juga berfungsi dengan baik.
Topik Weekly hari ini juga tentang Postgres, dan ternyata lagi-lagi Postgres. Sepertinya memang makin banyak artikel yang muncul sebanding dengan popularitasnya haha.
Untuk BM25, silakan lihat di bawah ini.
pg_bm25 - ekstensi pencarian Full-Text yang memberikan kualitas setara Elastic di Postgres
ParadeDB - PostgreSQL for Search