เผยแพร่: 13 พฤศจิกายน 2024
วาจาสร้างความเกลียดชัง การคุกคาม และการละเมิดทางออนไลน์กลายเป็นปัญหาที่แพร่หลายบนโลกออนไลน์ ความคิดเห็นที่เป็นพิษปิดกั้นเสียงที่สำคัญ และทำให้ผู้ใช้และลูกค้าหนีหาย การตรวจหาความเป็นพิษจะช่วยปกป้องผู้ใช้และสร้างสภาพแวดล้อมออนไลน์ที่ปลอดภัยยิ่งขึ้น
ในซีรีส์ 2 ตอนนี้ เราจะมาดูวิธีใช้ AI เพื่อตรวจหาและลดความเป็นพิษ ที่ต้นทาง ซึ่งก็คือคีย์บอร์ดของผู้ใช้
ในส่วนที่ 1 เราได้พูดถึงกรณีการใช้งาน และประโยชน์ของแนวทางนี้
ในส่วนที่ 2 นี้ เราจะเจาะลึกถึงการใช้งาน รวมถึงตัวอย่างโค้ด และเคล็ดลับ UX
การสาธิตและโค้ด
ลองใช้เดโม และดูโค้ดใน 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);
การสร้างไปป์ไลน์ เช่น ในโค้ดตัวอย่าง เป็นขั้นตอนแรกในการเรียกใช้ งานการอนุมาน
ฟังก์ชันไปป์ไลน์รับอาร์กิวเมนต์ 2 รายการ ได้แก่ งาน ('text-classification')
และโมเดล (Xenova/toxic-bert)
คำสำคัญ: ใน Transformers.js ไปป์ไลน์คือ API ระดับสูงที่ช่วยลดความซับซ้อน ของกระบวนการเรียกใช้โมเดล ML โดยจะจัดการงานต่างๆ เช่น การโหลดโมเดล การแยกโทเค็น และการประมวลผลภายหลัง
โค้ดตัวอย่างของเราทำได้มากกว่าแค่การเตรียมโมเดล เนื่องจากเรา จะส่งต่อขั้นตอนการเตรียมโมเดลที่ใช้การคำนวณสูงไปยัง Web Worker ซึ่งจะช่วยให้เธรดหลักยังคงตอบสนองได้ ดูข้อมูลเพิ่มเติมเกี่ยวกับ การส่งต่อภาระงานที่มีค่าใช้จ่ายสูงไปยัง Web Worker
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;
}
เราจะเรียกใช้ฟังก์ชันการจัดประเภทเมื่อเทรดหลักขอให้ Worker ทำเช่นนั้น
ในการสาธิต เราจะทริกเกอร์ตัวแยกประเภททันทีที่ผู้ใช้หยุดพิมพ์
(ดู TYPING_DELAY) เมื่อเกิดเหตุการณ์นี้ขึ้น เทรดหลักจะส่งข้อความไปยัง
Worker ซึ่งมีข้อมูลที่ผู้ใช้ป้อนเพื่อจัดประเภท
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) คุณอาจพบว่ามีประโยชน์สำหรับกรณีการใช้งานของคุณ
ประสบการณ์ของผู้ใช้
ในเดโม เราได้เลือกตัวเลือกต่อไปนี้
- อนุญาตให้โพสต์เสมอ คำแนะนำเกี่ยวกับความเป็นพิษฝั่งไคลเอ็นต์ของเราไม่ได้ป้องกันไม่ให้ ผู้ใช้โพสต์ ในการสาธิตของเรา ผู้ใช้จะโพสต์ความคิดเห็นได้แม้ว่าโมเดลจะยังโหลดไม่เสร็จ (และไม่ได้เสนอการประเมินความเป็นพิษ) และแม้ว่าระบบจะตรวจพบว่าความคิดเห็นนั้นเป็นพิษก็ตาม ตามที่แนะนำ คุณควรมีระบบที่ 2 เพื่อตรวจหาความคิดเห็นที่เป็นพิษ หากเหมาะสมกับแอปพลิเคชันของคุณ ให้พิจารณาแจ้งให้ผู้ใช้ทราบว่าความคิดเห็นของตนผ่านการตรวจสอบที่ฝั่งไคลเอ็นต์แล้ว แต่ถูกแจ้งว่าไม่เหมาะสมในเซิร์ฟเวอร์หรือระหว่างการตรวจสอบโดยเจ้าหน้าที่
- ระวังผลลบลวง เมื่อระบบไม่จัดประเภทความคิดเห็นว่าเป็นการคุกคาม เดโมของเราจะไม่แสดงความคิดเห็น (เช่น "ความคิดเห็นดีมาก!") นอกเหนือจากจะทำให้เกิดเสียงรบกวนแล้ว การให้ความคิดเห็นเชิงบวกอาจส่งสัญญาณที่ไม่ถูกต้อง เนื่องจากเครื่องมือจัดประเภทของเรา อาจพลาดความคิดเห็นที่เป็นพิษบางรายการเป็นครั้งคราว
การเพิ่มประสิทธิภาพและทางเลือกอื่นๆ
ข้อจำกัดและการปรับปรุงในอนาคต
- ภาษา: โมเดลที่เราใช้รองรับภาษาอังกฤษเป็นหลัก หากต้องการ การรองรับหลายภาษา คุณต้องปรับแต่ง โมเดลความเป็นพิษหลายรายการ ที่แสดงใน Hugging Face รองรับภาษาที่ไม่ใช่ภาษาอังกฤษ (รัสเซีย ดัตช์) แม้ว่าจะไม่สามารถใช้ร่วมกับ Transformers.js ได้ในขณะนี้
- ความแตกต่างเล็กๆ น้อยๆ: แม้ว่า Toxic-BERT จะตรวจจับความเป็นพิษที่โจ่งแจ้งได้อย่างมีประสิทธิภาพ แต่ก็อาจ มีปัญหาในกรณีที่ซับซ้อนกว่าหรือขึ้นอยู่กับบริบท (การประชดประชัน การเสียดสี) ความเป็นพิษอาจเป็นเรื่องที่ขึ้นอยู่กับแต่ละบุคคลและอาจเกิดขึ้นอย่างแนบเนียน เช่น คุณอาจต้องการให้ระบบจัดประเภทคำบางคำหรือแม้แต่อีโมจิเป็นคำที่เป็นพิษ การปรับแต่งจะช่วยปรับปรุงความแม่นยำในด้านต่างๆ เหล่านี้ได้
เรากำลังจะมีบทความเกี่ยวกับการปรับแต่งโมเดลความเป็นพิษ
ตัวเลือกอื่นๆ
- MediaPipe สำหรับการจัดประเภทข้อความ โปรดตรวจสอบว่าคุณใช้โมเดลที่เข้ากันได้กับงานการแยกประเภท
เครื่องมือคัดแยกความเป็นพิษของ TensorFlow.js โดยมีโมเดลที่เล็กกว่าและดึงข้อมูลได้เร็วกว่า แต่ไม่ได้ รับการเพิ่มประสิทธิภาพมาระยะหนึ่งแล้ว คุณจึงอาจสังเกตเห็นว่าการอนุมานช้ากว่าการใช้ Transformers.js อยู่บ้าง
บทสรุป
การตรวจหาความเป็นพิษฝั่งไคลเอ็นต์เป็นเครื่องมือที่มีประสิทธิภาพในการปรับปรุงชุมชนออนไลน์
การใช้ประโยชน์จากโมเดล AI เช่น Toxic-BERT ที่ทำงานในเบราว์เซอร์ด้วย Transformers.js จะช่วยให้คุณใช้กลไกความคิดเห็นแบบเรียลไทม์ที่ยับยั้ง พฤติกรรมที่เป็นพิษและลดภาระการจัดประเภทความเป็นพิษในเซิร์ฟเวอร์ได้
แนวทางฝั่งไคลเอ็นต์นี้ใช้ได้กับเบราว์เซอร์ต่างๆ อยู่แล้ว อย่างไรก็ตาม โปรดคำนึงถึงข้อจำกัด โดยเฉพาะในแง่ของต้นทุนการแสดงโมเดลและขนาดการดาวน์โหลด ใช้แนวทางปฏิบัติแนะนำด้านประสิทธิภาพสำหรับ AI ฝั่งไคลเอ็นต์ และแคชโมเดล
รวมแนวทางฝั่งไคลเอ็นต์และฝั่งเซิร์ฟเวอร์เพื่อตรวจหาความเป็นพิษอย่างครอบคลุม