Pgvector là gì và nó có thể giúp ích gì cho cơ sở dữ liệu Vector của bạn?

,

・Published on:

Có vô số cách để tăng tốc khối lượng công việc của Postgres. Tất cả phụ thuộc vào cách bạn lưu trữ và truy vấn dữ liệu, dung lượng dữ liệu và tần suất bạn chạy các truy vấn đó.

Trong bài đăng trên blog này, chúng ta sẽ khám phá cách pgvector có thể hỗ trợ khối lượng công việc dựa trên AI trong Postgres, giúp các thao tác vectơ cơ sở dữ liệu của bạn nhanh hơn và hiệu quả hơn.

pgvector: Lưu trữ và truy vấn dữ liệu vectơ trong Postgres

pgvector là tiện ích mở rộng của PostgreSQL cho phép bạn lưu trữ, truy vấn và lập chỉ mục các vector.

Kể từ Postgres 16, Postgres không có sẵn khả năng vector gốc, và Postgres pgvector được thiết kế để lấp đầy khoảng trống này. Bạn có thể lưu trữ dữ liệu vector cùng với phần còn lại của dữ liệu trong Postgres, thực hiện tìm kiếm độ tương đồng vector và vẫn tận dụng được tất cả các tính năng tuyệt vời của Postgres.

AI cần tìm kiếm sự tương đồng của vectơ?

Khi làm việc với dữ liệu đa chiều, đặc biệt là trong các ứng dụng như công cụ đề xuất, tìm kiếm hình ảnh và xử lý ngôn ngữ tự nhiên, tìm kiếm tương đồng vector đóng vai trò quan trọng. Nhiều ứng dụng AI liên quan đến việc tìm kiếm các mục hoặc đề xuất tương tự dựa trên hành vi người dùng hoặc độ tương đồng của nội dung. pgvector có thể thực hiện tìm kiếm tương đồng vector một cách hiệu quả, phù hợp với các hệ thống đề xuất, lọc dựa trên nội dung và các tác vụ AI dựa trên độ tương đồng.

Tiện ích mở rộng pgvector tích hợp liền mạch với Postgres – cho phép người dùng tận dụng các tính năng của nó trong cơ sở hạ tầng cơ sở dữ liệu hiện có. Điều này giúp đơn giản hóa việc triển khai và quản lý các ứng dụng AI, vì không cần kho dữ liệu riêng biệt hoặc quy trình truyền dữ liệu phức tạp.

Vector thực chất là gì?

Vectơ là danh sách các số. Nếu bạn đã học đại số tuyến tính, đây chính là lúc để tận dụng lợi ích, vì tìm kiếm tương tự thực hiện rất nhiều phép toán vectơ!

Trong hình học, một vectơ biểu diễn một tọa độ trong không gian n chiều, trong đó n là số chiều. Trong hình dưới đây, có một vectơ hai chiều (n = 2). Trong học máy, chúng ta sử dụng các vectơ nhiều chiều, không dễ hình dung như vectơ đơn giản được hiển thị bên dưới.

Hướng dẫn pgvector từng bước

Trong ví dụ này, chúng ta sẽ lưu trữ một vài tài liệu, tạo nhúng Postgres và lưu trữ các nhúng này. Chúng ta sẽ lập chỉ mục dữ liệu nhúng và chạy truy vấn tương tự trên chúng.

Điều kiện tiên quyết:

  • Đã cài đặt PostgreSQL (pgvector hỗ trợ PostgreSQL 11+)
  • phần mở rộng pgvector đã được cài đặt
  • Tài khoản OpenAPI và có một số dư tín dụng (sử dụng ít hơn 1 đô la).

Nếu bạn sử dụng postgresql được build từ docker thì bạn cần config thêm extension như sau:

FROM postgres:16

RUN apt-get update && \
    apt-get install -y postgresql-16-pgvector && \
    rm -rf /var/lib/apt/lists/*
  • Bàn cài thêm extension như vậy là vector đã được kích hoạt rồi nha.

Bước 1: Tạo bảng cho tài liệu

Hãy tạo một bảng đơn giản để lưu trữ tài liệu. Mỗi hàng trong bảng này đại diện cho một tài liệu, và chúng ta lưu trữ tiêu đề và nội dung của tài liệu.

Tạo bảng tài liệu:

CREATE TABLE documents (
    id int PRIMARY KEY,
    content text NOT NULL,
    vector vector(1536) NOT NULL
);

Chúng ta sẽ tạo một nhúng cho mỗi tài liệu lưu trữ và tạo một bảng document_embeddings để lưu trữ chúng. Bạn có thể thấy vector nhúng có kích thước là 1536 vì mô hình OpenAI chúng ta đang sử dụng có 1536 chiều.

Tôi sẽ thảo luận về lập chỉ mục trong cơ sở dữ liệu vector trong bài đăng trên blog tiếp theo, vì vậy tôi sẽ không đi sâu vào chi tiết ở đây, nhưng chúng ta biết rằng HNSW có hiệu suất truy vấn tốt hơn IVFFlat.

Ngoài ra, đối với chỉ mục IVFFlat , tốt nhất nên tạo chỉ mục sau khi bảng đã có dữ liệu. Đối với chỉ mục HNSW, không có bước huấn luyện nào như với IVFFlat, vì vậy chỉ mục có thể được tạo mà không cần dữ liệu trong bảng. Bạn có thể nhận thấy rằng tôi đã tạo chỉ mục trước khi chèn dữ liệu vào bảng, theo gợi ý.

Bây giờ, chúng ta có thể chèn một số dữ liệu mẫu vào bảng. Trong ví dụ này, tôi chọn tiện ích mở rộng Postgres và mô tả ngắn gọn của chúng.

Bước 2: Tạo nhúng

Bây giờ tài liệu của chúng ta đã được lưu trữ, chúng ta sẽ sử dụng mô hình nhúng để chuyển đổi chúng thành các tài liệu nhúng.

Nguồn hình ảnh: https://cdn.openai.com/new-and-improved-embedding-model/draft-20221214a/vectors-1.svg

Nhưng trước tiên, hãy nói về nhúng. Tôi thích định nghĩa trong tài liệu OpenAI nhất vì nó đơn giản và đúng trọng tâm:

Nhúng là một vectơ (danh sách) các số dấu phẩy động. Khoảng cách giữa hai vectơ đo lường mức độ liên quan của chúng. Khoảng cách nhỏ cho thấy mức độ liên quan cao, và khoảng cách lớn cho thấy mức độ liên quan thấp.

Vì vậy, nếu chúng ta so sánh mức độ liên quan về mặt ngữ nghĩa của hai tài liệu, chúng ta sẽ phải chuyển đổi các tài liệu đó thành các phần nhúng và chạy tìm kiếm điểm tương đồng trên chúng.

Bạn có thể chọn nhà cung cấp API và sử dụng các API này với ngôn ngữ bạn muốn. Tôi sẽ sử dụng NestJs để làm ví dụ:

const response = await this.openAiService.client.embeddings.create({
                model: 'text-embedding-3-small',
                input: content,
            });
const vectorEmbedding = response.data[0].embedding;

const insertDto = this.documentRepository.create({
            content: createDocumentDto.content,
            vector: vectorEmbedding,
        })

const result = await this.documentRepository.insert(insertDto);

Mã này lấy nội dung tài liệu từ cơ sở dữ liệu, sử dụng API OpenAI để tạo nhúng, rồi lưu trữ các nhúng này trở lại cơ sở dữ liệu. Mặc dù phương pháp này hiệu quả với cơ sở dữ liệu nhỏ của chúng tôi, nhưng trong thực tế, bạn sẽ muốn sử dụng xử lý hàng loạt trên dữ liệu hiện có và kích hoạt sự kiện, hoặc thay đổi luồng để cập nhật các vectơ khi cơ sở dữ liệu thay đổi.

Bước 3: Truy vấn nhúng

Bây giờ chúng ta đã lưu trữ các nhúng trong cơ sở dữ liệu, chúng ta có thể truy vấn chúng bằng pgvector.

Đoạn mã dưới đây cho biết cách thực hiện tìm kiếm tương tự để tìm các tài liệu tương tự với một tài liệu truy vấn nhất định.

const response = await this.openAiService.client.embeddings.create({
                model: 'text-embedding-3-small',
                input: question,
            });
const vectorEmbedding = response.data[0].embedding;

const context = await this.documentRepository.findDocumentContext(vectorEmbedding, DocumentType.text, 2);
findDocumentContext(vector: number[], type: DocumentType, limit: number): Promise<DocumentEntity[]> {
        return this.createQueryBuilder('doc')
            .where({ type })
            .orderBy('doc.vector <-> :embedding', 'ASC')
            .setParameters({ embedding: JSON.stringify(vector) })
            .limit(limit)
            .select(['id', 'content'])
            .getRawMany();
}

Truy vấn đầu tiên sẽ lấy một vectơ nhúng cho tài liệu có tiêu đề “pgvector”, sau đó sử dụng tìm kiếm tương tự để tìm các tài liệu có nội dung tương tự. Lưu ý toán tử “<->”: đó là nơi diễn ra tất cả các phép thuật pgvector. Đó là cách chúng ta xác định độ tương đồng giữa hai vectơ bằng chỉ mục HNSW.

Sau khi tìm kiếm được nội dung tương đồng thì bạn dùng nội dung này để làm context cho chatGPT để thực hiện trả lời câu hỏi.

const stringBuilder: Array<string> = [`Câu hỏi: ${question}`, `Context: ${context}`];
const completion = await this.openAiService.client.chat.completions.create({
                model: 'gpt-4o',
                response_format: { type: 'text' },
                messages: [
                    {
                        role: 'system',
                        content: `Bạn hãy cung cấp thông tin chính xác dựa trên dữ liệu mà trường cung cấp..`,
                    },
                    {
                        role: 'user',
                        content: stringBuilder.join('\n'),
                    },
                ],
            });
const result = completion.choices[0].message.content

Kết quả trả về là result. Bạn có thể cầm kết quả này trả ra cho người dùng.

Kết quả

Khi chúng tôi chạy tập lệnh truy vấn trên dữ liệu đã nhập, chúng tôi thấy rằng tìm kiếm tương tự đã tìm thấy hai tài liệu tương tự như pgvector, một trong số đó chính là pgvector.