Xuất bản: Ngày 13 tháng 11 năm 2024
Lời nói căm thù, hành vi quấy rối và hành vi sai trái trên mạng đã trở thành một vấn đề lan rộng trên mạng. Bình luận độc hại làm im tiếng những ý kiến quan trọng và khiến người dùng và khách hàng rời đi. Tính năng phát hiện nội dung độc hại giúp bảo vệ người dùng và tạo ra một môi trường trực tuyến an toàn hơn.
Trong loạt bài gồm 2 phần này, chúng ta sẽ khám phá cách sử dụng AI để phát hiện và giảm thiểu nội dung độc hại ngay từ nguồn: bàn phím của người dùng.
Trong phần một, chúng ta đã thảo luận về các trường hợp sử dụng và lợi ích của phương pháp này.
Trong phần thứ hai này, chúng ta sẽ tìm hiểu về cách triển khai, bao gồm cả các ví dụ về mã và mẹo về trải nghiệm người dùng.
Bản minh hoạ và mã
Hãy dùng thử bản minh hoạ của chúng tôi và tìm hiểu mã nguồn trên GitHub.
Hỗ trợ trình duyệt
Bản minh hoạ của chúng tôi chạy trên các phiên bản mới nhất của Safari, Chrome, Edge và Firefox.
Chọn một mô hình và thư viện
Chúng tôi sử dụng thư viện Transformers.js của Hugging Face. Thư viện này cung cấp các công cụ để làm việc với mô hình học máy trong trình duyệt. Mã minh hoạ của chúng tôi bắt nguồn từ ví dụ phân loại văn bản này.
Chúng tôi chọn mô hình toxic-bert, một mô hình được huấn luyện trước và được thiết kế để xác định các mẫu ngôn từ độc hại. Đây là phiên bản tương thích với web của unitary/toxic-bert. Để biết thêm thông tin về nhãn của mô hình và cách phân loại các cuộc tấn công giả mạo danh tính, hãy tham khảo trang mô hình Hugging Face.
Sau khi mô hình được tải xuống, quá trình suy luận sẽ diễn ra nhanh chóng.
Ví dụ: thường mất chưa đến 500 mili giây trong Chrome chạy trên một thiết bị Android tầm trung mà chúng tôi đã thử nghiệm (điện thoại Pixel 7 thông thường, không phải mẫu Pro có hiệu suất cao hơn). Chạy điểm chuẩn của riêng bạn, đại diện cho cơ sở người dùng của bạn.
Triển khai
Sau đây là các bước chính trong quá trình triển khai của chúng tôi:
Đặt ngưỡng nội dung độc hại
Trình phân loại độc hại của chúng tôi cung cấp điểm độc hại trong khoảng từ 0 đến 1. Trong phạm vi đó, chúng ta cần đặt một ngưỡng để xác định những gì cấu thành một bình luận độc hại. Ngưỡng thường dùng là 0.9. Điều này giúp bạn phát hiện những bình luận phản cảm một cách rõ ràng, đồng thời tránh độ nhạy quá mức có thể dẫn đến quá nhiều kết quả dương tính giả (nói cách khác, những bình luận vô hại bị phân loại là phản cảm).
export const TOXICITY_THRESHOLD = 0.9
Nhập các thành phần
Chúng ta bắt đầu bằng cách nhập các thành phần cần thiết từ thư viện @xenova/transformers. Chúng tôi cũng nhập các hằng số và giá trị cấu hình, bao gồm cả ngưỡng độc hại.
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';
Tải mô hình và giao tiếp với luồng chính
Chúng ta sẽ tải mô hình phát hiện nội dung độc hại toxic-bert và sử dụng mô hình này để chuẩn bị bộ phân loại. Phiên bản ít phức tạp nhất của tính năng này là const classifier = await pipeline('text-classification', MODEL_NAME);
Tạo một quy trình, chẳng hạn như trong mã ví dụ, là bước đầu tiên để chạy các tác vụ suy luận.
Hàm quy trình nhận 2 đối số: tác vụ ('text-classification') và mô hình (Xenova/toxic-bert).
Thuật ngữ chính: Trong Transformers.js, pipeline là một API cấp cao giúp đơn giản hoá quy trình chạy các mô hình ML. Thư viện này xử lý các tác vụ như tải mô hình, mã hoá và xử lý hậu kỳ.
Mã minh hoạ của chúng tôi làm nhiều việc hơn là chỉ chuẩn bị mô hình, vì chúng tôi chuyển các bước chuẩn bị mô hình tốn nhiều tài nguyên tính toán sang một web worker. Điều này giúp luồng chính luôn phản hồi. Tìm hiểu thêm về việc chuyển các tác vụ tốn nhiều tài nguyên sang một web worker.
Worker của chúng ta cần giao tiếp với luồng chính, sử dụng các thông báo để cho biết trạng thái của mô hình và kết quả đánh giá mức độ độc hại. Hãy xem mã thông báo mà chúng tôi đã tạo để liên kết với các trạng thái khác nhau của vòng đời chuẩn bị và suy luận mô hình.
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 });
}
})();
Phân loại thông tin đầu vào của người dùng
Trong hàm classify, chúng ta sử dụng trình phân loại đã tạo trước đó để phân tích bình luận của người dùng. Chúng tôi trả về đầu ra thô của trình phân loại nội dung độc hại: nhãn và điểm số.
// 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;
}
Chúng ta gọi hàm phân loại khi luồng chính yêu cầu worker làm như vậy. Trong bản minh hoạ, chúng ta kích hoạt trình phân loại ngay khi người dùng ngừng nhập (xem TYPING_DELAY). Khi điều này xảy ra, luồng chính sẽ gửi một thông báo đến worker chứa thông tin đầu vào của người dùng để phân loại.
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,
});
};
Xử lý đầu ra
Chúng tôi kiểm tra xem điểm đầu ra của bộ phân loại có vượt quá ngưỡng của chúng tôi hay không. Nếu có, chúng tôi sẽ ghi nhận nhãn hữu quan.
Nếu có bất kỳ nhãn độc hại nào được liệt kê, bình luận đó sẽ bị gắn cờ là có khả năng độc hại.
// 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,
});
};
Hiển thị gợi ý
Nếu isToxic là true, chúng ta sẽ hiển thị một gợi ý cho người dùng. Trong bản minh hoạ, chúng tôi không sử dụng loại độc hại chi tiết hơn, nhưng chúng tôi đã cung cấp loại này cho luồng chính nếu cần (toxicityTypeList). Bạn có thể thấy loại này hữu ích cho trường hợp sử dụng của mình.
Trải nghiệm người dùng
Trong bản minh hoạ, chúng tôi đã đưa ra các lựa chọn sau:
- Luôn cho phép đăng. Gợi ý về mức độ độc hại ở phía máy khách của chúng tôi không ngăn người dùng đăng nội dung. Trong bản minh hoạ của chúng tôi, người dùng có thể đăng bình luận ngay cả khi mô hình chưa tải (và do đó không đưa ra đánh giá về mức độ độc hại), và ngay cả khi bình luận đó bị phát hiện là độc hại. Theo khuyến nghị, bạn nên có một hệ thống thứ hai để phát hiện bình luận tiêu cực. Nếu có ý nghĩa đối với ứng dụng của bạn, hãy cân nhắc thông báo cho người dùng rằng bình luận của họ đã được gửi trên máy khách, nhưng sau đó bị gắn cờ trên máy chủ hoặc trong quá trình kiểm tra thủ công.
- Lưu ý trường hợp âm tính giả. Khi một bình luận không được phân loại là độc hại, bản minh hoạ của chúng tôi sẽ không đưa ra ý kiến phản hồi (ví dụ: "Bình luận hay!"). Ngoài việc gây phiền toái, việc đưa ra ý kiến phản hồi tích cực có thể gửi tín hiệu sai, vì bộ phân loại của chúng tôi đôi khi nhưng chắc chắn sẽ bỏ sót một số bình luận độc hại.
Các lựa chọn thay thế và tính năng nâng cao
Hạn chế và các điểm cải tiến trong tương lai
- Ngôn ngữ: Mô hình chúng tôi đang sử dụng chủ yếu hỗ trợ tiếng Anh. Để hỗ trợ nhiều ngôn ngữ, bạn cần tinh chỉnh. Nhiều mô hình độc hại được liệt kê trên Hugging Face có hỗ trợ các ngôn ngữ không phải tiếng Anh (tiếng Nga, tiếng Hà Lan), mặc dù hiện tại các mô hình này không tương thích với Transformers.js.
- Sắc thái: Mặc dù toxic-bert phát hiện hiệu quả nội dung độc hại công khai, nhưng mô hình này có thể gặp khó khăn với những trường hợp tinh tế hơn hoặc phụ thuộc vào ngữ cảnh (châm biếm, mỉa mai). Nội dung độc hại có thể mang tính chủ quan và tinh vi. Ví dụ: bạn có thể muốn một số cụm từ hoặc thậm chí cả biểu tượng cảm xúc được phân loại là nội dung độc hại. Việc tinh chỉnh có thể giúp cải thiện độ chính xác ở những khía cạnh này.
Chúng tôi sắp có một bài viết về việc tinh chỉnh mô hình độc hại.
Phương án thay thế
- MediaPipe để phân loại văn bản. Đảm bảo bạn sử dụng một mô hình tương thích với các tác vụ phân loại.
Trình phân loại độc hại TensorFlow.js. Mô hình này có kích thước nhỏ hơn và tốc độ tìm nạp nhanh hơn, nhưng chưa được tối ưu hoá trong một thời gian. Vì vậy, bạn có thể nhận thấy suy luận có phần chậm hơn so với Transformers.js.
Kết luận
Tính năng phát hiện nội dung độc hại phía máy khách là một công cụ mạnh mẽ giúp cải thiện cộng đồng trực tuyến.
Bằng cách tận dụng các mô hình AI như toxic-bert chạy trong trình duyệt bằng Transformers.js, bạn có thể triển khai các cơ chế phản hồi theo thời gian thực để ngăn chặn hành vi độc hại và giảm tải việc phân loại mức độ độc hại trên các máy chủ của mình.
Phương pháp phía máy khách này đã hoạt động trên nhiều trình duyệt. Tuy nhiên, hãy lưu ý đến những hạn chế, đặc biệt là về chi phí phân phát mô hình và kích thước tải xuống. Áp dụng các phương pháp hay nhất về hiệu suất cho AI phía máy khách và lưu mô hình vào bộ nhớ đệm.
Để phát hiện nội dung độc hại một cách toàn diện, hãy kết hợp các phương pháp phía máy khách và phía máy chủ.