Часть 2. Создание системы обнаружения токсичности на стороне клиента с помощью искусственного интеллекта

Мод Налпас
Maud Nalpas

Опубликовано: 13 ноября 2024 г.

Разжигание ненависти, преследования и оскорбления в Интернете стали широко распространенной проблемой в Интернете. Ядовитые комментарии заглушают важные голоса и отпугивают пользователей и клиентов . Обнаружение токсичности защищает ваших пользователей и создает более безопасную онлайн-среду.

В этой серии из двух частей мы исследуем, как использовать ИИ для обнаружения и снижения токсичности в ее источнике: клавиатурах пользователей.

В первой части мы обсудили варианты использования и преимущества этого подхода.

Во второй части мы углубимся в реализацию, включая примеры кода и советы по UX.

Демо и код

Поиграйтесь с нашей демо-версией и изучите код на GitHub .

Демонстрация публикации комментариев.
Когда пользователь перестает печатать, мы анализируем токсичность его комментария. Мы отображаем предупреждение в режиме реального времени, если комментарий классифицируется как токсичный.

Поддержка браузера

Наша демо-версия работает в последних версиях Safari, Chrome, Edge и Firefox.

Выберите модель и библиотеку

Мы используем библиотеку Transformers.js компании Hugging Face, которая предоставляет инструменты для работы с моделями машинного обучения в браузере. Наш демонстрационный код основан на этом примере классификации текста .

Мы выбираем модель токсичного берта — предварительно обученную модель, предназначенную для выявления токсичных языковых моделей. Это веб-совместимая версия unitary/ Toxic-bert . Более подробную информацию о метках модели и классификации атак с использованием личных данных можно найти на странице модели Hugging Face .

Размер загрузки Toxic-bert составляет 111 МБ.

После загрузки модели вывод выполняется быстро.

Например, обычно это занимает менее 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);

Создание конвейера, как в примере кода, — это первый шаг к выполнению задач вывода.

Функция конвейера принимает два аргумента: задачу ( 'text-classification' ) и модель ( Xenova/toxic-bert ).

Ключевой термин: в Transformers.js конвейер — это API высокого уровня, который упрощает процесс запуска моделей ML. Он выполняет такие задачи, как загрузка модели, токенизация и постобработка.

Наш демонстрационный код делает немного больше, чем просто подготовку модели, поскольку мы перекладываем трудоемкие этапы подготовки модели на веб-работника. Это позволяет основному потоку оставаться отзывчивым. Узнайте больше о передаче дорогостоящих задач веб-работнику .

Нашему работнику необходимо общаться с основным потоком, используя сообщения для указания статуса модели и результатов оценки токсичности. Взгляните на созданные нами коды сообщений , которые соответствуют различным статусам жизненного цикла подготовки модели и вывода.

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 эффективно обнаруживает явную токсичность, он может бороться с более тонкими или контекстно-зависимыми случаями (ирония, сарказм). Токсичность может быть весьма субъективной и незаметной. Например, вы можете захотеть, чтобы определенные термины или даже смайлы классифицировались как токсичные. Точная настройка может помочь повысить точность в этих областях.

У нас скоро выйдет статья о точной настройке модели токсичности.

Альтернативы

Заключение

Обнаружение токсичности на стороне клиента — мощный инструмент для улучшения онлайн-сообществ.

Используя модели искусственного интеллекта, такие как Toxic-bert, которые запускаются в браузере вместе с Transformers.js, вы можете реализовать механизмы обратной связи в реальном времени, которые препятствуют токсичному поведению и уменьшают нагрузку на классификацию токсичности на ваших серверах.

Этот подход на стороне клиента уже работает во всех браузерах. Однако имейте в виду ограничения, особенно с точки зрения стоимости обслуживания модели и размера загрузки. Примените лучшие практики производительности для ИИ на стороне клиента и кэшируйте модель .

Для комплексного обнаружения токсичности комбинируйте подходы на стороне клиента и на стороне сервера.