第 2 部分:建構用戶端 AI 有害內容偵測機制

Maud Nalpas
Maud Nalpas

發布日期:2024 年 11 月 13 日

仇恨言論、騷擾和網路濫用行為已成為普遍的線上問題。 惡意留言會扼殺重要意見,並趕走使用者和顧客。毒性偵測功能可保護使用者,並打造更安全的線上環境。

在這個分為兩部分的系列文章中,我們將探討如何運用 AI 技術,從源頭 (也就是使用者的鍵盤) 偵測並減少有害內容。

第一部分中,我們討論了這種做法的用途和優點。

在第二部分中,我們將深入探討實作方式,包括程式碼範例和使用者體驗提示。

示範和程式碼

試用試用版,並研究 GitHub 上的程式碼

留言發布示範。
使用者停止輸入後,我們會分析留言的毒性。如果留言遭分類為有害,系統會即時顯示警告訊息。

瀏覽器支援

我們的試用版可在最新版的 Safari、Chrome、Edge 和 Firefox 中執行。

選取模型和程式庫

我們使用 Hugging Face 的 Transformers.js 程式庫,該程式庫提供在瀏覽器中使用機器學習模型的工具。我們的示範程式碼衍生自這個文字分類範例

我們選擇 toxic-bert 模型,這是一種預先訓練模型,可識別惡意語言模式。這是 unitary/toxic-bert 的網頁相容版本。如要進一步瞭解模型的標籤和身分攻擊分類,請參閱 Hugging Face 模型頁面

toxic-bert 的下載大小為 111 MB。

模型下載完成後,推論速度會很快。

舉例來說,在我們測試過的中階 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;
}

當主執行緒要求工作者執行分類作業時,我們會呼叫分類函式。在我們的示範中,使用者停止輸入後,我們會立即觸發分類器 (請參閱 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 能有效偵測明顯的有害內容,但可能難以處理較細微或與情境相關的案例 (反諷、挖苦)。有害內容可能非常主觀且難以察覺。舉例來說,您可能希望將特定字詞或表情符號歸類為有害內容。微調有助於提升這些領域的準確度。

我們即將發布文章,說明如何微調毒性模型。

替代方案

結論

用戶端惡意指數偵測是一項強大工具,可提升線上社群的品質。

您可以運用 Transformers.js 在瀏覽器中執行的惡意指數 BERT 等 AI 模型,實作即時意見回饋機制,遏止惡意行為,並減輕伺服器的惡意指數分類負擔。

這個用戶端方法已適用於各個瀏覽器。但請注意限制,尤其是模型服務成本和下載大小。採用用戶端 AI 的效能最佳做法,並快取模型

如要全面偵測有害內容,請結合用戶端和伺服器端方法。