كيان واحد، متجهات متعددة: البحث على مستوى الكيان والعنصر باستخدام Milvus 3.0 StructArray
تبدأ معظم مخططات قواعد بيانات المتجهات بافتراض بسيط: كيان واحد، وتضمين واحد. يحصل المنتج على متجه واحد، وكذلك المستند. يتم تضمين استعلام المستخدم ومقارنته بتلك المتجهات من خلال بحث الجيران الأقرب التقريبي (ANN). يعمل هذا النموذج للجيل الأول من حالات استخدام بحث المتجهات، بما في ذلك RAG، والبحث الدلالي، وأنظمة التوصية.
بيانات الذكاء الاصطناعي الواقعية، مع ذلك، نادرًا ما تنطبق على هذا الافتراض. يحتوي الفيديو على مقاطع أو لقطات أو إطارات رئيسية، لكل منها تضمين خاص بها ونطاق زمني وتسمية توضيحية وملصق مشهد ودرجة ثقة. قد يحتوي المنتج على عدة صور وزوايا عرض. تحتوي المستندات الطويلة على فقرات أو أقسام يكون معناها المحلي أكثر أهمية من تضمين واحد للمستند بأكمله. تُظهر نماذج التفاعل المتأخر الشهيرة نفس القيد بدقّة أدق: ينتج ColBERT متجهًا واحدًا لكل رمز، بينما ينتج ColPali متجهًا واحدًا لكل رقعة بصرية.
في كل حالة، يظل الكيان الأصلي هو الوحدة التي يخزنها التطبيق ويعرضها ويؤمّنها ويعيدها. ومع ذلك، غالبًا ما تعتمد الصلة والتصفية وشرح النتائج على عناصر داخل ذلك الكيان.
تمنح ميزة StructArray الجديدة Milvus نموذج بيانات أصليًا لهذا الشكل: كيان واحد يحتوي على مصفوفة مرتبة من عناصر Struct المعرّفة في المخطط، وكل عنصر يمكن أن يحمل بيانات وصفية عددية، أو تضمينات متجهة، أو كليهما. يمكن لـ Milvus تصفية الحقول التي تنتمي إلى نفس العنصر، أو مقارنة قائمتي تضمين على مستوى الكيان، أو البحث في عناصر فردية وإرجاع الإزاحة المطابقة.
تستخدم هذه المقالة مثالًا للبحث عن الفيديو لشرح نموذج البيانات، ثم تتبعه من خلال تصميم المخطط، والتصفية، ودقّات بحث المتجهات، واستراتيجيات فهرسة EmbeddingList، ودمج النتائج الهجينة، والتخطيط المادي الذي يجعل الميزة قابلة للتنفيذ.
لماذا لم يعد نموذج المتجه الواحد والصف المسطح الواحد كافيًا
تأمل مستخدمًا يبحث في كتالوج فيديو عن «شخص يقطّع الخضروات في مطبخ». قد تكمن الإشارة ذات الصلة في مقطع واحد مدته ثماني ثوانٍ، وليس في تضمين للفيديو بأكمله. ضغط كل مقطع وكائن وحركة في متجه واحد قد يحافظ على الموضوع العام، لكنه قد يُفقد التفاصيل المحلية.
يظهر نفس التباين في أحمال عمل أخرى:
- قد تأتي صلة المنتج من واحدة من عدة صور أو زوايا.
- قد يطابق المستند بسبب فقرة واحدة بدلاً من موضوعه العام.
- قد تحتوي ذاكرة وكيل على عدة ملاحظات، واحدة فقط منها مهمة للمهمة الحالية.
- يحتوي سجل ColBERT أو ColPali على قائمة متغيرة الطول من متجهات الرموز أو الرقع بدلاً من متجه واحد كثيف.
أحد البدائل هو تقسيم كل مقطع أو صورة أو فقرة إلى صف منفصل في قاعدة البيانات. يمكّن ذلك البحث المحلي، ولكنه يفصل أيضًا كل جزء عن كيانه الأصلي. قد تتكرر البيانات الوصفية للكيان الأصلي عبر الصفوف، ويتطلب الاسترجاع على مستوى الكيان بعد ذلك تجميعًا وإزالة تكرار وإعادة ترتيب بعد بحث الأجزاء.
التخزين المتداخل وحده لا يحل مشكلة الاستعلام. يمكن لـ JSON تخزين الكائنات، لكنه لا يمنح Milvus مخططًا فرعيًا محددًا مسبقًا لفهرسة المتجهات والقيم العددية. يمكن للمصفوفات المتوازية تخزين التسميات التوضيحية وملصقات المشاهد وقيم الثقة، ولكن يجب على التطبيق الحفاظ على محاذاة الإزاحة. لا يمكن لقاعدة البيانات أن تستنتج بأمان أن scene_type[3] و label_confidence[3] يصفان نفس المقطع ما لم تكن هذه العلاقة جزءًا من نموذج البيانات.
يقوم StructArray بتشفير تلك العلاقة مباشرة. فهو يُبقي العناصر المحلية داخل الكيان الأصلي مع إتاحة حقولها الفرعية المتوافقة للتحقق من المخطط والفهرسة والتصفية وبحث المتجهات.
ما هو StructArray ونموذج البيانات الخاص به؟
يقوم StructArray، المعروف أيضًا باسم مصفوفة البنى، بتخزين مجموعة مرتبة من عناصر Struct في كل كيان. حقل StructArray هو Array جميع عناصره تتبع مخطط Struct واحد محدد مسبقًا. لمجموعة فيديو، يمكن أن يبدو الشكل المنطقي كما يلي:
Plaintext
clips: ARRAY<STRUCT<
clip_embedding_list: FLOAT_VECTOR,
clip_embedding: FLOAT_VECTOR,
start_sec: DOUBLE,
end_sec: DOUBLE,
caption: VARCHAR,
scene_type: VARCHAR,
label_confidence: FLOAT
>>
هنا:
clipsهو حقل StructArray الأصلي.clip_embedding_listوclip_embeddingوstart_secوالسمات الأخرى هي حقول فرعية.clips[0]هو المقطع الأول.- كل حقل فرعي عند الإزاحة
0ينتمي إلى نفس المقطع. - كل حقل فرعي عند الإزاحة
3ينتمي إلى مقطع آخر.
يعمل الحقلان الفرعيان للمتجهات في وضعي بحث مختلفين. يتم فهرسة clips[clip_embedding_list] بمعيار MAX_SIM* لبحث EmbeddingList على مستوى الكيان، بينما يتم فهرسة clips[clip_embedding] بمعيار متجه عادي للبحث على مستوى العنصر. نظرًا لأن حقل المتجه أو الحقل الفرعي للمتجه يقبل فهرسًا واحدًا فقط، يجب على المجموعة التي تحتاج إلى كلا الوضعين تعريف وفهرسة الحقلين الفرعيين بشكل منفصل.
يدعم هذا النموذج ثلاثة دلالات استعلام متميزة.
1. بحث EmbeddingList يُرجع الكيانات الأصلية
تشكل المتجهات في clips[clip_embedding_list] قائمة تضمين واحدة للفيديو. الاستعلام هو أيضًا EmbeddingList. يقارن Milvus قائمة الاستعلام مع كل قائمة مخزنة باستخدام معيار MAX_SIM* ويُرجع نتيجة على مستوى الكيان.
Plaintext
clips[clip_embedding_list] = [
embedding_0,
embedding_1,
embedding_2,
...
]
2. عائلة MATCH_* تقوم بتصفية الكيانات الأصلية
تقيّم MATCH_ANY و MATCH_ALL و MATCH_LEAST و MATCH_MOST و MATCH_EXACT مسندًا (شرطًا) مقابل عناصر Struct، وتحسب عدد العناصر التي تحققه، وتقرر ما إذا كان الكيان الأصلي يجتاز التصفية.
على سبيل المثال:
Plaintext
MATCH_ANY(clips, $[scene_type] == "kitchen" && $[label_confidence] > 0.8)
يجب أن يكون كلا الشرطين العدديين صحيحين عند نفس إزاحة المقطع. لا يجمع Milvus ملصق مطبخ من مقطع مع قيمة ثقة عالية من مقطع آخر.
3. البحث على مستوى العنصر يُرجع إزاحة العنصر المطابق
يمكن لمتجه استعلام عادي البحث في كل متجه في clips[clip_embedding] بشكل مستقل. يحدد كل تطابق الكيان الأصلي والإزاحة الصفرية لعنصر Struct المطابق. يمكن لـ element_filter تقييد العناصر التي تشارك في بحث المتجهات.
تشترك هذه العمليات في فرضية واحدة: يعرف Milvus أي قيم المتجهات والقيم العددية تنتمي إلى نفس العنصر، وأي العناصر تنتمي إلى نفس الكيان.
StructArray ليس نظام تداخل عشوائيًا للأغراض العامة. نموذجه الحالي هو Array واحد من عناصر Struct مع حقول فرعية عددية ومتجهية مدعومة. هذا الحد يجعل فهرسة الحقول الفرعية والتنفيذ المراعي للعناصر أمرًا ممكنًا.
بناء المخطط والفهارس ومسار الإدراج
يقوم مثال PyMilvus المبسط التالي بإنشاء مجموعة فيديو بمتجه واحد على المستوى الأعلى وStructArray للمقاطع. ويستخدم حقولًا فرعية منفصلة لمتجهات المقاطع حتى تتمكن نفس المجموعة من توضيح وضعي البحث.
from pymilvus import DataType, MilvusClient
client = MilvusClient(uri=“http://localhost:19530”)
schema = client.create_schema(auto_id=False, enable_dynamic_field=False)
schema.add_field(“id”, DataType.INT64, is_primary=True)
schema.add_field(“title”, DataType.VARCHAR, max_length=512)
schema.add_field(“video_embedding”, DataType.FLOAT_VECTOR, dim=768)
# Define the Struct schema explicitly.
clip_schema = client.create_struct_field_schema()
clip_schema.add_field(“clip_embedding_list”, DataType.FLOAT_VECTOR, dim=768)
clip_schema.add_field(“clip_embedding”, DataType.FLOAT_VECTOR, dim=768)
clip_schema.add_field(“start_sec”, DataType.DOUBLE)
clip_schema.add_field(“end_sec”, DataType.DOUBLE)
clip_schema.add_field(“caption”, DataType.VARCHAR, max_length=2048)
clip_schema.add_field(“scene_type”, DataType.VARCHAR, max_length=128)
clip_schema.add_field(“label_confidence”, DataType.FLOAT)
schema.add_field(
“clips”,
datatype=DataType.ARRAY,
element_type=DataType.STRUCT,
struct_schema=clip_schema,
max_capacity=1024,
)
client.create_collection(“videos”, schema=schema)
يجب فهرسة الحقول الفرعية للمتجهات قبل البحث. نظرًا لأن عائلة المعايير تحدد وضع البحث، يحصل كل حقل فرعي متجه على الفهرس الخاص به:
index_params = client.prepare_index_params()
# EmbeddingList search.
index_params.add_index(
field_name="clips[clip_embedding_list]",
index_type=“HNSW”,
metric_type=“MAX_SIM_COSINE”,
index_name=“clips_clip_embedding_list_maxsim_idx”,
params={“M”: 16, “efConstruction”: 200},
)
# Element-level search.
index_params.add_index(
field_name="clips[clip_embedding]",
index_type=“HNSW”,
metric_type=“COSINE”,
index_name=“clips_clip_embedding_cosine_idx”,
params={“M”: 16, “efConstruction”: 200},
)
client.create_index(“videos”, index_params=index_params)
فهارس القيم العددية اختيارية، ولكن الحقول الفرعية التي تظهر بشكل متكرر في عمليات التصفية واسعة النطاق يجب أن تستخدم فهرسًا عدديًا متوافقًا. على سبيل المثال، يمكن أن يستخدم clips[scene_type] فهرسًا مقلوبًا، بينما يمكن لحقل فرعي رقمي مثل clips[label_confidence] استخدام فهرس مناسب للتصفية الرقمية.
أدخل البيانات بشكلها الطبيعي ككيان: صف فيديو واحد مع مصفوفة من كائنات المقاطع. للإبقاء على المثال مختصرًا، يكتب نفس متجه المقطع في كلا الحقلين الفرعيين للمتجهات.
rows = [
{
"id": 1,
"title": "cooking tutorial",
"video_embedding": video_vec,
"clips": [
{
"clip_embedding_list": clip_vec_1,
"clip_embedding": clip_vec_1,
"start_sec": 0.0,
"end_sec": 8.0,
"caption": "A person washes vegetables.",
"scene_type": "kitchen",
"label_confidence": 0.92,
},
{
"clip_embedding_list": clip_vec_2,
"clip_embedding": clip_vec_2,
"start_sec": 8.0,
"end_sec": 16.0,
"caption": "A person cuts carrots on a board.",
"scene_type": "kitchen",
"label_confidence": 0.96,
},
],
}
]
client.insert(“videos”, rows)
client.flush(“videos”)
client.load_collection(“videos”)
عند حدود API، يظل clips مصفوفة من الكائنات المهيكلة. داخل Milvus، يتبع كل حقل فرعي المسار المحدد بالنوع المطلوب لفهرسته وتصفيته وسلوك إخراجه. هذا التمييز شفاف في وقت الإدراج ولكنه أساسي لكل ما يلي.
تصفية نفس العنصر هي الفرق بين البنية والمصفوفات المتوازية
الفائدة الرئيسية للتصفية ليست صياغة أقصر للحقول المتداخلة. بل هي الارتباط الصحيح عبر الحقول الفرعية العددية.
لنفترض أن التطبيق يحتاج إلى مقاطع فيديو تحتوي على مقطع مطبخ بثقة تسمية أعلى من 0.8. لا يكفي أن يحتوي الفيديو على مقطع مطبخ ما ومقطع عالي الثقة ما؛ بل يجب أن يحقق نفس المقطع كلا الشرطين.
تعبر عائلة MATCH_* الخاصة بـ StructArray عن ذلك مباشرة:
Plaintext
MATCH_ANY(clips, $[scene_type] == "kitchen" && $[label_confidence] > 0.8)
MATCH_ALL(clips, $[label_confidence] > 0.5)
MATCH_LEAST(clips, $[scene_type] == "sports", threshold=3)
MATCH_MOST(clips, $[label_confidence] < 0.2, threshold=1)
MATCH_EXACT(clips, $[scene_type] == "intro", threshold=1)
يقيم Milvus المسند عند كل إزاحة عنصر، ثم يطبق مُحدِّد الكمية الخاص بالمشغل لتحديد ما إذا كان الكيان الأصلي يجتاز أم لا:
MATCH_ANY: عنصر واحد على الأقل يطابق.MATCH_ALL: كل عنصر يطابق.MATCH_LEAST: عنصر واحد على الأقل يطابق وفقًا لـthreshold.MATCH_MOST: عنصر واحد على الأكثر يطابق وفقًا لـthreshold.MATCH_EXACT: عدد العناصر المطابقة يساويthresholdبالضبط.
إذا تم تخزين نفس البيانات كمصفوفتين مستقلتين، فإن التعبير التالي لن يحافظ على هذا الارتباط:
Plaintext
array_contains(clips[scene_type], "kitchen")
AND
array_contains(clips[label_confidence], 0.9)
قد تظهر القيمتان عند إزاحتين مختلفتين. قد يكون ذلك صالحًا للسمات غير المرتبطة، ولكنه غير صحيح عندما يصف كلا الشرطين نفس المقطع أو صورة المنتج أو فقرة المستند.
يجعل StructArray هوية العنصر جزءًا من مسند قاعدة البيانات بدلاً من كونه اصطلاحًا يجب على التطبيق فرضه.
دقّتا بحث متجه، هويتا نتائج
بمجرد أن يخزن كيان ما متجهات متعددة، يجب أن يحسم الاسترجاع سؤالًا نموذجيًا قبل بدء بحث ANN:
هل يجب تقييم المتجهات معًا كتمثيل واحد للكيان الأصلي، أم يجب أن يتنافس كل متجه عنصر بشكل مستقل؟
يدعم StructArray كلا النموذجين، لكنهما يستخدمان أشكال استعلام مختلفة وعائلات معايير مختلفة وحقولًا فرعية متجهة مختلفة وهويات نتائج مختلفة.
بحث EmbeddingList: قائمة متجهات الاستعلام تعثر على كيان
يحتوي استعلام EmbeddingList على متجهات متعددة. قد يتم تقسيم فيديو الاستعلام إلى عدة مقاطع؛ قد يحتوي استعلام منتج على عدة صور مرجعية؛ يحتوي استعلام ColBERT على متجه واحد لكل رمز استعلام.
لكل كيان، يقارن Milvus قائمة الاستعلام مع قائمة التضمين المخزنة للكيان. في ظل التقييم بنمط MaxSim، يختار كل متجه استعلام أفضل تطابق له في قائمة الكيان، ويجمع Milvus درجات أفضل التطابقات في درجة كيان. يمثل التطابق النهائي الكيان الأصلي، وليس عنصر Struct معينًا.
from pymilvus.client.embedding_list import EmbeddingList
query = EmbeddingList()
query.add(query_clip_vec_1)
query.add(query_clip_vec_2)
client.search(
collection_name=“videos”,
data=[query],
anns_field="clips[clip_embedding_list]",
search_params={“metric_type”: “MAX_SIM_COSINE”},
limit=10,
)
يجيب هذا البحث على السؤال: ما هي أفضل مقاطع الفيديو تطابقًا بشكل عام مع هذه المجموعة من مقاطع الاستعلام؟
إنه مناسب لاسترجاع الفيديو مقابل الفيديو، والبحث عن المنتجات بصور متعددة، والاسترجاع بنمط ColBERT وColPali، والحالات الأخرى التي يتم فيها تمثيل كل من الاستعلام والكيان المخزن بمتجهات متعددة.
البحث على مستوى العنصر: متجه استعلام واحد يعثر على مقطع داخل كيان
يستخدم البحث على مستوى العنصر متجه استعلام عادي. يشارك كل متجه في clips[clip_embedding] في بحث ANN كمرشح مستقل. يحدد كل تطابق الكيان الأصلي وإزاحة العنصر المطابق.
client.search(
collection_name="videos",
data=[query_vec],
anns_field="clips[clip_embedding]",
search_params={"metric_type": "COSINE"},
limit=10,
output_fields=["id", "title", "clips"],
)
للبحث في مقاطع محددة فقط، أرفق element_filter بشروط عددية تنطبق على نفس المقطع:
client.search(
collection_name="videos",
data=[query_vec],
anns_field="clips[clip_embedding]",
search_params={"metric_type": "COSINE"},
filter='element_filter(clips, $[scene_type] == "kitchen" && $[label_confidence] > 0.8)',
limit=10,
output_fields=["id", "title", "clips"],
)
لا يختار عامل التصفية أولاً مقطع مطبخ ثم يبحث عن مقطع مختلف عالي الثقة. يشير كل من المسندين ومرشح المتجه إلى نفس عنصر Struct.
قد يبدو الرد غير المجمَّع كما يلي:
Plaintext
id = 1, offset = 1, distance = 0.91
id = 8, offset = 4, distance = 0.88
id = 1, offset = 3, distance = 0.84
قد يظهر نفس الكيان أكثر من مرة لأن عدة مقاطع يمكن أن تطابق. يكون ذلك مفيدًا عندما يحتاج التطبيق إلى إظهار ليس فقط الفيديو أو المستند ذي الصلة، ولكن أيضًا المقطع أو الفقرة التي أنتجت التطابق.
| الجانب | بحث EmbeddingList | البحث على مستوى العنصر |
|---|---|---|
| مدخلات الاستعلام | متجه استعلام واحد أو أكثر في EmbeddingList | متجه استعلام عادي واحد |
| الهدف المثال | clips[clip_embedding_list] | clips[clip_embedding] |
| عائلة المعايير | MAX_SIM* | معايير عادية مثل COSINE أو IP أو L2 |
| وحدة مرشح ANN | قائمة تضمين الكيان الأصلي | كل متجه عنصر Struct |
| هوية النتيجة | الكيان الأصلي | الكيان الأصلي بالإضافة إلى إزاحة العنصر |
| حالة الاستخدام النموذجية | مطابقة استعلام متعدد المتجهات مع كيان متعدد المتجهات | العثور على المقطع أو الصورة أو الفقرة أو الرقعة أو الحقيقة الأكثر صلة |
لدعم كلا الوضعين في مجموعة واحدة، قم بتعريف وفهرسة حقول فرعية متجهة منفصلة. يجب أن يتوافق شكل الاستعلام وعائلة المعايير والفهرس المستهدف معًا.
فهرسة EmbeddingList هي قرار بين الجودة والتكلفة
مع تضمين واحد لكل كيان، يجد فهرس ANN الكيانات القريبة من متجه الاستعلام. يعد بحث EmbeddingList أكثر تكلفة لأن الصلة تعتمد على التفاعلات الزوجية بين قائمتي متجهات.
حساب MaxSim الدقيق مقابل كل متجه في كل كيان ينتج أنظف ترتيب مرجعي، لكن الفحص الكامل عادة ما يكون مكلفًا للغاية للاسترجاع عبر الإنترنت. لذلك يستخدم Milvus نموذجًا من مرحلتين:
- استراتيجية تقريبية تسترجع الكيانات الأصلية المرشحة.
- عند تفعيل
emb_list_rerank، يعيد Milvus حساب MaxSim على هؤلاء المرشحين لإنتاج الترتيب النهائي.
استرجاع المزيد من مرشحي المرحلة الأولى يحسن عمومًا فرصة وصول النتائج الأعلى فعليًا إلى مرحلة إعادة الترتيب، ولكنه يزيد أيضًا من زمن الاستجابة والحساب. تختلف الاستراتيجيات الثلاث أساسًا في كيفية إنتاج مجموعة المرشحين هذه.
| الاستراتيجية | تمثيل مرشح المرحلة الأولى | نقطة بداية جيدة عندما | المفاضلة الرئيسية |
|---|---|---|---|
| TokenANN | فهرسة كل متجه في كل قائمة تضمين. تعمل متجهات الاستعلام عبر ANN بشكل مستقل؛ يتم تجميع التطابقات مرة أخرى إلى الكيانات الأصلية قبل إعادة ترتيب MaxSim. | الجودة هي الأولوية، والقوائم قصيرة أو متوسطة، والمتجهات الفردية تمييزية. | حجم الفهرس وعمل بحث المرحلة الأولى ينموان مع طول القائمة وعدد متجهات الاستعلام. |
| MUVERA | تشفير كل قائمة تضمين في متجه واحد ثابت الأبعاد من خلال الإسقاطات العشوائية، ثم تشغيل ANN عادي. | يكون TokenANN ثقيلًا جدًا ويُفضَّل الضغط دون خط أنابيب تدريب. | يفقد التشفير المعلومات؛ إعدادات الإسقاط الأقوى تزيد من الأبعاد المشفرة وتكلفة ANN. |
| LEMUR | تدريب نموذج يخطط قائمة التضمين إلى متجه كيان أصلي ثابت الأبعاد. | التضمينات أقل تمييزًا، أو القوائم كبيرة، أو حمل العمل بصري أو متعدد الوسائط. | يتطلب تدريبًا ويمكن أن يكون حساسًا لتوزيع المجموعة وانحياز طول المستند. |
لا توجد استراتيجية واحدة هي الأفضل لكل حمل عمل. ابدأ بالبيانات المستهدفة وتوزيع الاستعلام:
- استخدم TokenANN كخط أساس يضع الجودة أولاً عندما يسمح حجم مجموعة البيانات بذلك.
- جرّب MUVERA عندما يصبح فهرس TokenANN أو استرجاع المرشحين مكلفًا للغاية مع نمو طول القائمة، وتريد تجنب خط أنابيب التدريب.
- قيّم LEMUR عندما تكون مساحة التضمين مشوشة أو ضعيفة التمييز، أو عندما يكون حمل العمل بصريًا أو متعدد الوسائط.
- قم بقياس الاستدعاء أو nDCG إلى جانب زمن الاستجابة وحجم الفهرس. الاستراتيجية التي تعمل مع النصوص القصيرة يمكن أن تتصرف بشكل مختلف مع أطوال المستندات طويلة الذيل أو آلاف الرقع البصرية.
يعالج StructArray مشكلة واحدة: كيفية تمثيل عناصر متوافقة وقابلة للتصفية وتحمل متجهات داخل كيان واحد. تعالج استراتيجية EmbeddingList مشكلة أخرى: كيفية تقريب MaxSim بتكلفة مقبولة لنموذج ومجموعة معينة.
البحث الهجين يجعل هوية النتائج صريحة
نادرًا ما يتبع الاسترجاع في الإنتاج مسار متجه واحد. قد يجمع طلب الفيديو بين تضمين فيديو على المستوى الأعلى، وتضمين واحد أو أكثر على مستوى المقطع، وإشارة تسمية توضيحية أو نصية، وإعادة ترتيب.
بمجرد دخول المرشحين على مستوى العنصر إلى هذا الخط، يجب على المحرك تحديد ما يحدد المرشح النهائي.
| تكوين الطلب الهجين | نطاق المرشح النهائي | هوية النتيجة |
|---|---|---|
| جميع عمليات البحث الفرعية على مستوى العنصر وتستهدف الحقول الفرعية المتجهة تحت نفس StructArray | مستوى العنصر | المفتاح الأساسي بالإضافة إلى حقل StructArray بالإضافة إلى إزاحة العنصر |
| تم تضمين حقل متجه على المستوى الأعلى | مستوى الكيان | المفتاح الأساسي |
| تم تضمين طلب EmbeddingList | مستوى الكيان | المفتاح الأساسي |
| طلبات على مستوى العنصر تستهدف حقول StructArray مختلفة | مستوى الكيان | المفتاح الأساسي |
يحافظ التكوين الأول على هوية العنصر لأن الإزاحة 3 تشير إلى نفس عنصر Struct لكل بحث فرعي تحت StructArray أصلي معين. يناسب هذا التطبيق الذي يريد إرجاع المقطع أو الفقرة الأكثر صلة بعد دمج عدة إشارات على مستوى العنصر.
تخلط التكوينات الأخرى دقّات المرشحين أو مساحات أسماء العناصر. لذلك يجب دمج نتيجة عنصر في درجة على مستوى الكيان قبل إعادة الترتيب النهائية. يدعم Milvus عدة استراتيجيات للدمج:
| استراتيجية الدمج | درجة الكيان من نتائج العناصر المُرجعة | الشرط المهم |
|---|---|---|
max | أفضل درجة عنصر | يعمل مع معايير المتجهات العادية المدعومة |
sum | مجموع كل درجات العناصر المُرجعة | استخدم مع معايير الارتباط الإيجابي مثل IP أو COSINE |
avg | متوسط درجات العناصر المُرجعة | يعمل مع معايير المتجهات العادية المدعومة |
topk_sum | مجموع أفضل K من درجات العناصر المُرجعة | يتطلب topk موجبًا؛ استخدم مع IP أو COSINE |
topk_avg | متوسط أفضل K من درجات العناصر المُرجعة | يتطلب topk موجبًا |
يعمل الدمج فقط على نتائج العناصر التي يُرجعها بحث ANN الفرعي؛ ولا يفحص كل عنصر في الكيان بعد الاسترجاع. لذلك يتحكم حد limit في الطلب في نتائج العناصر المتاحة لوظيفة الدمج.
يشكل هذا الاختيار دلالات الاسترجاع، وليس مجرد تنسيق المخرجات. إذا كان التطبيق يعرض مقطعًا أو فقرة، فإن الحفاظ على الإزاحة خلال الدمج أمر طبيعي. إذا كان يعرض فيديو أو منتجًا أو مستندًا، فإن الدمج على مستوى الكيان أمر طبيعي. عندما تعمل الإشارات بدقّات مختلفة، يحتاج النظام إلى قاعدة تقييم صريحة من العنصر إلى الكيان.
ينقل StructArray مشكلة الهوية والدمج من المعالجة اللاحقة المؤقتة إلى نموذج تنفيذ البحث.
كيف ينفذ Milvus StructArray دون معاملته ككتلة
النموذج المواجه للمستخدم هو ARRAY<STRUCT>. ومع ذلك، فإن تخزين القيمة بأكملها ككتلة واحدة غير شفافة سيجعل فهارس الحقول الفرعية والتصفية والإخراج الانتقائي غير فعالة.
يستخدم Milvus تصميمًا بعمود أصلي منطقي وأعمدة فرعية مادية.
في طبقة المخطط، يكون clips هو الحقل الأصلي المنطقي. وهو يحدد خصائص مثل مخطط Struct والسعة القصوى وقابلية القيمة null. يتم تطبيع حقوله الفرعية إلى مسارات مثل clips[clip_embedding_list] و clips[clip_embedding] و clips[scene_type] و clips[label_confidence].
تتبع الحقول الفرعية العددية مسارات تخزين مصفوفة عددية لكل كيان، بينما تتبع الحقول الفرعية المتجهة مسارات مصفوفة متجهة. يمكن لكل حقل فرعي بعد ذلك استخدام مسار البيانات المناسب لنوعه: تصفية عددية وفهارس عددية للبيانات الوصفية، وفهارس متجهة وبحث ANN للتضمينات.
عند الإدراج، يقوم Proxy بتوسيع قائمة Struct المتداخلة إلى أعمدة فرعية محددة الأنواع. أثناء التنفيذ، يحافظ Milvus على العلاقة بين كل عنصر مادي وكيانه الأصلي. من الناحية المفاهيمية، تبدو هذه العلاقة كما يلي:
Plaintext
entity 0 -> elements [0, 1, 2]
entity 1 -> elements [3]
entity 2 -> elements []
entity 3 -> elements [4, 5, 6, 7]
عندما يُرجع البحث على مستوى العنصر معرّف عنصر ماديًا، يعيّنه Milvus مرة أخرى إلى الكيان الأصلي وإزاحة العنصر. عندما ينتج element_filter خريطة بتات على مستوى العنصر، يقوم المحرك بمحاذاتها مع رؤية الكيان الأصلي وعمليات الحذف وعوامل التصفية الأخرى.
عند إرجاع النتائج، يستخدم Milvus المخطط المنطقي والإزاحات المشتركة لإعادة بناء شكل StructArray الذي أدخله التطبيق. يمكن للنظام التنفيذ عبر أعمدة فرعية محددة الأنواع بينما يواصل المستخدم قراءة وكتابة الكائنات المتداخلة الطبيعية. هذا التخطيط المادي يجعل StructArray أكثر من مجرد JSON محدد الأنواع: العلاقة المتداخلة تشارك في نموذج الفهرس والتنفيذ.
أين يناسب StructArray، وأين لا يناسب
يعد StructArray مناسبًا بقوة عندما تكون جميع الشروط التالية صحيحة:
- لدى التطبيق كيان أصلي ذو معنى، مثل فيديو أو منتج أو مستند أو صفحة بصرية أو سجل ذاكرة.
- يحتوي كل كيان أصلي على مجموعة مرتبة متغيرة الطول من العناصر المحلية.
- تحتاج تلك العناصر إلى بيانات وصفية عددية خاصة بها، أو متجهات، أو كليهما.
- يجب أن يحافظ البحث أو التصفية على العلاقة بين الحقول الفرعية عند نفس إزاحة العنصر.
- يحتاج التطبيق إلى استرجاع متعدد المتجهات على مستوى الكيان، أو نتائج على مستوى العنصر، أو كليهما.
ليس StructArray بالضرورة أفضل لكل مجموعة. قد تُخدم المستندات القصيرة أو الاستعلامات البسيطة جيدًا بتضمين كثيف واحد. تضيف الفهرسة متعددة المتجهات تكاليف تخزين وبحث، لذا يجب أن تكتسب التمثيلات الإضافية مكانها من خلال تحسين جودة الاسترجاع أو دقّة نتائج أكثر فائدة.
حدود المخطط والتنفيذ الحالية مهمة أيضًا:
- يتم دعم
Structكنوع عنصر لـArray، وليس كحقل مجموعة على المستوى الأعلى. - تشترك جميع العناصر في StructArray واحد في مخطط واحد محدد مسبقًا.
max_capacityمطلوب ويحدد عدد العناصر لكل كيان.- لا يتم دعم الحقول الفرعية المتداخلة
StructأوArrayأوArrayOfStructأوJSONداخل StructArray. - يقبل الحقل الفرعي المتجه فهرسًا واحدًا. استخدم حقولًا فرعية متجهة منفصلة لبحث EmbeddingList والبحث على مستوى العنصر عندما يكون كلاهما مطلوبًا.
- يجب فهرسة الحقول الفرعية المتجهة قبل البحث. يجب فهرسة الحقول الفرعية العددية المستخدمة بكثرة في عوامل التصفية بشكل مناسب.
- يكون مخطط الحقل الفرعي ثابتًا بعد إنشاء حقل StructArray، لذا خطط لسمات العنصر قبل الإطلاق في الإنتاج.
تجعل هذه القيود النموذج أضيق من التداخل العشوائي في قواعد بيانات المستندات، ولكنها تمنح Milvus أيضًا بنية كافية للتفكير في هوية العنصر، وفهرسة كل حقل فرعي، والتنفيذ بدقّتي بحث.
StructArray يُبقي الأدلة المحلية فئة أولى دون فقدان الكيان
يمنح StructArray Milvus كائن استرجاع تجد المخططات المسطحة صعوبة في تمثيله: كيان أصلي مع مجموعة مرتبة من العناصر المهيكلة. تشارك العلاقات بين تلك العناصر في التصفية والفهرسة والبحث بدلاً من أن تكون موجودة في التخزين فقط.
يحتفظ كل عنصر ببياناته الوصفية وتضميناته الخاصة. يمكن للعناصر تحقيق المسندات العددية لنفس العنصر، أو المشاركة معًا في بحث EmbeddingList على مستوى الكيان، أو التنافس بشكل مستقل في البحث على مستوى العنصر. في الوقت نفسه، تظل مرتبطة بالكيان الأصلي الذي تمنحها بياناته الوصفية وأذوناته وهويته في التطبيق سياقها.
بالنسبة لمقاطع الفيديو وصور المنتجات وفقرات المستندات والرقع البصرية وأجزاء الذاكرة، يمكن البحث في الأدلة المحلية وتصفيتها دون فقدان الكيان الذي تنتمي إليه. خيارات التصميم المتبقية صريحة: حدد دقّة البحث، وامنح كل حقل فرعي متجه المعيار والفهرس المطابقين، وقرر ما إذا كانت النتائج الهجينة يجب أن تحافظ على إزاحات العناصر أو تُدمج مرة أخرى إلى الكيانات.
جرّب StructArray في Milvus 3.0
StructArray متاح في Milvus 3.0. ابدأ بنظرة عامة على StructArray. إذا كنت تقيّم الاسترجاع متعدد المتجهات على مستوى الكيان، فاقرأ دليل استراتيجية EmbeddingList. بالنسبة لدقّة النتائج وسلوك الدمج، راجع البحث الهجين مع StructArray.
لسياق الإصدار الأوسع، راجع مدونة إطلاق Milvus 3.0 وملاحظات الإصدار ومستودع milvus-io/milvus.
يدعم Zilliz Cloud أيضًا StructArray وبحث EmbeddingList للنشر المُدار. راجع دليل Zilliz Cloud لـ StructArray لمعرفة الحدود الخاصة بالخدمة. في Zilliz Cloud، يتم حاليًا توثيق العوامل العددية على StructArray لمجموعات On-Demand.
لمناقشة مخطط أو تصميم استرجاع مع الفريق، انضم إلى مجتمع Milvus على Discord أو احجز جلسة Milvus Office Hours.
Try Managed Milvus for Free
Zilliz Cloud is hassle-free, powered by Milvus and 10x faster.
Get StartedLike the article? Spread the word



