Bagian 2: Membuat deteksi toksisitas AI sisi klien

Maud Nalpas
Maud Nalpas

Dipublikasikan: 13 November 2024

Ujaran kebencian, pelecehan, dan penyalahgunaan online telah menjadi masalah yang meluas di internet. Komentar tidak pantas membungkam pendapat penting dan membuat pengguna dan pelanggan menjauh. Deteksi toksisitas melindungi pengguna Anda dan menciptakan lingkungan online yang lebih aman.

Dalam seri dua bagian ini, kita akan mempelajari cara menggunakan AI untuk mendeteksi dan mengurangi toksisitas di sumbernya: keyboard pengguna.

Di bagian pertama, kita membahas kasus penggunaan dan manfaat pendekatan ini.

Di bagian kedua ini, kita akan mempelajari implementasinya, termasuk contoh kode dan tips UX.

Demo dan kode

Coba demo kami dan pelajari kode di GitHub.

Demo memposting komentar.
Saat pengguna berhenti mengetik, kami menganalisis toksisitas komentar mereka. Kami menampilkan peringatan secara real-time jika komentar diklasifikasikan sebagai negatif.

Dukungan browser

Demo kami berjalan di Safari, Chrome, Edge, dan Firefox versi terbaru.

Memilih model dan library

Kita menggunakan library Transformers.js Hugging Face, yang menyediakan alat untuk bekerja dengan model machine learning di browser. Kode demo kami berasal dari contoh klasifikasi teks ini.

Kami memilih model toxic-bert, yaitu model terlatih yang dirancang untuk mengidentifikasi pola bahasa negatif. Model ini adalah versi unitary/toxic-bert yang kompatibel dengan web. Untuk mengetahui detail selengkapnya tentang label model dan klasifikasi serangan identitasnya, lihat halaman model Hugging Face.

Ukuran download toxic-bert adalah 111 MB.

Setelah model didownload, inferensi akan berjalan cepat.

Misalnya, biasanya diperlukan waktu kurang dari 500 milidetik di Chrome yang berjalan di perangkat Android kelas menengah yang telah kami uji (ponsel Pixel 7 biasa, bukan model Pro yang lebih berperforma). Jalankan tolok ukur Anda sendiri yang mewakili basis pengguna Anda.

Penerapan

Berikut adalah langkah-langkah utama dalam penerapan kami:

Menetapkan nilai minimum toksisitas

Pengklasifikasi toksisitas kami memberikan skor toksisitas antara 0 dan 1. Dalam rentang tersebut, kita perlu menetapkan nilai minimum untuk menentukan apa yang merupakan komentar tidak sopan. Nilai minimum yang umum digunakan adalah 0.9. Dengan begitu, Anda dapat menemukan komentar yang jelas-jelas toksik, sekaligus menghindari sensitivitas berlebihan yang dapat menyebabkan terlalu banyak positif palsu (dengan kata lain, komentar tidak berbahaya dikategorikan sebagai toksik).

export const TOXICITY_THRESHOLD = 0.9

Impor komponen

Kita mulai dengan mengimpor komponen yang diperlukan dari library @xenova/transformers library. Kita juga mengimpor konstanta dan nilai konfigurasi, termasuk nilai batas toksisitas.

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

Memuat model dan berkomunikasi dengan thread utama

Kita memuat model deteksi toksisitas toxic-bert, dan menggunakannya untuk menyiapkan pengklasifikasi. Versi yang paling tidak rumit adalah const classifier = await pipeline('text-classification', MODEL_NAME);

Membuat pipeline, seperti dalam contoh kode, adalah langkah pertama untuk menjalankan tugas inferensi.

Fungsi pipeline mengambil dua argumen: tugas ('text-classification') dan model (Xenova/toxic-bert).

Istilah penting: Di Transformers.js, pipeline adalah API tingkat tinggi yang menyederhanakan proses menjalankan model ML. Class ini menangani tugas seperti pemuatan model, tokenisasi, dan pemrosesan pasca-pemrosesan.

Kode demo kita melakukan lebih dari sekadar menyiapkan model, karena kita mengalihkan langkah-langkah penyiapan model yang mahal secara komputasi ke pekerja web. Hal ini memungkinkan thread utama tetap responsif. Pelajari lebih lanjut cara menurunkan beban tugas berat ke web worker.

Worker kita perlu berkomunikasi dengan thread utama, menggunakan pesan untuk menunjukkan status model dan hasil penilaian toksisitas. Lihat kode pesan yang telah kami buat dan dipetakan ke berbagai status siklus proses penyiapan dan inferensi model.

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

Mengklasifikasikan input pengguna

Dalam fungsi classify, kita menggunakan pengklasifikasi yang dibuat sebelumnya untuk menganalisis komentar pengguna. Kami menampilkan output mentah dari pengklasifikasi toksisitas: label dan skor.

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

Kita memanggil fungsi klasifikasi saat thread utama meminta pekerja untuk melakukannya. Dalam demo ini, kita memicu pengklasifikasi segera setelah pengguna berhenti mengetik (lihat TYPING_DELAY). Saat ini terjadi, thread utama kita mengirim pesan ke pekerja yang berisi input pengguna untuk diklasifikasikan.

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

Memproses output

Kami memeriksa apakah skor output pengklasifikasi melebihi nilai minimum kami. Jika ya, kami akan mencatat label yang dipermasalahkan.

Jika salah satu label toksisitas tercantum, komentar tersebut ditandai sebagai berpotensi negatif.

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

Menampilkan petunjuk

Jika isToxic benar, kita akan menampilkan petunjuk kepada pengguna. Dalam demo kami, kami tidak menggunakan jenis toksisitas yang lebih terperinci, tetapi kami telah menyediakannya untuk thread utama jika diperlukan (toxicityTypeList). Anda mungkin menganggapnya berguna untuk kasus penggunaan Anda.

Pengalaman pengguna

Dalam demo kami, kami telah membuat pilihan berikut:

  • Selalu izinkan postingan. Petunjuk toksisitas sisi klien kami tidak mencegah pengguna memposting. Dalam demo kami, pengguna dapat memposting komentar meskipun model belum dimuat (dan dengan demikian tidak menawarkan penilaian toksisitas), dan meskipun komentar tersebut terdeteksi sebagai komentar negatif. Seperti yang direkomendasikan, Anda harus memiliki sistem kedua untuk mendeteksi komentar negatif. Jika masuk akal untuk aplikasi Anda, pertimbangkan untuk memberi tahu pengguna bahwa komentarnya berhasil dikirim di klien, tetapi kemudian ditandai di server atau selama pemeriksaan manual.
  • Perhatikan negatif palsu. Jika komentar tidak diklasifikasikan sebagai negatif, demo kami tidak memberikan masukan (misalnya, "Komentar yang bagus!"). Selain berisik, memberikan masukan positif dapat mengirimkan sinyal yang salah, karena pengklasifikasi kami terkadang, tetapi pasti melewatkan beberapa komentar negatif.
Demo memposting komentar.
Tombol Posting selalu diaktifkan: dalam demo kami, pengguna masih dapat memutuskan untuk memposting komentar mereka, meskipun komentar tersebut diklasifikasikan sebagai komentar tidak baik. Meskipun komentar tidak diklasifikasikan sebagai komentar tidak baik, kami tidak menampilkan masukan positif.

Peningkatan dan alternatif

Batasan dan peningkatan di masa mendatang

  • Bahasa: Model yang kami gunakan terutama mendukung bahasa Inggris. Untuk dukungan multibahasa, Anda perlu melakukan penyesuaian. Beberapa model toksisitas yang tercantum di Hugging Face mendukung bahasa non-Inggris (Rusia, Belanda), meskipun saat ini tidak kompatibel dengan Transformers.js.
  • Nuansa: Meskipun toxic-bert efektif mendeteksi toksisitas yang jelas, model ini mungkin kesulitan menangani kasus yang lebih halus atau bergantung pada konteks (ironi, sarkasme). Toksisitas bisa sangat subjektif dan halus. Misalnya, Anda mungkin ingin istilah atau bahkan emoji tertentu diklasifikasikan sebagai berbahaya. Penyesuaian dapat membantu meningkatkan akurasi di area ini.

Kami akan segera menerbitkan artikel tentang penyesuaian model toksisitas.

Alternatif

Kesimpulan

Deteksi toksisitas sisi klien adalah alat canggih untuk meningkatkan kualitas komunitas online.

Dengan memanfaatkan model AI seperti toxic-bert yang berjalan di browser dengan Transformers.js, Anda dapat menerapkan mekanisme masukan real-time yang mencegah perilaku tidak pantas dan mengurangi beban klasifikasi perilaku tidak pantas di server Anda.

Pendekatan sisi klien ini sudah berfungsi di seluruh browser. Namun, perlu diingat batasannya, terutama dalam hal biaya penayangan model dan ukuran download. Terapkan praktik terbaik performa untuk AI sisi klien dan simpan model dalam cache.

Untuk deteksi toksisitas yang komprehensif, gabungkan pendekatan sisi klien dan sisi server.