Creazione di giochi web potenziati da ML con Transformers.js

Creazione di giochi web con Transformers.js potenziati da ML.

In questo post del blog, ti mostrerò come ho creato Doodle Dash, un gioco web in tempo reale alimentato da ML che funziona completamente nel tuo browser (grazie a Transformers.js). L’obiettivo di questo tutorial è mostrarti quanto sia facile creare il tuo gioco web alimentato da ML… proprio in tempo per il prossimo Open Source AI Game Jam (7-9 luglio 2023). Unisciti al game jam se non l’hai ancora fatto!

Video: Doodle Dash demo video

  • Demo: Doodle Dash
  • Codice sorgente: doodle-dash
  • Unisciti al game jam: Open Source AI Game Jam

Panoramica

Prima di iniziare, parliamo di ciò che creeremo. Il gioco è ispirato al gioco Quick, Draw! di Google, dove ti viene dato una parola e una rete neurale ha 20 secondi per indovinare cosa stai disegnando (ripetuto 6 volte). In realtà, useremo i loro dati di addestramento per addestrare il nostro modello di rilevamento dei disegni! Non ami l’open source? 😍

Nella nostra versione, avrai un minuto per disegnare il maggior numero possibile di oggetti, un prompt alla volta. Se il modello predice l’etichetta corretta, il canvas verrà cancellato e ti verrà data una nuova parola. Continua a fare questo fino a quando il timer non si esaurisce! Poiché il gioco viene eseguito localmente nel tuo browser, non dobbiamo preoccuparci della latenza del server. Il modello è in grado di fare previsioni in tempo reale mentre disegni, fino a oltre 60 previsioni al secondo… 🤯 WOW!

Questo tutorial è diviso in 3 sezioni:

  1. Addestramento della rete neurale
  2. Esecuzione nel browser con Transformers.js
  3. Design del gioco

1. Addestramento della rete neurale

Dati di addestramento

Addestreremo il nostro modello utilizzando un subset del dataset Quick, Draw! di Google, che contiene oltre 5 milioni di disegni in 345 categorie. Ecco alcuni esempi dal dataset:

Architettura del modello

Stiamo per perfezionare apple/mobilevit-small, un Vision Transformer leggero e adatto ai dispositivi mobili che è stato pre-addestrato su ImageNet-1k. Ha solo 5,6 milioni di parametri (dimensione del file di circa 20 MB), un candidato perfetto per l’esecuzione nel browser! Per ulteriori informazioni, consulta il paper di MobileViT e l’architettura del modello sottostante.

Perfezionamento

Per mantenere il post del blog (relativamente) breve, abbiamo preparato un notebook Colab che ti mostrerà i passaggi esatti che abbiamo seguito per perfezionare apple/mobilevit-small sul nostro dataset. A grandi linee, ciò comporta:

  1. Caricare il dataset “Quick, Draw!”.

  2. Trasformare il dataset utilizzando un MobileViTImageProcessor.

  3. Definire la nostra funzione di collate e la metrica di valutazione.

  4. Caricare il modello pre-addestrato MobileVIT utilizzando MobileViTForImageClassification.from_pretrained.

  5. Addestrare il modello utilizzando le classi helper Trainer e TrainingArguments.

  6. Valutare il modello utilizzando 🤗 Evaluate.

NOTA: Puoi trovare il nostro modello perfezionato qui su Hugging Face Hub.

2. Esecuzione nel browser con Transformers.js

Cos’è Transformers.js?

Transformers.js è una libreria JavaScript che ti consente di eseguire 🤗 Transformers direttamente nel tuo browser (nessuna necessità di un server)! È progettato per essere funzionalmente equivalente alla libreria Python, il che significa che puoi eseguire gli stessi modelli pre-addestrati utilizzando un’API molto simile.

Nelle retrovie, Transformers.js utilizza ONNX Runtime , quindi dobbiamo convertire il nostro modello PyTorch raffinato in ONNX.

Conversione del nostro modello in ONNX

Fortunatamente, la libreria 🤗 Optimum rende estremamente semplice convertire il tuo modello raffinato in ONNX! Il modo più facile (e consigliato) è:

  1. Clonare il repository Transformers.js e installare le dipendenze necessarie:

    git clone https://github.com/xenova/transformers.js.git
    cd transformers.js
    pip install -r scripts/requirements.txt
  2. Eseguire lo script di conversione (utilizza Optimum sotto il cofano):

    python -m scripts.convert --model_id <model_id>

    dove <model_id> è il nome del modello che si desidera convertire (ad es. Xenova/quickdraw-mobilevit-small ).

Configurazione del nostro progetto

Iniziamo dalla struttura di base di un’app React semplice utilizzando Vite:

npm create vite@latest doodle-dash -- --template react

Successivamente, accedere alla directory del progetto e installare le dipendenze necessarie:

cd doodle-dash
npm install
npm install @xenova/transformers

Puoi quindi avviare il server di sviluppo eseguendo:

npm run dev

Esecuzione del modello nel browser

L’esecuzione dei modelli di apprendimento automatico richiede molte risorse computazionali, quindi è importante eseguire l’interferenza in un thread separato. In questo modo non bloccheremo il thread principale, che viene utilizzato per il rendering dell’interfaccia utente e per reagire ai gesti di disegno 😉. L’API dei Web Workers rende tutto questo estremamente semplice!

Crea un nuovo file (ad esempio, worker.js ) nella directory src e aggiungi il seguente codice:

import { pipeline, RawImage } from "@xenova/transformers";

const classifier = await pipeline("image-classification", 'Xenova/quickdraw-mobilevit-small', { quantized: false });

const image = await RawImage.read('https://hf.co/datasets/huggingface/documentation-images/resolve/main/blog/ml-web-games/skateboard.png');

const output = await classifier(image.grayscale());
console.log(output);

Puoi ora utilizzare questo worker nel file App.jsx aggiungendo il seguente codice al componente App:

import { useState, useEffect, useRef } from 'react'
// ... resto degli import

function App() {
    // Crea un riferimento all'oggetto worker.
    const worker = useRef(null);

    // Utilizziamo il hook `useEffect` per impostare il worker non appena il componente `App` viene montato.
    useEffect(() => {
        if (!worker.current) {
            // Crea il worker se non esiste ancora.
            worker.current = new Worker(new URL('./worker.js', import.meta.url), {
                type: 'module'
            });
        }

        // Crea una funzione di callback per i messaggi dal thread del worker.
        const onMessageReceived = (e) => { /* Vedi codice */ };

        // Collega la funzione di callback come listener degli eventi.
        worker.current.addEventListener('message', onMessageReceived);

        // Definisci una funzione di cleanup per quando il componente viene smontato.
        return () => worker.current.removeEventListener('message', onMessageReceived);
    });

    // ... resto del componente
}

Puoi verificare che tutto funzioni eseguendo il server di sviluppo (con npm run dev ), visitando il sito web locale (di solito http://localhost:5173/ ) e aprendo la console del browser. Dovresti vedere l’output del modello registrato nella console.

[{ label: "skateboard", score: 0.9980043172836304 }]

Woohoo! 🥳 Anche se il codice sopra è solo una piccola parte del prodotto finale , mostra quanto sia semplice il lato dell’apprendimento automatico! Il resto riguarda solo il renderlo bello ed aggiungere un po’ di logica di gioco.

3. Progettazione del gioco

In questa sezione, parlerò brevemente del processo di progettazione di un gioco. Come promemoria, è possibile trovare il codice sorgente completo del progetto su GitHub, quindi non andrò nel dettaglio sul codice stesso.

Approfittare delle prestazioni in tempo reale

Uno dei principali vantaggi della previsione in-browser è che possiamo effettuare previsioni in tempo reale (oltre 60 volte al secondo). Nel gioco originale Quick, Draw!, il modello fa una nuova previsione solo ogni paio di secondi. Potremmo fare lo stesso nel nostro gioco, ma così non sfrutteremmo le sue prestazioni in tempo reale! Quindi, ho deciso di ridisegnare il ciclo di gioco principale:

  • Al posto di sei round da 20 secondi (dove ogni round corrisponde a una nuova parola), la nostra versione chiede al giocatore di disegnare correttamente il maggior numero di schizzi possibile in 60 secondi (una richiesta alla volta).
  • Se incontri una parola che non riesci a disegnare, puoi saltarla (ma questo ti costerà 3 secondi del tempo rimanente).
  • Nel gioco originale, poiché il modello faceva una previsione ogni pochi secondi, poteva lentamente eliminare le etichette dalla lista finché alla fine indovinava correttamente. Nella nostra versione, invece, diminuiamo i punteggi del modello per le prime n etichette errate, con n che aumenta nel tempo mentre l’utente continua a disegnare.

Miglioramenti della qualità della vita

Il dataset originale contiene 345 classi diverse, e poiché il nostro modello è relativamente piccolo (~20MB), a volte non riesce a indovinare correttamente alcune delle classi. Per risolvere questo problema, abbiamo rimosso alcune parole che sono:

  • Troppo simili ad altre etichette (ad esempio, “fienile” vs. “casa”)
  • Troppo difficili da capire (ad esempio, “migrazione animale”)
  • Troppo difficili da disegnare con sufficiente dettaglio (ad esempio, “cervello”)
  • Ambigue (ad esempio, “pipistrello”)

Dopo il filtraggio, ci sono rimaste ancora oltre 300 classi diverse!

BONUS: Trovare il nome

Nello spirito dello sviluppo open-source, ho deciso di chiedere a Hugging Chat qualche idea di nome per il gioco… e inutile dire che non ha deluso!

Mi è piaciuta l’allitterazione di “Doodle Dash” (suggerimento n. 4), quindi ho deciso di adottare quello. Grazie Hugging Chat! 🤗


Spero che ti sia divertito a costruire questo gioco con me! Se hai domande o suggerimenti, puoi trovarmi su Twitter, GitHub o il 🤗 Hub. Inoltre, se vuoi migliorare il gioco (modalità di gioco? potenziamenti? animazioni? effetti sonori?), sentiti libero di fare il fork del progetto e inviare una pull request! Mi piacerebbe vedere cosa riesci a creare!

PS: Non dimenticare di partecipare all’Open Source AI Game Jam! Spero che questo post ti ispiri a costruire il tuo gioco web con Transformers.js! 😉 Ci vediamo al Game Jam! 🚀