Tính năng tìm kiếm ngữ nghĩa (semantic search)

Tính năng tìm kiếm ngữ nghĩa (semantic search)

Những mẩu tin ngắn hàng ngày dành cho bạn
  • Quá ghê ghớm, Codeium - vốn được biết đến như một đối thủ của Github Copilot, khi nó cho người dùng dùng miễn phí không giới hạn. Mới đây họ giới thiệu thêm Windsurf Editor - không chỉ còn là VSCode Extentions nữa mà là một trình Editor luôn - cạnh tranh trực tiếp với Cursor. Và điểm chính là nó... hoàn toàn miễn phí 🫣.

    » Xem thêm
  • Tin vui đầu này, Github Copilot đã chính thức có bản Free cho tất cả mọi người.

    Github Copilot là một trợ lý AI code cùng chúng ta, nó có thể tự động hoàn thành mã, trò chuyện hoặc sửa lỗi. Hiện đang hỗ trợ nhiều trình soạn thảo, IDE phổ biến như VSCode, JetBrains, XCode...

    Phiên bản miễn phí đang bị giới hạn 2000 Suggestions, và khoảng 50 requests đến tính năng Chat hàng tháng. Sau đó bạn có thể nâng cấp lên các phiên bản cao cấp hơn với giá từ 10$.

    Theo đánh giá của mình thì Copilot rất tốt và xứng đáng trong tầm giá, đang dùng hàng ngày 😄

    » Xem thêm
  • Có một nghiên cứu khá thú vị mà mình rút ra được đó là: "Người dùng chẳng bao giờ chịu đọc những thứ mà họ không muốn". (Khẳng định chắc nịch thế chứ, đa số thì đúng hơn 😅).

    Không tin á, mình dám cá chắc là bạn đã không ít lần gặp trường hợp nhấn vào một nút gì đó liên tục mà nó không phản hồi, nhưng thật ra là nó có báo một dòng lỗi ở đâu đó. Hoặc nhập xong xuôi các thứ rồi đến lúc nhấn nút gửi thì không gửi được. Bực mình mới kéo lên trên hoặc xuống dưới đọc thì à... hoá ra là phải làm thêm bước này bước kia đúng không?

    Cũng chẳng đâu xa ngay như blog của mình đây. Nghĩ là mọi người ai mà quan tâm đến blog sẽ nhấn vào nút "Cho phép bật thông báo" ở ngay phía dưới bài viết. Nhưng sự thật là chẳng ai thèm bấm cả. Có phải là họ không muốn nhận không? Chắc là không! Mình nghĩ do họ không đọc đến dòng đó thôi.

    Bằng chứng là chỉ khi cho hiện một thông báo chiếm một nửa cái màn hình, hoặc bất ngờ hiện ra để gây sự chú ý thì họ mới thật sự đọc và dĩ nhiên thu hút thêm vài người đăng ký - điều mà trước đó chẳng bao giờ đạt được.

    » Xem thêm

Vấn đề

Xin chào độc giả của 2coffee.dev. Các bạn ở Hà Nội có thấy tuần vừa rồi không khí mùa thu đã trở nên rõ rệt hơn không? Buổi sáng trời mát lạnh còn chiều tối thì đi kèm với những cơn gió lớn. Nhưng đằng sau đó lại là một tuần bận rộn đối với tôi. Vừa tập trung vào chạy "deadline" cho dự án ở công ty, tối về tranh thủ hoàn thiện chức năng tìm kiếm cho blog. Cái deadline này khác hẳn với mọi khi vì là tính năng chủ lực của năm cho sản phẩm. Còn về blog, tính năng tìm kiếm sớm hay muộn gì cũng phải hoàn thành, và đây là thời điểm thích hợp để làm điều đó.

Trước khi chuyển sang Fresh, blog vốn đã có tính năng tìm kiếm. Cách làm lúc đó là sử dụng fulltext-search của postgres. Nói thêm cho bạn đọc chưa biết, trước khi dùng postgres thì tôi đã dùng cả redisearch để phục vụ tìm kiếm. Nhìn chung thì postgres vẫn cho ra kết quả tốt hơn trong khi redisearch thì phức tạp hơn. Mà thực tế thì dữ liệu bài viết không nhiều đến mức để redisearch phát huy được hết tác dụng.

Thời điểm chuyển sang Fresh, lúc đó AI đang bùng nổ. Rất nhiều ánh nhìn đổ sang phía AI và cả những gì mà nó làm được. Sau khi hoàn thành những chức năng cơ bản đến bước chuẩn bị làm chức năng tìm kiếm thì tôi chợt nghĩ: "Hay là mình cũng thử ứng dụng AI!?". Thế là tôi quyết định "release" phiên bản blog mới mà không có chức năng tìm kiếm.

Để làm được tính năng tìm kiếm có sự góp mặt của AI thì tôi phải dành thời gian nghiên cứu cũng như thử nghiệm rất nhiều. Tìm hiểu về cách thức thực hiện, cách sử dụng các mô hình LLMs, mô hình embeddings, kiểu dữ liệu vector, cách biến dữ liệu thành vector và cả cách truy vấn...

Nói một cách ngắn gọn, vector là một tập hợp hữu hạn của các con số như trong toán học, số lượng của các con số đó tạo thành kích thước (dimension) của vector. Kích thước càng lớn, vector càng có khả năng tổng quát hóa dữ liệu mà nó biểu thị. Để biến một dữ liệu thông thường (văn bản, giọng nói, hình ảnh...) sang vector thì có nhiều cách, nhưng nhờ sự phổ biến của các mô hình LLMs hiện nay mà chỉ cần dưa dữ liệu vào một mô hình embeddings nào đó thì sẽ cho ra dữ liệu vector.

Lại nói về tìm kiếm ngữ nghĩa (semantic) nó khác với tìm kiếm theo keyword (fulltext) truyền thống. Tìm kiếm fulltext sẽ dựa vào lượng ký tự văn bản nhập vào để so khớp và cho ra những đoạn có chứa nhiều hoặc khớp nhất với từ khoá. Trong khi tìm kiếm ngữ nghĩa lại theo khuynh hướng nội dung. Giả sử bài viết của bạn đang giải thích cách hoạt động của node.js thì khi tìm kiếm theo cụm từ "node.js hoạt động như thế nào?" khả năng cao semantic sẽ tìm thấy được bài viết này. Trong khi đó fulltext sẽ cố tìm xem bài viết nào có chứa các từ "node.js", "hoạt", "động", "như"... Tất nhiên là không đến mức đó vì các thuật toán fulltext đã có thể loại bỏ được những từ "thừa" hoặc gom nhóm những từ đồng nghĩa...

Để truy vấn dữ liệu vector cần tối thiểu 2 bước. Đầu tiên là biến câu truy vấn thành vector, sau đó sử dụng các hàm truy vấn. Ví dụ với pg-vector - là một extensions hỗ trợ vector cho Postgres có các hàm truy vấn:

pg-vector search

Bạn đọc có thể nhìn thấy L2 distance, Cosine distance, L1 distance... là các dạng so sánh vector. Tuỳ vào trường hợp sử dụng mà lựa chọn kiểu truy vấn cho hợp lý. Ví dụ như trong bài toán tìm kiếm, tôi lựa chọn kiểu Cosine distance - tức là hình dạng của 2 vector càng giống nhau thì càng tốt.

Cách làm

Flow

Đầu tiên là lựa chọn cơ sở dữ liệu phù hợp. Tôi đang dùng Turso làm cơ sở dữ liệu chính. Tuy nhiên Turso hoạt động dựa trên SQLite, không tối ưu cho dữ liệu dạng vector. Mặc dù họ có giới thiệu một extension để hỗ trợ cho vecter nhưng hơi phức tạp.

pg-vector thì ngược lại, được nhiều người sử dụng. Đây là một extensions dành cho Postgres. Nhắc đến Postgres thì lại nghĩ ngay đến Supabase - cho dùng miễn phí. Supabase tích hợp sẵn pg-vector, kích hoạt chỉ bằng một nút bấm nên nó là một sự lựa chọn tuyệt vời.

Bước tiếp theo là chọn models. Để tiết kiệm thì ngay từ đầu tôi đã tìm những models miễn phí, hoặc các dịch vụ cung cấp models miễn phí. Không thể không nhắc đến groq với các API Completions. Tuy nhiên groq không có các mô hình embeddings nên lại phải tìm một cái khác.

nomic-embed-text là một embeddings tìm thấy trong thư viện của Ollama. Nó có khả năng vector hoá văn bản. Ngoài ra Nomic cũng cung cấp API embeddings miễn phí kèm giới hạn. Nhưng cũng phải nhắc lại rằng nomic không hẳn là một mô hình đa ngôn ngữ, nó hỗ trợ tiếng Việt một cách hạn chế. Nên rất có thể vector cho ra không được tối ưa hoá theo ngữ nghĩa tiếng Việt.

Sau khi đã chuẩn bị xong các bước trên thì đến lượt viết mã để thêm dữ liệu vector và logic tìm kiếm.

Đầu tiên, cần chuyển nội dung bài viết thành vector và lưu vào Supabase. Ở đây thay vì chuyển toàn bộ nội dung bài viết thì tôi qua một bước tóm tắt lại nội dung chính của bài viết rồi mới đưa vào nomic-embed-text. Điều này giúp loại bỏ những ý phụ hoạ, tập trung vào nội dung chính và giảm lượng token đầu vào cho mô hình xử lý.

Một lưu ý nữa là mặc dù các models trên có API miễn phí nhưng chúng luôn đi kèm với giới hạn. Quá trình xử lý dữ liệu lần đầu rất tốn kém vì tôi có hơn 400 bài viết cả tiếng Việt lẫn tiếng Anh. Nên cách tốt hơn là chạy mô hình Llama 3.2 3B và nomic-embed-text ở dưới local. Ở đây sử dụng LM Studio.

Logic tìm kiếm cũng đơn giản. Nhận câu truy vấn của người dùng -> đi qua nomic-embed-text để thành vector -> truy vấn cosin với vector bài viết và sắp xếp theo khoảng cách gần nhất giữa 2 vector.

Tuy vậy, nếu người dùng tìm kiếm theo từ khoá ví dụ như node.js, javascript... thì nhiều khả năng semantic sẽ không cho ra kết quả vì dữ liệu quá ngắn, vector tạo ra không mang đủ ý, khiến cho khoảng cách cosin trở nên quá xa. Vậy nên để xử lý trường hợp này phải duy trì thêm một phong cách tìm kiếm fulltext. May mắn là Supabase cũng hỗ trợ loại tìm kiếm này.

Thách thức

Kể ra thì có vẻ đơn giản, tuy nhiên điều khó nhất với tôi là các bước tiền xử lý dữ liệu.

Một bài viết thường biểu đạt nhiều nội dung, có nội dung chính cả nội dung phụ hoạ. Thông thường thì người tìm kiếm chỉ quan tâm đến nội dung chính của bài viết và họ có khuynh hướng tìm kiếm những thứ liên quan đến nội dung chính. Nếu như chuyển đổi toàn bộ nội dung của bài viết thành vector sẽ làm "loãng" hoặc "nhiễu" vì kích thước của vector là có hạn. Tôi nghĩ rằng nếu loại bỏ được yếu tố phụ hoạ và tăng cường ý chính thì tìm kiếm sẽ chuẩn xác hơn. Tưởng tượng, một bài viết 1500 từ chuyển sang vector 1024 chiều, so với một bài chỉ chứa nội dung chính 500 từ cũng trong vector đó thì cái nào biểu thị "sắc nét" hơn?

Quy tắc tìm kiếm của người dùng cũng rất khó đoán vì mỗi người có cách tìm kiếm khác nhau. Có người thích ngắn gọn, cũng có người thích viết dài hoặc có người thích cung cấp cả ngữ cảnh cho câu hỏi... vì thế xử lý dữ liệu nhập vào của người dùng cũng là một thách thức, làm sao để chuyển đổi nó thành một câu hỏi ngắn gọn mà đủ ý và phù hợp với nội dung tìm kiếm trong blog.

Chất lượng mô hình AI sử dụng cũng là một vấn đề. Thường thì model nào được đào tạo nhiều thì càng tốt, cũng như model được thương mại hoá sẽ đi kèm với chất lượng. Nhưng vì giảm thiểu chi phí, hiện tại tôi đang sử dụng các mô hình LLMs miễn phí đi kèm với nhiều giới hạn. Hy vọng một ngày nào đó tôi sẽ tích hợp được các models mạnh mẽ hơn để tăng chất lượng tìm kiếm cho blog của mình.

Cao cấp
Hello

Bí mật ngăn xếp của Blog

Là một lập trình viên, bạn có tò mò về bí mật công nghệ hay những khoản nợ kỹ thuật về trang blog này? Tất cả bí mật sẽ được bật mí ngay bài viết dưới đây. Còn chờ đợi gì nữa, hãy bấm vào ngay!

Là một lập trình viên, bạn có tò mò về bí mật công nghệ hay những khoản nợ kỹ thuật về trang blog này? Tất cả bí mật sẽ được bật mí ngay bài viết dưới đây. Còn chờ đợi gì nữa, hãy bấm vào ngay!

Xem tất cả

Đăng ký nhận thông báo bài viết mới

hoặc
* Bản tin tổng hợp được gửi mỗi 1-2 tuần, huỷ bất cứ lúc nào.
Author

Xin chào, tôi tên là Hoài - một anh Dev kể chuyện bằng cách viết ✍️ và làm sản phẩm 🚀. Với nhiều năm kinh nghiệm lập trình, tôi đã đóng góp một phần công sức cho nhiều sản phẩm mang lại giá trị cho người dùng tại nơi đang làm việc, cũng như cho chính bản thân. Sở thích của tôi là đọc, viết, nghiên cứu... Tôi tạo ra trang Blog này với sứ mệnh mang đến những bài viết chất lượng cho độc giả của 2coffee.dev.Hãy theo dõi tôi qua các kênh LinkedIn, Facebook, Instagram, Telegram.

Bạn thấy bài viết này có ích?
Không

Bình luận (0)

Nội dung bình luận...