28 poin oleh xguru 2024-08-26 | 2 komentar | Bagikan ke WhatsApp
  • 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

 
eajrezz 2024-08-27

Saya penasaran apakah pencarian dalam bahasa Korea juga berfungsi dengan baik.

 
xguru 2024-08-26

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