חיפוש שכנים קרובים משוערים (ANN) והטמעות של וקטורים לשאילתות

בדף הזה מוסבר איך למצוא שכנים קרובים משוערים (ANN) ולשאילת הטמעות של וקטורים באמצעות פונקציות המרחק של ANN.

כשמערך הנתונים קטן, אפשר להשתמש בשיטת K-nearest neighbors (KNN) כדי למצוא את הווקטורים המדויקים הכי קרובים. עם זאת, ככל שמערך הנתונים גדל, כך גדלים גם זמן האחזור והעלות של חיפוש KNN. אפשר להשתמש ב-ANN כדי למצוא את השכנים הקרובים ביותר (k-nearest neighbors) בקירוב, עם זמן אחזור ועלות נמוכים משמעותית.

בחיפוש ANN, הווקטורים שמוחזרים k הם לא השכנים האמיתיים הכי קרובים k כי חיפוש ANN מחשב מרחקים משוערים, ויכול להיות שהוא לא בודק את כל הווקטורים במערך הנתונים. לפעמים, המערכת מחזירה כמה וקטורים שלא נכללים בין k השכנים הקרובים ביותר. התופעה הזו נקראת אובדן היזכרות. כמה אובדן של זיכרון הוא סביר בעיניכם תלוי בתרחיש השימוש, אבל ברוב המקרים, אובדן קל של זיכרון בתמורה לשיפור הביצועים של מסד הנתונים הוא פשרה סבירה.

פרטים נוספים על פונקציות המרחק המשוער שנתמכות ב-Spanner מופיעים בדפי העיון הבאים לגבי הדיאלקט של מסד הנתונים:

שאילתות להטמעת וקטורים

‫Spanner מאיץ חיפושים של וקטורים של שכן קרוב משוער (ANN) באמצעות אינדקס וקטורי. אפשר להשתמש באינדקס וקטורי כדי לשלוח שאילתות להטמעות וקטוריות. כדי לשלוח שאילתות לגבי הטמעות וקטוריות, צריך קודם ליצור אינדקס וקטורי. אחר כך אפשר להשתמש באחת משלוש פונקציות המרחק המשוער כדי למצוא את ה-ANN.

ההגבלות שחלות על השימוש בפונקציות של מרחק משוער כוללות את ההגבלות הבאות:

  • פונקציית המרחק המשוער צריכה לחשב את המרחק בין עמודת הטמעה לבין ביטוי קבוע (לדוגמה, פרמטר או ערך מילולי).
  • הפלט של פונקציית המרחק המשוער חייב לשמש כערך היחיד של מפתח המיון במשפט ORDER BY, וצריך לציין LIMIT אחרי ORDER BY.
  • השאילתה צריכה לסנן במפורש שורות שלא נכללות באינדקס. ברוב המקרים, המשמעות היא שהשאילתה צריכה לכלול פסקה WHERE <column_name> IS NOT NULL שתואמת להגדרת אינדקס הווקטור, אלא אם העמודה כבר מסומנת כ-NOT NULL בהגדרת הטבלה.

רשימה מפורטת של המגבלות מופיעה בדף העזר בנושא פונקציית המרחק המשוער.

דוגמאות

נניח שיש לכם טבלה Documents עם עמודה DocEmbedding של הטמעות טקסט שחושבו מראש מהעמודה DocContents bytes, ועמודה NullableDocEmbedding שמולאה ממקורות אחרים שעשויים להיות null.

GoogleSQL

CREATE TABLE Documents (
  UserId       INT64 NOT NULL,
  DocId        INT64 NOT NULL,
  Author       STRING(1024),
  DocContents  BYTES(MAX),
  DocEmbedding ARRAY<FLOAT32> NOT NULL,
  NullableDocEmbedding ARRAY<FLOAT32>,
  WordCount    INT64
) PRIMARY KEY (UserId, DocId);

PostgreSQL

CREATE TABLE documents (
  user_id      bigint not null,
  doc_id       bigint not null,
  author       varchar(1024),
  doc_contents bytea,
  doc_embedding float4[] not null,
  nullable_doc_embedding float4[],
  word_count   bigint,
  PRIMARY KEY (user_id, doc_id)
);

כדי לחפש את 100 הווקטורים הקרובים ביותר ל-[1.0, 2.0, 3.0]:

GoogleSQL

SELECT DocId
FROM Documents
WHERE WordCount > 1000
ORDER BY APPROX_EUCLIDEAN_DISTANCE(
  ARRAY<FLOAT32>[1.0, 2.0, 3.0], DocEmbedding,
  options => JSON '{"num_leaves_to_search": 10}')
LIMIT 100

PostgreSQL

SELECT doc_id
FROM documents
WHERE word_count > 1000
ORDER BY spanner.approx_euclidean_distance(
  ARRAY[1.0, 2.0, 3.0]::float4[], doc_embedding,
  options=>jsonb'{"num_leaves_to_search": 10}'
)
LIMIT 100;

כדי לחפש את 100 הווקטורים הקרובים ביותר להטמעה שנוצרה על ידי ביטוי SQL, משתמשים בתבנית הבאה. בדוגמה הזו, השאילתה מחפשת את המסמכים שהכי דומים להטמעה של UserId = 1 ו-DocId = 1:

GoogleSQL

WITH emb AS (
  SELECT DocEmbedding AS value
  FROM Documents
  WHERE UserId = 1 AND DocId = 1
  LIMIT 1
)
SELECT DocId
FROM Documents, emb
ORDER BY APPROX_EUCLIDEAN_DISTANCE(
  emb.value, DocEmbedding,
  options => JSON '{"num_leaves_to_search": 10}')
LIMIT 100

PostgreSQL

SELECT documents.doc_id
FROM
  documents,
  (
    SELECT doc_embedding AS value
    FROM documents
    WHERE user_id = 1 AND doc_id = 1
    LIMIT 1
  ) vector
WHERE documents.doc_embedding IS NOT NULL
ORDER BY spanner.APPROX_EUCLIDEAN_DISTANCE(documents.doc_embedding,
         vector.value, options=>'{"num_leaves_to_search": 10}'::jsonb)
LIMIT 100

אם העמודה להטמעה מאפשרת ערך null:

GoogleSQL

SELECT DocId
FROM Documents
WHERE NullableDocEmbedding IS NOT NULL AND WordCount > 1000
ORDER BY APPROX_EUCLIDEAN_DISTANCE(
  ARRAY<FLOAT32>[1.0, 2.0, 3.0], NullableDocEmbedding,
  options => JSON '{"num_leaves_to_search": 10}')
LIMIT 100

PostgreSQL

SELECT doc_id
FROM documents
WHERE nullable_doc_embedding IS NOT NULL AND word_count > 1000
ORDER BY spanner.approx_euclidean_distance(
  ARRAY[1.0, 2.0, 3.0]::float4[], nullable_doc_embedding,
  options=>jsonb'{"num_leaves_to_search": 10}'
)
LIMIT 100;

המאמרים הבאים