หน้านี้จะแสดงวิธีใช้ Cloud Firestore เพื่อทำการค้นหาเวกเตอร์ K-Nearest Neighbor (KNN) โดยใช้เทคนิคต่อไปนี้
- ค่าเวกเตอร์ของร้านค้า
- สร้างและจัดการดัชนีเวกเตอร์ KNN
- สร้างการค้นหา K-Nearest-Neighbor (KNN) โดยใช้การวัดระยะทางเวกเตอร์ที่รองรับอย่างใดอย่างหนึ่ง
จัดเก็บการฝังเวกเตอร์
คุณสร้างค่าเวกเตอร์ เช่น การฝังข้อความ จากข้อมูล Cloud Firestore และจัดเก็บไว้ในเอกสาร Cloud Firestore ได้
การดำเนินการเขียนด้วยการฝังเวกเตอร์
ตัวอย่างต่อไปนี้แสดงวิธีกรอกข้อมูลเวกเตอร์ในเอกสาร Cloud Firestore
Python
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]) });
Go
Java
import com.google.cloud.firestore.CollectionReference; import com.google.cloud.firestore.DocumentReference; import com.google.cloud.firestore.FieldValue; import com.google.cloud.firestore.VectorQuery; CollectionReference coll = firestore.collection("coffee-beans"); Map<String, Object> docData = new HashMap<>(); docData.put("name", "Kahawa coffee beans"); docData.put("description", "Information about the Kahawa coffee beans."); docData.put("embedding_field", FieldValue.vector(new double[] {1.0, 2.0, 3.0})); ApiFuture<DocumentReference> future = coll.add(docData); DocumentReference documentReference = future.get();
คำนวณการฝังเวกเตอร์ด้วย Cloud Functions
หากต้องการคำนวณและจัดเก็บการฝังเวกเตอร์ทุกครั้งที่มีการอัปเดตหรือสร้างเอกสาร คุณสามารถตั้งค่า Cloud Functions ได้โดยทำดังนี้
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, }); }
Go
// Not yet supported in the Go client library
Java
// Not yet supported in the Java client library
สร้างและจัดการดัชนีเวกเตอร์
ก่อนที่จะทำการค้นหาเพื่อนบ้านที่ใกล้ที่สุดด้วยการฝังเวกเตอร์ได้ คุณต้องสร้างดัชนีที่สอดคล้องกัน ตัวอย่างต่อไปนี้แสดงวิธีสร้างและจัดการดัชนีเวกเตอร์ด้วย Google Cloud CLI คุณยังจัดการดัชนีเวกเตอร์ด้วย Firebase CLI และ Terraform ได้ด้วย
สร้างดัชนีเวกเตอร์
ก่อนสร้างดัชนีเวกเตอร์ ให้อัปเกรดเป็น Google Cloud CLI เวอร์ชันล่าสุดโดยทำดังนี้
gcloud components update
หากต้องการสร้างดัชนีเวกเตอร์ ให้ใช้ 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
ที่ไหน
- collection-group คือรหัสของกลุ่มคอลเล็กชัน
- vector-field คือชื่อของฟิลด์ที่มีการฝังเวกเตอร์
- database-id คือรหัสของฐานข้อมูล
- vector-configuration มีเวกเตอร์
dimension
และประเภทดัชนีdimension
เป็นจำนวนเต็มไม่เกิน 2048 ประเภทดัชนีต้องเป็นflat
จัดรูปแบบการกำหนดค่าดัชนีดังนี้{"dimension":"DIMENSION", "flat": "{}"}
ตัวอย่างต่อไปนี้สร้างดัชนีผสม ซึ่งรวมถึงดัชนีเวกเตอร์สำหรับฟิลด์ vector-field
และดัชนีจากน้อยไปมากสำหรับฟิลด์ color
คุณใช้ดัชนีประเภทนี้เพื่อกรองข้อมูลล่วงหน้าก่อนการค้นหาเพื่อนบ้านที่ใกล้ที่สุดได้
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
แสดงดัชนีเวกเตอร์ทั้งหมด
gcloud
gcloud firestore indexes composite list --database=database-id
แทนที่ database-id ด้วยรหัสของฐานข้อมูล
ลบดัชนีเวกเตอร์
gcloud
gcloud firestore indexes composite delete index-id --database=database-id
ที่ไหน
- index-id คือรหัสของดัชนีที่จะลบ
ใช้
indexes composite list
เพื่อดึงข้อมูลรหัสดัชนี - database-id คือรหัสของฐานข้อมูล
อธิบายดัชนีเวกเตอร์
gcloud
gcloud firestore indexes composite describe index-id --database=database-id
ที่ไหน
- index-id คือรหัสของดัชนีที่จะอธิบาย ใช้หรือ
indexes composite list
เพื่อดึงข้อมูลรหัสดัชนี - database-id คือรหัสของฐานข้อมูล
ทำการค้นหาแบบ Nearest Neighbor
คุณสามารถทำการค้นหาความคล้ายเพื่อค้นหาจุดข้อมูลที่อยู่ใกล้ที่สุดของ การฝังเวกเตอร์ การค้นหาความคล้ายคลึงต้องใช้ดัชนีเวกเตอร์ หากไม่มีดัชนีอยู่ Cloud Firestore จะแนะนำดัชนีที่จะสร้าง โดยใช้ gcloud CLI
ตัวอย่างต่อไปนี้จะค้นหาจุดข้อมูลข้างเคียงที่อยู่ใกล้ที่สุด 10 จุดของเวกเตอร์คำค้นหา
Python
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();
Go
Java
import com.google.cloud.firestore.VectorQuery; import com.google.cloud.firestore.VectorQuerySnapshot; VectorQuery vectorQuery = coll.findNearest( "embedding_field", new double[] {3.0, 1.0, 2.0}, /* limit */ 10, VectorQuery.DistanceMeasure.EUCLIDEAN); ApiFuture<VectorQuerySnapshot> future = vectorQuery.get(); VectorQuerySnapshot vectorQuerySnapshot = future.get();
ระยะทางเวกเตอร์
การค้นหาเพื่อนบ้านที่ใกล้ที่สุดรองรับตัวเลือกต่อไปนี้สำหรับระยะทางเวกเตอร์
EUCLIDEAN
: วัดระยะทางแบบยุคลิดระหว่างเวกเตอร์ ดูข้อมูลเพิ่มเติมได้ที่ EuclideanCOSINE
: เปรียบเทียบเวกเตอร์ตามมุมระหว่างเวกเตอร์ ซึ่งช่วยให้คุณวัดความคล้ายคลึงที่ไม่ขึ้นอยู่กับขนาดของเวกเตอร์ได้ เราขอแนะนำให้ใช้DOT_PRODUCT
กับเวกเตอร์ที่ปรับให้เป็นหน่วยแทนระยะทางโคไซน์ ซึ่งเทียบเท่ากันในทางคณิตศาสตร์และมีประสิทธิภาพดีกว่า ดูข้อมูลเพิ่มเติมได้ที่ ความคล้ายกันของโคไซน์DOT_PRODUCT
: คล้ายกับCOSINE
แต่ได้รับผลกระทบจากขนาดของเวกเตอร์ ดูข้อมูลเพิ่มเติมได้ที่ ดอทโปรดักต์
เลือกการวัดระยะทาง
คุณสามารถ ระบุมาตรการวัดระยะทางที่จะใช้เพื่อค้นหามาตรการวัดระยะทางได้ โดยขึ้นอยู่กับว่าการฝังเวกเตอร์ทั้งหมดได้รับการทําให้เป็นมาตรฐานหรือไม่ การฝังเวกเตอร์ที่ทำให้เป็นมาตรฐาน มีขนาด (ความยาว) เท่ากับ 1.0
นอกจากนี้ หากคุณทราบว่าโมเดลได้รับการฝึกด้วยการวัดระยะทางใด ให้ใช้การวัดระยะทางนั้นเพื่อคำนวณระยะห่างระหว่างการฝังเวกเตอร์
ข้อมูลที่ทําให้เป็นมาตรฐาน
หากคุณมีชุดข้อมูลที่การฝังเวกเตอร์ทั้งหมดได้รับการทำให้เป็นมาตรฐานแล้ว เมตริกการวัดระยะทางทั้ง 3 รายการจะให้ผลการค้นหาเชิงความหมายเดียวกัน กล่าวโดยสรุปคือ แม้ว่าการวัดระยะทางแต่ละรายการจะแสดงค่าที่แตกต่างกัน แต่ค่าเหล่านั้นจะจัดเรียงในลักษณะเดียวกัน เมื่อมีการทําให้การฝังเป็นมาตรฐาน DOT_PRODUCT
มักจะมีประสิทธิภาพด้านการคำนวณมากที่สุด แต่ความแตกต่างนั้นเล็กน้อยในกรณีส่วนใหญ่ อย่างไรก็ตาม หากแอปพลิเคชันของคุณมีความไวต่อประสิทธิภาพสูง DOT_PRODUCT
อาจช่วยในการปรับแต่งประสิทธิภาพได้
ข้อมูลที่ไม่ได้ทำให้เป็นมาตรฐาน
หากคุณมีชุดข้อมูลที่ไม่ได้ทำให้การฝังเวกเตอร์เป็นมาตรฐาน
การใช้ DOT_PRODUCT
เป็นการวัดระยะทาง
จึงไม่ถูกต้องตามหลักคณิตศาสตร์ เนื่องจากผลคูณจุดไม่ได้วัดระยะทาง ไม่ว่าการฝังจะสร้างขึ้นอย่างไรและคุณต้องการการค้นหาประเภทใด
การวัดระยะทาง COSINE
หรือ EUCLIDEAN
จะให้ผลการค้นหา
ที่ดูดีกว่าการวัดระยะทางอื่นๆ
คุณอาจต้องทำการทดลองกับ COSINE
หรือ EUCLIDEAN
เพื่อพิจารณาว่าตัวเลือกใดเหมาะกับกรณีการใช้งานของคุณมากที่สุด
ไม่แน่ใจว่าข้อมูลเป็นข้อมูลมาตรฐานหรือไม่
หากไม่แน่ใจว่าข้อมูลเป็นมาตรฐานหรือไม่และต้องการใช้
DOT_PRODUCT
เราขอแนะนำให้ใช้ COSINE
แทน
COSINE
เหมือนกับ DOT_PRODUCT
ที่มีการสร้างการทำให้เป็นมาตรฐานในตัว
ระยะทางที่วัดโดยใช้ COSINE
มีตั้งแต่ 0
ถึง 2
ผลลัพธ์
ที่ใกล้เคียงกับ 0
แสดงว่าเวกเตอร์มีความคล้ายคลึงกันมาก
กรองเอกสารล่วงหน้า
หากต้องการกรองเอกสารล่วงหน้าก่อนค้นหาเพื่อนบ้านที่ใกล้ที่สุด คุณสามารถรวมการค้นหาความคล้ายคลึงกับโอเปอเรเตอร์การค้นหาอื่นๆ ได้ ระบบรองรับฟิลเตอร์คอมโพสิต and
และ
or
ดูข้อมูลเพิ่มเติมเกี่ยวกับตัวกรองฟิลด์ที่รองรับได้ที่โอเปอเรเตอร์การค้นหา
Python
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();
Go
Java
import com.google.cloud.firestore.VectorQuery; import com.google.cloud.firestore.VectorQuerySnapshot; VectorQuery preFilteredVectorQuery = coll .whereEqualTo("color", "red") .findNearest( "embedding_field", new double[] {3.0, 1.0, 2.0}, /* limit */ 10, VectorQuery.DistanceMeasure.EUCLIDEAN); ApiFuture<VectorQuerySnapshot> future = preFilteredVectorQuery.get(); VectorQuerySnapshot vectorQuerySnapshot = future.get();
ดึงข้อมูลระยะทางเวกเตอร์ที่คำนวณแล้ว
คุณเรียกข้อมูลระยะทางเวกเตอร์ที่คำนวณแล้วได้โดยกำหนดdistance_result_field
ชื่อพร็อพเพอร์ตี้เอาต์พุตในคำค้นหา FindNearest
ดังที่แสดงในตัวอย่างต่อไปนี้
Python
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')); });
Go
Java
import com.google.cloud.firestore.VectorQuery; import com.google.cloud.firestore.VectorQueryOptions; import com.google.cloud.firestore.VectorQuerySnapshot; VectorQuery vectorQuery = coll.findNearest( "embedding_field", new double[] {3.0, 1.0, 2.0}, /* limit */ 10, VectorQuery.DistanceMeasure.EUCLIDEAN, VectorQueryOptions.newBuilder().setDistanceResultField("vector_distance").build()); ApiFuture<VectorQuerySnapshot> future = vectorQuery.get(); VectorQuerySnapshot vectorQuerySnapshot = future.get(); for (DocumentSnapshot document : vectorQuerySnapshot.getDocuments()) { System.out.println(document.getId() + " Distance: " + document.get("vector_distance")); }
หากต้องการใช้ Field Mask เพื่อแสดงผลชุดย่อยของช่องเอกสารพร้อมกับ distanceResultField
คุณต้องใส่ค่าของ distanceResultField
ใน Field Mask ด้วย ดังที่แสดงในตัวอย่างต่อไปนี้
Python
Node.js
const vectorQuery: VectorQuery = coll .select('name', 'description', 'vector_distance') .findNearest({ vectorField: 'embedding_field', queryVector: [3.0, 1.0, 2.0], limit: 10, distanceMeasure: 'EUCLIDEAN', distanceResultField: 'vector_distance' });
Go
Java
import com.google.cloud.firestore.VectorQuery; import com.google.cloud.firestore.VectorQueryOptions; import com.google.cloud.firestore.VectorQuerySnapshot; VectorQuery vectorQuery = coll .select("name", "description", "vector_distance") .findNearest( "embedding_field", new double[] {3.0, 1.0, 2.0}, /* limit */ 10, VectorQuery.DistanceMeasure.EUCLIDEAN, VectorQueryOptions.newBuilder() .setDistanceResultField("vector_distance") .build()); ApiFuture<VectorQuerySnapshot> future = vectorQuery.get(); VectorQuerySnapshot vectorQuerySnapshot = future.get(); for (DocumentSnapshot document : vectorQuerySnapshot.getDocuments()) { System.out.println(document.getId() + " Distance: " + document.get("vector_distance")); }
ระบุเกณฑ์ระยะทาง
คุณระบุเกณฑ์ความคล้ายกันที่จะแสดงเฉพาะเอกสารที่อยู่ภายในเกณฑ์ได้ ลักษณะการทำงานของช่องเกณฑ์ขึ้นอยู่กับหน่วยวัดระยะทาง ที่คุณเลือก ดังนี้
- ระยะทาง
EUCLIDEAN
และCOSINE
จะจำกัดเกณฑ์ไว้ที่เอกสารซึ่งมี ระยะทางน้อยกว่าหรือเท่ากับเกณฑ์ที่ระบุ ค่าการวัดระยะทางเหล่านี้จะลดลงเมื่อเวกเตอร์มีความคล้ายคลึงกันมากขึ้น DOT_PRODUCT
distance limits the threshold to documents where distance is greater than or equal to the specified threshold. ระยะทางของผลคูณจุด จะเพิ่มขึ้นเมื่อเวกเตอร์มีความคล้ายกันมากขึ้น
ตัวอย่างต่อไปนี้แสดงวิธีระบุเกณฑ์ระยะทางเพื่อแสดงเอกสารที่ใกล้ที่สุดสูงสุด 10 รายการซึ่งอยู่ห่างกันไม่เกิน 4.5 หน่วยโดยใช้เมตริกEUCLIDEAN
ระยะทาง
Python
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); });
Go
Java
import com.google.cloud.firestore.VectorQuery; import com.google.cloud.firestore.VectorQueryOptions; import com.google.cloud.firestore.VectorQuerySnapshot; VectorQuery vectorQuery = coll.findNearest( "embedding_field", new double[] {3.0, 1.0, 2.0}, /* limit */ 10, VectorQuery.DistanceMeasure.EUCLIDEAN, VectorQueryOptions.newBuilder() .setDistanceThreshold(4.5) .build()); ApiFuture<VectorQuerySnapshot> future = vectorQuery.get(); VectorQuerySnapshot vectorQuerySnapshot = future.get(); for (DocumentSnapshot document : vectorQuerySnapshot.getDocuments()) { System.out.println(document.getId()); }
ข้อจำกัด
โปรดทราบข้อจำกัดต่อไปนี้เมื่อทำงานกับการฝังเวกเตอร์
- มิติข้อมูลการฝังสูงสุดที่รองรับคือ 2048 หากต้องการจัดเก็บดัชนีขนาดใหญ่ ให้ใช้การลดมิติ
- จำนวนเอกสารสูงสุดที่จะแสดงจากคำค้นหาเพื่อนบ้านที่ใกล้ที่สุดคือ 1,000 รายการ
- การค้นหาเวกเตอร์ไม่รองรับเครื่องมือฟังภาพรวมแบบเรียลไทม์
- เฉพาะไลบรารีของไคลเอ็นต์ Python, Node.js, Go และ Java เท่านั้นที่รองรับการค้นหาเวกเตอร์
ขั้นตอนถัดไป
- อ่านเกี่ยวกับแนวทางปฏิบัติแนะนำสำหรับ Cloud Firestore
- ทำความเข้าใจการอ่านและการเขียนที่ปรับขนาดได้