[python] Semplice esercizio su TensorFlow e il riconoscimento delle immagini nel gioco del Tris (Machine Learning)

Vogliamo realizzare un semplice programma in Python che sfrutti il riconoscimento delle immagini mediante TensorFlow e machine learning.

Il programma consentirà all’utente di pescare il risultato di una partita a Tris, da una cartella di immagini come la seguente:

Selezionando una partita il programma ci dirà chi ritiene che sia il vincitore e mostrerà lo schema della partita che ha dedotto a partire dall’immagine che gli abbiamo passato, nella maniera seguente:

1. Configurazione iniziale e librerie utilizzate

Per affrontare l’esercizio in questione abbiamo bisogno di alcune librerie. Il progetto verrà sviluppato in Python 3.7.

Possiamo installare le opportune librerie utilizzando PIP, nel caso specifico avremo bisogno delle seguenti installazioni:

Creiamo un file elaboratore.py nel quale svilupperemo l’intero programma. All’inizio inseriamo le seguenti istruzioni di importazione:

Faccio notare che la prima istruzione from __future__ import absolute_import, division, print_function, unicode_literals deve trovarsi all’inizio e non è una vera e propria importazione. Si tratta di una configurazione sul funzionamento di Python, che importa “funzionalità future” dal modulo fittizio __future__. E’ un modo per rendere disponibili, nella versione attuale, funzionalità previste in versioni successive, ma che ancora non sono state ufficialmente implementate.

Nel nostro caso specifico questa importazione non è essenziale (stiamo già lavorando in Python 3.7 e la funzione print non è più uno statement, bensì una funzione appunto, ecc), ma la possiamo lasciare per rendere compatibile il programma anche con versioni precedenti.

Detto questo procediamo allo sviluppo del necessario.

2. Dati iniziali

Per sviluppare il gioco utilizzeremo i seguenti set di dati (qui l’allegato da cui scaricarli):

  1. Set delle immagini per l’addestramento del modello, ricaveremo le immagini da un’unica grande immagine originale chiamata training.jpg (questa è un’immagine contenente 144 disegni, metà sono O e l’altra metà sono X, disegnate a mano sul computer, ciascun elemento è grande 100×100 px, per un totale di 1200×1200 px, ovvero 12 righe x 12 colonne)

  2. Piccolo set di immagini di test nel file test.jpg (solita dimensione 100×100 px per ciascuna immagine, per un totale di 4×4 = 16 elementi)
  3. Archivio con 10 partite giocate a Tris di cui conosciamo il risultato e vogliamo farlo riconoscere al computer

3. Struttura del programma

Vogliamo creare 3 classi che si occuperanno di diversi aspetti del programma:

  1. ElaboraImmagini – con questa classe elaboreremo le immagini di test, di addestramento e delle singole partite. In tutti e tre i casi si tratta di suddividere un’immagine originale in immagini più piccole; nei primi due casi, quello di test e addestramento, salveremo le immagini in una cartella da cui leggerle successivamente, mentre nel terzo caso, quello delle immagini che compongono una partita, salveremo il risultato in un vettore
  2. CreaDBImmagini – con questa classe vogliamo elaborare le immagini di test e di addestramento salvate nelle rispettive cartelle, ottenendo dei vettori contenenti le immagini medesime e un elenco di output (o etichette) associati a ciascuna immagine
  3. GiocoTris – la vera e propria classe che si occuperà dello sviluppo del gioco, preparando il modello di apprendimento, salvandolo e valutando il punteggio su ciascuna partita che verrà passata dal giocatore

Infine metteremo tutto in un semplice ciclo while che permetterà all’utente di scegliere un’immagine dall’elenco di quelle disponibili e farla analizzare.

4. Elaboriamo le immagini di addestramento e test

Creiamo una classe nella maniera seguente (nel codice ho inserito il commento sui singoli passaggi):

Il metodo ritaglia(s, coor, i) accetta coordinate in (x,y) sull’immagine, le x rappresentano le “colonne”, mentre le y rappresentano le “righe”, nella maniera seguente:

La prima immagine verrà ritagliata con coordinate (0, 0, 100, 100), la seconda con (100, 0, 200, 100) ecc.

Possiamo subito elaborare le immagini di addestramento e di test aggiungendo le seguenti due righe di codice:

5. Preleviamo le immagini di addestramento e test, associandoci gli opportuni output

Adesso vogliamo creare la classe che ci consentirà di prelevare le immagini, inserendole in un vettore, ed associare a ciascuna immagine nel vettore l’opportuno output.

Ogni immagine può rappresentare o una O oppure un X, a queste due “etichette” vogliamo associare un valore numerico, che nel mio caso sarà 0 per la O e 1 per la X.

Il comportamento che ci aspettiamo sarà il seguente: train_img, train_desc = CreaDBImmagini(...).get()

train_img conterrà i valori RGB per ciascuna immagine, in una lista di immagini così codificate. Ciascuna immagine è una griglia di pixel, per esempio un’immagine di 3×3 px diventerebbe una matrice di questo tipo [[1,2,3],[4,5,6],[7,8,9]]. I pixel in realtà non sono numerati in questo modo, ma ciascun pixel ha un valore RGB da 0 a 255, per ciascun colore. Quindi se il primo pixel fosse completamente nero al posto dell’1 avremmo [0,0,0], una lista con 3 zeri. Se fosse completamente bianco avremmo [255,255,255], se fosse rosso avremmo [255,0,0] ecc. Questo significa che l’immagine del nostro esempio avrebbe una rappresentazione tridimensionale 3x3x3 diventando così per esempio: [[[0,0,0],[0,0,0],[0,0,0]],[[0,0,0],[255,255,255],[0,0,0]],[[0,0,0],[0,0,0],[0,0,0]]]. In questo caso il pixel centrale, quello che abbiamo numerato come 5, sarebbe bianco e gli altri tutti neri. Se avessimo 100 di queste immagini, avremmo un oggetto di dimensione 100x3x3x3. Ovvero una lista di 100 elementi, di cui ciascun elemento è questa lista, di liste di liste, rappresentante la singola immagine.

train_desc è invece una semplice lista monodimensionale, se avessimo 100 immagini questa sarebbe una lista di 100 elementi. Ogni elemento descrive l’immagine, nel nostro caso se l’immagine corrispondente in train_img fosse una O allora avremo uno 0, nel caso di una X avremo un 1. Sarà quindi una lista tipo [0,1,1,0,0,....]. In questo esempio vorrebbe dire che le immagini in train_img rappresentano in ordine: O, X, X, O, O, ecc.

Tutti gli altri commenti sono nel codice:

6. Sviluppiamo la classe di gioco

A questo punto sviluppiamo la classe di gioco integrando le precedenti classi.

Anche su questo facciamo un paio di appunti. Quando calcoliamo le previsioni con previsione = s.__modello.predict(gioco) otteniamo una lista di liste di probabilità. Ogni sub-lista contiene tante probabilità quanti sono i neuroni (o nodi) impostati nell’ultimo layer del modello. Nel nostro caso all’istruzione keras.layers.Dense(2, activation='softmax') abbiamo impostato 2 neuroni, quindi il vettore di previsione conterrà un elenco di liste del tipo [p1, p2], dove p1 e p2 sono le probabilità dell’output 0 e dell’output 1 (che ricordiamoci corrispondono a O e a X, rispettivamente). Il risultato di previsione sarà qualcosa del tipo [[1. 0.],[0.51 0.49], [0. 1.]]. Nel caso specifico vorrebbe dire che secondo il ML la prima immagina è una O, poi uno spazio vuoto, poi una X. Per quanto riguarda l’uso di softmax e relu fare riferimento a questa pagina.

Per i dettagli sull’optimizer Adam fare riferimento a questa pagina.

Il calcolo della griglia di vittoria è molto semplice, perché abbiamo un vettore lineare e non una matrice (o griglia vera e propria). In questo caso una partita come quella del gioco_1.jpg sarà rappresentata da una lista di 0, 1 e 2, in questo modo (dove 0 = vuoto, 1 = O, 2 = X): [2, 2, 1, 2, 1, 0, 2, 1, 1]

Quando il primo valore è diverso da 0, quindi da vuoto, basta controllare che a distanza di 3 siano uguali, come qui: [2, 2, 1, 2, 1, 0, 2, 1, 1]

In questo modo stiamo controllando le colonne.

Per controllare le righe basta verificare le triplette, così: [2, 2, 1, 2, 1, 0, 2, 1, 1]

7. Chiudiamo il programma

Infine costruiamo il ciclo while per poter far usare il programma:

Per completezza riporto di seguito l’intero codice di programmazione:

 

Vedi articolo

[python] Calcolare le soluzioni di un sistema lineare di primo grado utilizzando il metodo di Cramer (senza librerie aggiuntive)

Obiettivo: Vogliamo calcolare, utilizzando Python, tutte le soluzioni per un sistema lineare di primo grado utilizzando le matrici e il metodo di Cramer. Per farlo non utilizzeremo librerie aggiuntive, se non quanto disponibile nativamente dentro Python.

Ovviamente lo scopo dell’esercizio è l’utilizzo di Python, visto che si potrebbe risolvere agilmente con librerie come SciPy e NumPy.

Supponiamo di avere un sistema lineare come quello seguente (l’ho scopiazzato spudoratamente da YouMath, dove c’è anche un’esaustiva spiegazione sul metodo di Cramer):

\begin{cases}2x+y+z=1 \\ 4x-y+z=-5 \\ -x+y+2z=5 \end{cases}

La matrice associata ai coefficienti ed il relativo determinante sarebbe questo:

\mbox{D}=\left| \begin{matrix} 2 & 1 & 1 \\ 4 & -1 & 1 \\ -1 & 1 & 2 \end{matrix}\right|=-12

Mentre la matrice dei termini noti sarebbe:

\mbox{A}=\left| \begin{matrix} 1 \\ -5 \\ 5 \end{matrix}\right|

Giusto per curiosità la soluzione immediata con NumPy sarebbe la seguente:

Il risultato nel caso specifico darà: [-1. 2. 1.]

Noi vogliamo costruire qualcosa di simile in Python 2.7.

Cominciamo creando la base della nostra classe perché possa accettare i medesimi argomenti:

Fatto questo cominciamo a creare i metodi di cui avremo bisogno. Il metodo di Creamer con il calcolo del determinante implica, per matrici di dimensione superiore a 2×2, che si debba poter ridurre la matrice in sottomatrici.

Giusto per chiarezza, nel caso del determinante D della suddetta matrice, il calcolo si svolgerebbe in questo modo:

\mbox{D}=\left| \begin{matrix} 2 & 1 & 1 \\ 4 & -1 & 1 \\ -1 & 1 & 2 \end{matrix}\right|= 2 * \left| \begin{matrix} -1 & 1 \\ 1 & 2 \end{matrix}\right| - 1 * \left| \begin{matrix} 4 & 1 \\ -1 & 2 \end{matrix}\right| + 1 * \left| \begin{matrix} 4 & -1 \\ -1 & 1\end{matrix}\right| =\newline = 2*(-1*2-1*1)-1*(4*2-(-1)*1)+1*(4*1-(-1)*(-1)) = \newline = 2*(-3)-1*(9)+1*(3)=\newline =-6-9+3=-12

Ogni sottomatrice prende tutti i valori della matrice corrente eccetto quelli nella riga e nella colonna della posizione attuale. Scriviamo quindi la nostra funzione nella maniera seguente:

Per calcolare il determinante vogliamo fare due operazioni distinte: il calcolo per matrici 2×2 e il calcolo per riduzione delle matrici di dimensione superiore. Iniziamo scrivendo l’operazione per quelle 2×2:

Per calcolare gli altri determinanti procederemo invece con:

In particolare con (1-2*(c%2)) calcoliamo il segno per ogni elemento della prima riga. In particolare ricordiamo che gli elementi saranno calcolati in ordine da 0, 1, 2, 3, 4 ecc. Se calcoliamo il resto per ognuno di essi avremo 0, 1, 0, 1, 0 ecc. Eseguendo la suddetta operazione avremo quindi 1-0, 1-2, 1-0, 1-2, 1-0 ecc. Questo ci darà i segni come 1, -1, 1, -1, 1 ecc.

A questo punto ci manca solo un metodo che ci permetta di creare nuove matrici per calcolare i vari determinanti per x, y, z ecc. Ricordiamoci che i vari risultati saranno dati da:

x=\frac{\mbox{D}_x}{\mbox{D}}=\frac{12}{-12}=-1\\ \\ \\ y=\frac{\mbox{D}_y}{\mbox{D}}=\frac{-24}{-12}=2\\ \\ \\ z=\frac{\mbox{D}_z}{\mbox{D}}=\frac{-12}{-12}=1

Dobbiamo fare attenzione ad un dettaglio importante. In Python le liste vengono passate per riferimento, questo vuol dire che modificando ogni lista “copiata” si modifica in realtà la lista originale all’indirizzo di memoria. Per copiare una matrice multidimensionale è necessario quindi copiare i singoli valori nella nuova matrice, nella maniera seguente darebbe errore:

Quindi procediamo in questo modo:

La nostra funzione di sostituzione sostituirà quindi i termini noti nella colonna preselezionata.

A questo punto possiamo scrivere il metodo che calcolerà la soluzione, alla maniera seguente:

Infine implementiamo il metodo solve aggiungendo un paio di chiamate di errore, nel caso in cui la matrice non sia adatta.

Nel suo complesso la classe che avremo creato sarà la seguente:

Per utilizzarla sarà sufficiente scrivere:

Se abbiamo fatto tutto correttamente il risultato che otterremo sarà: [-1.0, 2.0, 1.0]

Vedi articolo

[python] Radiazione di Hawking e tempo che impiega un buco nero ad evaporare

In memoria di Stephen Hawking propongo un modesto esercizio in Python per calcolare il tempo di evaporazione di un buco nero, secondo la radiazione di Hawking.

In questo caso utilizzeremo le librerie scipy e numpy di Python.

Il mio esempio sarà fatto sotto Windows. Anzitutto installiamo quindi le librerie necessarie utilizzando pip, nella PowerShell digitiamo:

Una volta fatto avviamo un nuovo progetto in python e andiamo anzitutto a calcolare la suddetta formula. L’equazione di evaporazione di un buco nero, che si trovasse in una condizione ideale (ovvero nessuna aggiunta di energia, quindi ipoteticamente in un universo completamente vuoto e senza radiazione di fondo) è la seguente:

t_{ev} = \frac{5120 \pi G^2 M_0^3}{\hbar c^4}

Per scriverla in Python digitiamo:

Faccio notare che da scipy importiamo le costanti fisiche e matematiche che possiamo utilizzare secondo la seguente tabella.

M0 è l’ipotetica massa iniziale di un buco nero di massa solare, nello specifico di 1,98892 * 10^{30} kg .

Il risultato dovrebbe essere:

Il risultato è in secondi, possiamo convertirlo in miliardi di anni digitando:

Il risultato è qualcosa come 2 * 10^{58} miliardi di anni, molto più dell’attuale vita dell’universo stimata attorno ai 13 miliardi di anni.

Adesso proviamo ad inserire l’equazione in un grafico, dove vogliamo confrontare vari tipi di buco nero in base alla massa ed il tempo di evaporazione.

Per farlo anzitutto convertiamo la nostra precedente equazione in una funzione digitando:

Ricordiamoci che il risultato della funzione è in secondi. A questo punto prepariamo i valori del nostro asse x, che saranno le masse di diversi buchi neri. Prendiamo 100 valori tra 0 e 1030.

Sull’asse Y calcoliamo i corrispondenti valori:

Adesso impacchettiamo tutto in un grafico generato con matplotlib.

Il risultato sarà qualcosa di simile a questo:

Vedi articolo