Tensorflow.js: Machine Learning per Web Developer8 min read

Sommario: Tensorflow.js è una libreria Python open source per la computazione machine learning nel browser. Scopriamo come settarlo e usarlo con alcune demo. In questo articolo: Street Fighter, ragazze in webcam, ricci.
Qualche settimana fa sono capitato su un articolo che spiegava come giocare a Street Fighter con il proprio corpo (link). Facile penserete, vai al parchetto e fatti picchiare da qualcuno. A tutti noi piaceva pestare bottoni a caso nelle sale arcade (no, non sono così vecchio, l’ho giocato su pc) ma oggi, con VR e compagnia l’esperienza di gioco si è fatta così immersiva che provare una cosa simile non poteva proprio sfuggirmi.
Leggendo l’articolo ho scoperto che per realizzare questo, l’autore del progetto ha usato una libreria chiamata Tensorflow.js. Machine Learning to the win! E ho pensato: il Machine Learning può far questo? Può farmi buttare ore della mia vita a menare pugni per aria come un invasato? (Street Fighter è difficile, lo sapete). Così mi son detto, questo è il futuro. Mascherando il mio interesse nel gioco come nuova skill da sfruttare nel mio lavoro, mi sono deciso a scoprire cosa sia Tensorflow.js. E a condividerlo con voi.

Cos’è Tensorflow.js

Tensorflow.js è una libreria open source scritta in Python e realizzata da Google che consente di operare il machine learning e numerazioni computazionali su larga scala. In generale, consente di acquisire dati, fare il training di modelli, operare predictions, e rifinire risultati futuri.
Link al sito: Tensorflow.js

Machine Learning spiegato in breve

Il Machine Learning (ML) è l’abilità di un computer di apprendere dati senza essere programmato. Prenderà in input dei dati e vi individuerà pattern per operare predictions senza che noi gli indicassimo come farlo.

Concetti base

Data-set
Un data-set è una collezione di dati su un determinato dominio di interesse. Possiamo avere centinaia di data-set su centinaia di diversi argomenti. Per esempio: possiamo avere un data-set con tutte le specie canine. O di gatti! I data-set sono il pane del Machine Learning. Senza di essi un algoritmo ML sarebbe incapace di operare predictions.
Labels e features
Una label è un sistema di classificazione di un dato passato a un algoritmo ML. In altre parole definiamo come classificare / organizzare / nominare un dato quando lo passiamo a un algoritmo ML. Per esempio, immaginiamo di avere un data-set di animali, e classifichiamo i dati in base all’animale stesso: quindi le label saranno “cane”, “gatto”, “serpente” e così via.
Le features invece sono caratteristiche di un dato. Sempre con l’esempio di animali, avremo che un dato classificato come “cane” avrà feature “abbaia” o “intelligente”.
Features e labels sono essenziali nella creazione di un modello in quanto grazie a esse l’algoritmo sarà in grado di individuare correlazioni tra i dati.
Modello
Un modello è l’output dato da un algoritmo ML trainato con un data-set. In termini poveri, è una funzione che restituisce un risultato in base a un input fornito.
Reti neurali
Le reti neurali sono set di algoritmi ML che cercano di imitare il funzionamento del cervello umano attraverso l’uso di neuroni artificiali. In questo articolo non approfondiremo questo aspetto. Non ancora, almeno!

Come usare Tensorflow.js

Ci sono diversi modi di usare questa libreria:
  1. usare un modello già trainato
  2. trasferire conoscenza al modello esistente
  3. creare un modello da zero
In questo articolo esamineremo i primi due. Con il terzo ci divertiremo più avanti.

Usare Tensorflow.js con un modello pre-trained

Come accennato sopra, esistono in rete moltissimi data-set pieni zeppi di ottimi dati. E qualcuno ha avuto la grande pazienza di costruire un modello per noi basandosi su questi dati. Questo ci consente di fare sperimentazioni fast and easy!
Vediamo ora come usare Tensorflow.js per riconoscere immagini di ricci (o porcospini, che dir si voglia). (Ho impiegato notevoli sforzi per cercare nella lista animali strani, ma non sono giunto a risultati soddisfacenti. Inoltre ho un riccio, per cui mi sembrava dovuto).
Per lo scopo useremo un modello per riconoscimento di immagini abbastanza importante, chiamato MobileNetMettiamo giù un pò di codice.
<html lang="it">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Hedgehog spotter</title>
    <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@1.0.1"> </script>
    <script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/mobilenet@1.0.0"> </script>
  </head>
  <body>
    <img id="image" alt="Ilja the hedgehog" src="ilja.jpeg"/>

    <script>
      const img = document.getElementById('image');

      const predictImage = async () => {
        console.log("Model loading...");
        const model = await mobilenet.load();
        console.log("Model is loaded!")

        const predictions = await model.classify(img);
        console.log('Predictions: ', predictions);
      }
      predictImage();
    </script>
  </body>
</html>
 
Iniziamo includendo Tensorflow.js e MobileNet nella
head
del nostro file.
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@1.0.1"> </script>
<script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/mobilenet@1.0.0"> </script>
 
Nel
body
, inseriamo l’immagine che vogliamo sottoporre a riconoscimento, assegnando un
id
che useremo come riferimento.
<img id="image" alt="Ilja the hedgehog" src="ilja.jpeg"/>
 
Nel tag
script
abbiamo il cuore del nostro algoritmo ML. Questo codice fa 3 cose:
  1. carica il modello (riga 3)
  2. classifica l’immagine ricavata dall’
    id
    specificato (riga 5)
  3. restituisce un array di 3 predictions ordinate in base al probability score (cioè il grado di probabilità di certezza del dato)
const predictImage = async () => {
 console.log("Model loading...");
 const model = await mobilenet.load();
 console.log("Model is loaded!")
 const predictions = await model.classify(img);
 console.log('Predictions: ', predictions);
}
predictImage();
 
Usare Tensorflow.js e MobileNet per classificare immagini.
Usare Tensorflow.js e MobileNet per classificare immagini.
La predizione funziona! (nell’esempio ho limitato il display delle predictions al primo risultato).

Trasferire conoscenza a Tensorflow.js

Tensorflow.js ci consente di trasferire dei dati su un modello già esistente. Questo vuol dire che possiamo aggiungere nostri dati al modello espandendolo ulteriormente.
Perché potrebbe servirci? Supponiamo di voler rendere ancora più preciso un modello esistente. O trainarlo a riconoscere nuovi pattern.
La demo che costruiremo userà la webcam per rilevare il nostro head tilting. Mi spiego meglio: traineremo il modello a riconoscere la direzione in cui punta la nostra testa, se a destra o sinistra. Per questa demo, data la mia riluttanza patologica ad apparire in pubblico, userò le captures della bravissima Charlie Gerard. Thank you Charlie!
Trasferire il learning a Tensorflow.js usando la webcam. Copyright di Charlie Gerard.
Trasferire il learning a Tensorflow.js usando la webcam. Copyright di Charlie Gerard.
Includiamo come sempre Tensorflow.js e MobileNet, ma stavolta aggiungiamo anche KNN Classifier.
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs"></script>
<script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/mobilenet"></script>
<script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/knn-classifier"></script>
 
KNN Classifier ha il compito di unire il custom data che andiamo a fornire all’algoritmo con il modello esistente.
<video autoplay id="webcam" width="227" height="227"></video>
 
Rimpiazziamo quindi il tag
image
con
video
, al fine di poter usare la webcam per questo esperimento.
Impostiamo dei tasti che ci consentiranno di catalogare i dati in ingresso.
<section>
  <button class="button">Sinistra</button>
  <button class="button">Destra</button>
  <button class="test-predictions">Test</button>
</section>
 
E adesso, tempo di Javascript! Settiamo alcune variabili.
// Numero delle classi da classificare
const NUM_CLASSES = 2;
// Labels sinistra e destra
const classes = ["Left", "Right"];
// Impostiamo grandezza della webcam. Deve essere 227 px
const IMAGE_SIZE = 227;
// Valore K per KNN
const TOPK = 10;

const video = document.getElementById("webcam");
 
Tempo dello spiegone.
  • Il numero delle classi è pari a 2: ricordiamo che stiamo trainando il modello a riconoscere 2 posizioni (destra e sinistra) (riga 2).
  • Analogamente le classi saranno chiamate in questo modo (riga 4)
  • L’immagine della webcam settata a 227 px dipende dalle dimensioni delle immagini già usate per trainare il modello su MobileNet. Potremmo naturalmente usare valori differenti, ma questo implicherebbe dover processare le immagini per renderle della misura richiesta. Per semplicità non lo faremo in questo articolo.
  • Infine, settiamo il valore di K per KNN pari a 10 (riga 8). Questo settaggio corrisponde al numero di istanze che prendiamo in considerazione per classificare la classe. In altre parole, il numero di campioni. In questo caso, usando il valore 10 stiamo dicendo al nostro modello di considerare i 10 vicini più prossimi per classificare il nuovo input.
Otteniamo ora l’elemento
video
. Iniziamo caricando i moduli:
async load() {
    const knn = knnClassifier.create();
    const mobilenetModule = await mobilenet.load();
    console.log("Model loaded");
}
 
E adesso, otteniamo lo stream dalla webcam:
navigator.mediaDevices
  .getUserMedia({ video: true, audio: false })
  .then(stream => {
    video.srcObject = stream;
    video.width = IMAGE_SIZE;
    video.height = IMAGE_SIZE;
  });
 
Quindi impostiamo i tasti precedentemente creati per “registrare” i movimenti e trainare così il modello:
setupButtonEvents() {
    for (let i = 0; i < NUM_CLASSES; i++) {
      let button = document.getElementsByClassName("button")[i];

      button.onmousedown = () => {
        this.training = i;
        this.recordSamples = true;
      };
      button.onmouseup = () => (this.training = -1);
    }
  }
 
Ora la parte difficile. Impostiamo una funzione per prendere in input le immagini, formattarle e darle in pasto a MobileNet:
// Ottieni le immagini dal tag video
const image = tf.browser.fromPixels(video);

let logits;
const infer = () => this.mobilenetModule.infer(image, "conv_preds");

// Traina la classe se uno dei bottoni è stato premuto
if (this.training != -1) {
  logits = infer();

  // Aggiungi l'immagine al classifier
  this.knn.addExample(logits, this.training);
}
 
La parte difficile è andata. Bravo! Ora, una volta ottenute alcune immagini, possiamo testare le predictions:
logits = infer();
const res = await this.knn.predictClass(logits, TOPK);
const prediction = classes[res.classIndex];
 
E infine, disattiviamo la webcam quando non serve più (per gli amici privacy concerned):
// Dispose image when done
image.dispose();
if (logits != null) {
  logits.dispose();
}
 
Tempo di test! Provate voi stessi dal link della buona Charlie: demo

Links e risorse

Esempi

TwitterFacebookLinkedIn
FollowIns.Lin.
...

This is a unique website which will require a more modern browser to work!

Please upgrade today!