«[Figlio dell'uomo] Porgi l'orecchio e ascolta le parole di KGB
e applica la tua mente alla SUA istruzione
» Pv. 22,17

Qui si straparla di vari argomenti:
1. Il genere dei pezzi è segnalato da varie immagini, vedi Legenda
2. Per contattarmi e istruzioni per i nuovi lettori (occasionali e non) qui
3. L'ultimo corto è questo
4. Molti articoli di questo blog fanno riferimento a definizioni e concetti che ho enunciato nella mia Epitome gratuitamente scaricabile QUI. Tali riferimenti sono identificati da una “E” fra parentesi quadre e uno o più capitoli. Per esempio: ([E] 5.1 e 5.4)

lunedì 10 dicembre 2018

Rete al massimo

In Aggiornamento pitonesco avevo accennato a un mio primo progettino: la teoria infatti mi piace fino a un certo punto e il metterla in pratica è la verifica di averla compresa.
Ovviamente volevo iniziare con qualcosa di facile e così mi è venuto in mente di realizzare una rete neurale per risolvere il seguente problema: identificare il massimo fra dieci numeri in ingresso.

Sembrerebbe una banalità ma in realtà non sono neppure sicuro che sia risolvibile.
Da una parte infatti nell’esempio della rete neurale sui vini portoghesi gli ingressi erano i valori di acidità, densità, percentuale di alcool, percentuali di solfati e così via. Mi pare normale che in questi casi la rete neurale, attraverso i vari esempi, “comprenda” le relazioni fra le varie misure perché queste effettivamente esistono (*1).
Invece, nel mio caso, io genero 10 numeri casuali compresi fra 0 e 1 e non c’è alcuna relazione fra un numero e gli altri: il conoscere i primi 9 numeri ad esempio non ci dice niente sul decimo. Come può quindi la rete neurale capirci qualcosa?

D’altra parte però, in un esempio del libro Deep Learning Cookbook, la rete neurale “imparava” a riconoscere le relazioni fra pellicole e marcatori (come il regista, l’anno di produzione, gli attori, etc). La cosa interessante e che pellicole e marcatori erano identificati da un singolo numero intero (da 0 fino, rispettivamente, al numero di film e marcatori) ma questo veniva “tradotto” in un vettore di 50 dimensioni tramite degli appositi strati di Keras chiamati Embedding.
In altre parole “Guerre Stellari”, che magari aveva il numero identificativo 2303, veniva tradotto in un vettore di 50 numeri. Il film “Leon”, col numero 2304, veniva convertito in un vettore di altri 50 numeri. Che relazione c’è fra identificativi e vettori? Non lo so ma non è importante visto che comunque, in partenza, non c’è alcuna relazione fra i codici identificativi!
Eppure la rete neurale era in grado di imparare a riconoscere pellicole e marcatori da questi numeri all’apparenza casuali…
Quindi perché lo stesso non potrebbe accadere col mio problema di identificazione del maggiore fra dieci numeri?

Insomma, con le mie scarse conoscenze attuali, non sono in grado di prevedere se la mia rete neurale sarà in grado di risolvere questo mio problemino. Così ho scritto il seguente codice Phyton:

1:  import numpy as np  
2:  np.random.seed(330)  
3:    
4:  def batchifier():  
5:    np.random.seed(30)  
6:    for i in range(2000):  
7:      x=np.random.random((1, 10))  
8:      x.sort(1)  
9:      y=np.zeros((1,10))  
10:    
11:      z=np.append(x,y,0)  
12:      z[1,9]=1  
13:      z=np.rot90(z,1,(0,1))  
14:      np.random.shuffle(z)  
15:      z=np.rot90(z,1,(0,1))  
16:    
17:      X_Test=z.compress([True,False],axis=0)  
18:      Y_Test=z.compress([False, True],axis=0)  
19:      yield (X_Test,Y_Test)  
20:    
21:  from keras.models import Sequential  
22:  from keras.layers import Dense, Activation  
23:    
24:  model = Sequential()  
25:  #model.add(Flatten())  
26:  model.add(Dense(20, input_shape=(10,), use_bias=True))  
27:  model.add(Activation('relu'))  
28:    
29:  model.add(Dense(10))  
30:  model.add(Activation('softmax'))  
31:    
32:  model.compile(optimizer='rmsprop',  
33:         loss='categorical_crossentropy',  
34:         metrics=['accuracy'])  
35:    
36:  model.summary()  
37:    
38:  model.fit_generator(  
39:    batchifier(),  
40:    epochs=10,  
41:    steps_per_epoch=200, #positive_samples_per_batch,  
42:    verbose=1  
43:    )  
44:    
45:  def sample():  
46:    x=np.random.random((1, 10))  
47:    return x  
48:    
49:  def somma(l):  
50:    tot=0  
51:    for x in l[0,]:  
52:      tot+=x  
53:    return tot  
54:    
55:  print("Esempi:")  
56:  for i in range(10):  
57:    s=sample()  
58:    print(s)  
59:    p=model.predict(s,batch_size=1,verbose=0)  
60:    print(p)  
61:    print(somma(p))  

Il codice è piuttosto semplice e, probabilmente, apparirà orrorifico a che conosce un po’ di Python: soprattutto il generatore batchifier() è scritto in maniera artificiosa. Chi conoscesse il Python probabilmente sarebbe in grado di riscrivere lo stesso codice in un paio di linee…
Comunque creo un vettore di 10 numeri casuali, lo ordino, poi vi appendo una secondo vettore di zeri (ottenendo una matrice di dimensione 2x10), cambio il valore dell’ultimo 0 in un 1, la giro di 90 gradi (ho trovato solo una funzione che mescola le righe e non le colonne!), la mescolo, la rigiro di 90 gradi e ne estraggo le due righe.
X_Test conterrà i 10 numeri casuali e Y_Test conterrà 9 zeri e un 1 in corrispondenza del numero più grande di X_Test…

Poi creo il modello vero e proprio della mia rete neurale usando Keras.
Non ci ho riflettuto molto: non mi interessa che sia efficiente, voglio solo qualcosa che funzioni e che risponda al mio dubbio se una rete neurale può o meno imparare a individuare il valore massimo fra 10 numeri…

In pratica creo una rete neurale a due strati. Per la precisione: 10 ingressi connessi a 20 neuroni (primo strato) e 10 neuroni di uscita (secondo strato). Il secondo strato ha come funzione di attivazione la “softmax” che dovrebbe dare la probabilità che il numero rappresentato da ogni uscita sia il massimo: ovvero 1 per il massimo e 0 per gli altri.
Onestamente gli altri parametri li ho inseriti un po’ a casaccio: in effetti adesso ho un forte dubbio che la funzione “loss” (misura l’errore fra l’uscita della rete e il valore previsto) non sia adatta… vabbè, cambiarla sarà uno degli esperimenti che dovrò fare…
Idem per l’ottimizzatore (ovvero il metodo usato per calcolare i nuovi pesi della rete a ogni iterazione)…

Per allenare la rete uso il metodo fit_generator perché pensavo fosse più adatto al mio generatore ma in realtà ho socraticamente capito solo di non aver capito come funziona! In particolare ho dubbi sugli oggetti Generator in Pyhton e sui parametri di tale metodo, soprattutto su steps_per_epoch (v. fit_generator()). Poi, a martellate, l’ho fatto funzionare…

Comunque, dopo aver corretto una miriade di problemi tecnici, alla prima esecuzione (funzionante!) ho ottenuto, con steps_per_epoch=20, la seguente uscita durante l’addestramento:
 _________________________________________________________________  
 Layer (type)         Output Shape       Param #    
 =================================================================  
 dense_47 (Dense)       (None, 20)        220      
 _________________________________________________________________  
 activation_47 (Activation)  (None, 20)        0       
 _________________________________________________________________  
 dense_48 (Dense)       (None, 10)        210      
 _________________________________________________________________  
 activation_48 (Activation)  (None, 10)        0       
 =================================================================  
 Total params: 430  
 Trainable params: 430  
 Non-trainable params: 0  
 _________________________________________________________________  
 Epoch 1/10  
 20/20 [==============================] - 1s 35ms/step - loss: 11.4159 - acc: 0.2500  
 Epoch 2/10  
 20/20 [==============================] - 0s 1ms/step - loss: 10.7166 - acc: 0.1500  
 Epoch 3/10  
 20/20 [==============================] - 0s 1ms/step - loss: 11.6816 - acc: 0.3500  
 Epoch 4/10  
 20/20 [==============================] - 0s 2ms/step - loss: 11.5668 - acc: 0.3000  
 Epoch 5/10  
 20/20 [==============================] - 0s 1ms/step - loss: 11.4079 - acc: 0.4000  
 Epoch 6/10  
 20/20 [==============================] - 0s 2ms/step - loss: 12.3548 - acc: 0.3500  
 Epoch 7/10  
 20/20 [==============================] - 0s 1ms/step - loss: 12.0055 - acc: 0.4500  
 Epoch 8/10  
 20/20 [==============================] - 0s 1ms/step - loss: 12.2185 - acc: 0.3500  
 Epoch 9/10  
 20/20 [==============================] - 0s 1ms/step - loss: 11.2393 - acc: 0.5500  
 Epoch 10/10  
 20/20 [==============================] - 0s 1ms/step - loss: 11.5122 - acc: 0.3500  
   
Come si può vedere l’accuratezza (*2) non sembra assolutamente convergere….
Poi però ho aumentato steps_per_epoch a 200 ottenendo:
 Epoch 1/10  
 200/200 [==============================] - 1s 6ms/step - loss: 11.6119 - acc: 0.3500   
 Epoch 2/10  
 200/200 [==============================] - 0s 1ms/step - loss: 11.4142 - acc: 0.5750  
 Epoch 3/10  
 200/200 [==============================] - 0s 1ms/step - loss: 11.5151 - acc: 0.7900  
 Epoch 4/10  
 200/200 [==============================] - 0s 1ms/step - loss: 11.3502 - acc: 0.8550  
 Epoch 5/10  
 200/200 [==============================] - 0s 1ms/step - loss: 11.6687 - acc: 0.9950  
 Epoch 6/10  
 200/200 [==============================] - 0s 1ms/step - loss: 11.5078 - acc: 1.0000  
 Epoch 7/10  
 200/200 [==============================] - 0s 1ms/step - loss: 11.2786 - acc: 1.0000  
 Epoch 8/10  
 200/200 [==============================] - 0s 1ms/step - loss: 11.0907 - acc: 1.0000  
 Epoch 9/10  
 200/200 [==============================] - 0s 1ms/step - loss: 11.3907 - acc: 1.0000  
 Epoch 10/10  
 200/200 [==============================] - 0s 1ms/step - loss: 11.2664 - acc: 1.0000  
   
Qui dalla 6° epoca in poi l’accuratezza risultava massima!

Allora ho provato a fare qualche verifica aggiungendo l’ultima parte di codice.
Una nuova funzione sample() per creare un vettore di 10 numeri casuali che ho confrontato, tramite dei print() col predict() basato sul modello addestrato.
Ah! ho anche creato una funzione somma() per verificare che softmax funzionasse come previsto (ovvero che la somma di tutte le percentuali corrispondesse a 1).
Così ho ottenuto:
 Esempi:  
 [[0.00165925 0.54706793 0.03279361 0.43974022 0.42073148 0.28536211 0.06292913 0.79490123 0.15313805 0.60390858]]  
 [[0.09731148 0.10597415 0.06355619 0.11758436 0.08850987 0.13482948 0.09137243 0.10004369 0.09401508 0.10680328]]  
 1.0000000149011612  
 [[0.30016505 0.50204183 0.76092403 0.62494041 0.512936  0.28072881 0.2103085 0.1174437 0.27243399 0.953455 ]]  
 [[0.14268513 0.08294746 0.07771755 0.13093427 0.10235667 0.10485999 0.09181388 0.07278178 0.09750422 0.09639902]]  
 0.9999999701976776  
 [[0.21122699 0.28753969 0.89949822 0.35109501 0.29346201 0.95339557 0.49435746 0.99354878 0.01448176 0.84808755]]  
 [[0.08441722 0.07368112 0.06456804 0.08632565 0.1038466 0.19162604 0.07344794 0.14045519 0.09820914 0.08342304]]  
 0.9999999850988388  
 [[0.17723921 0.04884328 0.29435293 0.78523847 0.90869309 0.61865404 0.21038514 0.23809492 0.44592849 0.90516956]]  
 [[0.10775398 0.07558212 0.04765695 0.12270646 0.12226721 0.13314444 0.09520464 0.09438299 0.07767287 0.1236283 ]]  
 0.9999999478459358  
 [[0.89644952 0.0573787 0.64280447 0.56323963 0.01383937 0.20340611 0.63202377 0.01243697 0.33693535 0.92840944]]  
 [[0.20072532 0.06654133 0.06988712 0.11413237 0.08207329 0.10415517 0.08122559 0.061967  0.09125605 0.12803675]]  
 0.9999999813735485  
 [[0.16227171 0.37088684 0.51723029 0.5156923 0.47168066 0.72516925 0.02657806 0.30200434 0.33494513 0.15693052]]  
 [[0.09709164 0.08828155 0.07931186 0.10216423 0.1105952 0.13434379 0.09474652 0.10160992 0.1036953 0.08815996]]  
 0.999999962747097  
 [[0.38116618 0.18740264 0.86084261 0.67028089 0.36506087 0.36466077 0.6184897 0.19350823 0.31067974 0.0678281 ]]  
 [[0.10927812 0.07878433 0.10044078 0.11539444 0.10105398 0.11936115 0.11325683 0.08424763 0.10736588 0.07081694]]  
 1.0000000670552254  
 [[0.08857103 0.37228275 0.71509346 0.24248225 0.90751504 0.12114918 0.68641394 0.15519364 0.90995662 0.7746463 ]]  
 [[0.13223796 0.07232565 0.06962329 0.09257585 0.08711651 0.12275507 0.10142472 0.07036604 0.14854519 0.10302974]]  
 1.0000000149011612  
 [[0.48634901 0.50588741 0.31723363 0.6749636 0.32224859 0.6793131 0.18933995 0.25533336 0.82270563 0.1612204 ]]  
 [[0.10887279 0.08330661 0.05775265 0.10981435 0.08519744 0.13596174 0.10652026 0.085779  0.12599134 0.10080389]]  
 1.0000000670552254  
 [[0.37234676 0.25302492 0.08980321 0.57312637 0.9967274 0.41463969 0.28328975 0.84090096 0.99757804 0.26696341]]  
 [[0.11740944 0.07081304 0.04454628 0.10406217 0.08472245 0.12506601 0.10900114 0.10168419 0.12784785 0.11484744]]  
 1.0000000223517418  
   
Beh, almeno softmax funziona! Ma i valori previsti sembrano casuali rispetto agli ingressi forniti (avrei dovuto ottenere valori molto prossimi a 0 tranne che un 1 in corrispondenza del numero più grande). Evidentemente ho ottenuto dell’overfitting anche se non mi è chiaro su cosa: evidentemente la rete non si è addestrata su tutti i 2000 campioni prodotti da batchfier() ma su un suo sottoinsieme anche se non mi è chiaro quale…

Conclusione: adesso che ho una base funzionante devo fare altri esperimenti e: 1. capire come funziona fit_generator(); 2. provare ad aggiungere qualche strato intermedio; 3. cambiare la funzione di loss e l’ottimizzatore; 4. sicuramente mi verranno molte altre idee lavorandoci...

Nota (*1): fra le varie esperienze proposte dal minicorso c’era anche la visualizzazione di della correlazione fra le diverse misure…
Nota (*2): la loss non so invece come interpretarla, probabilmente finito di scrivere questo pezzo, la cambierò con una mse (l’errore quadratico medio)...

domenica 9 dicembre 2018

Dal 1995 (6/??)

Da qualche tempo, come si può facilmente constatare osservando il numero di nuovi pezzi scritti, sto trascurando questo mio ghiribizzo.
Oltretutto avrei da scrivere anche dei pezzi “facili” per queste evenienze: quelli basati sul diario del 1995 oppure quelli sugli articoli meritevoli di marcatore “Wow” (che però, in effetti, mi prendono molto tempo anche se poi mi diverto a scriverli).

Negli ultimi mesi ho trascurato anche la chitarra mentre la stesura della versione 1.1.1 dell’Epitome non procede. Come al solito non saprei dire perché: sono cose che succedono e basta; io seguo il mio umore perché non mi sembra abbia senso fare diversamente…
Beh, negli ultimi giorni sto anche aggeggiando parecchio con Python e reti neurali: qualche giorno fa volevo pubblicare la mia prima “creazione”, un esperimento di rete neurale non privo di interesse, ma al momento non mi funziona (*1)!

Comunque, siccome mi sento in colpa, oggi trascriverò qualche altro pezzetto dalla mia agenda del 1995. Poca roba però perché sono di fretta…

6-10 febbraio
[Come spiegato in Dal 1995 (5/??) riporto una sintesi dei miei incubi infantili. Siccome ci sono dei disegnini dovrei usare lo scannerizzatore ma ovviamente non ne ho voglia. Li rimando quindi a un’altra occasione.]

11 febbraio
SCCF (Strane Cose Che Faccio): Trovo spesso utile per rilassarmi e dormire cambiare posizione e mettendo la testa al posto dei piedi, oppure mettermi per terra sul tappeto.

[Ok, forse questo SCCF avrei dovuto evitare di riportarlo! C’è da dire che all’epoca avevo giganteschi problemi di sonno e tentato un po’ di tutto: in effetti credo di aver provato più volte a dormire al contrario ma sul tappetto (oltretutto iperpolveroso) credo di essercimi steso solo una volta o due!]

12 febbraio
«Non c’è dubbio che questo sia il migliore dei mondi possibili. Ci sono io.» Solomon Short

[gli aforismi di Solomon Short provengono da dei libri di fantascienza del ciclo della guerra contro gli Chtorr. Suppongo quindi che il vero autore sia David Gerrold! Verificando su Internet ho trovato la seguente breve selezione di originali: Solomon Short quotes]

13 febbraio (L)
[Di nuovo sui miei incubi infantili…]

14 febbraio (Ma)
[Niente. Per la cronaca ho scoperto che in tal giorno feci il mio esame più disastroso e traumatico! Vabbè, farò un’eccezione alla regola e ne riporterò un frammento:
«…
Poi arrivo fuori dall’ufficio del V. dove si è formato un gruppetto di studenti. Parliamo per far passare la tensione. L’attesa è lunga, i primi 3 non sono andati bene. Alle 11:30 tocca a me: ha inizio il massacro, il professore è nervoso e mi mette subito in difficoltà, è l’inizio della fine. Mi fa domande che ritiene banali. Dopo alcune scene patetiche mi passa alla R. [l’assistente] con la quale vo benino, ma non basta. Il V. è convinto che io della teoria non abbia capito nulla. Ciò non è vero, la mia preparazione era da 24 [modesto!] ma il cattivo umore del professore, la mia sensibilità e soprattutto la mia schifosissima mancanza di abilità ad esporre quello che so, mi sono stati fatali. Inoltre bisogna tenere presente il disgregamento della mia memoria a causa dei farmaci (spero! Ma dovrò indagare) che mi costringe a leggere e rileggere le stesse cose per memorizzarne una frazione, mentre prima mi bastava uno sguardo. Il resto della giornata lo passo molto male.
...
(*2)»]

15-16 febbraio
[Ancora sugli incubi infantili]

17-19 febbraio
[Tutto pieno: non c’è posto per commenti]

20 febbraio (L)
[Di nuovo sui sogni ma finalmente ho smesso di scrivere di quelli dell’infanzia!]
SSS (Strani Sogni Sognati): La cosa che più mi secca riguardo ai miei sogni è la scarsezza di quelli cosiddetti “erotici” che invece, secondo le statistiche, dovrebbero essere molto numerosi. Oltre che rari sono pure brutti [eh sì: la qualità è importante!], ricordano molto quei vecchi film con Alvaro Vitali, Edwige Fenech ecc ecc a stento vietati ai minori di 14 anni.

21 febbraio (Ma)
SSS: anche i sogni diversi dagli incubi tendono a essere piuttosto ripetitivi specialmente nelle ambientazioni urbane e campestri [!?]. In genere questi luoghi sono miscugli di posti reali con l’aggiunta (spesso massiccia) di luoghi immaginari o fantastici. Mi piacerebbe tracciare una mappa del mio universo onirico.

22 febbraio (Me)
SSS: Un sogno che faccio abbastanza frequentemente è ambientato in una F. contorta (tipo Escher) nella quale cerco di raggiungere una libreria dove è presente un vasto assortimento di Giochi di Ruolo [sigh: i miei interessi…]. Io mi muovo con una certa difficoltà fra il liceo, la stazione, piazza San Marco e via Martelli. Di solito, raggiunto l’obiettivo, il problema è tornare a casa in autobus. Di solito scopro con rabbia che il 27 che ho preso non è il B ma il fantomatico “F” che porta tutto da un’altra parte.

Conclusione: alla fine ho scritto più di quanto avevo in mente ma, in realtà, anche se sono pigro quando inizio a scrivere poi mi diverto...

Nota (*1): credo si tratti di un problema tecnico: devo trovare il modo di far capire a Python che una matrice a due dimensioni 10x1 può essere considerata anche a tre dimensioni 10x1x1…
Nota (*2): questo breve frammento dovrebbe aiutare a far capire la mia attuale ossessione per la memoria: vedi i pezzi col relativo marcatore “Memoria”. La medicina in questione era il Surmontil: in teoria avrebbe dovuto aiutarmi a dormire; in realtà non dormivo e in più ne subivo gli effetti collaterali. Secondo il dottore l’unico problema avrebbe potuto essere una diminuzione della concentrazione ma in realtà sia a me che a mia madre (che qualche anno dopo provò lo stesso farmaco) annullava completamente la memoria. Anni dopo lessi un trafiletto di giornale che parlava proprio dei problemi di memoria causati dal Surmontil: curiosamente però, controllando adesso fra gli effetti collaterali su Wikipedia, non ve n’è menzione. Sarò cattivo ma mi viene da pensare che le case farmaceutiche siano brave a nascondere gli effetti collaterali indesiderati…

martedì 4 dicembre 2018

Aggiornamento pitonesco

Come stabilito (v. Pitoni e bachi) ho cercato un minicorso su Keras.
Il mio interesse sta divenendo piuttosto specifico e così anche Google non mi ha dato moltissima scelta: il primo minicorso a cui ho dato un’occhiata è stato Keras Tutorial: How to get started with Keras, Deep Learning, and Python…
Ho cercato di capire rapidamente come era impostato e mi sono reso conto che: 1. è estremamente recente (settembre 2018); 2. il tizio che gestisce il sito deve essere estremamente brillante; 3. passaggi molto dettagliati.
Insomma sembrava estremamente promettente ma, sempre nell’introduzione, spiegava come installare, oltre a Keras e TensorFlow, anche un certo OpenCV. Seguendo i vari collegamenti ho scoperto che questo OpenCV e una libreria gratuita per l’analisi delle immagini (infatti il minicorso si prefigge lo scopo di realizzare una rete neurale capace di distinguere fra immagini di cani, gatti e panda!).
Ora, considerando che il progetto è molto interessante, io non ho niente in contrario a installare una libreria in più o in meno, sempre però tenendo presente che il mio interesse è per Keras.
Così ho guardato le istruzioni per l’installazione di OpenCV e mi sono sembrate estremamente complicate: in pratica andava scaricato il sorgente e compilato il tutto sul proprio calcolatore. Così, mentre scorrevo le varie righe di comandi, pensavo ai “soliti” mezzi più semplici come, ad esempio, scaricare una versione precompilata tramite Synaptic e perché non fosse possibili usarli.
In fondo alle spiegazioni l’autore “mi rispondeva” spiegando che usare una versione precompilata “potrebbe anche funzionare” ma 1. non sarebbe stata l’ultima versione disponibile; 2. non sarebbe stata ottimizzata per il nostro calcolatore.
A questo punto mi sono detto: “Questo qui mi fa perdere troppo tempo su dettagli! A me non interesse usare l’ultimo codice disponibile né risparmiare qualche minuto di tempo mentre la rete si addestra: cerchiamo altro!”
Però, in effetti… da oggi (martedì) inizio adesso a pensare che l’autore non avesse tutti i torti…

Così ho provato un altro minicorso: Keras Tutorial: Deep Learning in Python.
Ero molto sospettoso perché questo minicorso si appoggia a un sito che vende corsi in linea e così temevo che fosse incompleto o che comunque, per leggerlo tutto, occorresse registrarsi.
In realtà l’accesso è completamente libero e gratuito e il corso è in effetti molto semplice (sta in un'unica pagina): una parte complessa e difficile per la costruzioni di rete neurali è trovare i dati per allenarla; sia il libro che sto leggendo che il precedente minicorso mettevano l’accento proprio su questo aspetto che però a me, al momento, non mi interessa molto; questo minicorso aggira il problema utilizzando dei dati sui dei vini portoghesi messi liberamente a disposizione da un’università americana. In pratica qui la raccolta dei dati si riduce a importare un paio di archivi in formato CSV…
Inoltre il minicorso è del maggio 2017, relativamente recente quindi, ma comunque mi ha dato ugualmente qualche problema.

Il minicorso era studiato per essere eseguito in linea ma io ho voluto replicare tutti i vari passaggi sul mio calcolatore installando via via quello che serviva (mi sono costruito un ambiente virtuale apposito come sembra sia la prassi con Python). Non ho trovato grossi problemi: ho installato quello che mancava con PIP e ho corretto dei problemi minori nella visualizzazione di alcune immagini (basate su una libreria che, ovviamente, non conoscevo ma che “a martellate” ho fatto funzionare).
Sul finale, quando la rete andava effettivamente addestrata, ho però dovuto installare TensorFlow ed ecco un grosso problema:
The TensorFlow library was compiled to use SSE4.2 instructions, but these aren't available on your machine.

Ho cercato su Google ma il problema non mi è parso di semplice soluzione così ho pensato a qualche alternativa.
La prima cosa che mi è venuta in mente è stato di copiare il codice e i dati nell’ambiente preparato per gli esercizi del mio libro (con l’eventuale idea di copiarmi brutalmente i vari archivi di TensorFlow nell’ambiente che avevo preparato io).
Qui il codice ha funzionato ma ho notato un avviso che nei giorni scorsi mi era sfuggito:
Your CPU supports instructions that this TensorFlow binary was not compiled to use: SSE4.1
In altre parole il TensorFlow che mi funziona non usa le istruzioni SSE4.1 (che il mio CPU supporta) mentre il TensorFlow brutalmente installato tramite PIP usa le SSE4.2 (che invece il mio CPU non supporta)!
Ecco perché oggi ho rivalutato il consiglio dell’autore del primo minicorso di compilarsi ogni cosa sul proprio calcolatore: finito questo minicorso ritornerò sul suo e mi installerò, compilandolo come vuole lui, OpenCV…

Ci sarebbero poi da raccontare le mie scoperte su Keras ma ormai ho già scritto abbastanza per oggi…

Conclusione: Ah! Finito questo minicorso ho deciso di leggermi le istruzioni di Keras… io sono infatti uno di quelli che, in genere, non leggono le istruzioni!

domenica 2 dicembre 2018

Pitoni e bachi

Come deciso nella conclusione di Reti smagliate ho guardato con più cognizione di causa cosa avevo a disposizione fra i libri comprati in blocco. Ovviamente nessuno era su Keras come speravo; molti usano il linguaggio R un altro paio il Python e (forse) ce n’è uno anche per Java…
Quindi, seguendo il piano stabilito, mi cercherò un minicorso di Keras in linea prima di proseguire con il libro di esercizi pratici in maniera da capire poi meglio quello che leggerò.
Prima però ho dato la precedenze a un minicorso di Python altrimenti mi sarei trascinato dietro una difficoltà di comprensione del codice molto fastidiosa.

Fra ieri e oggi mi sono quindi studiato un minicorso di Python per chiarirmi degli aspetti del linguaggio che non mi erano evidenti leggendo direttamente il codice.
In verità non mi sono impegnato molto per trovare un buon corso ma ho scelto il primo che mi sembrava decente (tipo il terzo o quarto propostomi da Google), ovvero il seguente: ThePythonGuru.com.
Ho seguito sia la sezione “Beginning Python” che la “Advanced Python”: ciascuna sezione è divisa su più pagine che illustrano specifici argomenti del linguaggio. Sono decisamente superficiali ma danno quell’infarinatura che mi serviva.

Nel pezzo di oggi voglio divertirmi a riportare tutte le stranezze del Python che NON mi sono piaciute (molte altre, e ben di più, le ho invece apprezzate!).

1. le “tuple” vengono definite tramite parentesi tonde.
Questo mi ha confuso molto nella lettura del codice perché le parentesi tonde sono usate anche nelle espressioni e nell’invocazione di funzioni. Contemporaneamente per sequenze e dizionari si usano invece parentesi specifiche (quadre e graffe) e quindi mi sarei aspettato delle parentesi speciali anche per le tuple. Lo so che non ci sono altre parentesi a portata di tastiera ma si sarebbe potuto usare una combinazione di due caratteri, tipo <| e |> per identificarle chiaramente.
Tipicamente quando leggevo il codice non facevo caso a queste parentesi esterne e poi non capivo a cosa si riferissero (ad esempio) le parentesi quadre successive tipo [-10:] (*1). Tutto invece mi sarebbe stato più chiaro, anche a colpo d'occhio, usando delle specifiche parentesi.

2. Separazione fisica delle librerie.
Come spiegato in Python virtualenv Guide per evitare confusione fra versioni diverse della stessa libreria si crea una vero e proprio ambiente di lavoro distinto per ogni progetto.
Non so, suppongo che questa necessità possa derivare dal fatto che di Python ne esistono due versioni fra loro incompatibili: la 2.7 e quelle dalla 3 in poi…
Ma non capisco perché non adottare una separazione logica invece che fisica: cioè nella dichiarazione dei moduli (librerie) usati obbligare a indicare anche la versione voluta.
Invece nella situazione attuale se due progetti usano la stessa libreria questa viene installata due volte negli ambienti dei due progetti!

3. Definizione di variabili private basata sul nome!
Le variabili private di un oggetto sono identificate tramite il doppio underscore iniziale, ovvero __.
Secondo me era più logico e chiaro usare un’apposita parola chiave tipo “private”.
Probabilmente si è scelta la soluzione col __ perché in Python le variabili si possono usare direttamente senza bisogno di dichiararle in anticipo. Ma per le variabili private di un oggetto avrei fatto un’eccezione invece di basarmi sul loro nome per identificarle come tali…

4. Dichiarazione dei metodi di un oggetto con parola chiave “self”.
Nella definizione dei metodi di un oggetto il primo parametro è “self”. Questo parametro non è poi passato quando tali metodi sono invocati.
Suppongo che se un metodo non ha la parola “self” come primo parametro allora sia una specie di metodo statico Java, ovvero non legato alla particolare istanza dell’oggetto ma comune a tutti quelli dello stesso tipo.
Ma se anche questo fosse il caso allora mi sarebbe sembrato più logico identificare questi particolari metodi (usati molto più raramente) con una specifica parola chiave (tipo “static”) e togliere la necessità di inserire il parametro “self” in tutti gli altri metodi...

5. Il comando “yield” per restituire un oggetto di tipo Iteratore.
Suppongo che la necessità di usare una specifica parola chiave sia dovuta al fatto che Python non definisce in anticipo il tipo di una variabile. Non si può quindi scrivere “a” è di tipo Iteratore a tempo di compilazione ma lo si potrebbe scoprire solo a tempo di esecuzione (ad esempio usando la funzione “type”). Quindi se si vogliono effettuare controlli al tempo di compilazione si usa il trucco di usare una specifica parola chiave per identificare il tipo di un oggetto!
Ecco “trucco” mi pare una parola importante nella definizione del linguaggio Python: mi sembra che alcune scelte, a mio parere discutibili, siano state fatte come sorta di trucchi per rendere più semplice la scrittura dell’interprete…

Ah! e poi credo di aver perfino trovato un baco nell’interprete (Python 3.6.7 su linux)!
Ho scritto il seguente programmino in un archivio chiamato “baco.py”:
import pickle

f=open("pick.dat",'wb')
pickle.dump(11,f)
pickle.dump("Mamma mia dammi 100$",f)
pickle.dump([1,2,4,5],f)
f.close

g=open("pick.dat", "rb")
letto=pickle.load(g)
print(letto)
g.close()

Il programmino crea un archivio chiamato “pick.dat” e vi inserisce tre oggetti in formato binario: il numero 11, la stringa "Mamma mia dammi 100$" e la sequenza [1,2,4,5]. Poi l’archivio viene chiuso e immediatamente riaperto. La seconda sezione del codice dovrebbe leggere (e successivamente stampare) il primo oggetto in formato binario dal medesimo archivio, ovvero il numero 11.
Invece si ottiene il messaggio di errore:
Traceback (most recent call last):
File "baco.py", line 10, in
letto=pickle.load(g)
EOFError: Ran out of input

In pratica il tentativo di lettura finisce perché l’archivio è già terminato. Fortunatamente per queste cose ho naso: mi sono immaginato che, per qualche motivo (il baco), l’archivio non venisse aperto alla posizione iniziale ma dal fondo. Allora ho provato a commentare la prima sezione e, in questa maniera, funziona!
Cioè:
import pickle

#f=open("pick.dat",'wb')
#pickle.dump(11,f)
#pickle.dump("Mamma mia dammi 100$",f)
#pickle.dump([1,2,4,5],f)
#f.close

g=open("pick.dat", "rb")
letto=pickle.load(g)
print(letto)
g.close()

Stampa correttamente il numero 11!
Non so, magari il baco è legato alla gestione degli archivi del sistema operativo linux, però… non dovrebbe accadere!

Conclusione: per onestà col Python dovrei anche mostrare le parti del linguaggio che invece mi sono piaciute ma in realtà sono troppe! E comunque mi pare più interessante evidenziare, motivandoli, i dettagli che non mi sono piaciuti…
E adesso mi cerco un corso di Keras!

Nota (*1): queste parentesi quadre non indicano una sequenza ma servono per “sezionare” la tupla. In particolare [-10:] ne seleziona gli ultimi dieci elementi partendo da destra.