تاريخ النشر: 13 تشرين الثاني (نوفمبر) 2024
أصبح الكلام الذي يحضّ على الكراهية والتحرش والإساءة على الإنترنت مشكلة منتشرة. تؤدي التعليقات المسيئة إلى إسكات الأصوات المهمة وإبعاد المستخدمين والعملاء. تحمي ميزة رصد المحتوى الضار المستخدمين وتوفّر بيئة أكثر أمانًا على الإنترنت.
في هذه السلسلة المكونة من جزأين، نستكشف كيفية استخدام الذكاء الاصطناعي لرصد المحتوى السام والحدّ منه عند مصدره: لوحات مفاتيح المستخدمين.
في الجزء الأول، ناقشنا حالات الاستخدام ومزايا هذا النهج.
في هذا الجزء الثاني، سنوضّح كيفية التنفيذ، بما في ذلك أمثلة على الرموز البرمجية وبعض النصائح حول تجربة المستخدم.
العرض التجريبي والرمز
يمكنك تجربة الإصدار التجريبي وفحص الرمز على GitHub.
دعم المتصفح
يعمل الإصدار التجريبي في أحدث إصدارات Safari وChrome وEdge وFirefox.
اختيار نموذج ومكتبة
نستخدم مكتبة Transformers.js التي توفّرها شركة Hugging Face، وهي مكتبة توفّر أدوات للعمل مع نماذج تعلُّم الآلة في المتصفح. تم استخراج الرمز البرمجي التجريبي من مثال تصنيف النصوص هذا.
نختار نموذج toxic-bert، وهو نموذج pre-trained (تم تدريبه مسبقًا) مصمّم لتحديد أنماط اللغة المسيئة. وهو إصدار متوافق مع الويب من unitary/toxic-bert. لمزيد من التفاصيل حول تصنيفات النموذج وكيفية تصنيفه للهجمات المتعلّقة بالهوية، يُرجى الرجوع إلى صفحة نموذج Hugging Face.
بعد تنزيل النموذج، يكون الاستنتاج سريعًا.
على سبيل المثال، يستغرق عادةً Chrome أقل من 500 ملي ثانية في جهاز 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، مسار الإحالة هو واجهة برمجة تطبيقات عالية المستوى تعمل على تبسيط عملية تشغيل نماذج تعلُّم الآلة. وتعالج هذه الوحدات مهامًا مثل تحميل النماذج، وإنشاء الرموز، والمعالجة اللاحقة.
لا يقتصر دور الرمز التجريبي على إعداد النموذج فقط، لأنّنا نحمّل خطوات إعداد النموذج المُكلّفة حسابيًا إلى Web Worker. ويسمح ذلك للسلسلة الرئيسية بالبقاء سريعة الاستجابة. اطّلِع على مزيد من المعلومات حول تحمّل المهام المستهلكة للطاقة إلى Web Worker.
يجب أن يتواصل العامل مع سلسلة المحادثات الرئيسية باستخدام الرسائل للإشارة إلى حالة النموذج ونتائج تقييم السمية. اطّلِع على رموز الرسائل التي أنشأناها والتي تُحدِّد حالات مختلفة لإعداد النموذج ودورة معالجة البيانات.
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 في الوقت الحالي.
- Nuance: على الرغم من أنّ نموذج Toxic-Bert يرصد بفعالية المحتوى السام الواضح، إلا أنّه قد يواجه صعوبة في الحالات الأكثر دقة أو التي تعتمد على السياق (المفارقة والسخرية). يمكن أن تكون اللغة السامة ذاتية للغاية ودقيقة. على سبيل المثال، قد تريد تصنيف عبارات معيّنة أو حتى رموز تعبيرية على أنّها مسيئة. يمكن أن تساعد عملية التحسين الدقيق في تحسين الدقة في هذه المجالات.
سننشر قريبًا مقالة عن تحسين نموذج اللغة السامة.
الحلول البديلة
- MediaPipe لتصنيف النصوص احرص على استخدام نموذج متوافق مع مهام التصنيف.
TensorFlow.js تصنيف اللغة غير اللائقة يقدّم هذا الإصدار نموذجًا أصغر حجمًا وأسرع في الجلب، ولكن لم يتم تحسينه لبعض الوقت، لذا قد تلاحظ أنّ الاستنتاج يكون أبطأ نوعًا ما مقارنةً باستخدام Transformers.js.
الخاتمة
إنّ رصد المحتوى المسيء من جهة العميل هو أداة فعّالة لتحسين المنتديات على الإنترنت.
من خلال الاستفادة من نماذج الذكاء الاصطناعي، مثل toxic-bert التي تعمل في المتصفّح باستخدام ترانسفورمرز.js، يمكنك تنفيذ آليات ملاحظات فورية تشجّع على عدم استخدام لغة مسيئة وتقلل من عبء تصنيف اللغة المسيئة على خوادمك.
يعمل هذا النهج من جهة العميل على جميع المتصفّحات. ومع ذلك، يُرجى مراعاة القيود، خاصةً من حيث تكاليف عرض النماذج وحجم التنزيل. طبِّق أفضل الممارسات المتعلّقة بالأداء في الذكاء الاصطناعي من جهة العميل وخزِّن النموذج.
لرصد المحتوى الضار بشكل شامل، يمكنك الجمع بين الأسلوبَين من جهة العميل ومن جهة الخادم.