naive_bayes (NAIVE_BAYES) النصوص باستخدام نموذج Naive Bayes متعدد الحدود، وهو نموذج الأحداث القياسي للنصوص: إذ يقيّم كل فئة بناءً على مدى تكرار ظهور n-gram counts الخاصة بالمدخل فيها. وتزوّده بجدول يحتوي على n-gram counts لكل فئة، فيُجمِّعها في نموذج مرة واحدة عند وقت التحميل، ثم يستخدمه لتصنيف أي نص تمرّره إليه.
وهو مناسب لتصنيف النصوص بسرعة وبأسلوب خفيف، مثل تحليل المشاعر، ووضع تسميات للموضوعات أو الرسائل غير المرغوب فيها، واكتشاف اللغة أو برنامج نصي.
يمكنك query القاموس باستخدام إحدى الدوال الثلاث التالية:
- تعيد
naiveBayesClassifierمعرّف الفئة المتوقعة. - تعيد
naiveBayesClassifierWithProbالفئة المتوقعة مع احتمالها. - تعيد
naiveBayesClassifierWithAllProbsكل فئة مع احتمالها.
dictGet العادية إجراء التصنيف (راجع الملاحظات). وهناك دالة أخرى، هي naiveBayesNgrams، لا تُجري التصنيف، بل تقسّم النص إلى n-grams بالطريقة نفسها التي يستخدمها القاموس، بحيث يمكنك بناء بيانات التدريب من raw text (راجع بناء بيانات التدريب من raw text).
البدء السريع
n = 1) بوضع token لتحليل المشاعر.
1. أنشئ جدول مصدر لإحصاءات تكرار n-gram لكل فئة:
1) والفئة السلبية (0):
NAIVE_BAYES:
PRIMARY KEY ngram العمود ngram هو المفتاح — ولكن في قاموس NAIVE_BAYES يكون هذا “المفتاح” هو النص الذي تمرّره للتصنيف، لا قيمةً مخزّنة تبحث عنها (راجع بنية القاموس). ويُهيّئ LAYOUT النموذج: إذ تحدد class_attribute 'class_id' أن class_id هو تسمية الفئة (لذا تكون السمة الأخرى، count، هي عدد مرات الظهور لكل فئة)، ويستخدم n 1 أحاديات، بينما يقسم mode 'token' النص إلى كلمات تفصل بينها مسافات بيضاء (راجع معلمات التخطيط).
4. صنِّف — تُرجع naiveBayesClassifier معرّف الفئة:
1 يقابل الفئة الإيجابية، استنادًا إلى بيانات التدريب التي أدرجناها في الخطوة 2.
0 إلى الفئة السلبية.
النتيجة نفسها باستخدام dictGet:
0 (سلبية) باحتمال 0.64.
naiveBayesClassifierWithAllProbs جميع الفئات مرتبة من الأعلى إلى الأدنى احتمالًا، مع احتمالات مجموعها 1.0 — وهنا 0.64 للفئة السلبية و0.36 للفئة الإيجابية.
كيف يعمل
(n-gram, class, count). وعند تحميل القاموس، تُصرَّف الصفوف مرةً واحدة إلى النموذج. وتُجمع الصفوف المكررة من الشكل (n-gram, class)، بينما تُتجاهل الصفوف التي فيها count = 0.
التصنيف (وقت الاستعلام). لتصنيف سلسلة نصية، يقوم النموذج بما يلي:
- يقسّمها إلى n-grams وفقًا لـ
modeوn(راجع أوضاع التقسيم إلى رموز). - يحسب درجة لكل فئة من خلال الجمع بين الاحتمال القبلي للفئة ومدى تكرار ظهور n-grams الخاصة بالإدخال ضمن تلك الفئة.
- يرتّب الفئات حسب الدرجة. وتكون الفئة ذات أعلى درجة هي التنبؤ الذي تعيده
naiveBayesClassifier؛ كما تعيدnaiveBayesClassifierWithProbوnaiveBayesClassifierWithAllProbsأيضًا احتمالات — لتلك الفئة أو لجميع الفئات.
alpha، ويُستخدم للتنعيم. ويمنع التنعيم النموذج من إعطاء فئة درجة صفر لمجرد أن n-gram واحدة لم تظهر في تلك الفئة أثناء التدريب. تجعل قيمة alpha الأصغر النموذج يعتمد أكثر على بيانات التدريب، لذلك قد تحصل إحدى الفئات على درجة أعلى بكثير من غيرها، لكنها قد تجعل النموذج أيضًا مفرط الحساسية عندما تكون بيانات التدريب قليلة أو غير متوازنة. أما قيمة alpha الأكبر فتجعل أعداد n-grams أقل تأثيرًا، فتغدو درجات الفئات المختلفة أكثر تقاربًا. وإذا كانت alpha كبيرة جدًا، تكاد معلومات n-gram ألا تؤثر، وتصبح الدرجة معتمدةً في الغالب على الاحتمال القبلي للفئة (الموصوف لاحقًا).
أما العامل الثاني فهو الاحتمال القبلي للفئة — أي ما يفترضه النموذج بشأن مدى احتمال كل فئة قبل النظر إلى النص. ويعمل هذا كدرجة ابتدائية تحصل عليها كل فئة قبل أخذ أي n-grams في الحسبان، لذا فإن الاحتمال القبلي الأعلى يجعل التنبؤ بتلك الفئة أكثر ترجيحًا. وتعتمد طريقة ضبطه على priors_mode. افتراضيًا (proportional)، تبدأ الفئة التي لها إجمالي أكبر من عدد n-grams في بيانات التدريب بدرجة أعلى. ومع uniform، تبدأ جميع الفئات على قدم المساواة، بحيث تكون n-grams وحدها هي التي تحسم. ومع explicit، يمكنك تحديد نقطة البداية لكل فئة بنفسك. راجع أوضاع الاحتمال القبلي.
وأي n-gram لم تظهر مطلقًا في أي موضع ضمن بيانات التدريب تُتجاهل: فهي ليست جزءًا من مفردات النموذج، لذلك لا تفيد أي فئة ولا تضرها.
تتبع الخوارزمية نموذج Naive Bayes متعدد الحدود لتصنيف النصوص؛ راجع Manning, Raghavan & Schütze, Introduction to Information Retrieval, ch. 13 (Text Classification and Naive Bayes).
بنية القاموس
NAIVE_BAYES بنية ثابتة:
- يتكوّن
PRIMARY KEYمن عمود واحد من النوعString— وهو n-gram. في وقت تنفيذ الاستعلام، يكون هذا “المفتاح” هو النص الذي تمرّره للتصنيف، وليس مفتاح بحث مخزّنًا. - إلى جانبه، عرّف سِمتين فقط من نوع عدد صحيح غير موقّع: وسم الفئة وعدد مرات الظهور. تستخدم معرّفات الفئات دائمًا
UInt32داخليًا، لذلك يجب أن تتسع قيمة وسم الفئة ضمنUInt32(بحد أقصى4294967295) حتى إذا أعلنت سِمته على أنهاUInt64. وتُرفض أي قيمة أكبر عند تحميل القاموس، لا عند إنشائه. وينطبق الأمر نفسه على الأنواع المعلنة: فإذا كان معرّف فئة المصدر أو العدد لا يتّسع لنوع السمة المعلن، يفشل التحميل بدلًا من اقتطاعه بصمت. - تحدد مَعلمة التخطيط
class_attributeأي سمة تمثل وسم الفئة، وتُعدّ السمة الأخرى تلقائيًا العدد. ويمكن تعريف السمتين بأي ترتيب.
(n-gram, class) مع عدد مرات ظهور ذلك الـ n-gram في تلك الفئة. وتُنتج هذه counts عبر تقسيم corpus إلى tokens وتجميع النتيجة، إمّا ضمن pipeline التدريب الخاصة بك أو في ClickHouse انطلاقًا من raw text موسوم (راجع Build training data from raw text). ولا يستهلك القاموس سوى هذه counts.
تحديث النموذج. لأن النموذج عبارة عن قاموس يستند إلى table، فأعِد تدريبه بتحديث table ثم إعادة تحميله:
معلمات البنية
يمكنك تعريف القاموس باستخدام DDL
CREATE DICTIONARY (كما في الدليل التمهيدي أعلاه) أو في ملف تكوين XML؛ راجع بُنى القواميس لمعرفة مكان هذا الملف. يضبط المثال أدناه كل خيارات البنية حتى تتمكن من رؤيتها جميعًا — ولا يُطلب سوى class_attribute و n و mode، بينما يوضح الجدول أعلاه القيم الافتراضية للباقي. في ملف التكوين، تُكتب الاحتمالات القبلية كعناصر prior متكررة (عنصر واحد لكل فئة، كما هو موضح أدناه)، وتكون رموز الحشو لـ byte و codepoint أرقامًا (لأن ملف التكوين لا يمكنه حمل بايتات خام)، وتكون القيمة الحرفية token مُفلَتة في XML عند الحاجة، لذا تتحول <s> إلى <s>.
- DDL
- ملف الإعدادات
أوضاع التقسيم إلى رموز
mode ما يُعدّ “رمزًا”، وبالتالي شكل سلاسل n-grams. ويجب أن تكون سلاسل n-grams المصدر قد أُنتجت باستخدام mode وn نفسهما.
byte— كل رمز عبارة عن بايت واحد؛ ولا يُفترض استخدام UTF-8. عندn = 2، تنتج'abc'البيغرامات البايتية'ab'و'bc'. مناسب لـ اكتشاف اللغة أو الترميز في تسلسلات بايتية عشوائية، وأي بيانات تكون فيها الإشارات دون مستوى المحرف مهمة. وعادةً ما يُستخدم معn >= 2.codepoint— كل رمز هو نقطة ترميز Unicode واحدة؛ ويُفسَّر الإدخال على أنه UTF-8. عندn = 1، تنتج'café'نقاط الترميز'c'و'a'و'f'و'é'. مناسب لـ اكتشاف نظام الكتابة واللغة، وللنصوص القصيرة أو نصوص CJK حيث لا يمكن الاعتماد على حدود الكلمات القائمة على المسافات. (يجب أن تكون سلاسل n-grams المصدر بترميز UTF-8 صالح؛ وتُفك شيفرة مدخلات الاستعلام بتسامح — راجع الملاحظات.)token— كل رمز هو كلمة تفصل بينها محارف ASCII البيضاء (المسافة، وعلامة الجدولة، والسطر الجديد، وعودة العربة، وتغذية النموذج، وعلامة الجدولة العمودية؛ وتُختزل التتابعات إلى فاصل واحد). أما محارف Unicode البيضاء غير التابعة لـ ASCII مثلU+00A0(مسافة غير قابلة للفصل) أوU+2003(مسافة em) فهي ليست فاصلًا وتبقى داخل الرمز. والمحارف البيضاء هي الشيء الوحيد الذي يسبّب التقسيم — فلا يُحوَّل أي شيء إلى أحرف صغيرة ولا يُزال أي شيء — لذا تصبح'Hello, World!'الرمزين'Hello,'و'World!'(وتُحفَظ الفاصلة و!والأحرف الكبيرة جميعًا)، ومعn = 2تُشكّل البيغرام الوحيد'Hello, World!'. مناسب لـ التصنيف على مستوى الكلمات في اللغات التي تفصل كلماتها بمسافات — مثل تحليل المشاعر، والموضوع، والبريد المزعج، ولغة الجملة.
أوضاع prior
priors_mode كيفية تعيينه.
-
proportional(الافتراضي) — يكون prior لكل صنف متناسبًا مع إجمالي عدد n-gram الخاصة به في بيانات التدريب — أي مجموع عمودcountلذلك الصنف، وليس عدد الصفوف أو مستندات التدريب — لذا تبدأ الأصناف الأكثر ظهورًا باحتمال أعلى. اختره عندما تتوافق نسب الأصناف في التدريب (بحسب إجمالي عدد n-gram) مع التكرارات التي تتوقعها وقت الاستعلام. لا حاجة إلى تمرير أي شيء — إذ يُستمد من الأعداد في المصدر. -
uniform— تكون جميع الأصناف متساوية الاحتمال في البداية، لذلك لا يحصل أي صنف على أفضلية أولية، ويعتمد التنبؤ بالكامل على n-grams الموجودة في المُدخل. اختره عندما تكون الأصناف متوازنة، أو عندما لا تعكس تكرارات التدريب مدى ظهور كل صنف وقت الاستعلام. لا حاجة إلى تمرير أي شيء. -
explicit— توفّر أنت قيم prior باستخدامpriors [(0, 0.6), (1, 0.4)]: زوج واحد من(class, probability)لكل صنف، بحيث تكون كل قيمة احتمال أكبر من 0 وبحد أقصى 1، ويكون مجموعها معًا1.0. اختره عندما تعرف المعدلات الأساسية الحقيقية وتختلف عن التدريب — مثلًا، قد لا تتجاوز الرسائل المزعجة 1% من حركة المرور في بيئة الإنتاج رغم أن مجموعة التدريب كانت متوازنة. احسبها من الحصة الفعلية المتوقعة لكل صنف.
الرموز الحدّية (الحشو)
n > 1، إذ يمكنه تحسين الدقة عبر تمكين النموذج من استخدام إشارات عند بداية النص ونهايته.
لماذا يساعد ذلك؟ عندما تكون n > 1، تحصل n-grams في وسط النص على سياق كامل من اليسار واليمين، لكن أول token وآخره لا يحظيان بذلك. تؤدي إضافة الرموز الحدّية إلى إنشاء n-grams تشير إلى “بداية النص” و”نهاية النص”، بحيث يتمكن النموذج من تعلّم الأنماط المرتبطة بالموضع — مثل كلمة تكون مميّزة عندما تبدأ رسالة، أو حرف يكون شائعًا في نهاية كلمة.
ما الذي يجب عليك فعله:
- حدّد كل جانب على حدة. إن
start_tokenوend_tokenمستقلان — اضبط أحدهما أو كليهما أو اتركهما معًا دون ضبط. وتعني القيمة الفارغة أن هذا الجانب لا يُحشّى. - اختر قيمًا نادرة لا تتصادم مع البيانات الفعلية، مثل
0x01/0xFFلـbyte، وU+10FFFE/U+10FFFFلـcodepoint، أو<s>/</s>لـtoken. - أنشئ n-grams الخاصة بالتدريب باستخدام الحشو نفسه. يقوم القاموس بحشو مدخل query فقط، ولا يحشو المصدر مطلقًا، لذا يجب أن تكون الرموز الحدّية مضمنة مسبقًا في n-grams التي تحمّلها. وأسهل طريقة لضمان التطابق هي إنشاء المصدر باستخدام
naiveBayesNgrams، مع تمريرstart_tokenوend_tokenنفسيهما (إلى جانبnوmode) اللذين تمرّرهما إلى layout — إذ يُنتج بالضبط n-grams المحشّاة التي ينشئها القاموس في query time.
-
byte— رقم لقيمة البايت، بالنظام العشري أو الست عشري0x(لذا فإن'1'و'0x01'متماثلان): -
codepoint— رقم لنقطة ترميز UTF-8، بالنظام العشري أو الست عشري0x(لذا فإن'1114110'و'0x10FFFE'متماثلان): -
token— سلسلة الـ token الحرفية:
أنشئ بيانات التدريب من النص الخام
naiveBayesNgrams لتقسيمه إلى n-grams. أعطِها القيم نفسها لـ n وmode وstart_token وend_token كما في البنية التي تستخدمها، وستنتج n-grams نفسها تمامًا التي يتوقعها القاموس، بحيث تتطابق بيانات التدريب مع ما يراه النموذج عند تنفيذ الاستعلام.
إذا كان لديك جدول من الصفوف بالشكل (class_id, text)، فأنشئ المصدر بالشكل (ngram, class_id, count) باستخدام GROUP BY واحد:
training_data الآن مصدرًا صالحًا لقاموس NAIVE_BAYES (هنا أُحاديات token؛ غيّر وسيطَي n وmode ليتوافقا مع layout لديك). يُجزّئ القاموس مُدخلات الاستعلام إلى token تمامًا كما تَرِد، لذا إذا كان نص التدريب مكتوبًا بأحرف صغيرة بينما نص الاستعلام ليس كذلك، فلن تتطابق n-grams بينهما وستتراجع دقة النموذج.
الأوليات وعدد المستنداتتُوزن الأولية
proportional (الافتراضية) بحسب إجمالي عدد n-grams لكل فئة، لا بحسب عدد مستنداتها. إذا كنت تريد الأولية الكلاسيكية المعتمدة على تكرار المستندات (documents_in_class / total_documents)، فاحسبها من جدول docs الخام ومرّرها مع priors_mode 'explicit':training_data مع تمرير الأولية الصريحة المحسوبة أعلاه، ثم صنّف المراجعات الجديدة:
1 إيجابية و0 سلبية، لذا صُنِّفت كلتا المراجعتين بشكل صحيح.
أمثلة إضافية
n = 2, mode 'byte'; الفئة 0 = سلاسل من الأحرف a–d، والفئة 1 = الأحرف x–z):
n = 1, mode 'codepoint'; الفئة 0 = اللاتينية، 1 = السيريلية):
store_source:
n = 2, mode 'codepoint'; الفئة 0 = الإنجليزية، 1 = الإسبانية). تُبنى مقاطع n-gram التدريبية من الكلمات الخام باستخدام naiveBayesNgrams، وتتيح رموز الحدود — التي تُمرَّر إلى كلٍّ من الدالة والبنية — للنموذج استخدام الحرف الأول والأخير من كل كلمة:
ملاحظات
- دلالات القاموس الحاسوبي. هذا قاموس حاسوبي: تستعمل الدالة
dictGet(dict, '<class_attribute>', text)لتصنيفtext(فالمفتاح هنا مُدخل للتصنيف، وليس مفتاحًا مخزّنًا)، ولا يمكن الاستعلام عن سمة العدّ، كما أنdictHasتُرجع دائمًا القيمة1. - التحقق من المصدر عند التحميل. يجب أن يطابق كل n-gram في المصدر القيمتَين
nوmodeالمضبوطة (وفي وضعcodepointيجب أيضًا أن يكون UTF-8 صالحًا)؛ وأي عدم تطابق يؤدي إلى فشل التحميل. ولأن الصفوف ذات العدّ الصفري يتم تجاهلها (راجع كيف يعمل)، فإن المصدر الفارغ أو الذي لا يحتوي إلا على قيم عدّ صفرية لا يوفّر أي بيانات للتدريب، وبالتالي يفشل تحميله. - تجزئة الرموز وقت الاستعلام متساهلة. بخلاف التحقق من المصدر، لا يُرفَض مُدخل الاستعلام مطلقًا. في وضع
codepoint، تُفكّ البايتات غير الصالحة وفق UTF-8 بأفضل جهد بدلًا من أن تتسبب في فشل الاستعلام؛ وفي وضعtoken، لا يفصل بين الكلمات إلا محارف المسافات البيضاء من ASCII (أما محارف المسافات البيضاء في Unicode مثلU+00A0فتبقى داخل الرمز). وحتى مع الإدخال غير السليم، يستمر التصنيف — وغالبًا ما يستند إلى الاحتمالات القبلية، لأن n-grams الخاصة به لن تطابق تلك التي دُرِّب عليها.