Tìm hiểu về cơ sở dữ liệu vector và ứng dụng (Phần 2): Hands-on
Cùng tiếp tục giải mã cơ sở dữ liệu vector qua bài demo tạo ứng dụng gợi ý bài viết đơn giản sử dụng Pipecone.
Giới thiệu
Ở phần trước, chúng ta đã tìm hiểu về khái niệm cũng như chức năng của hệ cơ sở dữ liệu vector.
Trong phần này, chúng ta sẽ cùng làm một ứng dụng AI đơn giản sử dụng cơ sở dữ liệu Pinecone. Ứng dụng này có chức năng phân tích thói quen đọc báo của người dùng để gợi ý các bài báo tương tự.
Kiến trúc chung
-
Hệ thống gợi ý bài viết đơn giản này bao gồm 2 phần:
- Trích xuất dữ liệu, tạo ra các embedding của bài viết và ghi vào cơ sở dữ liệu vector. Ở đây, embedding có thể hiểu là phương pháp chuyển đổi từ dữ liệu vector có số chiều lớn (bài báo) thành dữ liệu vector có số chiều nhỏ hơn để dễ tính toán.
- Lấy thông tin lịch sử đọc bài của người dùng để truy vấn ra những bài viết tương tự dựa trên các embedding đã lưu.
Kiến trúc của hệ thống gợi ý bài viết
- Trong ví dụ này chúng ta sử dụng bộ dữ liệu All the News 2.0. Đây là bộ dữ liệu bao gồm 2,7 triệu bài báo trong thời gian từ năm 2016 đến 2020 từ 27 nhà xuất bản báo chí tại Mỹ.
- Để trích xuất embedding từ dữ liệu văn bản, chúng ta sẽ sử dụng mô hình ngôn ngữ all-MiniLM-L6-v2. Đây là một mô hình ngôn ngữ đơn giản gồm 6 lớp, dựa trên mô hình đa ngôn ngữ Multilingual-MiniLM-L12-H384 của Microsoft. Đặc biệt, mô hình của Microsoft cũng có hỗ trợ phân tích ngôn ngữ Tiếng Việt cũng như nhiều ngôn ngữ phổ biến khác. Các bạn có thể tìm hiểu thêm mô hình ngôn ngữ này* tại đây*.
Thực hành
Chuẩn bị tài khoản Pinecone
- Tạo tài khoản miễn phí trên Pinecone.io. Các bạn có thể liên kết bằng tài khoản Google hoặc Github có sẵn.
Giao diện đăng nhập tài khoản Pinecone
-
Tạo 1 dự án demo-vector với thông tin như sau:
- Tên dự án: demo-vector
- Dịch vụ cloud: GCP
- Khu vực: South East Asia
Hướng dẫn tạo Pinecone Project
- Tạo khóa API cho dự án với tên demo như sau:
Hướng dẫn tạo Pipecone API
Chuẩn bị môi trường lập trình
- Cài đặt NodeJS phiên bản LTS từ 18 trở lên.
- Cài đặt các công cụ lập trình cơ bản như VS Code, Docker, Git,...
- Đối với máy sử dụng Windows, yêu cầu cài thêm Visual Studio 2017 trở lên với C++ Development option.
Chuẩn bị mã nguồn
-
Đầu tiên lấy mã nguồn từ github:
git clone https://github.com/pinecone-io/recommender-example-typescript cd recommender-example-typescript/
-
Tiếp theo chạy lệnh
npm install
để cài các gói thư viện cần thiết cho ứng dụng. -
Cập nhật file
.env.example
với các thông tin đã tạo ở bước trước.PINECONE_API_KEY=<your-api-key> PINECONE_ENVIRONMENT=asia-southeast1-gcp-free PINECONE_INDEX=article-recommendations
-
Đổi tên
.env.example
thành.env
để ứng dụng tự động cập nhật các thông số môi trường.cp .env.example .env
Chuẩn bị dữ liệu
-
Lấy bộ dữ liệu tại đây và lưu vào thư mục
data/
trong bộ mã nguồn vừa lấy về.wget https://www.dropbox.com/s/cn2utnr5ipathhh/all-the-news-2-1.zip -q --show-progress unzip -q all-the-news-2-1.zip mkdir data mv all-the-news-2-1.csv data/.
-
Sau khi giải nén, bộ dữ liệu có dung lượng khoảng 8.5GB. Mỗi dòng dữ liệu trong tập tin sẽ bao gồm các thông tin như sau:
- date (str): Ngày giờ xuất bản của bài viết. - year (int): Năm xuất bản. - month (float): Tháng xuất bản. - day (int): Ngày xuất bản. - author (str): Tác giả nếu có. Nếu có nhiều tác giả thì các tác giả cách nhau bằng dấu phẩy (,). - title (str): Tiêu đề bài viết. - article (str): Nội dung bài viết, được lược bỏ toàn bộ ký tự phân đoạn. - url (str): URL của bài viết. - section (str): Chủ đề củ bài viết nếu có. - publication (str): Tên nhà xuất bản.
Tạo chỉ mục vector
-
Để tạo chỉ mục và ghi vào cơ sở dữ liệu vector, ta chạy lệnh
npm run index
. Tùy vào tốc độ xử lý của máy và đường truyền mạng, việc ghi dữ liệu có thể lên đến hàng giờ. Trong thời gian đó, chúng ta sẽ tìm hiểu chi tiết hơn việc tạo chỉ mục này. -
Đầu tiên, trong file
src/index.ts
chúng ta sẽ thấy hàm đoạn code để chia fileall-the-news-2-1.csv
thành các file nhỏ như sau:const fileParts = await splitFile("./data/all-the-news-2-1.csv", 500000); const firstFile = fileParts[0];
-
Vào thư mục
/data
chúng ta sẽ thấy các phần của fileall-the-news-2-1.csv
đã được cắt nhỏ thành 18 file con.
Các phần của file all-the-news-2-1.csv
-
Tiếp theo, các file này sẽ được xử lý tuần tự
processInChunks
async function* processInChunks<T, M extends keyof T, P extends keyof T>( dataFrame: dfd.DataFrame, chunkSize: number, metadataFields: M[], pageContentField: P ): AsyncGenerator<Document[]> { for (let i = 0; i < dataFrame.shape[0]; i += chunkSize) { const chunk = await getChunk(dataFrame, i, chunkSize); const records = dfd.toJSON(chunk) as T[]; yield records.map((record: T) => { const metadata: Partial<Record<M, T[M]>> = {}; for (const field of metadataFields) { metadata[field] = record[field]; } return new Document({ pageContent: record[pageContentField] as string, metadata, }); }); } }
-
Sau khi tạo xong, các bạn có thể vào Pinecone để kiểm tra lại dữ liệu:
Kiểm tra dữ liệu trong Pipecone
Truy vấn dữ liệu
-
Để tiện so sánh kết quả, chúng ta sẽ tạo 1 người dùng giả lập với danh sách 10 bài viết đã đọc dựa trên chủ đề và nội dung đã đọc:
const index = pinecone.index<ArticleRecord>(indexName).namespace('default'); await embedder.init("Xenova/all-MiniLM-L6-v2"); const { query, section } = getQueryingCommandLineArguments(); // We create a simulated user with an interest given a query and a specific section const queryEmbedding = await embedder.embed(query) const queryResult = await index.query({ vector: queryEmbedding.values, includeMetadata: true, includeValues: true, filter: { section: { "$eq": section } }, topK: 10 });
-
Khi làm thực tế thì lịch sử bài viết đã đọc sẽ được lưu trong 1 bảng riêng và chúng ta sẽ trích xuất thông tin embedding từ bảng này.
-
Tiếp theo, dựa trên các bài viết đã đọc, chúng ta sẽ tính vector trung bình bằng hàm
mean
như sau:// We extract the vectors of the results const userVectors = queryResult?.matches?.map((result: ScoredPineconeRecord<ArticleRecord>) => result.values); // A couple of functions to calculate mean vector const mean = (arr: number[]): number => arr.reduce((a, b) => a + b, 0) / arr.length; const meanVector = (vectors: number[][]): number[] => { const { length } = vectors[0]; return Array.from({ length }).map((_, i) => mean(vectors.map(vec => vec[i])) ); }; // We calculate the mean vector of the results // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const meanVec = meanVector(userVectors!);
-
Cuối cùng, chúng ta lấy 10 bài viết tương tự với vector đã tính bằng cách truy vấn Pinecone như sau:
const recommendations = await pineconeIndex.query({ queryRequest: { vector: meanVec, includeMetadata: true, includeValues: true, namespace: "default", topK: 10, }, });
-
Chúng ta có thể thử gợi ý cho 1 người dùng quan tâm đến chủ đề thể thao và nội dung liên quan đến quần vợt như sau:
npm run recommend -- --query="tennis" --section="Sports"
. Kết quả nhận được bao gồm:
> Lịch sử đọc bài
> Gợi ý bài đọc
-
Có thể thấy hệ thống gợi ý hoạt động khá tốt với các bài viết đều liên quan đến quần vợt. Các bạn cũng có thể tự thử với các chủ đề và truy vấn khác như:
-
Tìm kiếm bài viết có liên quan đến Trung Quốc trong chủ đề chính trị:
npm run recommend -- --query="China" --section="Politics"
-
Tìm kiếm bài viết có liên quan đến Xbox trong chủ đề game:
npm run recommend -- --query="Xbox" --section="Games"
-
-
Trong thực tế, chúng ta sẽ cần thêm bước lọc dữ liệu để bỏ bớt các các gợi ý bài viết mà người dùng đã đọc rồi hoặc các bài viết quá cũ, không còn phù hợp.
Kết luận
Như vậy chúng ta đã có thể tạo ra một hệ thống gợi ý bài viết đơn giản, hiệu quả sử dụng cơ sở dữ liệu vector. Với một chút chỉnh sửa nho nhỏ, các bạn có thể dễ dàng thay Pinecone bằng một cơ sở dữ liệu vector khác như Milvus hoặc PGVector.
Ngoài ra, các bạn cũng có thể sử dụng các bộ dữ liệu khác để tạo ra một hệ thống gợi ý của riêng mình, ví dụ như gợi ý bài viết tiếng Việt.
Tài liệu tham khảo