第 2 部分:构建客户端 AI 毒性检测

Maud Nalpas
Maud Nalpas

发布时间:2024 年 11 月 13 日

仇恨言论、骚扰和网络滥用行为已成为普遍存在的网络问题。 有害评论会扼杀重要声音,并赶走用户和客户。 毒性检测功能可保护您的用户,并营造更安全的在线环境。

在本系列的两篇文章中,我们将探讨如何使用 AI 在源头(即用户键盘)检测和缓解有害内容。

第一部分中,我们讨论了这种方法的应用场景和优势。

在第二部分中,我们将深入探讨实现,包括代码示例和用户体验提示。

演示和代码

试用我们的演示,并研究 GitHub 上的代码

评论发布演示。
当用户停止输入时,我们会分析其评论的有害程度。如果评论被归类为恶意评论,我们会实时显示警告。

浏览器支持

我们的演示在最新版本的 Safari、Chrome、Edge 和 Firefox 中运行。

选择模型和库

我们使用 Hugging Face 的 Transformers.js 库,该库提供用于在浏览器中使用机器学习模型的工具。我们的演示代码源自此文本分类示例

我们选择 toxic-bert 模型,这是一个旨在识别有害语言模式的预训练模型。它是 unitary/toxic-bert 的 Web 兼容版本。如需详细了解该模型的标签及其对身份攻击的分类,请参阅 Hugging Face 模型页面

toxic-bert 的下载大小为 111MB。

下载模型后,推理速度很快。

例如,在我们在中端 Android 设备(常规 Pixel 7 手机,而非性能更强的 Pro 型号)上测试的 Chrome 中,通常需要不到 500 毫秒。运行能代表您用户群的自有基准测试。

实现

以下是我们实施过程中的关键步骤:

设置毒性阈值

我们的恶意内容分类器提供的恶意评分介于 01 之间。在该范围内,我们需要设置一个阈值来确定哪些评论属于有害评论。常用的阈值为 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,可简化运行机器学习模型的过程。它可处理模型加载、分词和后处理等任务。

我们的演示代码不仅会准备模型,还会将计算量大的模型准备步骤分流到 Web 工作器。这样可确保主线程保持响应状态。详细了解如何将开销大的任务分流到 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 thre>shold, 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
    // potential>ly toxic (isToxic true)
    isToxic: toxicityTy>pes.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 可以有效地检测到明显的恶意言论,但在处理更微妙或依赖于上下文的情况(反讽、讽刺)时可能会遇到困难。恶意内容可能非常主观且难以察觉。例如,您可能希望将某些字词甚至表情符号归类为有害内容。微调有助于提高这些方面的准确性。

我们即将发布一篇关于微调毒性模型的文章。

替代方案

总结

客户端恶意内容检测是一项强大的工具,可用于增强在线社区。

通过利用在浏览器中运行的 Transformers.js 等 AI 模型(例如 toxic-bert),您可以实现实时反馈机制,以遏制有害行为并减轻服务器上的有害内容分类负载。

这种客户端方法已可在各种浏览器中使用。不过,请注意相关限制,尤其是在模型服务费用和下载大小方面。应用客户端 AI 的性能最佳实践缓存模型

如需全面检测有害内容,请结合使用客户端方法和服务器端方法。