Data di pubblicazione: 13 novembre 2024
L'incitamento all'odio, le molestie e gli abusi online sono diventati un problema pervasivo online. I commenti tossici mettono a tacere voci importanti e allontanano utenti e clienti. Il rilevamento della tossicità protegge gli utenti e crea un ambiente online più sicuro.
In questa serie in due parti, esploriamo come utilizzare l'IA per rilevare e ridurre la tossicità alla fonte: le tastiere degli utenti.
Nella prima parte, abbiamo discusso i casi d'uso e i vantaggi di questo approccio.
In questa seconda parte, analizziamo l'implementazione, inclusi esempi di codice e suggerimenti per l'esperienza utente.
Demo e codice
Prova la nostra demo e esamina il codice su GitHub.
Supporto browser
La nostra demo funziona nelle versioni più recenti di Safari, Chrome, Edge e Firefox.
Seleziona un modello e una libreria
Utilizziamo la libreria Transformers.js di Hugging Face, che fornisce strumenti per lavorare con i modelli di machine learning nel browser. Il nostro codice di dimostrazione è derivato da questo esempio di classificazione del testo.
Abbiamo scelto il modello toxic-bert, un modello preaddestrato progettato per identificare schemi di linguaggio tossico. È una versione compatibile con il web di unitary/toxic-bert. Per ulteriori dettagli sulle etichette del modello e sulla sua classificazione degli attacchi di identità, consulta la pagina del modello Hugging Face.
Una volta scaricato il modello, l'inferenza è rapida.
Ad esempio, in genere sono necessari meno di 500 millisecondi in Chrome su un dispositivo Android di fascia media che abbiamo testato (uno smartphone Pixel 7 normale, non il modello Pro più performante). Esegui i tuoi benchmark rappresentativi della tua base utenti.
Implementazione
Ecco i passaggi chiave della nostra implementazione:
Impostare una soglia di tossicità
Il nostro classificatore della tossicità fornisce punteggi di tossicità compresi tra 0
e 1
. All'interno di questo intervallo, dobbiamo impostare una soglia per determinare cosa costituisce un commento velenoso. Una soglia di utilizzo comune è 0.9
. In questo modo puoi rilevare i commenti apertamente tossici, evitando al contempo una sensibilità eccessiva che potrebbe portare a troppi falsi positivi (in altre parole, commenti innocui classificati come tossici).
export const TOXICITY_THRESHOLD = 0.9
Importa i componenti
Iniziamo importando i componenti necessari dalla libreria @xenova/transformers
. Importiamo anche costanti e valori di configurazione, inclusa la nostra
soglia di tossicità.
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';
Carica il modello e comunica con il thread principale
Caricammo il modello di rilevamento della tossicità toxic-bert e lo utilizziamo per preparare il nostro
classificatore. La versione meno complessa è
const classifier = await pipeline('text-classification', MODEL_NAME);
La creazione di una pipeline, come nel codice di esempio, è il primo passaggio per eseguire le attività di inferenza.
La funzione pipeline accetta due argomenti: l'attività ('text-classification'
)
e il modello (Xenova/toxic-bert
).
Termine chiave: in Transformers.js, una pipeline è un'API di alto livello che semplifica il processo di esecuzione dei modelli ML. Gestisce attività come il caricamento del modello, la tokenizzazione e la post-elaborazione.
Il nostro codice di dimostrazione fa qualcosa in più rispetto alla semplice preparazione del modello, perché offloadiamo i passaggi di preparazione del modello dal costo computazionale elevato a un web worker. In questo modo, il thread principale rimane reattivo. Scopri di più su come offloadare attività costose su un web worker.
Il nostro worker deve comunicare con il thread principale utilizzando messaggi per indicare lo stato del modello e i risultati della valutazione della tossicità. Dai un'occhiata ai codici messaggio che abbiamo creato e che mappano i diversi stati del ciclo di vita della preparazione e dell'inferenza del modello.
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 });
}
})();
Classificare l'input dell'utente
Nella nostra funzione classify
, utilizziamo il classificatore creato in precedenza per analizzare un commento dell'utente. Restituisce l'output non elaborato del classificatore di tossicità: etichette e punteggi.
// 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;
}
Chiamiamo la nostra funzione di classificazione quando il thread principale lo chiede al worker.
Nella nostra demo, attiviamo il classificatore non appena l'utente smette di digitare
(vedi TYPING_DELAY
). In questo caso, il thread principale invia un messaggio al
worker contenente l'input dell'utente da classificare.
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,
});
};
Elabora l'output
Controlliamo se i punteggi di output dello strumento di classificazione superano la nostra soglia. In questo caso, prendiamo nota dell'etichetta in questione.
Se è presente una delle etichette di tossicità, il commento viene segnalato come potenzialmente tossico.
// 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,
});
};
Mostrare un suggerimento
Se isToxic
è true, viene mostrato un suggerimento all'utente. Nella nostra demo non utilizziamo il tipo di tossicità più granulare, ma lo abbiamo reso disponibile per il thread principale, se necessario (toxicityTypeList
). Potresti trovarlo utile per il tuo caso d'uso.
Esperienza utente
Nella nostra demo abbiamo fatto le seguenti scelte:
- Consenti sempre la pubblicazione. Il nostro suggerimento sulla tossicità lato client non impedisce all'utente di pubblicare. Nella nostra demo, l'utente può pubblicare un commento anche se il modello non è stato caricato (e quindi non offre una valutazione della tossicità) e anche se il commento viene rilevato come tossico. Come consigliato, dovresti avere un secondo sistema per rilevare i commenti tossici. Se ha senso per la tua applicazione, valuta la possibilità di informare l'utente che il suo commento è stato elaborato sul client, ma è stato segnalato sul server o durante l'ispezione da parte di persone fisiche.
- Attenzione ai falsi negativi. Quando un commento non viene classificato come tossico, la nostra demo non offre un feedback (ad esempio, "Bel commento!"). Oltre a essere poco affidabile, l'offerta di feedback positivi potrebbe inviare il segnale sbagliato, perché il nostro classificatore occasionalmente, ma inevitabilmente, perde alcuni commenti tossici.
Miglioramenti e alternative
Limitazioni e miglioramenti futuri
- Lingue: il modello che utilizziamo supporta principalmente l'inglese. Per l'assistenza in più lingue, è necessaria la messa a punto. Diversi modelli di tossicità elencati su Hugging Face supportano lingue diverse dall'inglese (russo, olandese), anche se al momento non sono compatibili con Transformers.js.
- Nuance: sebbene toxic-bert riesca a rilevare efficacemente la tossicità palese, potrebbe avere difficoltà con casi più sottili o dipendenti dal contesto (ironia, sarcasmo). La tossicità può essere molto soggettiva e sottile. Ad esempio, potresti voler classificare come dannosi determinati termini o persino emoji. La messa a punto può contribuire a migliorare la precisione in queste aree.
A breve pubblicheremo un articolo sulla messa a punto di un modello di tossicità.
Alternative
- MediaPipe per la classificazione del testo. Assicurati di utilizzare un modello compatibile con le attività di classificazione.
Classificatore di tossicità di TensorFlow.js. Offre un modello più piccolo e più veloce da recuperare, ma non è stato ottimizzato da un po' di tempo, quindi potresti notare che l'inferenza è leggermente più lenta rispetto a Transformers.js.
Conclusione
Il rilevamento della tossicità lato client è uno strumento efficace per migliorare le community online.
Sfruttando modelli di IA come toxic-bert che vengono eseguiti nel browser con Transformers.js, puoi implementare meccanismi di feedback in tempo reale che scoraggiano il comportamento tossico e riducono il carico di classificazione della tossicità sui tuoi server.
Questo approccio lato client funziona già su tutti i browser. Tuttavia, tieni presente le limitazioni, in particolare in termini di costi di pubblicazione del modello e dimensioni del download. Applica le best practice per le prestazioni dell'IA lato client e memorizza nella cache il modello.
Per un rilevamento completo della tossicità, combina gli approcci lato client e lato server.