تاريخ النشر: 13 نوفمبر 2024
أصبح الكلام الذي يحضّ على الكراهية والتحرش وإساءة المعاملة على الإنترنت مشكلة منتشرة. تؤدي التعليقات السامة إلى إسكات الأصوات المهمة وإبعاد المستخدمين والعملاء. تساعد ميزة "رصد المحتوى السام" في حماية المستخدمين وتوفير بيئة أكثر أمانًا على الإنترنت.
في هذه السلسلة المكوّنة من جزأين، سنتعرّف على كيفية استخدام الذكاء الاصطناعي لرصد السلوكيات السامة والحدّ منها من مصدرها، أي لوحات مفاتيح المستخدمين.
في الجزء الأول، ناقشنا حالات الاستخدام ومزايا هذا النهج.
في هذا الجزء الثاني، سنتناول عملية التنفيذ بالتفصيل، بما في ذلك أمثلة على الرموز البرمجية ونصائح بشأن تجربة المستخدم.
العرض التوضيحي والرمز
يمكنك تجربة العرض التوضيحي والاطّلاع على الرمز على GitHub.
دعم المتصفح
تعمل النسخة التجريبية في أحدث إصدارات Safari وChrome وEdge وFirefox.
اختيار نموذج ومكتبة
نستخدم مكتبة Transformers.js من Hugging Face، والتي توفّر أدوات للتعامل مع نماذج تعلُّم الآلة في المتصفح. تم استخلاص الرمز التجريبي من مثال تصنيف النصوص هذا.
نختار نموذج toxic-bert، وهو نموذج مدرَّب مسبقًا ومصمّم لتحديد أنماط اللغة المسيئة. وهو إصدار متوافق مع الويب من unitary/toxic-bert. لمزيد من التفاصيل حول تصنيفات النموذج وتصنيفه لهجمات انتحال الهوية، يُرجى الرجوع إلى صفحة النموذج على Hugging Face.
بعد تنزيل النموذج، يصبح الاستنتاج سريعًا.
على سبيل المثال، يستغرق ذلك عادةً أقل من 500 مللي ثانية في Chrome على جهاز Android متوسط المدى اختبرناه (هاتف Pixel 7 العادي، وليس طراز Pro الأفضل أداءً). إجراء قياسات الأداء الخاصة بك التي تمثّل قاعدة المستخدمين
التنفيذ
في ما يلي الخطوات الرئيسية التي اتّبعناها لتنفيذ هذه الميزة:
ضبط حدّ السمية
يقدّم مصنّف اللغة غير اللائقة تقييمات تتراوح بين 0 و1. ضمن هذا النطاق، علينا تحديد عتبة لتحديد ما يشكّل تعليقًا سامًا. يتم استخدام الحدّ 0.9 بشكل شائع. يتيح لك ذلك رصد التعليقات التي تتضمّن لغة غير لائقة بشكل واضح، مع تجنُّب الحساسية المفرطة التي قد تؤدي إلى عدد كبير جدًا من النتائج الإيجابية الخاطئة (أي التعليقات غير الضارة التي تم تصنيفها على أنّها تتضمّن لغة غير لائقة).
export const TOXICITY_THRESHOLD = 0.9
استيراد المكوّنات
نبدأ باستيراد المكوّنات الضرورية من مكتبة @xenova/transformers. نستورد أيضًا الثوابت وقيم الإعدادات، بما في ذلك
حد السمية.
import { env, pipeline } from '@xenova/transformers';
// Model name: 'Xenova/toxic-bert'
// Our threshold is set to 0.9
import { TOXICITY_THRESHOLD, MODEL_NAME } from './config.js';
تحميل النموذج والتواصل مع سلسلة التعليمات الرئيسية
نحمّل نموذج رصد المحتوى السام toxic-bert، ونستخدمه لإعداد المصنّف. أبسط إصدار من هذا هو const classifier = await pipeline('text-classification', MODEL_NAME);
إنشاء مسار، كما هو الحال في نموذج الرمز البرمجي، هو الخطوة الأولى لتنفيذ مهام الاستدلال.
تتلقّى دالة خط الأنابيب وسيطتَين: المهمة ('text-classification') والنموذج (Xenova/toxic-bert).
المصطلح الأساسي: في Transformers.js، عملية نقل البيانات هي واجهة برمجة تطبيقات عالية المستوى تسهّل عملية تشغيل نماذج تعلُّم الآلة. وهي تتعامل مع مهام مثل تحميل النماذج وتقسيم النص إلى رموز مميزة والمعالجة اللاحقة.
لا يقتصر الرمز التجريبي على إعداد النموذج، بل يتضمّن خطوات إعداد النموذج التي تتطلّب قدرًا كبيرًا من العمليات الحسابية، ويتم تنفيذها في عامل ويب. يتيح ذلك بقاء سلسلة التعليمات الرئيسية متجاوبة. مزيد من المعلومات حول تفويض المهام المكلفة إلى عامل ويب
يحتاج العامل إلى التواصل مع سلسلة التعليمات الرئيسية باستخدام الرسائل للإشارة إلى حالة النموذج ونتائج تقييم السمية. يمكنك الاطّلاع على رموز الرسائل التي أنشأناها والتي تتوافق مع الحالات المختلفة لدورة حياة إعداد النموذج والاستدلال.
let classifier = null;
(async function () {
// Signal to the main thread that model preparation has started
self.postMessage({ code: MESSAGE_CODE.PREPARING_MODEL, payload: null });
try {
// Prepare the model
classifier = await pipeline('text-classification', MODEL_NAME);
// Signal to the main thread that the model is ready
self.postMessage({ code: MESSAGE_CODE.MODEL_READY, payload: null });
} catch (error) {
console.error('[Worker] Error preparing model:', error);
self.postMessage({ code: MESSAGE_CODE.MODEL_ERROR, payload: null });
}
})();
تصنيف إدخال المستخدم
في الدالة classify، نستخدم المصنّف الذي أنشأناه سابقًا لتحليل تعليق أحد المستخدمين. نعرض الناتج الأولي لمصنّف المحتوى السام، أي التصنيفات والنتائج.
// Asynchronous function to classify user input
// output: [{ label: 'toxic', score: 0.9243140482902527 },
// ... { label: 'insult', score: 0.96187334060668945 }
// { label: 'obscene', score: 0.03452680632472038 }, ...etc]
async function classify(text) {
if (!classifier) {
throw new Error("Can't run inference, the model is not ready yet");
}
let results = await classifier(text, { topk: null });
return results;
}
نستدعي دالة التصنيف عندما تطلب سلسلة المحادثات الرئيسية من العامل تنفيذ ذلك. وفي العرض التوضيحي، نشغّل المصنّف فور توقّف المستخدم عن الكتابة (راجِع TYPING_DELAY). وعند حدوث ذلك، ترسل سلسلة المحادثات الرئيسية رسالة إلى العامل تتضمّن إدخال المستخدم لتصنيفه.
self.onmessage = async function (message) {
// User input
const textToClassify = message.data;
if (!classifier) {
throw new Error("Can't run inference, the model is not ready yet");
}
self.postMessage({ code: MESSAGE_CODE.GENERATING_RESPONSE, payload: null });
// Inference: run the classifier
let classificationResults = null;
try {
classificationResults = await classify(textToClassify);
} catch (error) {
console.error('[Worker] Error: ', error);
self.postMessage({
code: MESSAGE_CODE.INFERENCE_ERROR,
});
return;
}
const toxicityTypes = getToxicityTypes(classificationResults);
const toxicityAssessement = {
isToxic: toxicityTypes.length > 0,
toxicityTypeList: toxicityTypes.length > 0 ? toxicityTypes.join(', ') : '',
};
console.info('[Worker] Toxicity assessed: ', toxicityAssessement);
self.postMessage({
code: MESSAGE_CODE.RESPONSE_READY,
payload: toxicityAssessement,
});
};
معالجة الناتج
نتحقّق مما إذا كانت نتائج التصنيف تتجاوز الحدّ الأدنى. إذا كان الأمر كذلك، نسجّل الملاحظة المعنيّة.
إذا تم إدراج أي من تصنيفات اللغة غير اللائقة، سيتم الإبلاغ عن التعليق على أنّه غير لائق.
// input: [{ label: 'toxic', score: 0.9243140482902527 }, ...
// { label: 'insult', score: 0.96187334060668945 },
// { label: 'obscene', score: 0.03452680632472038 }, ...etc]
// output: ['toxic', 'insult']
function getToxicityTypes(results) {
const toxicityAssessment = [];
for (let element of results) {
// If a label's score > our threshold, save the label
if (element.score > TOXICITY_THRESHOLD) {
toxicityAssessment.push(element.label);
}
}
return toxicityAssessment;
}
self.onmessage = async function (message) {
// User input
const textToClassify = message.data;
if (!classifier) {
throw new Error("Can't run inference, the model is not ready yet");
}
self.postMessage({ code: MESSAGE_CODE.GENERATING_RESPONSE, payload: null });
// Inference: run the classifier
let classificationResults = null;
try {
classificationResults = await classify(textToClassify);
} catch (error) {
self.postMessage({
code: MESSAGE_CODE.INFERENCE_ERROR,
});
return;
}
const toxicityTypes = getToxicityTypes(classificationResults);
const toxicityAssessement = {
// If any toxicity label is listed, the comment is flagged as
// potentially toxic (isToxic true)
isToxic: toxicityTypes.length > 0,
toxicityTypeList: toxicityTypes.length > 0 ? toxicityTypes.join(', ') : '',
};
self.postMessage({
code: MESSAGE_CODE.RESPONSE_READY,
payload: toxicityAssessement,
});
};
عرض تلميح
إذا كانت قيمة isToxic صحيحة، نعرض تلميحًا للمستخدم. في العرض التوضيحي، لم نستخدم نوع السمية الأكثر دقة، ولكن أتحناه في سلسلة التعليمات الرئيسية عند الحاجة (toxicityTypeList)، وقد تجده مفيدًا لحالة الاستخدام الخاصة بك.
تجربة المستخدم
في العرض التوضيحي، اتّخذنا الخيارات التالية:
- السماح دائمًا بالنشر لا يمنع تلميحنا بشأن المحتوى السام من جهة العميل المستخدم من نشر المحتوى. في العرض التوضيحي، يمكن للمستخدم نشر تعليق حتى إذا لم يتم تحميل النموذج (وبالتالي لن يتم تقديم تقييم بشأن مدى سميته)، وحتى إذا تم رصد التعليق على أنّه غير لائق. كما يُنصح بذلك، يجب أن يتوفّر لديك نظام ثانٍ لرصد التعليقات المسيئة. إذا كان ذلك مناسبًا لتطبيقك، ننصحك بإعلام المستخدم بأنّ تعليقه تم نشره على الجهاز، ولكن تم الإبلاغ عنه على الخادم أو أثناء المراجعة من قِبل فريقنا.
- تجنُّب النتائج السلبية الخاطئة: عندما لا يتم تصنيف تعليق على أنّه غير لائق، لا تقدّم النسخة التجريبية ملاحظات (مثلاً، "تعليق لطيف"). بالإضافة إلى أنّها مزعجة، قد تؤدي الملاحظات الإيجابية إلى إرسال إشارة خاطئة، لأنّ المصنّف الخاص بنا يفوّت أحيانًا بعض التعليقات المكتوبة بلغة غير لائقة، وهذا أمر لا مفرّ منه.
التحسينات والبدائل
القيود والتحسينات المستقبلية
- اللغات: النموذج الذي نستخدمه يتيح اللغة الإنجليزية بشكل أساسي. للحصول على دعم متعدد اللغات، عليك إجراء عملية ضبط دقيق. تتوفّر نماذج متعددة للكشف عن المحتوى السام على Hugging Face، وهي متوافقة مع لغات أخرى غير الإنجليزية (مثل الروسية والهولندية)، ولكنّها غير متوافقة مع Transformers.js في الوقت الحالي.
- الفروق الدقيقة: على الرغم من أنّ أداة toxic-bert ترصد المحتوى السام بشكل فعّال، قد تواجه صعوبة في رصد الحالات الأكثر دقة أو التي تعتمد على السياق (مثل السخرية). يمكن أن تكون السمية ذاتية ودقيقة للغاية. على سبيل المثال، قد تريد تصنيف عبارات معيّنة أو حتى رموز إيموجي على أنّها مسيئة. يمكن أن يساعد الضبط الدقيق في تحسين الدقة في هذه المجالات.
سننشر قريبًا مقالة حول الضبط الدقيق لنموذج السمية.
الحلول البديلة
- MediaPipe لتصنيف النصوص: احرص على استخدام طراز متوافق مع مهام التصنيف.
مصنّف السمية في TensorFlow.js يوفّر هذا الخيار نموذجًا أصغر حجمًا وأسرع في الاسترجاع، ولكن لم يتم تحسينه منذ فترة، لذا قد تلاحظ أنّ الاستدلال أبطأ بعض الشيء مقارنةً بخيار Transformers.js.
الخاتمة
يُعدّ رصد اللغة غير اللائقة من جهة العميل أداة فعّالة لتحسين المنتديات على الإنترنت.
من خلال الاستفادة من نماذج الذكاء الاصطناعي، مثل toxic-bert، التي تعمل في المتصفّح باستخدام Transformers.js، يمكنك تنفيذ آليات تقديم ملاحظات في الوقت الفعلي لتثبيط السلوكيات غير اللائقة وتقليل عبء تصنيف المحتوى غير اللائق على خوادمك.
يعمل هذا الأسلوب من جهة العميل حاليًا على جميع المتصفّحات. ومع ذلك، يجب مراعاة القيود، خاصةً من حيث تكاليف عرض النموذج وحجم التنزيل. اتّبِع أفضل الممارسات المتعلّقة بالأداء في ما يخصّ الذكاء الاصطناعي من جهة العميل وخزِّن النموذج مؤقتًا.
للكشف الشامل عن المحتوى السام، يمكنك الجمع بين الأسلوبَين من جهة العميل ومن جهة الخادم.