تصميم نموذج البيانات مع مجموعة من الهياكلCompatible with Milvus 2.6.4+
تطبيقات الذكاء الاصطناعي الحديثة، خاصة في إنترنت الأشياء (IoT) والقيادة الذاتية، عادةً ما تستند إلى أحداث غنية ومنظمة: قراءة مستشعر مع طابعها الزمني وتضمين متجهها، أو سجل تشخيصي مع رمز خطأ ومقتطف صوتي، أو مقطع رحلة مع الموقع والسرعة وسياق المشهد. يتطلب ذلك أن تدعم قاعدة البيانات أصلاً استيعاب البيانات المتداخلة والبحث فيها.
بدلًا من مطالبة المستخدم بتحويل أحداثه الهيكلية الذرية إلى نماذج بيانات مسطحة، يقدم ميلفوس مصفوفة الهياكل، حيث يمكن لكل هيكل في المصفوفة أن يحتوي على كميات قياسية ومتجهات مع الحفاظ على التكامل الدلالي.
لماذا صفيف الهياكل
تعتمد تطبيقات الذكاء الاصطناعي الحديثة، من القيادة الذاتية إلى الاسترجاع متعدد الوسائط، بشكل متزايد على البيانات المتداخلة وغير المتجانسة. وتكافح نماذج البيانات المسطحة التقليدية لتمثيل العلاقات المعقدة مثل"مستند واحد مع العديد من الأجزاء المشروحة" أو"مشهد قيادة واحد مع مناورات متعددة مرصودة". هذا هو المكان الذي يتألق فيه نوع بيانات Array of Structs في Milvus.
تسمح لك مصفوفة الهياكل بتخزين مجموعة مرتبة من العناصر المهيكلة، حيث تحتوي كل بنية على مجموعة خاصة بها من الحقول القياسية والتضمينات المتجهة. وهذا يجعلها مثالية لـ
البيانات الهرمية: الكيانات الأصلية ذات السجلات الفرعية المتعددة، مثل كتاب يحتوي على العديد من الأجزاء النصية، أو مقطع فيديو يحتوي على العديد من الإطارات المشروحة.
التضمينات متعددة الوسائط: يمكن أن تحتوي كل بنية على متجهات متعددة، مثل تضمين النص بالإضافة إلى تضمين الصور، إلى جانب البيانات الوصفية.
البيانات الزمنية أو المتسلسلة: تمثل الهياكل في حقل المصفوفة بشكل طبيعي سلاسل زمنية أو أحداث متسلسلة خطوة بخطوة.
على عكس الحلول التقليدية التي تخزن نقاط JSON أو تقسم البيانات عبر مجموعات متعددة، توفر مصفوفة الهياكل تطبيقًا أصليًا للمخطط وفهرسة المتجهات والتخزين الفعال داخل Milvus.
إرشادات تصميم المخطط
بالإضافة إلى جميع الإرشادات التي تمت مناقشتها في تصميم نموذج البيانات للبحث، يجب عليك أيضًا مراعاة الأمور التالية قبل البدء في استخدام مصفوفة الهياكل في تصميم نموذج البيانات الخاص بك.
تحديد مخطط الهياكل
قبل إضافة حقل المصفوفة إلى مجموعتك، حدد مخطط الهيكل الداخلي. يجب أن يكون كل حقل في البنية مكتوبًا بشكل صريح، سواءً كان مكتوبًا بشكل صريح، أو قياسيًا(VARCHAR، INT، BOOLEAN، إلخ) أو متجهًا(FLOAT_VECTOR).
يُنصح بالحفاظ على مخطط الهيكل بسيطًا من خلال تضمين الحقول التي ستستخدمها للاسترجاع أو العرض فقط. تجنب الانتفاخ بالبيانات الوصفية غير المستخدمة.
عيّن السعة القصوى بعناية
يحتوي كل حقل مصفوفة على سمة تحدد الحد الأقصى لعدد العناصر التي يمكن أن يحتفظ بها حقل المصفوفة لكل كيان. قم بتعيين هذا بناءً على الحد الأعلى لحالة الاستخدام الخاصة بك. على سبيل المثال، هناك 1000 قطعة نصية لكل مستند، أو 100 مناورة لكل مشهد قيادة.
القيمة العالية للغاية تهدر الذاكرة، وستحتاج إلى إجراء بعض الحسابات لتحديد الحد الأقصى لعدد الهياكل في حقل المصفوفة.
فهرسة الحقول المتجهة في الهياكل
تعد الفهرسة إلزامية للحقول المتجهة، بما في ذلك كل من الحقول المتجهة في مجموعة وتلك المحددة في بنية. بالنسبة للحقول المتجهة في هيكل، يجب استخدام AUTOINDEX أو HNSW كنوع الفهرس و MAX_SIM سلسلة كنوع القياس.
للحصول على تفاصيل حول جميع الحدود القابلة للتطبيق، راجع الحدود.
مثال واقعي: نمذجة مجموعة بيانات CoVLA للقيادة الذاتية
توفر مجموعة بيانات الرؤية-اللغة-الإجراء الشاملة (CoVLA)، التي قدمتها شركة Turing Motors وتم قبولها في المؤتمر الشتوي لتطبيقات الرؤية الحاسوبية (WACV) لعام 2025، أساساً غنياً لتدريب وتقييم نماذج الرؤية-اللغة-الإجراء في القيادة الذاتية. ولا تحتوي كل نقطة بيانات، والتي عادةً ما تكون مقطع فيديو، على مدخلات بصرية أولية فحسب، بل تحتوي أيضاً على تعليقات منظمة تصف
سلوكيات مركبة الأنا (على سبيل المثال، "الانعطاف يسارًا أثناء الانعطاف إلى حركة المرور القادمة"),
الأجسام المكتشفة الموجودة (على سبيل المثال، المركبات الأمامية والمشاة وإشارات المرور)، و
شرح على مستوى الإطار للمشهد.
هذه الطبيعة الهرمية والمتعددة الوسائط تجعلها مرشحة مثالية لميزة صفيف الهياكل. للحصول على تفاصيل حول مجموعة بيانات CoVLA، راجع الموقع الإلكتروني لمجموعة بيانات CoVLA.
الخطوة 1: تعيين مجموعة البيانات في مخطط مجموعة
مجموعة بيانات CoVLA عبارة عن مجموعة بيانات قيادة متعددة الوسائط وواسعة النطاق تضم 10,000 مقطع فيديو، بإجمالي أكثر من 80 ساعة من اللقطات. تقوم المجموعة بأخذ عينات من الإطارات بمعدل 20 هرتز وتعلق على كل إطار بتعليقات توضيحية مفصلة باللغة الطبيعية إلى جانب معلومات عن حالة السيارة وإحداثيات الأجسام المكتشفة.
هيكل مجموعة البيانات على النحو التالي:
├── video_1 (VIDEO) # video.mp4
│ ├── video_id (INT)
│ ├── video_url (STRING)
│ ├── frames (ARRAY)
│ │ ├── frame_1 (STRUCT)
│ │ │ ├── caption (STRUCT) # captions.jsonl
│ │ │ │ ├── plain_caption (STRING)
│ │ │ │ ├── rich_caption (STRING)
│ │ │ │ ├── risk (STRING)
│ │ │ │ ├── risk_correct (BOOL)
│ │ │ │ ├── risk_yes_rate (FLOAT)
│ │ │ │ ├── weather (STRING)
│ │ │ │ ├── weather_rate (FLOAT)
│ │ │ │ ├── road (STRING)
│ │ │ │ ├── road_rate (FLOAT)
│ │ │ │ ├── is_tunnel (BOOL)
│ │ │ │ ├── is_tunnel_yes_rate (FLOAT)
│ │ │ │ ├── is_highway (BOOL)
│ │ │ │ ├── is_highway_yes_rate (FLOAT)
│ │ │ │ ├── has_pedestrain (BOOL)
│ │ │ │ ├── has_pedestrain_yes_rate (FLOAT)
│ │ │ │ ├── has_carrier_car (BOOL)
│ │ │ ├── traffic_light (STRUCT) # traffic_lights.jsonl
│ │ │ │ ├── index (INT)
│ │ │ │ ├── class (STRING)
│ │ │ │ ├── bbox (LIST<FLOAT>)
│ │ │ ├── front_car (STRUCT) # front_cars.jsonl
│ │ │ │ ├── has_lead (BOOL)
│ │ │ │ ├── lead_prob (FLOAT)
│ │ │ │ ├── lead_x (FLOAT)
│ │ │ │ ├── lead_y (FLOAT)
│ │ │ │ ├── lead_speed_kmh (FLOAT)
│ │ │ │ ├── lead_a (FLOAT)
│ │ ├── frame_2 (STRUCT)
│ │ ├── ... (STRUCT)
│ │ ├── frame_n (STRUCT)
├── video_2
├── ...
├── video_n
يمكنك أن تجد أن بنية مجموعة بيانات CoVLA هرمية للغاية، حيث تقسم البيانات المجمعة إلى عدة ملفات .jsonl ، إلى جانب مقاطع الفيديو بتنسيق .mp4.
في Milvus، يمكنك استخدام حقل JSON أو حقل Array-of-Structs لإنشاء بنيات متداخلة داخل مخطط المجموعة. عندما تكون التضمينات المتجهة جزءًا من التنسيق المتداخل، يتم دعم حقل صفيف من البنى فقط. ومع ذلك، لا يمكن أن تحتوي بنية داخل مصفوفة في حد ذاتها على هياكل متداخلة أخرى. لتخزين مجموعة بيانات CoVLA مع الاحتفاظ بالعلاقات الأساسية، تحتاج إلى إزالة التسلسل الهرمي غير الضروري وتسوية البيانات بحيث تناسب مخطط مجموعة Milvus.
يوضح الرسم البياني أدناه كيف يمكننا نمذجة مجموعة البيانات هذه باستخدام المخطط الموضح في المخطط التالي:
نموذج مجموعة البيانات
يوضح الرسم البياني أعلاه بنية مقطع الفيديو، والذي يتألف من الحقول التالية:
video_idبمثابة المفتاح الأساسي، والذي يقبل الأعداد الصحيحة من نوع INT64.statesهو جسم JSON الخام الذي يحتوي على حالة مركبة الأنا في كل إطار من الفيديو الحالي.captionsعبارة عن مصفوفة من الهياكل مع كل هيكل يحتوي على الحقول التالية:frame_idتحدد إطارًا محددًا داخل الفيديو الحالي.plain_captionهو وصف للإطار الحالي بدون البيئة المحيطة، مثل الطقس، وحالة الطريق، وما إلى ذلك، وplain_cap_vectorهو التضمين المتجه المقابل له.rich_captionهو وصف للإطار الحالي مع البيئة المحيطة، وrich_cap_vectorهو التضمينات المتجهة المقابلة له.riskهو وصف للمخاطر التي تواجهها مركبة الأنا في الإطار الحالي، وrisk_vectorهو التضمينات المتجهة المقابلة لها، وجميع السمات الأخرى للإطار، مثل
road،weather، ،is_tunnel،has_pedestrain، إلخ...
traffic_lightsهي هيئة JSON تحتوي على جميع إشارات المرور الضوئية المحددة في الإطار الحالي.front_carsهي أيضًا مصفوفة من الهياكل التي تحتوي على جميع السيارات الرائدة المحددة في الإطار الحالي.
الخطوة 2: تهيئة المخططات
للبدء، نحتاج إلى تهيئة المخطط الخاص بهيكل التسمية التوضيحية، وهيكل السيارات الأمامية، والمجموعة.
تهيئة المخطط لهيكل التسمية التوضيحية.
client = MilvusClient("http://localhost:19530") # create the schema for the caption struct schema_for_caption = client.create_struct_field_schema() schema_for_caption.add_field( field_name="frame_id", datatype=DataType.INT64, description="ID of the frame to which the ego vehicle's behavior belongs" ) schema_for_caption.add_field( field_name="plain_caption", datatype=DataType.VARCHAR, max_length=1024, description="plain description of the ego vehicle's behaviors" ) schema_for_caption.add_field( field_name="plain_cap_vector", datatype=DataType.FLOAT_VECTOR, dim=768, description="vectors for the plain description of the ego vehicle's behaviors" ) schema_for_caption.add_field( field_name="rich_caption", datatype=DataType.VARCHAR, max_length=1024, description="rich description of the ego vehicle's behaviors" ) schema_for_caption.add_field( field_name="rich_cap_vector", datatype=DataType.FLOAT_VECTOR, dim=768, description="vectors for the rich description of the ego vehicle's behaviors" ) schema_for_caption.add_field( field_name="risk", datatype=DataType.VARCHAR, max_length=1024, description="description of the ego vehicle's risks" ) schema_for_caption.add_field( field_name="risk_vector", datatype=DataType.FLOAT_VECTOR, dim=768, description="vectors for the description of the ego vehicle's risks" ) schema_for_caption.add_field( field_name="risk_correct", datatype=DataType.BOOL, description="whether the risk assessment is correct" ) schema_for_caption.add_field( field_name="risk_yes_rate", datatype=DataType.FLOAT, description="probability/confidence of risk being present" ) schema_for_caption.add_field( field_name="weather", datatype=DataType.VARCHAR, max_length=50, description="weather condition" ) schema_for_caption.add_field( field_name="weather_rate", datatype=DataType.FLOAT, description="probability/confidence of the weather condition" ) schema_for_caption.add_field( field_name="road", datatype=DataType.VARCHAR, max_length=50, description="road type" ) schema_for_caption.add_field( field_name="road_rate", datatype=DataType.FLOAT, description="probability/confidence of the road type" ) schema_for_caption.add_field( field_name="is_tunnel", datatype=DataType.BOOL, description="whether the road is a tunnel" ) schema_for_caption.add_field( field_name="is_tunnel_yes_rate", datatype=DataType.FLOAT, description="probability/confidence of the road being a tunnel" ) schema_for_caption.add_field( field_name="is_highway", datatype=DataType.BOOL, description="whether the road is a highway" ) schema_for_caption.add_field( field_name="is_highway_yes_rate", datatype=DataType.FLOAT, description="probability/confidence of the road being a highway" ) schema_for_caption.add_field( field_name="has_pedestrian", datatype=DataType.BOOL, description="whether there is a pedestrian present" ) schema_for_caption.add_field( field_name="has_pedestrian_yes_rate", datatype=DataType.FLOAT, description="probability/confidence of pedestrian presence" ) schema_for_caption.add_field( field_name="has_carrier_car", datatype=DataType.BOOL, description="whether there is a carrier car present" )تهيئة المخطط الخاص ببنية السيارة الأمامية
على الرغم من أن السيارة الأمامية لا تتضمن تضم تضمينات متجهة، إلا أنك لا تزال بحاجة إلى تضمينها كمصفوفة من Struct لأن حجم البيانات يتجاوز الحد الأقصى لحقل JSON.
schema_for_front_car = client.create_struct_field_schema() schema_for_front_car.add_field( field_name="frame_id", datatype=DataType.INT64, description="ID of the frame to which the ego vehicle's behavior belongs" ) schema_for_front_car.add_field( field_name="has_lead", datatype=DataType.BOOL, description="whether there is a leading vehicle" ) schema_for_front_car.add_field( field_name="lead_prob", datatype=DataType.FLOAT, description="probability/confidence of the leading vehicle's presence" ) schema_for_front_car.add_field( field_name="lead_x", datatype=DataType.FLOAT, description="x position of the leading vehicle relative to the ego vehicle" ) schema_for_front_car.add_field( field_name="lead_y", datatype=DataType.FLOAT, description="y position of the leading vehicle relative to the ego vehicle" ) schema_for_front_car.add_field( field_name="lead_speed_kmh", datatype=DataType.FLOAT, description="speed of the leading vehicle in km/h" ) schema_for_front_car.add_field( field_name="lead_a", datatype=DataType.FLOAT, description="acceleration of the leading vehicle" )قم بتهيئة المخطط للمجموعة
schema = client.create_schema() schema.add_field( field_name="video_id", datatype=DataType.VARCHAR, description="primary key", max_length=16, is_primary=True, auto_id=False ) schema.add_field( field_name="video_url", datatype=DataType.VARCHAR, max_length=512, description="URL of the video" ) schema.add_field( field_name="captions", datatype=DataType.ARRAY, element_type=DataType.STRUCT, struct_schema=schema_for_caption, max_capacity=600, description="captions for the current video" ) schema.add_field( field_name="traffic_lights", datatype=DataType.JSON, description="frame-specific traffic lights identified in the current video" ) schema.add_field( field_name="front_cars", datatype=DataType.ARRAY, element_type=DataType.STRUCT, struct_schema=schema_for_front_car, max_capacity=600, description="frame-specific leading cars identified in the current video" )
الخطوة 3: تعيين معلمات الفهرس
يجب فهرسة جميع الحقول المتجهة. لفهرسة حقول المتجهات في بنية عنصر، تحتاج إلى استخدام AUTOINDEX أو HNSW كنوع الفهرس ونوع مقياس السلسلة MAX_SIM لقياس أوجه التشابه بين قوائم التضمين.
index_params = client.prepare_index_params()
index_params.add_index(
field_name="captions[plain_cap_vector]",
index_type="AUTOINDEX",
metric_type="MAX_SIM_COSINE",
index_name="captions_plain_cap_vector_idx", # mandatory for now
index_params={"M": 16, "efConstruction": 200}
)
index_params.add_index(
field_name="captions[rich_cap_vector]",
index_type="AUTOINDEX",
metric_type="MAX_SIM_COSINE",
index_name="captions_rich_cap_vector_idx", # mandatory for now
index_params={"M": 16, "efConstruction": 200}
)
index_params.add_index(
field_name="captions[risk_vector]",
index_type="AUTOINDEX",
metric_type="MAX_SIM_COSINE",
index_name="captions_risk_vector_idx", # mandatory for now
index_params={"M": 16, "efConstruction": 200}
)
يُنصح بتمكين تمزيق JSON لحقول JSON لتسريع التصفية داخل هذه الحقول.
الخطوة 4: إنشاء مجموعة
بمجرد أن تصبح المخططات والفهارس جاهزة، يمكنك إنشاء المجموعة المستهدفة على النحو التالي:
client.create_collection(
collection_name="covla_dataset",
schema=schema,
index_params=index_params
)
الخطوة 5: إدراج البيانات
ينظّم تورينج موتوس مجموعة بيانات CoVLA في ملفات متعددة، بما في ذلك مقاطع الفيديو الخام (.mp4)، والحالات (states.jsonl)، والتعليقات التوضيحية (captions.jsonl)، وإشارات المرور (traffic_lights.jsonl)، والسيارات الأمامية (front_cars.jsonl).
تحتاج إلى دمج أجزاء البيانات لكل مقطع فيديو من هذه الملفات وإدراج البيانات. فيما يلي البرنامج النصي التالي لدمج أجزاء البيانات لمقطع فيديو معين.
import json
from openai import OpenAI
openai_client = OpenAI(
api_key='YOUR_OPENAI_API_KEY',
)
video_id = "0a0fc7a5db365174" # represent a single video with 600 frames
# get all front car records in the specified video clip
entries = []
front_cars = []
with open('data/front_car/{}.jsonl'.format(video_id), 'r') as f:
for line in f:
entries.append(json.loads(line))
for entry in entries:
for key, value in entry.items():
value['frame_id'] = int(key)
front_cars.append(value)
# get all traffic lights identified in the specified video clip
entries = []
traffic_lights = []
frame_id = 0
with open('data/traffic_lights/{}.jsonl'.format(video_id), 'r') as f:
for line in f:
entries.append(json.loads(line))
for entry in entries:
for key, value in entry.items():
if not value or (value['index'] == 1 and key != '0'):
frame_id+=1
if value:
value['frame_id'] = frame_id
traffic_lights.append(value)
else:
value_dict = {}
value_dict['frame_id'] = frame_id
traffic_lights.append(value_dict)
# get all captions generated in the video clip and convert them into vector embeddings
entries = []
captions = []
with open('data/captions/{}.jsonl'.format(video_id), 'r') as f:
for line in f:
entries.append(json.loads(line))
def get_embedding(text, model="embeddinggemma:latest"):
response = openai_client.embeddings.create(input=text, model=model)
return response.data[0].embedding
# Add embeddings to each entry
for entry in entries:
# Each entry is a dict with a single key (e.g., '0', '1', ...)
for key, value in entry.items():
value['frame_id'] = int(key) # Convert key to integer and assign to frame_id
if "plain_caption" in value and value["plain_caption"]:
value["plain_cap_vector"] = get_embedding(value["plain_caption"])
if "rich_caption" in value and value["rich_caption"]:
value["rich_cap_vector"] = get_embedding(value["rich_caption"])
if "risk" in value and value["risk"]:
value["risk_vector"] = get_embedding(value["risk"])
captions.append(value)
data = {
"video_id": video_id,
"video_url": "https://your-storage.com/{}".format(video_id),
"captions": captions,
"traffic_lights": traffic_lights,
"front_cars": front_cars
}
بمجرد الانتهاء من معالجة البيانات وفقًا لذلك، يمكنك إدراجها على النحو التالي:
client.insert(
collection_name="covla_dataset",
data=[data]
)
# {'insert_count': 1, 'ids': ['0a0fc7a5db365174'], 'cost': 0}