Teil 2: Clientseitige KI-Toxizitätserkennung erstellen

Maud Nalpas
Maud Nalpas

Veröffentlicht: 13. November 2024

Hassrede, Belästigung und Onlinemissbrauch sind zu einem weit verbreiteten Problem geworden. Mit schädlichen Kommentaren werden wichtige Stimmen zum Schweigen gebracht und Nutzer und Kunden vertrieben. Die Erkennung von toxischem Verhalten schützt Ihre Nutzer und schafft eine sicherere Onlineumgebung.

In dieser zweiteiligen Reihe erfahren Sie, wie Sie mithilfe von KI toxische Inhalte an der Quelle erkennen und bekämpfen können: an der Tastatur der Nutzer.

In Teil 1 haben wir die Anwendungsfälle und Vorteile dieses Ansatzes besprochen.

In diesem zweiten Teil gehen wir auf die Implementierung ein, einschließlich Codebeispielen und UX-Tipps.

Demo und Code

Sehen Sie sich unsere Demo an und sehen Sie sich den Code auf GitHub an.

Demo zum Posten von Kommentaren
Wenn der Nutzer aufhört zu tippen, analysieren wir die Toxizität seines Kommentars. Wenn ein Kommentar als unangemessen eingestuft wird, wird in Echtzeit eine Warnung angezeigt.

Unterstützte Browser

Unsere Demo funktioniert mit den neuesten Versionen von Safari, Chrome, Edge und Firefox.

Modell und Bibliothek auswählen

Wir verwenden die Bibliothek Transformers.js von Hugging Face, die Tools für die Arbeit mit Machine-Learning-Modellen im Browser bietet. Unser Democode basiert auf diesem Beispiel für die Textklassifizierung.

Wir wählen das Modell toxic-bert aus, ein vorab trainiertes Modell, das toxische Sprachmuster erkennen soll. Es ist eine webkompatible Version von unitary/toxic-bert. Weitere Informationen zu den Labels des Modells und zur Klassifizierung von Identitätsangriffen finden Sie auf der Modellseite von Hugging Face.

Die Downloadgröße von toxic-bert beträgt 111 MB.

Nach dem Herunterladen des Modells ist die Inferenz schnell.

In Chrome auf einem von uns getesteten Android-Gerät im mittleren Preissegment (ein normales Google Pixel 7, nicht das leistungsstärkere Pro-Modell) dauert es beispielsweise in der Regel weniger als 500 Millisekunden. Führen Sie eigene Benchmarks aus, die für Ihre Nutzerbasis repräsentativ sind.

Implementierung

Das sind die wichtigsten Schritte bei der Implementierung:

Grenzwert für toxische Inhalte festlegen

Unser Klassifikator für unangemessene Äußerungen liefert Werte zwischen 0 und 1. Innerhalb dieses Bereichs müssen wir einen Grenzwert festlegen, um zu bestimmen, was einen schädlichen Kommentar ausmacht. Ein häufig verwendeter Grenzwert ist 0.9. So können offensichtlich unangemessene Kommentare erkannt werden, ohne dass es zu einer übermäßigen Sensibilität kommt, die zu zu vielen fälschlich als unangemessen gekennzeichneten Kommentaren führen könnte.

export const TOXICITY_THRESHOLD = 0.9

Komponenten importieren

Zuerst importieren wir die erforderlichen Komponenten aus der @xenova/transformers-Bibliothek. Außerdem importieren wir Konstanten und Konfigurationswerte, einschließlich unseres Grenzwerts für toxische Inhalte.

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';

Modell laden und mit dem Haupt-Thread kommunizieren

Wir laden das Modell zur Erkennung von schädlichen Inhalten „toxic-bert“ und verwenden es, um unseren Klassifikator vorzubereiten. Die einfachste Version davon ist: const classifier = await pipeline('text-classification', MODEL_NAME);

Das Erstellen einer Pipeline wie im Beispielcode ist der erste Schritt zum Ausführen von Inferenzaufgaben.

Die Pipeline-Funktion nimmt zwei Argumente an: die Aufgabe ('text-classification') und das Modell (Xenova/toxic-bert).

Wichtiger Begriff: In Transformers.js ist eine Pipeline eine API auf höherer Ebene, die das Ausführen von ML-Modellen vereinfacht. Er übernimmt Aufgaben wie das Laden von Modellen, die Tokenisierung und die Nachbearbeitung.

Unser Democode erledigt etwas mehr als nur die Modellvorbereitung, da wir die rechenintensiven Schritte zur Modellvorbereitung auf einen Webworker auslagern. So bleibt der Hauptthread reaktionsschnell. Weitere Informationen zum Auslagern von kostenintensiven Aufgaben auf einen Webworker

Der Worker muss mit dem Hauptthread kommunizieren und den Status des Modells sowie die Ergebnisse der Toxizitätsbewertung mit Nachrichten angeben. Sehen Sie sich die Nachrichtencodes an, die wir für die verschiedenen Status des Modellierungs- und Inferenzzyklus erstellt haben.

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 });
  }
})();

Nutzereingabe klassifizieren

In unserer classify-Funktion verwenden wir den zuvor erstellten Klassifikator, um einen Nutzerkommentar zu analysieren. Wir geben die Rohausgabe des Klassifikators für toxische Inhalte zurück: Labels und Bewertungen.

// 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;
}

Wir rufen unsere Klassifizierungsfunktion auf, wenn der Hauptthread den Worker dazu auffordert. In unserer Demo wird der Klassifikator ausgelöst, sobald der Nutzer aufgehört hat zu tippen (siehe TYPING_DELAY). In diesem Fall sendet der Hauptthread eine Nachricht an den Worker, die die zu klassifizierende Nutzereingabe enthält.

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,
  });
};

Ausgabe verarbeiten

Wir prüfen, ob die Ausgabebewertungen des Klassifikators unseren Grenzwert überschreiten. In diesem Fall notieren wir uns das betreffende Label.

Wenn eines der Labels für unangemessene Äußerungen aufgeführt ist, wird der Kommentar als potenziell unangemessen gekennzeichnet.

// 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,
  });
};

Tipp anzeigen

Wenn isToxic wahr ist, wird dem Nutzer ein Hinweis angezeigt. In unserer Demo verwenden wir den detaillierteren Typ für toxische Inhalte nicht, haben ihn aber für den Hauptthread bei Bedarf verfügbar gemacht (toxicityTypeList). Er kann für Ihren Anwendungsfall nützlich sein.

Nutzererfahrung

In unserer Demo haben wir folgende Auswahl getroffen:

  • Das Posten immer zulassen Der clientseitige Hinweis auf toxisches Verhalten verhindert nicht, dass der Nutzer etwas postet. In unserer Demo kann der Nutzer einen Kommentar posten, auch wenn das Modell nicht geladen wurde (und daher keine Bewertung der Unangemessenheit bietet) und auch wenn der Kommentar als unangemessen erkannt wird. Wie empfohlen solltest du ein zweites System zum Erkennen von unangemessenen Kommentaren haben. Wenn es für Ihre Anwendung sinnvoll ist, können Sie den Nutzer darüber informieren, dass sein Kommentar auf dem Client gesendet, aber dann auf dem Server oder bei der manuellen Überprüfung gemeldet wurde.
  • Achten Sie auf falsch negative Ergebnisse. Wenn ein Kommentar nicht als unangemessen eingestuft wird, gibt unsere Demo kein Feedback (z. B. „Netter Kommentar!“). Positives Feedback kann nicht nur zu unnötigen Meldungen führen, sondern auch ein falsches Signal senden, da unser Klassifikator gelegentlich, aber unvermeidlich einige unangemessene Kommentare übersieht.
Demo zum Posten von Kommentaren
Die Schaltfläche Posten ist immer aktiviert: In unserer Demo kann der Nutzer seinen Kommentar auch dann posten, wenn er als toxisch eingestuft wird. Auch wenn ein Kommentar nicht als toxisch eingestuft wird, wird kein positives Feedback angezeigt.

Verbesserungen und Alternativen

Einschränkungen und künftige Optimierungen

  • Sprachen: Das verwendete Modell unterstützt hauptsächlich Englisch. Für die mehrsprachige Unterstützung ist eine Feinabstimmung erforderlich. Mehrere auf Hugging Face aufgeführte Toxizitätsmodelle unterstützen auch andere Sprachen als Englisch (z. B. Russisch und Niederländisch), sind aber derzeit nicht mit Transformers.js kompatibel.
  • Nuance Toxic-BERT erkennt zwar effektiv offensichtliche Toxizität, hat aber möglicherweise Probleme mit subtileren oder kontextabhängigen Fällen (Ironie, Sarkasmus). Toxische Inhalte können sehr subjektiv und subtil sein. Sie können beispielsweise festlegen, dass bestimmte Begriffe oder sogar Emojis als schädlich eingestuft werden. Eine Feinabstimmung kann die Genauigkeit in diesen Bereichen verbessern.

In einem kommenden Artikel erfahren Sie, wie Sie ein Modell zur Erkennung von schädlichen Inhalten optimieren.

Alternativen

Fazit

Die clientseitige Erkennung von schädlichen Inhalten ist ein leistungsstarkes Tool, mit dem Online-Communitys verbessert werden können.

Mithilfe von KI-Modellen wie toxic-bert, die im Browser mit Transformers.js ausgeführt werden, können Sie Echtzeit-Feedbackmechanismen implementieren, die unangemessenes Verhalten erschweren und die Belastung Ihrer Server durch die Klassifizierung von schädlichem Verhalten reduzieren.

Dieser clientseitige Ansatz funktioniert bereits plattformübergreifend. Beachten Sie jedoch die Einschränkungen, insbesondere in Bezug auf die Kosten für die Modellbereitstellung und die Größe der Downloaddatei. Wenden Sie Best Practices für die Leistung von clientseitiger KI an und speichern Sie das Modell im Cache.

Für eine umfassende Erkennung von schädlichen Inhalten sollten Sie clientseitige und serverseitige Ansätze kombinieren.