Opublikowano: 13 listopada 2024 r.
Szerzenie nienawiści, nękanie i wykorzystywanie w internecie stały się powszechnym problemem. Toksyczne komentarze uciszają ważne głosy i odstraszają użytkowników i klientów. Wykrywanie toksycznych treści chroni użytkowników i tworzy bezpieczniejsze środowisko online.
W tej 2-częściowej serii artykułów dowiesz się, jak używać AI do wykrywania i ograniczania toksyczności u źródła, czyli na klawiaturach użytkowników.
W pierwszej części omówiliśmy przypadki użycia i korzyści wynikające z tego podejścia.
W tej drugiej części omówimy implementację, w tym przykłady kodu i wskazówki dotyczące UX.
Wersja demonstracyjna i kod
Wypróbuj wersję demonstracyjną i zapoznaj się z kodem na GitHubie.
Obsługa przeglądarek
Nasza wersja demonstracyjna działa w najnowszych wersjach przeglądarek Safari, Chrome, Edge i Firefox.
Wybierz model i bibliotekę
Korzystamy z biblioteki Transformers.js od Hugging Face, która udostępnia narzędzia do pracy z modelami uczenia maszynowego w przeglądarce. Nasz kod demonstracyjny pochodzi z tego przykładu klasyfikacji tekstu.
Wybieramy model toxic-bert, czyli wstępnie wytrenowany model zaprojektowany do identyfikowania toksycznych wzorców językowych. Jest to wersja unitary/toxic-bert zgodna z internetem. Więcej informacji o etykietach modelu i jego klasyfikacji ataków na tożsamość znajdziesz na stronie modelu w Hugging Face.
Po pobraniu modelu wnioskowanie jest szybkie.
Na przykład w przypadku Chrome na testowanym przez nas urządzeniu z Androidem średniej klasy (zwykłym telefonie Pixel 7, a nie wydajniejszym modelu Pro) trwa to zwykle mniej niż 500 milisekund. Przeprowadzaj własne testy porównawcze, które reprezentują Twoją bazę użytkowników.
Implementacja
Oto najważniejsze etapy naszej implementacji:
Ustawianie progu toksyczności
Nasz klasyfikator toksyczności podaje wyniki w zakresie od 0 do 1. W tym zakresie musimy ustawić próg, aby określić, co stanowi toksyczny komentarz. Często używany próg to 0.9. Dzięki temu możesz wychwytywać jawnie toksyczne komentarze, unikając przy tym nadmiernej czułości, która mogłaby prowadzić do zbyt wielu wyników fałszywie pozytywnych (czyli nieszkodliwych komentarzy sklasyfikowanych jako toksyczne).
export const TOXICITY_THRESHOLD = 0.9
Importowanie komponentów
Zaczynamy od zaimportowania niezbędnych komponentów z @xenova/transformersbiblioteki. Importujemy też stałe i wartości konfiguracji, w tym próg toksyczności.
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';
Wczytywanie modelu i komunikacja z głównym wątkiem
Wczytujemy model wykrywania toksyczności toxic-bert i używamy go do przygotowania klasyfikatora. Najprostsza wersja tego to:const classifier = await pipeline('text-classification', MODEL_NAME);
Utworzenie potoku, tak jak w przykładowym kodzie, jest pierwszym krokiem do uruchomienia zadań wnioskowania.
Funkcja potoku przyjmuje 2 argumenty: zadanie ('text-classification') i model (Xenova/toxic-bert).
Kluczowe pojęcie: w Transformers.js potok to interfejs API wysokiego poziomu, który upraszcza proces uruchamiania modeli uczenia maszynowego. Obsługuje zadania takie jak wczytywanie modelu, tokenizacja i przetwarzanie końcowe.
Nasz kod demonstracyjny robi coś więcej niż tylko przygotowuje model, ponieważ przenosimy kosztowne obliczeniowo etapy przygotowania modelu do procesu roboczego w internecie. Dzięki temu główny wątek pozostaje responsywny. Dowiedz się więcej o przekazywaniu kosztownych zadań do wątku roboczego.
Wątek roboczy musi komunikować się z wątkiem głównym, używając wiadomości do wskazywania stanu modelu i wyników oceny toksyczności. Zapoznaj się z utworzonymi przez nas kodami wiadomości, które są powiązane z różnymi stanami cyklu życia przygotowania modelu i wnioskowania.
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 });
}
})();
Klasyfikowanie danych wejściowych użytkownika
W naszej classify używamy utworzonego wcześniej klasyfikatora do analizy komentarza użytkownika. Zwracamy surowe dane wyjściowe klasyfikatora toksyczności: etykiety i wyniki.
// 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;
}
Funkcję klasyfikacji wywołujemy, gdy wątek główny prosi o to wątek roboczy. W naszym przykładzie uruchamiamy klasyfikator, gdy tylko użytkownik przestanie pisać (patrz TYPING_DELAY). W takiej sytuacji wątek główny wysyła do wątku roboczego wiadomość zawierającą dane wejściowe użytkownika do sklasyfikowania.
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,
});
};
Przetwarzanie danych wyjściowych
Sprawdzamy, czy wyniki klasyfikatora przekraczają nasz próg. W takim przypadku zwracamy uwagę na daną etykietę.
Jeśli pojawi się któraś z etykiet toksyczności, komentarz zostanie oznaczony jako potencjalnie toksyczny.
// 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,
});
};
Wyświetlanie podpowiedzi
Jeśli wartość isToxic to prawda, wyświetlamy użytkownikowi wskazówkę. W naszej wersji demonstracyjnej nie używamy bardziej szczegółowego typu toksyczności, ale w razie potrzeby udostępniliśmy go w głównym wątku (toxicityTypeList). Może się on przydać w Twoim przypadku.
Interfejs użytkownika
W naszej wersji demonstracyjnej wybraliśmy te opcje:
- Zawsze zezwalaj na publikowanie. Wskazówka o toksyczności po stronie klienta nie uniemożliwia użytkownikowi opublikowania posta. W naszej wersji demonstracyjnej użytkownik może opublikować komentarz, nawet jeśli model nie został jeszcze wczytany (a tym samym nie oferuje oceny toksyczności) i nawet jeśli komentarz zostanie wykryty jako toksyczny. Zgodnie z zaleceniami powinieneś mieć drugi system do wykrywania toksycznych komentarzy. Jeśli ma to sens w przypadku Twojej aplikacji, możesz poinformować użytkownika, że jego komentarz został przesłany na urządzeniu klienta, ale potem został oznaczony na serwerze lub podczas weryfikacji przez człowieka.
- Pamiętaj o wynikach fałszywie negatywnych. Gdy komentarz nie zostanie zaklasyfikowany jako toksyczny, nasza wersja demonstracyjna nie wyświetli opinii (np. „Fajny komentarz!”). Oprócz tego, że jest to uciążliwe, przekazywanie pozytywnych opinii może wysyłać niewłaściwy sygnał, ponieważ nasz klasyfikator czasami, ale nieuchronnie, pomija niektóre toksyczne komentarze.
Ulepszenia i alternatywy
Ograniczenia i przyszłe udoskonalenia
- Języki: używany przez nas model obsługuje głównie język angielski. Aby uzyskać obsługę wielu języków, musisz przeprowadzić dostrajanie. Wiele modeli toksyczności wymienionych na platformie Hugging Face obsługuje języki inne niż angielski (rosyjski, holenderski), ale obecnie nie są one zgodne z Transformers.js.
- Nuance: model toxic-bert skutecznie wykrywa jawną toksyczność, ale może mieć problemy z bardziej subtelnymi lub zależnymi od kontekstu przypadkami (ironia, sarkazm). Toksyczność może być bardzo subiektywna i trudna do wykrycia. Możesz na przykład chcieć, aby określone słowa lub nawet emotikony były klasyfikowane jako szkodliwe. Dostrajanie może pomóc zwiększyć dokładność w tych obszarach.
Wkrótce opublikujemy artykuł o dostrajaniu modelu toksyczności.
Alternatywy
- MediaPipe do klasyfikacji tekstu Użyj modelu, który jest zgodny z zadaniami klasyfikacji.
Klasyfikator toksyczności TensorFlow.js. Jest to mniejszy model, który można szybciej pobrać, ale od jakiegoś czasu nie był optymalizowany, więc wnioskowanie może być nieco wolniejsze niż w przypadku Transformers.js.
Podsumowanie
Wykrywanie toksycznych treści po stronie klienta to zaawansowane narzędzie do ulepszania społeczności online.
Korzystając z modeli AI, takich jak toxic-bert, które działają w przeglądarce z Transformers.js, możesz wdrożyć mechanizmy informacji zwrotnych w czasie rzeczywistym, które zniechęcają do toksycznych zachowań i zmniejszają obciążenie serwerów związane z klasyfikacją toksyczności.
To podejście po stronie klienta działa już w różnych przeglądarkach. Pamiętaj jednak o ograniczeniach, zwłaszcza w zakresie kosztów udostępniania modelu i rozmiaru pobierania. Zastosuj sprawdzone metody dotyczące wydajności w przypadku AI po stronie klienta i zapisz model w pamięci podręcznej.
Aby uzyskać kompleksowe wykrywanie toksyczności, połącz podejścia po stronie klienta i serwera.