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

בדף הזה מוסבר איך לבצע חיפוש של דמיון בין וקטורים ב-Spanner באמצעות פונקציות וקטוריות של מרחק קוסינוס, מרחק אוקלידי ומכפלה סקלרית, כדי למצוא את השכנים הקרובים ביותר (K-nearest neighbors). המידע הזה רלוונטי גם למסדי נתונים של ניב GoogleSQL וגם למסדי נתונים של ניב PostgreSQL. לפני שקוראים את הדף הזה, חשוב להבין את המושגים הבאים:

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

אתם יכולים להשתמש בפונקציות של מרחק וקטורי כדי לבצע חיפוש וקטורי של K-nearest neighbors ‏ (KNN) לתרחישי שימוש כמו חיפוש דמיון או יצירה משופרת של אחזור. ‫Spanner תומך בפונקציות ,‏  ו-, שפועלות על הטמעות של וקטורים ומאפשרות למצוא את ה-KNN של ההטמעה של הקלט.COSINE_DISTANCE()EUCLIDEAN_DISTANCE()DOT_PRODUCT()

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

כל שלוש פונקציות המרחק מקבלות את הארגומנטים vector1 ו-vector2, שהם מסוג array<>, וצריכים להיות בעלי אותם ממדים ואותו אורך. מידע נוסף על הפונקציות האלה זמין במאמרים הבאים:

דוגמאות

בדוגמאות הבאות מוצגות שאילתות חיפוש KNN, שאילתות חיפוש KNN על נתונים מחולקים ושימוש באינדקס משני עם KNN.

בכל הדוגמאות נעשה שימוש ב-EUCLIDEAN_DISTANCE(). אפשר גם להשתמש ב-COSINE_DISTANCE(). בנוסף, אם כל ההטמעות של הווקטורים במערך הנתונים שלכם מנורמלות, אתם יכולים להשתמש ב-DOT_PRODUCT() כפונקציית מרחק.

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

GoogleSQL

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

PostgreSQL

CREATE TABLE Documents (
UserId       bigint NOT NULL,
DocId        bigint NOT NULL,
Author       varchar(1024),
DocContents  bytea,
DocEmbedding float4[],
PRIMARY KEY  (UserId, DocId)
);

בהנחה שהטמעת הקלט של 'בייסבול, אבל לא בייסבול מקצועי' היא המערך [0.3, 0.3, 0.7, 0.7], אפשר למצוא את חמשת המסמכים הקרובים ביותר שתואמים לשאילתה הבאה:

GoogleSQL

SELECT DocId, DocEmbedding FROM Documents
ORDER BY EUCLIDEAN_DISTANCE(DocEmbedding,
ARRAY<FLOAT32>[0.3, 0.3, 0.7, 0.8])
LIMIT 5;

PostgreSQL

SELECT DocId, DocEmbedding FROM Documents
ORDER BY spanner.euclidean_distance(DocEmbedding,
'{0.3, 0.3, 0.7, 0.8}'::float4[])
LIMIT 5;

התוצאות הצפויות של הדוגמה הזו:

Documents
+---------------------------+-----------------+
| DocId                     | DocEmbedding    |
+---------------------------+-----------------+
| 24                        | [8, ...]        |
+---------------------------+-----------------+
| 25                        | [6, ...]        |
+---------------------------+-----------------+
| 26                        | [3.2, ...]      |
+---------------------------+-----------------+
| 27                        | [38, ...]       |
+---------------------------+-----------------+
| 14229                     | [1.6, ...]      |
+---------------------------+-----------------+

דוגמה 2: חיפוש KNN בנתונים שחולקו למחיצות

אפשר לשנות את השאילתה בדוגמה הקודמת על ידי הוספת תנאים לסעיף WHERE כדי להגביל את החיפוש הווקטורי לקבוצת משנה של הנתונים. אחד השימושים הנפוצים הוא חיפוש בנתונים מחולקים, כמו שורות ששייכות לUserId ספציפי.

GoogleSQL

SELECT UserId, DocId, DocEmbedding FROM Documents
WHERE UserId=18
ORDER BY EUCLIDEAN_DISTANCE(DocEmbedding,
ARRAY<FLOAT32>[0.3, 0.3, 0.7, 0.8])
LIMIT 5;

PostgreSQL

SELECT UserId, DocId, DocEmbedding FROM Documents
WHERE UserId=18
ORDER BY spanner.euclidean_distance(DocEmbedding,
'{0.3, 0.3, 0.7, 0.8}'::float4[])
LIMIT 5;

התוצאות הצפויות של הדוגמה הזו:

Documents
+-----------+-----------------+-----------------+
| UserId    | DocId           | DocEmbedding    |
+-----------+-----------------+-----------------+
| 18        | 234             | [12, ...]       |
+-----------+-----------------+-----------------+
| 18        | 12              | [1.6, ...]      |
+-----------+-----------------+-----------------+
| 18        | 321             | [22, ...]       |
+-----------+-----------------+-----------------+
| 18        | 432             | [3, ...]        |
+-----------+-----------------+-----------------+

דוגמה 3: חיפוש KNN בטווחים של אינדקס משני

אם מסנן משפטי ה-WHERE שבו אתם משתמשים לא מהווה חלק מהמפתח הראשי של הטבלה, אפשר ליצור אינדקס משני כדי להאיץ את הפעולה באמצעות סריקה של אינדקס בלבד.

GoogleSQL

CREATE INDEX DocsByAuthor
ON Documents(Author)
STORING (DocEmbedding);

SELECT Author, DocId, DocEmbedding FROM Documents
WHERE Author="Mark Twain"
ORDER BY EUCLIDEAN_DISTANCE(DocEmbedding,
   <embeddings for "book about the time traveling American">)
LIMIT 5;

PostgreSQL

CREATE INDEX DocsByAuthor
ON Documents(Author)
INCLUDE (DocEmbedding);

SELECT Author, DocId, DocEmbedding FROM Documents
WHERE Author="Mark Twain"
ORDER BY spanner.euclidean_distance(DocEmbedding,
   <embeddings for "that book about the time traveling American">)
LIMIT 5;

התוצאות הצפויות של הדוגמה הזו:

Documents
+------------+-----------------+-----------------+
| Author     | DocId           | DocEmbedding    |
+------------+-----------------+-----------------+
| Mark Twain | 234             | [12, ...]       |
+------------+-----------------+-----------------+
| Mark Twain | 12              | [1.6, ...]      |
+------------+-----------------+-----------------+
| Mark Twain | 321             | [22, ...]       |
+------------+-----------------+-----------------+
| Mark Twain | 432             | [3, ...]        |
+------------+-----------------+-----------------+
| Mark Twain | 375             | [9, ...]        |
+------------+-----------------+-----------------+

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