Tìm kiếm bằng cách nhúng vectơ

Trang này cho bạn biết cách sử dụng Cloud Firestore để thực hiện giá trị K-gần nhất tìm kiếm vectơ lân cận (KNN) bằng cách sử dụng các kỹ thuật sau:

  • Lưu trữ giá trị vectơ
  • Tạo và quản lý chỉ mục vectơ KNN
  • Thực hiện truy vấn K-gần nhất-neighbor (KNN) bằng cách sử dụng một trong các vectơ được hỗ trợ đo khoảng cách

Lưu trữ các vectơ nhúng

Bạn có thể tạo các giá trị vectơ như Nhúng văn bản từ Cloud Firestore và lưu trữ chúng trong Cloud Firestore tài liệu.

Viết toán tử có nhúng vectơ

Ví dụ sau đây cho thấy cách lưu trữ một vectơ nhúng trong Tài liệu Cloud Firestore:

Python
from google.cloud import firestore
from google.cloud.firestore_v1.vector import Vector

firestore_client = firestore.Client()
collection = firestore_client.collection("coffee-beans")
doc = {
    "name": "Kahawa coffee beans",
    "description": "Information about the Kahawa coffee beans.",
    "embedding_field": Vector([1.0, 2.0, 3.0]),
}

collection.add(doc)
Node.js
import {
  Firestore,
  FieldValue,
} from "@google-cloud/firestore";

const db = new Firestore();
const coll = db.collection('coffee-beans');
await coll.add({
  name: "Kahawa coffee beans",
  description: "Information about the Kahawa coffee beans.",
  embedding_field: FieldValue.vector([1.0 , 2.0, 3.0])
});

Tính toán các mục nhúng vectơ bằng Hàm đám mây

Để tính toán và lưu trữ các mục nhúng vectơ bất cứ khi nào một tài liệu được cập nhật, hoặc tạo, bạn có thể thiết lập Chức năng đám mây:

Python
@functions_framework.cloud_event
def store_embedding(cloud_event) -> None:
  """Triggers by a change to a Firestore document.
  """
  firestore_payload = firestore.DocumentEventData()
  payload = firestore_payload._pb.ParseFromString(cloud_event.data)

  collection_id, doc_id = from_payload(payload)
  # Call a function to calculate the embedding
  embedding = calculate_embedding(payload)
  # Update the document
  doc = firestore_client.collection(collection_id).document(doc_id)
  doc.set({"embedding_field": embedding}, merge=True)
Node.js
/**
 * A vector embedding will be computed from the
 * value of the `content` field. The vector value
 * will be stored in the `embedding` field. The
 * field names `content` and `embedding` are arbitrary
 * field names chosen for this example.
 */
async function storeEmbedding(event: FirestoreEvent<any>): Promise<void> {
  // Get the previous value of the document's `content` field.
  const previousDocumentSnapshot = event.data.before as QueryDocumentSnapshot;
  const previousContent = previousDocumentSnapshot.get("content");

  // Get the current value of the document's `content` field.
  const currentDocumentSnapshot = event.data.after as QueryDocumentSnapshot;
  const currentContent = currentDocumentSnapshot.get("content");

  // Don't update the embedding if the content field did not change
  if (previousContent === currentContent) {
    return;
  }

  // Call a function to calculate the embedding for the value
  // of the `content` field.
  const embeddingVector = calculateEmbedding(currentContent);

  // Update the `embedding` field on the document.
  await currentDocumentSnapshot.ref.update({
    embedding: embeddingVector,
  });
}

Tạo và quản lý chỉ mục vectơ

Trước khi bạn có thể thực hiện tìm kiếm lân cận gần nhất bằng các mục nhúng vectơ, bạn phải tạo chỉ mục tương ứng. Các ví dụ sau minh hoạ cách tạo và quản lý chỉ mục vectơ.

Tạo chỉ mục vectơ

Trước khi bạn tạo chỉ mục vectơ, hãy nâng cấp lên phiên bản mới nhất của Google Cloud CLI:

gcloud components update

Để tạo chỉ mục vectơ, hãy sử dụng gcloud firestore indexes composite create:

gcloud
gcloud firestore indexes composite create \
--collection-group=collection-group \
--query-scope=COLLECTION \
--field-config field-path=vector-field,vector-config='vector-configuration' \
--database=database-id

trong đó:

  • collection-group là mã nhận dạng của nhóm thu thập.
  • vector-field là tên của trường chứa nhúng vectơ.
  • database-id là mã nhận dạng của cơ sở dữ liệu.
  • vector-configuration bao gồm vectơ dimension và loại chỉ mục. dimension là một số nguyên có giá trị tối đa đến 2048. Loại chỉ mục phải là flat. Định dạng cấu hình chỉ mục như sau: {"dimension":"DIMENSION", "flat": "{}"}.

Ví dụ sau đây sẽ tạo một chỉ mục tổng hợp, bao gồm cả chỉ mục vectơ cho trường vector-field và chỉ mục tăng dần cho trường color. Bạn có thể sử dụng loại chỉ mục này để lọc trước trước khi tìm kiếm lân cận gần nhất.

gcloud
gcloud firestore indexes composite create \
--collection-group=collection-group \
--query-scope=COLLECTION \
--field-config=order=ASCENDING,field-path="color" \
--field-config field-path=vector-field,vector-config='{"dimension":"1024", "flat": "{}"}' \
--database=database-id

Liệt kê tất cả chỉ mục vectơ

gcloud
gcloud firestore indexes composite list --database=database-id

Thay thế database-id bằng mã nhận dạng của cơ sở dữ liệu.

Xoá chỉ mục vectơ

gcloud
gcloud firestore indexes composite delete index-id --database=database-id

trong đó:

  • index-id là mã nhận dạng của chỉ mục cần xoá. Sử dụng indexes composite list để truy xuất mã chỉ mục.
  • database-id là mã nhận dạng của cơ sở dữ liệu.

Mô tả một chỉ mục vectơ

gcloud
gcloud firestore indexes composite describe index-id --database=database-id

trong đó:

  • index-id là mã nhận dạng của chỉ mục cần mô tả. Sử dụng hoặc indexes composite list để truy xuất mã chỉ mục.
  • database-id là mã nhận dạng của cơ sở dữ liệu.

Thực hiện truy vấn lân cận gần nhất

Bạn có thể thực hiện tìm kiếm tương tự để tìm hàng xóm gần nhất của một nhúng vectơ. Tìm kiếm tương tự yêu cầu chỉ mục vectơ. Nếu một chỉ mục không tồn tại, Cloud Firestore sẽ đề xuất một chỉ mục để tạo bằng cách sử dụng gcloud CLI.

Ví dụ sau đây tìm 10 lân cận gần nhất của vectơ truy vấn.

Python
from google.cloud.firestore_v1.base_vector_query import DistanceMeasure
from google.cloud.firestore_v1.vector import Vector

collection = db.collection("coffee-beans")

# Requires a single-field vector index
vector_query = collection.find_nearest(
    vector_field="embedding_field",
    query_vector=Vector([3.0, 1.0, 2.0]),
    distance_measure=DistanceMeasure.EUCLIDEAN,
    limit=5,
)
Node.js
import {
  Firestore,
  FieldValue,
  VectorQuery,
  VectorQuerySnapshot,
} from "@google-cloud/firestore";

// Requires a single-field vector index
const vectorQuery: VectorQuery = coll.findNearest({
  vectorField: 'embedding_field',
  queryVector: [3.0, 1.0, 2.0],
  limit: 10,
  distanceMeasure: 'EUCLIDEAN'
});

const vectorQuerySnapshot: VectorQuerySnapshot = await vectorQuery.get();

Khoảng cách vectơ

Các truy vấn lân cận gần nhất hỗ trợ các tuỳ chọn sau cho khoảng cách vectơ:

  • EUCLIDEAN: Đo khoảng cách EUCLIDEAN giữa các vectơ. Để tìm hiểu thêm, hãy xem Euclide.
  • COSINE: So sánh các vectơ dựa trên góc giữa chúng, điều này cho phép bạn đo sự tương đồng không dựa trên độ lớn của vectơ. Bạn nên sử dụng DOT_PRODUCT với vectơ chuẩn hoá đơn vị thay vì Khoảng cách cosINE, tương đương về mặt toán học với khoảng cách tốt hơn hiệu suất. Để tìm hiểu thêm, hãy xem Sự tương đồng về cosin để tìm hiểu khác.
  • DOT_PRODUCT: Tương tự như COSINE nhưng bị ảnh hưởng bởi cường độ của vectơ. Để tìm hiểu thêm, hãy xem Chấm sản phẩm.

Chọn số đo khoảng cách

Tuỳ thuộc vào việc tất cả các mục nhúng vectơ của bạn có được chuẩn hoá hay không, bạn có thể xác định cần dùng thước đo khoảng cách nào để tìm số đo khoảng cách. A chuẩn hoá nhúng vectơ có độ lớn (độ dài) chính xác là 1,0.

Ngoài ra, nếu biết mô hình đo khoảng cách nào được dùng để huấn luyện, sử dụng số đo khoảng cách đó để tính khoảng cách giữa các vectơ video nhúng.

Dữ liệu được chuẩn hoá

Nếu bạn có một tập dữ liệu trong đó tất cả các mục nhúng vectơ đều được chuẩn hoá, thì cả ba các số đo khoảng cách cung cấp cùng một kết quả tìm kiếm ngữ nghĩa. Về cơ bản, mặc dù mỗi số đo khoảng cách trả về một giá trị khác, các giá trị này sắp xếp theo cách tương tự nhau. Thời gian các mục nhúng được chuẩn hoá, DOT_PRODUCT thường là số liệu tính toán hiệu quả nhất hiệu quả, nhưng sự khác biệt là không đáng kể trong hầu hết các trường hợp. Tuy nhiên, nếu ứng dụng nhạy cảm với hiệu suất cao, DOT_PRODUCT có thể giúp ích cho tinh chỉnh hiệu suất.

Dữ liệu không được chuẩn hoá

Nếu bạn có một tập dữ liệu mà các nhúng vectơ không được chuẩn hoá, thì việc sử dụng DOT_PRODUCT làm khoảng cách sẽ không chính xác về mặt toán học đo lường vì tích vô hướng không đo khoảng cách. Tuỳ thuộc vào về cách tạo các mục nhúng và loại tìm kiếm được ưu tiên, khi đo khoảng cách COSINE hoặc EUCLIDEAN tạo ra kết quả tìm kiếm chủ quan tốt hơn so với các phép đo khoảng cách khác. Thử nghiệm với COSINE hoặc EUCLIDEAN có thể là cần thiết để xác định xem đâu là phù hợp nhất với trường hợp sử dụng của bạn.

Không rõ dữ liệu được chuẩn hoá hay không được chuẩn hoá

Nếu bạn không chắc chắn liệu dữ liệu của mình đã được chuẩn hoá hay chưa và bạn muốn sử dụng DOT_PRODUCT, bạn nên sử dụng COSINE. COSINE giống như DOT_PRODUCT được tích hợp sẵn tính năng chuẩn hoá. Quãng đường được đo bằng COSINE sẽ nằm trong khoảng từ 0 đến 2. Một kết quả gần với 0 cho biết các vectơ rất giống nhau.

Lọc tài liệu trước

Để lọc sơ bộ tài liệu trước khi tìm các tài liệu lân cận gần nhất, bạn có thể kết hợp một tìm kiếm tương tự với toán tử truy vấn khác. andor bộ lọc tổng hợp được hỗ trợ. Để biết thêm thông tin về các bộ lọc trường được hỗ trợ, hãy xem phần Toán tử truy vấn.

Python
from google.cloud.firestore_v1.base_vector_query import DistanceMeasure
from google.cloud.firestore_v1.vector import Vector

collection = db.collection("coffee-beans")

# Similarity search with pre-filter
# Requires a composite vector index
vector_query = collection.where("color", "==", "red").find_nearest(
    vector_field="embedding_field",
    query_vector=Vector([3.0, 1.0, 2.0]),
    distance_measure=DistanceMeasure.EUCLIDEAN,
    limit=5,
)
Node.js
// Similarity search with pre-filter
// Requires composite vector index
const preFilteredVectorQuery: VectorQuery = coll
    .where("color", "==", "red")
    .findNearest({
      vectorField: "embedding_field",
      queryVector: [3.0, 1.0, 2.0],
      limit: 5,
      distanceMeasure: "EUCLIDEAN",
    });

const vectorQueryResults = await preFilteredVectorQuery.get();

Truy xuất khoảng cách vectơ đã tính

Bạn có thể truy xuất khoảng cách vectơ đã tính bằng cách gán Tên thuộc tính đầu ra distance_result_field trên truy vấn FindNearest, dưới dạng như trong ví dụ sau:

Python
from google.cloud.firestore_v1.base_vector_query import DistanceMeasure
from google.cloud.firestore_v1.vector import Vector

collection = db.collection("coffee-beans")

vector_query = collection.find_nearest(
    vector_field="embedding_field",
    query_vector=Vector([3.0, 1.0, 2.0]),
    distance_measure=DistanceMeasure.EUCLIDEAN,
    limit=10,
    distance_result_field="vector_distance",
)

docs = vector_query.stream()

for doc in docs:
    print(f"{doc.id}, Distance: {doc.get('vector_distance')}")
Node.js
const vectorQuery: VectorQuery = coll.findNearest(
    {
      vectorField: 'embedding_field',
      queryVector: [3.0, 1.0, 2.0],
      limit: 10,
      distanceMeasure: 'EUCLIDEAN',
      distanceResultField: 'vector_distance'
    });

const snapshot: VectorQuerySnapshot = await vectorQuery.get();

snapshot.forEach((doc) => {
  console.log(doc.id, ' Distance: ', doc.get('vector_distance'));
});

Nếu muốn sử dụng mặt nạ trường để trả về một nhóm nhỏ các trường tài liệu cùng với distanceResultField, thì bạn cũng phải đưa giá trị của distanceResultField vào mặt nạ trường, như trong ví dụ sau:

Python
vector_query = collection.select(["color", "vector_distance"]).find_nearest(
    vector_field="embedding_field",
    query_vector=Vector([3.0, 1.0, 2.0]),
    distance_measure=DistanceMeasure.EUCLIDEAN,
    limit=10,
    distance_result_field="vector_distance",
)
Node.js
const vectorQuery: VectorQuery = coll
    .select('color', 'vector_distance')
    .findNearest({
      vectorField: 'embedding_field',
      queryVector: [3.0, 1.0, 2.0],
      limit: 10,
      distanceMeasure: 'EUCLIDEAN',
      distanceResultField: 'vector_distance'
    });

Chỉ định ngưỡng khoảng cách

Bạn có thể chỉ định một ngưỡng tương tự sẽ chỉ trả về tài liệu trong ngưỡng. Hành vi của trường ngưỡng phụ thuộc vào số đo khoảng cách bạn chọn:

  • Khoảng cách EUCLIDEANCOSINE giới hạn ngưỡng đối với các tài liệu có khoảng cách nhỏ hơn hoặc bằng ngưỡng được chỉ định. Khoảng cách này số đo sẽ giảm xuống khi các vectơ trở nên giống nhau.
  • Khoảng cách là DOT_PRODUCT giới hạn ngưỡng ở các tài liệu có khoảng cách lớn hơn hoặc bằng ngưỡng quy định. Khoảng cách giữa các sản phẩm theo dấu chấm tăng lên khi các vectơ trở nên giống nhau.

Ví dụ sau đây minh hoạ cách chỉ định ngưỡng khoảng cách để trả về tối đa 10 tài liệu gần nhất cách tối đa 4,5 đơn vị bằng cách sử dụng chỉ số khoảng cách EUCLIDEAN:

Python
from google.cloud.firestore_v1.base_vector_query import DistanceMeasure
from google.cloud.firestore_v1.vector import Vector

collection = db.collection("coffee-beans")

vector_query = collection.find_nearest(
    vector_field="embedding_field",
    query_vector=Vector([3.0, 1.0, 2.0]),
    distance_measure=DistanceMeasure.EUCLIDEAN,
    limit=10,
    distance_threshold=4.5,
)

docs = vector_query.stream()

for doc in docs:
    print(f"{doc.id}")
Node.js
const vectorQuery: VectorQuery = coll.findNearest({
  vectorField: 'embedding_field',
  queryVector: [3.0, 1.0, 2.0],
  limit: 10,
  distanceMeasure: 'EUCLIDEAN',
  distanceThreshold: 4.5
});

const snapshot: VectorQuerySnapshot = await vectorQuery.get();

snapshot.forEach((doc) => {
  console.log(doc.id);
});

Các điểm hạn chế

Khi bạn làm việc với các mục nhúng vectơ, hãy lưu ý các hạn chế sau:

  • Phương diện nhúng được hỗ trợ tối đa là 2048. Để lưu trữ các chỉ mục lớn hơn, hãy sử dụng giảm số lượng kích thước.
  • Số tài liệu tối đa cần trả về từ truy vấn lân cận gần nhất là 1000.
  • Tính năng tìm kiếm vectơ không hỗ trợ trình nghe ảnh chụp nhanh theo thời gian thực.
  • Chỉ các thư viện ứng dụng Python và Node.js mới hỗ trợ tính năng tìm kiếm vectơ.

Bước tiếp theo