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 i tuoi utenti e crea un ambiente online più sicuro.
In questa serie in due parti, esploreremo come utilizzare l'AI per rilevare e mitigare la tossicità alla sua fonte: le tastiere degli utenti.
Nella prima parte abbiamo discusso i casi d'uso e i vantaggi di questo approccio.
In questa seconda parte, approfondiamo l'implementazione, inclusi esempi di codice e suggerimenti per la UX.
Demo e codice
Prova la nostra demo ed esamina il codice su GitHub.
Supporto browser
La nostra demo viene eseguita 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 demo deriva da questo esempio di classificazione del testo.
Scegliamo il modello toxic-bert, un modello preaddestrato progettato per identificare i pattern linguistici tossici. Si tratta di una versione di unitary/toxic-bert compatibile con il web. Per maggiori dettagli sulle etichette del modello e sulla classificazione degli attacchi all'identità, consulta la pagina del modello Hugging Face.
Una volta scaricato il modello, l'inferenza è rapida.
Ad esempio, in genere bastano meno di 500 millisecondi in Chrome in esecuzione 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
Di seguito sono riportati i passaggi chiave della nostra implementazione:
Impostare una soglia di tossicità
Il nostro classificatore di 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
tossico. Una soglia di utilizzo comune è 0.9. In questo modo puoi rilevare i commenti
apertamente tossici, evitando un'eccessiva sensibilità che potrebbe portare a troppi
falsi positivi (ovvero 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
Carichiamo 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 attività di inferenza.
La funzione della 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 demo fa un po' di più della semplice preparazione del modello, perché scarichiamo i passaggi di preparazione del modello computazionalmente costosi in un web worker. In questo modo, il thread principale rimane reattivo. Scopri di più su come scaricare 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 dei messaggi che abbiamo creato e che corrispondono a 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. Restituiamo 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 chiede al worker di farlo.
Nella nostra demo, attiviamo il classificatore non appena l'utente smette di digitare
(vedi TYPING_DELAY). Quando ciò accade, il nostro thread principale invia un messaggio al
worker che contiene 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,
});
};
Elaborare l'output
Controlliamo se i punteggi di output del classificatore superano la nostra soglia. In questo caso, prendiamo nota dell'etichetta in questione.
Se è presente una delle etichette di tossicità, il commento viene contrassegnato 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, mostriamo un suggerimento all'utente. Nella nostra demo non utilizziamo
il tipo di tossicità più granulare, ma l'abbiamo reso disponibile al thread principale, se necessario (toxicityTypeList). Potresti
trovarlo utile per il tuo caso d'uso.
Esperienza utente
Nella nostra demo, abbiamo effettuato le seguenti scelte:
- Consenti sempre la pubblicazione. Il 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 pubblicato sul client, ma è stato segnalato sul server o durante l'ispezione umana.
- Tieni conto dei falsi negativi. Quando un commento non viene classificato come tossico, la nostra demo non offre feedback (ad esempio, "Bel commento!"). Oltre a essere rumoroso, offrire feedback positivi può inviare un segnale sbagliato, perché il nostro classificatore a volte, ma inevitabilmente, non rileva alcuni commenti tossici.
Miglioramenti e alternative
Limitazioni e miglioramenti futuri
- Lingue: il modello che utilizziamo supporta principalmente l'inglese. Per il supporto multilingue, è necessario il perfezionamento. 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.
- Sfumature: sebbene toxic-bert rilevi efficacemente la tossicità palese, potrebbe avere difficoltà con casi più sottili o dipendenti dal contesto (ironia, sarcasmo). La tossicità può essere altamente soggettiva e sottile. Ad esempio, potresti voler che determinati termini o persino emoji vengano classificati come tossici. Il perfezionamento può contribuire a migliorare l'accuratezza in questi ambiti.
È in arrivo un articolo sul perfezionamento 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à 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 è un po' più lenta rispetto a Transformers.js.
Conclusione
Il rilevamento della tossicità lato client è uno strumento potente per migliorare le community online.
Sfruttando modelli di AI come Toxic-BERT che vengono eseguiti nel browser con Transformers.js, puoi implementare meccanismi di feedback in tempo reale che scoraggiano comportamenti tossici e riducono il carico di classificazione della tossicità sui server.
Questo approccio lato client funziona già su tutti i browser. Tuttavia, tieni presente le limitazioni, soprattutto in termini di costi di pubblicazione del modello e dimensioni del download. Applica le best practice per il rendimento dell'AI lato client e memorizza il modello nella cache.
Per un rilevamento completo della tossicità, combina approcci lato client e lato server.