เผยแพร่เมื่อวันที่ 13 พฤศจิกายน 2024
วาจาสร้างความเกลียดชัง การล่วงละเมิด และการละเมิดทางออนไลน์กลายเป็นปัญหาที่แพร่หลายบนโลกออนไลน์ ความคิดเห็นที่เป็นพิษจะปิดกั้นเสียงที่สำคัญและขับไล่ผู้ใช้และลูกค้า การตรวจหาเนื้อหาที่เป็นพิษจะช่วยปกป้องผู้ใช้และสร้างสภาพแวดล้อมออนไลน์ที่ปลอดภัยยิ่งขึ้น
ในซีรีส์ 2 ภาคนี้ เราจะสำรวจวิธีใช้ AI เพื่อตรวจหาและลดความรุนแรงของเนื้อหาที่เป็นพิษตั้งแต่ต้นทาง ซึ่งก็คือแป้นพิมพ์ของผู้ใช้
ในส่วนที่ 1 เราได้พูดถึงกรณีการใช้งานและประโยชน์ของแนวทางนี้
ในส่วนที่ 2 นี้ เราจะเจาะลึกการใช้งาน รวมถึงตัวอย่างโค้ดและเคล็ดลับเกี่ยวกับ UX
การสาธิตและโค้ด
ลองใช้เดโมและดูโค้ดบน GitHub
การสนับสนุนเบราว์เซอร์
การแสดงตัวอย่างจะทำงานใน Safari, Chrome, Edge และ Firefox เวอร์ชันล่าสุด
เลือกโมเดลและไลบรารี
เราใช้ไลบรารี Transformers.js ของ Hugging Face ซึ่งมีเครื่องมือสำหรับทำงานกับโมเดลแมชชีนเลิร์นนิงในเบราว์เซอร์ โค้ดเดโมของเรามาจากตัวอย่างการจัดประเภทข้อความนี้
เราเลือกโมเดล toxic-bert ซึ่งเป็นโมเดลที่ผ่านการฝึกอบรมล่วงหน้าซึ่งออกแบบมาเพื่อระบุรูปแบบภาษาที่เป็นพิษ ซึ่งเป็น unitary/toxic-bert เวอร์ชันที่ใช้กับเว็บได้ ดูรายละเอียดเพิ่มเติมเกี่ยวกับป้ายกำกับของโมเดลและการแยกประเภทการโจมตีข้อมูลประจำตัวได้ที่หน้าโมเดลของ Hugging Face
เมื่อดาวน์โหลดโมเดลแล้ว การอนุมานจะรวดเร็ว
ตัวอย่างเช่น โดยปกติ Chrome ที่ทำงานในอุปกรณ์ Android ระดับกลางที่เราทดสอบจะใช้เวลาน้อยกว่า 500 มิลลิวินาที (โทรศัพท์ 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
เจ้าหน้าที่ของเราต้องสื่อสารกับเธรดหลักโดยใช้ข้อความเพื่อระบุสถานะของโมเดลและผลการประเมินความเป็นพิษ โปรดดูรหัสข้อความที่เราสร้างขึ้นซึ่งจับคู่กับสถานะต่างๆ ของการเตรียมโมเดลและวงจรการอนุมาน
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
) คุณอาจพบว่าประเภทนี้มีประโยชน์สำหรับกรณีการใช้งานของคุณ
ประสบการณ์ของผู้ใช้
ในเดโมนี้ เราได้เลือกตัวเลือกต่อไปนี้
- อนุญาตการโพสต์เสมอ คำแนะนำเกี่ยวกับเนื้อหาที่เป็นพิษฝั่งไคลเอ็นต์ไม่ได้ป้องกันไม่ให้ผู้ใช้โพสต์ ในเดโม ผู้ใช้สามารถโพสต์ความคิดเห็นได้แม้ว่าระบบจะยังไม่ได้โหลดโมเดล (และไม่ได้แสดงการประเมินความเป็นพิษ) และแม้ว่าระบบจะตรวจพบว่าความคิดเห็นเป็นพิษก็ตาม ตามที่แนะนำ คุณควรมีระบบที่ 2 เพื่อตรวจหาความคิดเห็นที่เป็นพิษ หากเหมาะสมกับแอปพลิเคชันของคุณ ให้พิจารณาแจ้งให้ผู้ใช้ทราบว่าความคิดเห็นของผู้ใช้ได้รับการเผยแพร่ในไคลเอ็นต์แล้ว แต่ถูกแจ้งว่าไม่เหมาะสมในเซิร์ฟเวอร์หรือระหว่างการตรวจสอบโดยเจ้าหน้าที่
- ระวังผลลบลวง เมื่อความคิดเห็นไม่จัดอยู่ในประเภทเป็นพิษ เวอร์ชันเดโมจะไม่แสดงความคิดเห็น (เช่น "ความคิดเห็นดีมาก") นอกจากจะมีความคิดเห็นที่ไม่เกี่ยวข้องแล้ว การเสนอความคิดเห็นเชิงบวกอาจส่งสัญญาณที่ไม่ถูกต้อง เนื่องจากบางครั้งตัวจัดประเภทอาจพลาดความคิดเห็นที่เป็นพิษไปบ้าง
การเพิ่มประสิทธิภาพและทางเลือก
ข้อจํากัดและการปรับปรุงในอนาคต
- ภาษา: โมเดลที่เราใช้รองรับภาษาอังกฤษเป็นหลัก หากต้องการการสนับสนุนหลายภาษา คุณจะต้องปรับแต่ง โมเดลความเป็นพิษหลายรายการที่แสดงใน Hugging Face รองรับภาษาที่ไม่ใช่ภาษาอังกฤษ (รัสเซีย ดัตช์) แต่ยังไม่เข้ากันได้กับ Transformers.js ในขณะนี้
- Nuance: แม้ว่า Toxic-BERT จะตรวจจับเนื้อหาที่เป็นพิษอย่างโจ่งแจ้งได้อย่างมีประสิทธิภาพ แต่ก็อาจไม่เหมาะกับกรณีที่ละเอียดอ่อนหรือขึ้นอยู่กับบริบท (การเสียดสี การประชดประชัน) ความเป็นพิษเป็นสิ่งที่อาจขึ้นอยู่กับความคิดเห็นของแต่ละบุคคลและมองไม่เห็นได้ เช่น คุณอาจต้องการจัดประเภทคำหรือแม้แต่อีโมจิบางรายการเป็นเนื้อหาที่เป็นพิษ การปรับแต่งอย่างละเอียดจะช่วยปรับปรุงความแม่นยำในบริเวณเหล่านี้ได้
เราจะเผยแพร่บทความเกี่ยวกับการปรับแต่งโมเดลความเป็นพิษในเร็วๆ นี้
ตัวเลือกอื่นๆ
- MediaPipe สำหรับการจัดประเภทข้อความ โปรดตรวจสอบว่าคุณใช้โมเดลที่เข้ากันได้กับงานการจัดประเภท
TensorFlow.js classifier ความเป็นพิษ โมเดลนี้มีขนาดเล็กกว่าและดึงข้อมูลได้เร็วกว่า แต่ไม่ได้ได้รับการเพิ่มประสิทธิภาพมาระยะหนึ่งแล้ว คุณจึงอาจเห็นว่าการอนุมานช้ากว่าเมื่อใช้ Transformers.js
บทสรุป
การตรวจหาเนื้อหาที่เป็นพิษฝั่งไคลเอ็นต์เป็นเครื่องมือที่มีประสิทธิภาพในการปรับปรุงชุมชนออนไลน์
การใช้โมเดล AI เช่น toxic-bert ที่ทำงานในเบราว์เซอร์ด้วย Transformers.js จะช่วยให้คุณใช้กลไกการแสดงความคิดเห็นแบบเรียลไทม์ที่ยับยั้งพฤติกรรมที่เป็นพิษและลดภาระการแยกประเภทเนื้อหาที่เป็นพิษในเซิร์ฟเวอร์ได้
แนวทางฝั่งไคลเอ็นต์นี้ใช้งานได้กับเบราว์เซอร์ต่างๆ อยู่แล้ว อย่างไรก็ตาม โปรดคำนึงถึงข้อจำกัดต่างๆ โดยเฉพาะในด้านต้นทุนการแสดงโมเดลและขนาดการดาวน์โหลด ใช้แนวทางปฏิบัติแนะนำด้านประสิทธิภาพสําหรับ AI ฝั่งไคลเอ็นต์ และแคชโมเดล
หากต้องการการตรวจหาเนื้อหาที่เป็นพิษอย่างครอบคลุม ให้ใช้ทั้งวิธีการฝั่งไคลเอ็นต์และฝั่งเซิร์ฟเวอร์