Skip to main content
في هذا الدليل، ستتعرّف على ما يلي:
  • مقدمة موجزة عن البحث المتجهي
  • مفهوم أقرب الجيران التقريبي (ANN) والعالم الصغير الهرمي القابل للتنقّل (HNSW)
  • Quantised Bit ‏(QBit)
  • استخدام QBit لإجراء بحث متجهي باستخدام مجموعة بيانات DBPedia

تمهيد في البحث المتجهي

في الرياضيات والفيزياء، يُعرَّف المتجه رسميًا بأنه كائن له مقدار واتجاه. وغالبًا ما يكون على هيئة قطعة مستقيمة أو سهم في الفضاء، ويمكن استخدامه لتمثيل كميات مثل السرعة والقوة والتسارع. أما في علوم الحاسوب، فالمتجه هو تسلسل منتهٍ من الأعداد. وبعبارة أخرى، فهو بنية بيانات تُستخدم لتخزين القيم العددية. في تعلّم الآلة، تكون المتجهات هي بنى البيانات نفسها التي نتحدث عنها في علوم الحاسوب، لكن القيم العددية المخزنة فيها تحمل معنى خاصًا. فعندما نأخذ كتلة من النص أو صورة ونختزلها إلى المفاهيم الأساسية التي تمثلها، تُسمّى هذه العملية بالترميز. ويكون الناتج تمثيلًا آليًا لتلك المفاهيم الأساسية بصيغة رقمية. وهذا هو التضمين، ويُخزَّن داخل متجه. وبصياغة أخرى، عندما يُضمَّن هذا المعنى السياقي داخل متجه، يمكن الإشارة إليه بوصفه تضمينًا. أصبح البحث المتجهي حاضرًا في كل مكان الآن. فهو يشغّل توصيات الموسيقى، والتوليد المعزّز بالاسترجاع (RAG) للنماذج اللغوية الكبيرة، حيث تُجلَب معرفة خارجية لتحسين الإجابات، وحتى البحث في Google يعتمد إلى حدّ ما على البحث المتجهي. وغالبًا ما يفضّل المستخدمون قواعد البيانات التقليدية ذات الإمكانات المتجهية المخصّصة على مخازن المتجهات المتخصصة بالكامل، رغم المزايا التي تقدمها قواعد البيانات المتخصصة. ويدعم ClickHouse البحث المتجهي الشامل وكذلك طرق البحث التقريبي عن أقرب الجيران (ANN)، بما في ذلك HNSW، وهو المعيار الحالي للاسترجاع السريع للمتجهات.

فهم التضمينات

لنلقِ نظرة على مثال بسيط لفهم كيفية عمل البحث المتجهي. لنأخذ تضمينات (تمثيلات متجهية) لبعض الكلمات: أنشئ الجدول أدناه باستخدام بعض التضمينات على سبيل المثال:
يمكنك البحث عن الكلمات الأكثر تشابهًا مع تضمين معيّن:
تضمين الاستعلام هو الأقرب إلى “apple” (بأصغر مسافة)، وهذا يبدو منطقيًا إذا نظرنا إلى التضمينين جنبًا إلى جنب:

أقرب الجيران التقريبيون (ANN)

بالنسبة إلى مجموعات البيانات الكبيرة، يصبح البحث بالقوة الغاشمة بطيئًا جدًا. وهنا يأتي دور خوارزميات أقرب الجيران التقريبيين.

التكميم

يتضمن التكميم تحويل القيم إلى أنواع رقمية أصغر. الأرقام الأصغر تعني بيانات أصغر، والبيانات الأصغر تعني حسابات مسافة أسرع. يمكن لمحرك تنفيذ الاستعلامات المتجهية في ClickHouse أن يضع قيماً أكثر في مسجلات المعالج في كل عملية، مما يزيد معدل المعالجة مباشرةً. لديك خياران:
  1. الاحتفاظ بالنسخة المكمَّمة إلى جانب العمود الأصلي - يضاعف هذا مساحة التخزين، لكنه آمن لأننا نستطيع دائماً الرجوع إلى الدقة الكاملة
  2. استبدال القيم الأصلية بالكامل (عن طريق تحويلها إلى نوع أصغر عند الإدراج) - يوفر هذا المساحة وعمليات الإدخال/الإخراج، لكنه خيار لا رجعة فيه

العالم الصغير الهرمي القابل للتنقّل (HNSW)

يتكوّن HNSW من طبقات متعددة من العقد (المتجهات). وتُسنَد كل عقدة عشوائيًا إلى طبقة واحدة أو أكثر، مع تناقص احتمال ظهورها في الطبقات الأعلى بشكل أُسّي. عند إجراء بحث، نبدأ من عقدة في الطبقة العليا ونتحرك بشكل جشع نحو أقرب الجيران. وعندما يتعذر العثور على عقدة أقرب، ننتقل إلى الطبقة التالية الأكثر كثافة. وبفضل هذا التصميم الطبقي، يحقق HNSW تعقيد بحث لوغاريتميًا بالنسبة إلى عدد العقد.
قيود HNSWيتمثل عنق الزجاجة الرئيسي في الذاكرة. يستخدم ClickHouse تنفيذ usearch لـ HNSW، وهي بنية بيانات داخل الذاكرة لا تدعم التقسيم. ونتيجة لذلك، تتطلب مجموعات البيانات الأكبر قدرًا أكبر من RAM بشكل متناسب.

مقارنة الأساليب

نظرة متعمقة على QBit

البت المُكمَّم (QBit)

QBit هي بنية بيانات جديدة يمكنها تخزين قيم BFloat16 وFloat32 وFloat64 بالاستفادة من طريقة تمثيل الأعداد ذات الفاصلة العائمة، أي على شكل بتات. وبدلًا من تخزين كل عدد كقيمة كاملة، تقسّم QBit القيم إلى طبقات بتات: كل بت أول، وكل بت ثانٍ، وكل بت ثالث، وهكذا. يعالج هذا النهج القيد الرئيسي في أساليب التكميم التقليدية. فلا حاجة إلى تخزين بيانات مكررة أو المخاطرة بأن تصبح القيم بلا معنى. كما أنه يتجنب اختناقات RAM في HNSW، لأن QBit تعمل مباشرة على البيانات المخزنة بدلًا من الاحتفاظ بفهرس داخل الذاكرة.
الفائدةوالأهم من ذلك، أنه لا حاجة إلى اتخاذ قرارات مسبقة. يمكن ضبط الدقة والأداء ديناميكيًا في وقت الاستعلام، مما يتيح للمستخدمين استكشاف التوازن بين الدقة والسرعة بأقل قدر ممكن من التعقيد.
القيدعلى الرغم من أن QBit تُسرّع البحث المتجهي، فإن تعقيدها الحسابي يظل O(n). وبعبارة أخرى: إذا كانت مجموعة بياناتك صغيرة بما يكفي ليتسع فهرس HNSW داخل RAM بسهولة، فسيظل هذا هو الخيار الأسرع.

نوع البيانات

فيما يلي كيفية إنشاء عمود QBit:
عند إدراج البيانات في عمود QBit، يُعاد ترتيبها بحيث تصطف جميع البتات الأولى معًا، وجميع البتات الثانية معًا، وهكذا. ونُطلق على هذه مجموعات. تُخزَّن كل مجموعة في عمود FixedString(N) منفصل: سلاسل نصية ثابتة الطول بحجم N بايت، تُخزَّن بشكل متتالٍ في الذاكرة من دون أي فواصل بينها. ثم تُضم جميع هذه المجموعات معًا في Tuple واحد، وهو ما يشكّل البنية الأساسية لـ QBit. مثال: إذا بدأنا بمتجه من 8 عناصر من النوع Float64، فستحتوي كل مجموعة على 8 بتات. وبما أن Float64 يتكوّن من 64 بتًا، فسينتهي بنا الأمر إلى 64 مجموعة (واحدة لكل بت). لذلك، يبدو التخطيط الداخلي لـ QBit(Float64, 8) كأنه Tuple مكوَّن من 64 عمودًا من النوع FixedString(1).
إذا كان طول المتجه الأصلي لا ينقسم بالتساوي على 8، فستُستكمَل البنية بعناصر غير مرئية لجعلها مصطفّة على 8. وهذا يضمن التوافق مع FixedString، التي تعمل حصريًا على بايتات كاملة.

حساب المسافة

للاستعلام باستخدام QBit، استخدم الدالة L2DistanceTransposed مع معلَمة الدقة:
تحدد المعلمة الثالثة (16) مستوى الدقة بوحدة البِتات.

تحسين عمليات I/O

قبل أن نتمكن من حساب المسافات، يجب أولًا قراءة البيانات المطلوبة من القرص ثم فكّ نقلها (أي تحويلها مرة أخرى من تمثيل البتات المجمّعة إلى متجهات كاملة). ولأن QBit يخزّن القيم بصيغة البتات المنقولة بحسب مستوى الدقة، يمكن لـ ClickHouse قراءة أعلى طبقات البت فقط اللازمة لإعادة بناء الأرقام بالدقة المطلوبة. في الاستعلام أعلاه، نستخدم مستوى دقة مقداره 16. وبما أن Float64 يتكوّن من 64 بت، فإننا نقرأ فقط أول 16 طبقة بت، متجاوزين 75% من البيانات. بعد القراءة، نعيد بناء الجزء العلوي فقط من كل رقم من طبقات البت المحمّلة، مع إبقاء البتات غير المقروءة صفراً.

تحسين العمليات الحسابية

قد يتساءل البعض عمّا إذا كان التحويل إلى نوع أصغر، مثل Float32 أو BFloat16، يمكن أن يزيل هذا الجزء غير المستخدم. هذا ممكن بالفعل، لكن التحويلات الصريحة مكلفة عند تطبيقها على كل صف. بدلًا من ذلك، يمكننا التصغير إلى نوع أقل دقة للمتجه المرجعي فقط، والتعامل مع بيانات QBit كما لو كانت تحتوي على قيم أضيق (“متجاهلين” وجود بعض الأعمدة)، لأن بنيتها غالبًا ما تتوافق مع نسخة مبتورة من تلك الأنواع.

تحسين BFloat16

BFloat16 هو Float32 مُقتطع إلى النصف. ويحتفظ بنفس بت الإشارة وبالأسّ ذي 8 بتات، لكنه يحتفظ فقط بالبتات السبع العليا من المانتيسا ذات 23 بتًا. لذلك، فإن قراءة أول 16 مستوى بت من عمود QBit تعيد فعليًا إنتاج بنية قيم BFloat16. لذا، في هذه الحالة يمكننا — ونفعل ذلك بالفعل — تحويل متجه المرجع بأمان إلى BFloat16.

تعقيد Float64

أما Float64، فالأمر مختلف. فهو يستخدم أسًّا من 11 بت ومانتيسا من 52 بت، ما يعني أنه ليس مجرد Float32 بعدد بتات مضاعف. بنيته وانحياز الأسّ فيه مختلفان تمامًا. ويتطلب التحويل التنازلي من Float64 إلى صيغة أصغر مثل Float32 إجراء تحويل IEEE-754 فعليًا، حيث تُقرَّب كل قيمة إلى أقرب Float32 قابل للتمثيل. وتُعد خطوة التقريب هذه مكلفة حسابيًا.
إذا كنت مهتمًا بالتعمق في عناصر الأداء في QBit، فراجع “Let’s vectorize”

مثال باستخدام DBpedia

لنرَ QBit قيد الاستخدام في مثال واقعي باستخدام مجموعة بيانات DBpedia، التي تضم مليون مقالة من ويكيبيديا ممثّلة على شكل تضمينات Float32.

التهيئة

أولًا، أنشئ الجدول
أدرِج البيانات باستخدام سطر الأوامر:
قد يستغرق إدخال البيانات بعض الوقت. حان وقت استراحة قهوة!
بدلاً من ذلك، يمكن تنفيذ عبارات SQL الفردية كما هو موضح أدناه لتحميل كل ملف من ملفات Parquet الخمسة والعشرين:
تأكّد من ظهور مليون صف في جدول dbpedia:
بعد ذلك، أضِف عمود QBit:

استعلام البحث

سنبحث عن المفاهيم الأكثر ارتباطًا بمصطلحات البحث المتعلقة بالفضاء التالية: Moon وApollo 11 وSpace Shuttle وAstronaut وRocket:
يبحث الاستعلام عن أفضل 1000 عنصر متشابه دلاليًا لكل واحد من المفاهيم الخمسة. ويُرجع العناصر التي تظهر في ثلاثة على الأقل من تلك النتائج، مرتبةً بحسب عدد المفاهيم التي تطابقها وأدنى مسافة بينها وبين أيٍّ منها (باستثناء العناصر الأصلية). باستخدام 5 بتات فقط (1 للإشارة + 4 بتات للأس، ومن دون مانتيسا):
الأداء: 10 صفوف في مجموعة النتائج. الوقت المنقضي: 0.271 ثانية. تمت معالجة 8.46 مليون صف، و4.54 GB (31.19 مليون صف/ثانية، و16.75 GB/ثانية.) ذروة استخدام الذاكرة: 739.82 MiB.
الأداء: 10 rows in set. Elapsed: 1.157 sec. Processed 10.00 million rows, 32.76 GB (8.64 million rows/s., 28.32 GB/s.) Peak memory usage: 6.05 GiB.

الفكرة الجوهرية

النتائج؟ ليست جيدة فحسب، بل جيدة على نحو مفاجئ. فليس من البديهي أن الأعداد ذات الفاصلة العائمة، حتى بعد تجريدها من المانتيسا بالكامل ونصف الأس، تظل تحتفظ بمعلومات ذات معنى. الفكرة الجوهرية وراء QBit هي أن البحث المتجهي يظل فعّالًا حتى إذا تجاهلنا البتات غير المهمة. انخفض استخدام الذاكرة من 6.05 GB إلى 740 MB مع الحفاظ على جودة ممتازة للبحث الدلالي!

الخلاصة

QBit هو نوع أعمدة يخزّن الأعداد العائمة على شكل مستويات بت. ويتيح لك اختيار عدد البتات التي تُقرأ أثناء البحث المتجهي، بما يضبط الاسترجاع والأداء من دون تغيير البيانات. ولكل طريقة من طرق البحث المتجهي معلماتها الخاصة التي تحدد أوجه المفاضلة بين الاسترجاع والدقة والأداء. وعادةً ما يجب اختيار هذه المعلمات مسبقًا. وإذا أخطأت في اختيارها، فسيُهدر الكثير من الوقت والموارد، وسيصبح تغيير المسار لاحقًا أمرًا مرهقًا. ومع QBit، لا حاجة إلى اتخاذ قرارات مبكرة. يمكنك ضبط المفاضلة بين الدقة والسرعة مباشرةً وقت الاستعلام، واستكشاف التوازن المناسب أثناء التقدّم.
مقتبس بتصرّف من مقالة المدونة بقلم Raufs Dunamalijevs، والمنشورة في 28 أكتوبر 2025
آخر تعديل في ٣ يوليو ٢٠٢٦