Опубликовано: 13 ноября 2024 г.
Язык ненависти, травля и онлайн-оскорбления стали распространенной проблемой в интернете. Токсичные комментарии заставляют замолчать важные голоса и отталкивают пользователей и клиентов . Система обнаружения токсичности защищает ваших пользователей и создает более безопасную онлайн-среду.
В этой серии из двух частей мы рассмотрим, как использовать ИИ для обнаружения и смягчения негативного воздействия в его источнике: на клавиатурах пользователей.
В первой части мы обсудили варианты применения и преимущества этого подхода.
Во второй части мы подробно рассмотрим процесс реализации, включая примеры кода и советы по пользовательскому интерфейсу.
Демонстрация и код
Поэкспериментируйте с нашей демо-версией и изучите код на GitHub .

Поддержка браузеров
Наша демонстрационная версия работает в последних версиях Safari, Chrome, Edge и Firefox.
Выберите модель и библиотеку.
Мы используем библиотеку Transformers.js от Hugging Face, которая предоставляет инструменты для работы с моделями машинного обучения в браузере. Наш демонстрационный код основан на этом примере классификации текста .
Мы выбрали модель toxic-bert , предварительно обученную модель, предназначенную для выявления токсичных языковых паттернов. Это веб-совместимая версия unitary/toxic-bert . Более подробную информацию о метках модели и классификации атак на личность можно найти на странице модели Hugging Face .

После загрузки модели процесс вывода данных происходит быстро.
Например, в Chrome на протестированном нами устройстве Android среднего уровня (обычном телефоне Pixel 7, а не более производительной модели Pro) это обычно занимает менее 500 миллисекунд. Проведите собственные тесты, репрезентативные для вашей пользовательской базы.
Выполнение
Вот основные этапы нашей реализации:
Установите пороговое значение токсичности.
Наш классификатор токсичности присваивает значения от 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 конвейер — это высокоуровневый API, упрощающий процесс запуска моделей машинного обучения. Он обрабатывает такие задачи, как загрузка модели, токенизация и постобработка.
Наш демонстрационный код делает немного больше, чем просто подготовку модели, поскольку мы переносим ресурсоемкие этапы подготовки модели на веб-воркер. Это позволяет основному потоку оставаться отзывчивым. Узнайте больше о переносе ресурсоемких задач на веб-воркер .
Нашему рабочему потоку необходимо взаимодействовать с основным потоком, используя сообщения для указания статуса модели и результатов оценки токсичности. Ознакомьтесь с созданными нами кодами сообщений , которые соответствуют различным статусам жизненного цикла подготовки и вывода модели.
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 равно true, мы отображаем пользователю подсказку. В нашей демонстрации мы не используем более детализированный тип токсичности, но при необходимости сделали его доступным для основного потока ( toxicityTypeList ). Возможно, он окажется полезным в вашем случае.
пользовательский опыт
В нашей демонстрации мы сделали следующий выбор:
- Всегда разрешайте публикацию. Наша подсказка о токсичности на стороне клиента не препятствует пользователю оставлять комментарии. В нашей демонстрации пользователь может оставить комментарий, даже если модель еще не загружена (и, следовательно, не предлагает оценку токсичности), и даже если комментарий был определен как токсичный. Как рекомендуется , у вас должна быть вторая система для обнаружения токсичных комментариев. Если это целесообразно для вашего приложения, рассмотрите возможность информирования пользователя о том, что его комментарий прошел на стороне клиента, но затем был помечен как токсичный на сервере или при проверке человеком.
- Обратите внимание на ложные отрицательные результаты . Если комментарий не классифицируется как токсичный, наша демонстрационная версия не предлагает обратной связи (например, «Хороший комментарий!»). Помимо того, что это создает излишний шум, предоставление положительной обратной связи может посылать неверный сигнал, поскольку наш классификатор иногда, но неизбежно, пропускает некоторые токсичные комментарии.

Улучшения и альтернативы
Ограничения и перспективы дальнейшего совершенствования
- Языки : Используемая нами модель в основном поддерживает английский язык. Для многоязычной поддержки потребуется тонкая настройка. Несколько моделей токсичности, перечисленных на Hugging Face, поддерживают неанглийские языки (русский, голландский), хотя в данный момент они несовместимы с Transformers.js.
- Нюансы : Хотя toxic-bert эффективно выявляет явную токсичность, он может испытывать трудности с более тонкими или зависящими от контекста случаями (ирония, сарказм). Токсичность может быть очень субъективной и тонкой. Например, вы можете захотеть, чтобы определенные термины или даже эмодзи классифицировались как токсичные. Тонкая настройка может помочь повысить точность в этих областях.
Вскоре у нас выйдет статья о тонкой настройке модели токсичности.
Альтернативы
- MediaPipe для классификации текста . Убедитесь, что используете модель, совместимую с задачами классификации .
Классификатор токсичности TensorFlow.js . Он предлагает модель меньшего размера и более быструю загрузку, но она давно не оптимизировалась, поэтому вы можете заметить, что вывод результатов несколько медленнее, чем с Transformers.js .
Заключение
Выявление токсичности на стороне клиента — мощный инструмент для улучшения онлайн-сообществ.
Используя модели искусственного интеллекта, такие как toxic-bert, работающие в браузере с помощью Transformers.js, вы можете внедрить механизмы обратной связи в реальном времени, которые препятствуют токсичному поведению и снижают нагрузку на ваши серверы при классификации токсичности.
Этот клиентский подход уже работает во всех браузерах. Однако следует помнить об ограничениях, особенно в отношении затрат на обслуживание модели и размера загружаемых данных. Применяйте лучшие практики повышения производительности для клиентского ИИ и кэшируйте модель .
Для комплексного обнаружения токсичности следует комбинировать подходы на стороне клиента и сервера.