Biến Postgres thành công cụ tìm kiếm
(anyblockers.com)- Có thể xây dựng một công cụ tìm kiếm lai ngay בתוך Postgres với đầy đủ tìm kiếm ngữ nghĩa, toàn văn và mờ
- Tìm kiếm là phần quan trọng trong nhiều ứng dụng nhưng không dễ triển khai cho đúng. Đặc biệt trong các pipeline RAG, chất lượng tìm kiếm có thể quyết định thành bại của toàn bộ quy trình
- Tìm kiếm ngữ nghĩa (Semantic) đang là xu hướng, nhưng tìm kiếm dựa trên từ vựng truyền thống vẫn là trụ cột của tìm kiếm
- Các kỹ thuật ngữ nghĩa có thể cải thiện kết quả, nhưng chúng hoạt động tốt nhất khi dựa trên nền tảng tìm kiếm văn bản vững chắc
Xây dựng công cụ tìm kiếm với Postgres
- Kết hợp ba kỹ thuật:
- tìm kiếm toàn văn với
tsvector - tìm kiếm ngữ nghĩa với
pgvector - đối sánh mờ với
pg_trgm
- tìm kiếm toàn văn với
- Cách tiếp cận này có thể không phải là tốt nhất tuyệt đối trong mọi tình huống, nhưng là một lựa chọn thay thế rất tốt cho việc xây dựng một dịch vụ tìm kiếm riêng
- Đây là một điểm khởi đầu vững chắc có thể được triển khai và mở rộng ngay trong cơ sở dữ liệu Postgres hiện có
- Lý do nên dùng Postgres cho mọi thứ: Cứ dùng Postgres cho mọi thứ, PostgreSQL là đủ, Cứ dùng Postgres đi
Triển khai FTS và tìm kiếm ngữ nghĩa
- Supabase có tài liệu rất hay về triển khai tìm kiếm lai, nên bài viết sẽ lấy đó làm điểm bắt đầu
- Theo hướng dẫn, FTS được triển khai bằng chỉ mục GIN và tìm kiếm ngữ nghĩa được triển khai bằng pgvector (còn gọi là bi-encoder dense retrieval)
- Theo kinh nghiệm cá nhân, chọn embedding 1536 chiều cho kết quả tốt hơn nhiều
- Thay các hàm Supabase bằng CTE và truy vấn, đồng thời thêm
$trước tham số - Ở đây dùng RRF (Reciprocal Ranked Fusion) để gộp kết quả
- Cách này đảm bảo các mục có thứ hạng cao trong nhiều danh sách sẽ được xếp hạng cao trong danh sách cuối cùng
- Đồng thời cũng đảm bảo rằng những mục xếp cao ở một số danh sách nhưng thấp ở danh sách khác sẽ không bị đẩy lên quá cao trong kết quả cuối cùng
- Việc tính điểm bằng cách đưa thứ hạng xuống mẫu số có thể làm bất lợi cho các bản ghi xếp hạng thấp
- Một số điểm đáng chú ý
$rrf_k: để tránh việc mục đứng đầu có điểm quá cực đoan (vì chia theo thứ hạng), người ta thường thêm hằng số k vào mẫu số để làm mượt điểm$_weight: có thể gán trọng số cho từng phương pháp. Điều này rất hữu ích khi muốn tinh chỉnh kết quả
Triển khai tìm kiếm mờ
- Các phương pháp trên có thể giải quyết được nhiều trường hợp, nhưng sẽ phát sinh vấn đề ngay khi có lỗi chính tả trong named entity
- Tìm kiếm ngữ nghĩa có thể nắm bắt được sự tương đồng nên loại bỏ được một phần các vấn đề này, nhưng vẫn gặp khó với tên riêng, từ viết tắt và các loại văn bản khác không tương đồng về mặt ngữ nghĩa
- Để giảm bớt điều này, có thể thêm extension
pg_trgmđể cho phép tìm kiếm mờ- Nó hoạt động dựa trên trigram. Trigram phân tách từ thành các chuỗi 3 ký tự nên rất hữu ích cho tìm kiếm mờ
- Nhờ đó có thể khớp các từ tương tự ngay cả khi có lỗi gõ hoặc biến thể nhỏ
- Ví dụ, "hello" và "helo" chia sẻ nhiều trigram nên dễ được khớp hơn trong tìm kiếm mờ
- Tạo chỉ mục mới cho cột mong muốn rồi thêm nó vào truy vấn tìm kiếm tổng thể
- Extension pg_trgm cung cấp toán tử
%để lọc văn bản có độ tương đồng lớn hơnpg_trgm.similarity_threshold(mặc định là 0.3) - Ngoài ra còn có nhiều toán tử hữu ích khác
Tinh chỉnh tìm kiếm toàn văn
- Điều chỉnh trọng số
tsvector: tài liệu thực tế không chỉ có tiêu đề mà còn có cả nội dung - Dù có nhiều cột, vẫn chỉ giữ một cột embedding
- Theo trải nghiệm cá nhân, việc giữ
titlevàbodytrong cùng một embedding không khác biệt lớn về hiệu năng so với việc duy trì nhiều embedding - Suy cho cùng,
titlenên là biểu đạt ngắn gọn của phần thân bài. Tốt nhất là thử nghiệm điều này theo nhu cầu của bạn titleđược kỳ vọng là ngắn và giàu từ khóa, cònbodysẽ dài hơn và chứa nhiều chi tiết hơn- Vì vậy cần điều chỉnh cách các cột tìm kiếm toàn văn được gán trọng số với nhau
- Có thể ưu tiên theo vị trí của từ trong tài liệu hoặc theo mức độ quan trọng
- A-weight: quan trọng nhất (ví dụ: tiêu đề, header). Mặc định
1.0 - B-weight: quan trọng (ví dụ: phần đầu tài liệu, tóm tắt). Mặc định
0.4 - C-weight: mức quan trọng tiêu chuẩn (ví dụ: văn bản thân bài). Mặc định
0.2 - D-weight: ít quan trọng nhất (ví dụ: chú thích chân trang, ghi chú). Mặc định
0.1
- A-weight: quan trọng nhất (ví dụ: tiêu đề, header). Mặc định
- Có thể tinh chỉnh độ liên quan bằng cách điều chỉnh trọng số theo cấu trúc tài liệu và yêu cầu của ứng dụng
- Lý do nên gán nhiều trọng số hơn cho tiêu đề
- Vì tiêu đề thường diễn đạt ngắn gọn chủ đề chính của tài liệu
- Người dùng thường lướt tiêu đề trước khi tìm kiếm, nên việc khớp từ khóa ở tiêu đề nhìn chung liên quan đến ý định người dùng hơn so với khớp trong phần thân
Điều chỉnh theo độ dài
- Đọc tài liệu
ts_rank_cdsẽ thấy có tham số chuẩn hóa-
Cả hai hàm xếp hạng đều dùng tùy chọn số nguyên
normalizationđể chỉ định việc độ dài tài liệu nên ảnh hưởng đến thứ hạng như thế nào. Tùy chọn số nguyên này là một bit mask vì nó điều khiển nhiều hành vi: có thể dùng|để chỉ định một hoặc nhiều hành vi (ví dụ:2|4).
-
- Có thể dùng các tùy chọn này để
- điều chỉnh thiên lệch theo độ dài tài liệu
- cân bằng độ liên quan giữa các tập tài liệu khác nhau
- điều chỉnh kết quả xếp hạng để biểu diễn nhất quán
- Đặt
0(không chuẩn hóa) cho tiêu đề và1(độ dài tài liệu theo log) cho phần thân có thể cho kết quả tốt - Một lần nữa, nên thử nhiều tùy chọn khác nhau để tìm ra cấu hình phù hợp nhất với trường hợp sử dụng của bạn
Xếp hạng lại bằng cross-encoder
- Nhiều hệ thống tìm kiếm gồm hai giai đoạn
- Tức là dùng bi-encoder để lấy ra N kết quả ban đầu, sau đó dùng cross-encoder để so sánh các kết quả này với truy vấn tìm kiếm và xếp hạng lại
- bi-encoder: nhanh nên phù hợp để truy xuất số lượng lớn tài liệu
- cross-encoder
- chậm hơn nhưng hiệu năng tốt hơn nên phù hợp để xếp hạng lại các kết quả đã truy xuất
- xử lý truy vấn và tài liệu cùng nhau để hiểu sắc thái quan hệ giữa hai bên tốt hơn
- đổi lại cho độ chính xác xếp hạng cao hơn nhưng phải đánh đổi về thời gian tính toán và khả năng mở rộng
- Có nhiều công cụ để làm việc này
- Một trong những lựa chọn tốt nhất là Rerank của Cohere
- Một cách khác là tự xây bằng GPT của OpenAI
- Cross-encoder có thể tăng độ chính xác của kết quả tìm kiếm nhờ hiểu rõ hơn mối quan hệ giữa truy vấn và tài liệu
- Tuy nhiên, chi phí tính toán lớn nên bị hạn chế về khả năng mở rộng
- Vì vậy, cách tiếp cận hai giai đoạn dùng bi-encoder cho truy xuất ban đầu rồi chỉ áp dụng cross-encoder cho một số ít tài liệu đã truy xuất là rất hiệu quả
Khi nào nên tìm giải pháp thay thế
- PostgreSQL là lựa chọn phù hợp cho nhiều kịch bản tìm kiếm, nhưng không phải không có giới hạn
- Việc thiếu các thuật toán nâng cao như BM25 có thể trở nên rõ rệt khi xử lý các tài liệu có độ dài rất khác nhau
- Tìm kiếm toàn văn của PostgreSQL phụ thuộc vào TF-IDF nên có thể gặp khó với tài liệu rất dài và các thuật ngữ hiếm trong những bộ sưu tập lớn
- Trước khi tìm giải pháp thay thế, nhất định nên đo đạc trước. Có thể việc đó không đáng
Kết luận
- Bài viết này đã đề cập nhiều nội dung, từ tìm kiếm toàn văn cơ bản đến các kỹ thuật nâng cao như đối sánh mờ, tìm kiếm ngữ nghĩa và tăng cường kết quả
- Có thể tận dụng sức mạnh của Postgres để tạo ra một công cụ tìm kiếm mạnh mẽ và linh hoạt, phù hợp với nhu cầu cụ thể
- Postgres có thể không phải công cụ đầu tiên người ta nghĩ tới cho tìm kiếm, nhưng nó thực sự có thể đi rất xa
- Chìa khóa để có trải nghiệm tìm kiếm tuyệt vời
- lặp lại và tinh chỉnh liên tục
- đừng ngại dùng các kỹ thuật debug đã nêu để hiểu hiệu năng tìm kiếm, rồi điều chỉnh trọng số và tham số dựa trên phản hồi cũng như hành vi của người dùng
- PostgreSQL có thể thiếu một số tính năng tìm kiếm nâng cao, nhưng trong phần lớn trường hợp vẫn đủ mạnh để xây dựng một công cụ tìm kiếm hiệu quả
- Trước khi tìm giải pháp thay thế, nên tận dụng tối đa khả năng của Postgres và đo hiệu năng trước; nếu vẫn chưa đủ thì khi đó mới cân nhắc giải pháp khác
2 bình luận
Không biết liệu khả năng tìm kiếm tiếng Hàn có tốt không nhỉ.
Chủ đề Weekly hôm nay cũng là về Postgres, và đúng là lại thêm một bài về Postgres nữa. Có vẻ rõ ràng là mức độ phổ biến tỷ lệ thuận với số lượng bài viết được đăng haha
Về BM25 thì hãy tham khảo bên dưới.
pg_bm25 - Tiện ích mở rộng tìm kiếm toàn văn cho Postgres, cung cấp chất lượng ở mức Elastic
ParadeDB - PostgreSQL for Search