by Tom Rogers on 13 FEB 2025 in Amazon Bedrock, Amazon RDS, Artificial Intelligence, RDS for PostgreSQL, Serverless, Technical How-to
Trong bài viết này, chúng ta thảo luận về embeddings là gì, hướng dẫn cách sử dụng language embeddings trong thực tế, và khám phá cách tận dụng chúng để bổ sung các tính năng như zero-shot classification và semantic search. Sau đó, chúng ta sẽ sử dụng Amazon Bedrock và language embeddings để thêm các tính năng này vào ứng dụng RSS aggregator.
Amazon Bedrock là dịch vụ được quản lý hoàn toàn, cung cấp các foundation models (FMs) từ các startup AI hàng đầu và Amazon thông qua API, cho phép bạn lựa chọn từ nhiều FM để tìm mô hình phù hợp nhất với trường hợp sử dụng của mình. Amazon Bedrock mang đến trải nghiệm serverless, giúp bạn bắt đầu nhanh chóng, tùy chỉnh FM riêng với dữ liệu của bạn, và tích hợp triển khai chúng vào ứng dụng sử dụng dịch vụ Amazon Web Services (AWS) mà không cần quản lý hạ tầng. Trong bài này, chúng ta sử dụng Cohere v3 Embed model trên Amazon Bedrock để tạo language embeddings.
Trường hợp sử dụng: RSS aggregator
Để minh họa một số cách sử dụng language embeddings, chúng tôi đã phát triển website RSS aggregator. RSS là web feed cho phép các ấn phẩm xuất bản cập nhật theo cách chuẩn hóa, máy tính có thể đọc được. Trên website của chúng tôi, người dùng có thể đăng ký RSS feed và có danh sách bài viết mới được tổng hợp, phân loại. Chúng tôi sử dụng embeddings để thêm các chức năng sau:
- Zero-shot classification – Các bài viết được phân loại theo các chủ đề khác nhau. Có một số chủ đề mặc định như Công nghệ, Chính trị và Sức khỏe & Đời sống, như hiển thị trong ảnh chụp màn hình sau. Người dùng cũng có thể tạo chủ đề riêng.
- Semantic search – Người dùng có thể tìm kiếm bài viết bằng semantic search, như hiển thị trong ảnh chụp màn hình sau. Người dùng không chỉ có thể tìm kiếm một chủ đề cụ thể mà còn có thể thu hẹp tìm kiếm theo các yếu tố như giọng điệu hoặc phong cách.
Bài viết này sử dụng ứng dụng này làm điểm tham chiếu để thảo luận về việc triển khai kỹ thuật của các tính năng semantic search và zero-shot classification.
Tổng quan giải pháp
Giải pháp này sử dụng các dịch vụ sau:
- Amazon API Gateway – API có thể truy cập thông qua Amazon API Gateway. Caching được thực hiện trên Amazon CloudFront cho một số chủ đề để giảm tải database.
- Amazon Bedrock with Cohere v3 Embed – Các bài viết và chủ đề được chuyển đổi thành embeddings với sự hỗ trợ của Amazon Bedrock và Cohere v3 Embed.
- Amazon CloudFront và Amazon Simple Storage Service (Amazon S3) – Ứng dụng React single-page được lưu trữ sử dụng Amazon S3 và Amazon CloudFront.
- Amazon Cognito – Xác thực được thực hiện bằng Amazon Cognito user pools.
- Amazon EventBridge – Amazon EventBridge và EventBridge schedules được sử dụng để điều phối các cập nhật mới.
- AWS Lambda – API là ứng dụng Fastify được viết bằng TypeScript. Nó được lưu trữ trên AWS Lambda.
- Amazon Aurora PostgreSQL-Compatible Edition và pgvector – Amazon Aurora PostgreSQL-Compatible được sử dụng làm database, cả cho chức năng của ứng dụng và làm vector store sử dụng pgvector.
- Amazon RDS Proxy – Amazon RDS Proxy được sử dụng cho connection pooling.
- Amazon Simple Queue Service (Amazon SQS) – Amazon SQS được sử dụng để xếp hàng các sự kiện. Nó xử lý một sự kiện mỗi lần để không vượt quá giới hạn tốc độ của Cohere trong Amazon Bedrock.
Sơ đồ sau minh họa kiến trúc giải pháp.
Embeddings là gì?
Phần này giới thiệu tổng quan về embeddings và cách chúng được sử dụng.
Embeddings là các biểu diễn số học của các khái niệm hoặc đối tượng, như ngôn ngữ hoặc hình ảnh. Trong bài này, chúng ta thảo luận về language embeddings. Bằng cách chuyển đổi các khái niệm này thành biểu diễn số học, chúng ta có thể sử dụng chúng theo cách mà máy tính có thể hiểu và thao tác được.
Hãy lấy Berlin và Paris làm ví dụ. Là con người, chúng ta hiểu được mối liên hệ khái niệm giữa hai từ này. Berlin và Paris đều là thành phố, đều là thủ đô của quốc gia tương ứng, và đều nằm ở châu Âu. Chúng ta hiểu những điểm tương đồng này một cách bản năng, vì chúng ta có thể tạo ra mô hình thế giới trong đầu. Tuy nhiên, máy tính không có cách tích hợp sẵn nào để biểu diễn các khái niệm này.
Để biểu diễn các khái niệm này theo cách máy tính có thể hiểu được, chúng ta chuyển đổi chúng thành language embeddings. Language embeddings là các vector đa chiều học được mối quan hệ với nhau thông qua quá trình huấn luyện neural network. Trong quá trình huấn luyện, neural network được tiếp xúc với lượng văn bản khổng lồ và học các mẫu dựa trên cách các từ được đặt cạnh nhau và liên quan đến nhau trong các ngữ cảnh khác nhau.
Embedding vectors cho phép máy tính mô hình hóa thế giới từ ngôn ngữ. Ví dụ, nếu chúng ta embed “Berlin” và “Paris,” chúng ta có thể thực hiện các phép toán trên các embeddings này. Sau đó chúng ta có thể quan sát một số mối quan hệ thú vị. Chẳng hạn, chúng ta có thể thực hiện: Paris – France + Germany ≈ Berlin. Điều này là do embeddings nắm bắt được mối quan hệ giữa từ “Paris” và “France” cũng như giữa “Germany” và “Berlin”—cụ thể là Paris và Berlin đều là thủ đô của quốc gia tương ứng.
Biểu đồ sau đây cho thấy khoảng cách vector từ giữa các quốc gia và thủ đô tương ứng của chúng.
Biểu diễn embedding của các quốc gia và thủ đô.
Việc trừ “France” từ “Paris” loại bỏ ngữ nghĩa quốc gia, để lại một vector biểu diễn khái niệm thủ đô. Thêm “Germany” vào vector này, chúng ta có được thứ gì đó gần giống với “Berlin”, thủ đô của Đức. Các vector cho mối quan hệ này được hiển thị trong biểu đồ sau.
Trong trường hợp sử dụng của chúng tôi, chúng tôi sử dụng mô hình Cohere Embeddings đã được huấn luyện trước trong Amazon Bedrock, mô hình này embed toàn bộ văn bản thay vì một từ đơn lẻ. Các embeddings biểu diễn ý nghĩa của văn bản và có thể được thao tác bằng các phép toán. Tính chất này có thể hữu ích để ánh xạ các mối quan hệ như độ tương đồng giữa các văn bản.
Zero-shot classification
Một cách chúng tôi sử dụng language embeddings là bằng cách sử dụng các tính chất của chúng để tính toán độ tương đồng của một bài viết với một trong các chủ đề.
Để làm điều này, chúng tôi chia nhỏ một chủ đề thành một loạt các embeddings khác nhau và có liên quan. Ví dụ, đối với văn hóa, chúng tôi có một tập hợp embeddings cho thể thao, chương trình TV, âm nhạc, sách, v.v. Sau đó, chúng tôi embed tiêu đề và mô tả của các bài viết RSS đến, và tính toán độ tương đồng với các embeddings chủ đề. Từ đó, chúng tôi có thể gán nhãn chủ đề cho một bài viết.
Hình sau minh họa cách thức hoạt động. Các embeddings mà Cohere tạo ra có chiều cao, chứa 1.024 giá trị (hoặc chiều). Tuy nhiên, để minh họa cách hệ thống này hoạt động, chúng tôi sử dụng một thuật toán được thiết kế để giảm chiều của embeddings, t-distributed Stochastic Neighbor Embedding (t-SNE), để có thể xem chúng trong hai chiều. Hình ảnh sau sử dụng các embeddings này để trực quan hóa cách các chủ đề được phân cụm dựa trên độ tương đồng và ý nghĩa.
Bạn có thể sử dụng embedding của một bài viết và kiểm tra độ tương đồng của bài viết với các embeddings trước đó. Sau đó, bạn có thể nói rằng nếu một bài viết được phân cụm gần với một trong những embeddings này, nó có thể được phân loại với chủ đề liên quan.
Đây là thuật toán k-nearest neighbor (k-NN). Thuật toán này được sử dụng để thực hiện các tác vụ phân loại và hồi quy. Trong k-NN, bạn có thể đưa ra các giả định về một điểm dữ liệu dựa trên khoảng cách của nó với các điểm dữ liệu khác. Ví dụ, bạn có thể nói rằng một bài viết có độ gần với chủ đề âm nhạc được hiển thị trong sơ đồ trước có thể được gắn thẻ với chủ đề văn hóa.
Hình sau minh họa điều này với một bài viết của ArsTechnica. Chúng tôi vẽ đồ thị dựa trên embedding của tiêu đề và mô tả bài viết: (Khí hậu đang thay đổi nhanh đến mức chúng ta chưa thấy được mức độ tồi tệ của thời tiết cực đoan có thể xảy ra: Các thống kê hàng thập kỷ không còn đại diện cho những gì có thể xảy ra trong hiện tại).
Ưu điểm của cách tiếp cận này là bạn có thể thêm các chủ đề tùy chỉnh do người dùng tạo. Bạn có thể tạo một chủ đề bằng cách đầu tiên tạo một loạt các embeddings của các mục có liên quan về mặt khái niệm. Ví dụ, một chủ đề AI sẽ tương tự với các embeddings cho AI, Generative AI, LLM và Anthropic, như được hiển thị trong ảnh chụp màn hình sau.
Trong hệ thống phân loại truyền thống, chúng ta cần phải huấn luyện một bộ phân loại—một tác vụ học có giám sát trong đó chúng ta cần cung cấp một loạt các ví dụ để xác định xem một bài viết có thuộc chủ đề tương ứng hay không. Làm như vậy có thể là một tác vụ khá tốn kém, đòi hỏi dữ liệu có nhãn và huấn luyện mô hình. Đối với trường hợp sử dụng của chúng tôi, chúng tôi có thể cung cấp các ví dụ, tạo cụm và gắn thẻ các bài viết mà không cần phải cung cấp các ví dụ có nhãn hoặc huấn luyện các mô hình bổ sung. Điều này được hiển thị trong ảnh chụp màn hình trang kết quả của trang web của chúng tôi.
Trong ứng dụng của chúng tôi, chúng tôi tiếp nhận các bài viết mới theo lịch trình. Chúng tôi sử dụng EventBridge schedules để định kỳ gọi một Lambda function, kiểm tra xem có bài viết mới hay không. Nếu có, nó tạo một embedding từ chúng bằng cách sử dụng Amazon Bedrock và Cohere.
Chúng tôi tính toán khoảng cách của bài viết đến các embeddings chủ đề khác nhau, và sau đó có thể xác định xem bài viết có thuộc danh mục đó hay không. Điều này được thực hiện với Aurora PostgreSQL với pgvector. Chúng tôi lưu trữ các embeddings của các chủ đề và sau đó tính toán khoảng cách của chúng bằng truy vấn SQL sau:
const topics = await sqlClient.then(it=> it.query(
`SELECT name, embedding_description, similarity
FROM (SELECT topic_id as name, embedding_description, (1- ABS( 1 –(embed.embedding <-> $1))) AS “similarity” FROM topic_embedding_link embed) topics
ORDER BY similarity desc`,
[toSql(articleEmbedding)]
))
Toán tử <-> trong đoạn mã trước tính toán khoảng cách Euclidean giữa bài viết và topic embedding. Con số này cho phép chúng ta hiểu được mức độ gần gũi của một bài viết với một trong các chủ đề. Sau đó, chúng ta có thể xác định sự phù hợp của một chủ đề dựa trên thứ hạng này.
Tiếp theo, chúng ta gắn tag chủ đề cho bài viết. Chúng ta làm điều này để các yêu cầu tiếp theo về chủ đề được xử lý nhẹ nhàng nhất có thể về mặt tính toán; chúng ta thực hiện một phép join đơn giản thay vì phải tính toán lại khoảng cách Euclidean.
const formattedTopicInsert = pgformat(
`INSERT INTO feed_article_topic_link(topic_id, feed_article_id) VALUES %L ON CONFLICT DO NOTHING`,
topicLinks
)
Chúng tôi cũng cache một tổ hợp topic/feed cụ thể vì chúng được tính toán theo giờ và không mong đợi sẽ thay đổi trong khoảng thời gian đó.
Semantic search
Như đã thảo luận trước đây, các embeddings được tạo ra bởi Cohere chứa nhiều đặc trưng; chúng nhúng các ý nghĩa và ngữ nghĩa của một từ hoặc cụm từ. Chúng tôi cũng phát hiện ra rằng chúng ta có thể thực hiện các phép toán trên các embeddings này để làm những việc như tính toán độ tương đồng giữa hai cụm từ hoặc từ.
Chúng ta có thể sử dụng các embeddings này và tính toán độ tương đồng giữa một từ khóa tìm kiếm và embedding của một bài viết với thuật toán k-NN để tìm các bài viết có ngữ nghĩa và ý nghĩa tương tự với từ khóa tìm kiếm đã cung cấp.
Ví dụ, trong một trong các RSS feed của chúng tôi, chúng tôi có nhiều bài viết đánh giá sản phẩm khác nhau. Trong hệ thống tìm kiếm truyền thống, chúng ta sẽ dựa vào việc khớp từ khóa để cung cấp kết quả liên quan. Mặc dù có thể dễ dàng tìm một bài viết cụ thể (ví dụ: bằng cách tìm kiếm “sổ tay kỹ thuật số tốt nhất”), chúng ta sẽ cần một phương pháp khác để nắm bắt nhiều bài viết danh sách sản phẩm.
Trong hệ thống semantic search, trước tiên chúng ta chuyển đổi thuật ngữ “Product list” thành một embedding. Sau đó, chúng ta có thể sử dụng các thuộc tính của embedding này để thực hiện tìm kiếm trong không gian embedding của chúng ta. Sử dụng thuật toán k-NN, chúng ta có thể tìm các bài viết có ngữ nghĩa tương tự. Như được hiển thị trong ảnh chụp màn hình sau, mặc dù không chứa văn bản “Product list” trong tiêu đề hoặc mô tả, chúng tôi đã có thể tìm thấy các bài viết chứa danh sách sản phẩm. Điều này là do chúng tôi đã có thể nắm bắt ngữ nghĩa của truy vấn và khớp nó với các embeddings hiện có cho mỗi bài viết.
Trong ứng dụng của chúng tôi, chúng tôi lưu trữ các embeddings này bằng pgvector trên Aurora PostgreSQL. pgvector là một extension mã nguồn mở cho phép tìm kiếm tương đồng vector trong PostgreSQL. Chúng tôi chuyển đổi từ khóa tìm kiếm thành embedding bằng Amazon Bedrock và Cohere v3 Embed.
Sau khi chúng tôi đã chuyển đổi từ khóa tìm kiếm thành embedding, chúng ta có thể so sánh nó với các embeddings trên bài viết đã được lưu trong quá trình tiếp nhận. Sau đó, chúng ta có thể sử dụng pgvector để tìm các bài viết được nhóm lại với nhau. Mã SQL cho việc đó như sau:
SELECT *
FROM (
SELECT feed_articles.id as id, title, feed_articles.feed_id as feed, feedName, slug, description, url, author, image, published_at as published, 1 – ABS(1 – (embedding <-> $2)) AS “similarity”
FROM feed_articles
INNER JOIN (select feed_id, name as feedName from feed_user_subscription fus where fus.user_id=$1) sub on feed_articles.feed_id=sub.feed_id
${feedId != undefined ? `WHERE feed_articles.feed_id = $4` : “”}
)
WHERE similarity > 0.95
ORDER BY similarity desc
LIMIT $3;
Mã này tính toán khoảng cách giữa các chủ đề và embedding của bài viết này như “độ tương đồng”. Nếu khoảng cách này gần, chúng ta có thể giả định rằng chủ đề của bài viết có liên quan và do đó gắn chủ đề vào bài viết.
Điều kiện tiên quyết
Để triển khai ứng dụng này trong tài khoản của bạn, bạn cần các điều kiện tiên quyết sau:
- Một tài khoản AWS đang hoạt động.
- Quyền truy cập model cho Cohere Embed English. Trên Amazon Bedrock console, chọn Model access trong navigation pane, sau đó chọn Manage model access. Chọn các FM bạn muốn và yêu cầu quyền truy cập.
- AWS Cloud Development Kit (AWS CDK) đã được thiết lập. Để biết hướng dẫn cài đặt, tham khảo Getting started with the AWS CDK.
- Một virtual private cloud (VPC) được thiết lập với quyền truy cập vào private VPCs. Để biết thêm thông tin, tham khảo Create a VPC.
Triển khai AWS CDK stack
Khi các bước điều kiện tiên quyết hoàn tất, bạn đã sẵn sàng để thiết lập giải pháp:
- Clone repository GitHub chứa các file giải pháp:
git clone https://github.com/aws-samples/rss-aggregator-using-cohere-embeddings-bedrock
- Di chuyển đến thư mục giải pháp:
cd infrastructure
- Trong terminal, export thông tin xác thực AWS cho role hoặc user trong ACCOUNT_ID. Role cần có đầy đủ quyền để triển khai AWS CDK:
- export AWS_REGION=”<region>”
– Region AWS bạn muốn triển khai ứng dụng
- export AWS_ACCESS_KEY_ID=”<access-key>”
– Access key của role hoặc user của bạn
- export AWS_SECRET_ACCESS_KEY=”<secret-key>”
– Secret key của role hoặc user của bạn
- Nếu bạn triển khai AWS CDK lần đầu tiên, chạy lệnh sau:
cdk bootstrap
- Để tổng hợp template AWS CloudFormation, chạy lệnh:
cdk synth -c vpc_id=<ID Of your VPC>
- Để triển khai, sử dụng lệnh:
cdk deploy -c vpc_id=<ID Of your VPC>
Khi triển khai hoàn tất, bạn có thể kiểm tra các stack đã triển khai bằng cách truy cập vào AWS CloudFormation console.
Dọn dẹp
Chạy lệnh sau trong terminal để xóa stack CloudFormation được tạo bằng AWS CDK:
cdk destroy –all
Kết luận
Trong bài viết này, chúng ta đã tìm hiểu về language embeddings và cách chúng có thể được sử dụng để nâng cao ứng dụng của bạn. Chúng ta đã học được cách sử dụng các thuộc tính của embeddings để triển khai zero-shot classifier thời gian thực và có thể thêm các tính năng mạnh mẽ như semantic search.
Code cho ứng dụng này có thể được tìm thấy trong repo GitHub đi kèm. Chúng tôi khuyến khích bạn thử nghiệm với language embeddings và khám phá những tính năng mạnh mẽ mà chúng có thể mang lại cho ứng dụng của bạn!