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

Maud Nalpas
Maud Nalpas

发布时间:2024 年 11 月 13 日

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

在本系列的第 2 部分中,我们将探讨如何使用 AI 从源头(用户的键盘)检测和减少有害内容。

第 1 部分中,我们讨论了此方法的用例和优势。

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

试用我们的演示版,并研究 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

我们的 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;
}

当主线程请求 worker 执行分类时,我们会调用分类函数。在我们的演示中,我们会在用户停止输入后立即触发分类器(请参阅 TYPING_DELAY)。发生这种情况时,我们的主线程会向 worker 发送一条消息,其中包含要分类的用户输入。

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。
  • Nuance:虽然 toxic-bert 可以有效检测明显的恶意言论,但对于更细微或取决于上下文的情况(讽刺、挖苦),它可能无法胜任。毒性可能非常主观且细微。例如,您可能希望将某些字词甚至表情符号归类为有害内容。微调有助于提高这些方面的准确性。

我们即将推出一篇介绍如何微调毒性内容检测模型的文章。

替代方案

总结

客户端毒性检测是一款强大的工具,可用于改善在线社区。

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

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

如需进行全面的毒性检测,请结合使用客户端和服务器端方法。