Esempio pratico deep learng (DL) : le previsioni del ristorante Collatz, con Python e Pytorch (LSTM)

Aggiornato il: mar 16


Il ristorante Collaz

Collatz è… un ristoratore DOLO_MITICO, a detta sua… anche perché non è facile gestire un rifugio in alta montagna… dove il tempo varia anche più volte al giorno… e questo è un fattore importante per chi deve prevedere quanti clienti si fermeranno a mangiare …

il mio amico Collatz, sin da piccolo ha dimostrato di avere una gran passione per la matematica e, da quando ha scoperto che gli elaboratori sono in grado di fare delle previsioni, perché si è imbattuto nel sito di intelligenzaartificialeitalia.net si è messo in mente di sfruttare il suo PC, anche per cercare di gestire anticipatamente ordini e personale.

Già da qualche tempo aveva visto che nel web c’erano dei diagrammi che indicavano ad esempio l’andamento della pandemia, come questo:

Andamento Pandemico

o similmente qualcosa che riguardava il numero dei passeggeri che, nel tempo avevano deciso di imbarcarsi con una determinata flotta, come quest’altro:


il numero dei passeggeri che, nel tempo avevano deciso di imbarcarsi con una determinata flotta

oppure, qualche diagramma che riportava l’andamento dei titoli in borsa, come quest’ultimo:

Andamento dei titoli in borsa

Fu così che si mise a riportare, giorno per giorno, il numero di clienti che dall’inizio della stagione, erano andati al suo “ristorante”, che in realtà rimaneva sempre un rifugio di montagna in balia del tempo… e annotando su di un file del tipo “Seq_work.csv” i dati, ottenne un grafico come questo:


Avventori giornalieri ristorante Collaz

Così, si rese conto che era veramente difficile fare delle previsioni, ma decise comunque di proseguire nel suo intento, quello di addestrare una rete neurale, nella versione LSTM (Long-Short Term Memory), come aveva visto fare visitando il sito: intelligenzaartificialeitalia.net , utilizzando però python e pytorch. al posto di keras.

Armato di buona volontà installò il software occorrente sul suo PC, mi sembra abbia utilizzato una GUI che si chiama VisualStudio Code, e che gli abbia fornito in seguito, l’estensione per il linguaggio python.

Mi disse anche che aveva generato uno spazio di memoria particolare, appositamente per inserirci dentro tutto l’occorrente, utilizzando il modulo “venv”, di pyhton che, mi disse, è un modo efficace per ottenere ambienti python virtuali sui quali lavorare in santa pace!.

Per fare questo, mi disse che nel suo PC, aveva installato una versione di Linux “Ubuntu” in quanto lo riteneva adeguato allo scopo.

Mi informò anche che python aveva diverse librerie e moduli, che si rendevano necessari per lavorare sui modelli di intelligenza artificiale, e che si dovevano installare anche queste librerie., come indicato dal sito intelligenzaartificialeitalia.net

Poi incominciò a descrivermi il software che aveva scritto per l’occasione, per prima cosa bisognava importare le librerie contenenti il necessario per portare a termine il progetto:


#!/usr/bin/python3#
import numpy as np
import torch
import torch.nn as nn
from sklearn.preprocessing import MinMaxScaler
import pandas as pd
import matplotlib.pyplot as plt

In particolare mi disse che il modulo “numpy”, è una libreria per il trattamento di vettori e matrici e che ovviamente contiene molte funzioni matematiche, molto utili per il trattamento dei dati numerici, mi disse che il modulo “pandas” non è una malattia autoimmune, ma una libreria per il trattamento dei dati scritti in modo tabellare e che in questo caso la avrebbe utilizzata per leggere i suo file “Seq_work.csv”, aggiunse che, per dare una forma grafica ai dati riportati avrebbe utilizzato le funzionlità di “matplotlib”.


Aggiunse poi, che per questo esperimento pensava di effettuare una “normalizzazione” dei dati tra un valore minimo e un massimo, che però al riguardo, gli rimanevano forti perplessità, ma, strizzandomi l’occhio, mi disse sottovoce, tentar non nuoce e quindi, che per fare le dovute trasformazioni – riparametrazioni dei dati grezzi, avrebbe usato il modulo “sklearn.preprocessing”.


Infine, disse che per questo esperimento avrebbe utilizzato una libreria famosa (scoperta in Facesbook) che si chiamava Torch, ma che non aveva nulla a che fare con i citomegalovirus, ma con array di numeri rettangolari multidimensionali omogenei, che denominava “tensori”, e che con questa libreria si potevano costruire delle reti neurali, proprio quelle che avrebbe utilizzato per l’elaborazione di previsione che a lui interessava.


Per prima cosa, disse, bisogna caricare i dati, che aveva scritto in formato “.csv”(acronimo di comma-separated values), nel file “DataSeeq.csv” e per fare questo veniva appunto utilizzato il modulo “pandas”:


#acquisizione dati
pd.read_csv("DataSeq.csv")
#salvare i dati dentro una variabile
dataset = pd.read_csv("DataSeq.csv")

mi disse che, per sua curiosità personale, voleva vedere a schermo almeno una idea del formato dei dati caricati con pandas nella variabile dataset.

E per fare questo bastavano due linee di programma:


print ("*****************")
print(dataset.head())
print(dataset.shape)
print('***********')

Se tutto fosse andato a buon fine si dovrebbe ottenere un risultato simile a questo:

Output print Dataset e dimensioni Dataset




come si vede l’output è un file ordinato che contiene 200 sequenze per 4 colonne:

le sequenze sono le registrazioni dei dati relativi al numero dei clienti (n_cycle) e suddivisi ulteriormente in ( numero di clienti maschili: n_alpha e numero di clienti femminili: n_beta).

Si noti la deformazione professionale di Collatz nella nomenclatura dei dati….

Per il momento tralasciamo i valori di n_alpha e n_beta, che magari potranno essere utilizzati in un secondo momento per giocare con una applicazione successiva di predizione….

In qualsiasi caso, potremmo identificare la tipologia dei dati riportati nelle colonne facendo stampare l’oggetto colonne con l’istruzione:


print(dataset.columns)

a cui corrisponderà un output del tipo:


Index(['n_sequence', 'n_cycle', 'n_alpha', 'n_beta'], dtype='object')

La libreria PyTorch “predilige” i dati di tipo “float”(in inglese floating point) o numerazione a virgola mobile, e quindi si dovrà eseguire una prima trasformazione con l’istruzione:


all_data = dataset['n_cycle'].values.astype(float)

assegnando alla variabile “all_data”, tutto il pacchetto contenuto in dataset, relativo a “n_cycle”, che per Collatz, sarebbe il numero di clienti che hanno usufruito delle prestazioni culinarie del “ristorante”, giorno per giorno, nei primi “200 giorni” dalla data di apertura stagionale, qualcosa di simile a questo:


[ 3. 1. 7. 2. 5. 8. 16. 3. 19. 6. 14. 9. 9. 17.
 17. 4. 12. 20. 20. 7. 7. 15. 15. 10. 23. 10. 111. 18.
 18. 18. 106. 5. 26. 13. 13. 21. 21. 21. 34. 8. 109. 8.
 29. 16. 16. 16. 104. 11. 24. 24. 24. 11. 11. 112. 112. 19.
 32. 19. 32. 19. 19. 107. 107. 6. 27. 27. 27. 14. 14. 14.
 102. 22. 115. 22. 14. 22. 22. 35. 35. 9. 22. 110. 110. 9.
 9. 30. 30. 17. 30. 17. 92. 17. 17. 105. 105. 12. 118. 25.
 25. 25. 25. 25. 87. 12. 38. 12. 100. 113. 113. 113. 69. 20.
 12. 33. 33. 20. 20. 33. 33. 20. 95. 20. 46. 108. 108. 108.
 46. 7. 121. 28. 28. 28. 28. 28. 41. 15. 90. 15. 41. 15.
 15. 103. 103. 23. 116. 116. 116. 23. 23. 15. 15. 23. 36. 23.
 85. 36. 36. 36. 54. 10. 98. 23. 23. 111. 111. 111. 67. 10.
 49. 10. 124. 31. 31. 31. 80. 18. 31. 31. 31. 18. 18. 93.
 93. 18. 44. 18. 44. 106. 106. 106. 44. 13. 119. 119. 119. 26.
 26. 26. 119. 26.]

se si utilizzasse una istruzione tipo questa:


print(all_data)

Ora, Collatz mi disse, per verificare l’affidabilità di predizione del modello che andremo a testare, una cosa da fare, molto importante, è fargli fare allenamento su un numero sufficiente di dati, che chiameremo “train_data” e poi testare il modello su un set di dati conosciuti, che chiameremo “test_data”.


Dopo averci pensato sopra un po’, affermò che, visto che avevamo a disposizione 200 sequenze, avremmo potuto utilizzarne la gran parte per allenare il modello e fare il test su un numero piccolo di sequenze, e visto che si trattava di dati ottenuti con una metodologia temporale, considerando i tempi di approvvigionamento delle materie prime e del preavviso ai dipendenti, aveva deciso di utilizzare le prime 185 sequenze per allenare il modello e le ultime per fare il test.


Per fare questo, disse bastavano poche istruzioni, le seguenti:

#configurazione train data e test data
test_data_size = 15
train_data = all_data[:-test_data_size]
test_data = all_data[-test_data_size:]
print ('--- len train and test data ---')
print(len(train_data))
print(len(test_data))
print('-----test data ------')
print(test_data)

e da quanto sopra viene ottenuto un output come questo:


--- len train and test data ---
185
15
-----test data ------
[ 18. 44. 106. 106. 106. 44. 13. 119. 119. 119. 26. 26. 26. 119. 26.]

A questo punto, per fare in modo di ottenere dei dati normalizzati, sulla parte dei dati utilizzata per l’allenamento: il train_data, appunto, centrandoli in questo caso, sullo zero, ed ottenendo quindi dei valori compresi tra -1 e 1, si sarebbe usato il modulo “sklearn.preprocessing” , con la seguente modalità :


#normalizzazione dati
scaler = MinMaxScaler(feature_range=(-1, 1))
train_data_normalized = scaler.fit_transform(train_data .reshape(-1, 1))
print('--- stampa esempio dati normalizzati ---')
print(train_data_normalized[:5])
print(train_data_normalized[-5:])

ottenendo un output per le prime cinque sequenze che dovrebbe risultare simile a

questo:


--- stampa esempio dati normalizzati ---
[[-0.96747967]
 [-1. ]
 [-0.90243902]
 [-0.98373984]
 [-0.93495935]]


mentre per le ultime sequenze, l’output sarà:


[[-0.72357724]
 [ 0.49593496]
 [ 0.49593496]
 [-0.72357724]
 [-0.30081301]]


ed è forse per questo motivo che il mio amico Collatz, mi espresse la sua perplessità!

(si riferiva probabilmente al fatto che nella ultima sequenza comparivano dei numeri negativi...)

In qualsiasi caso, decise di continuare con l’esperimento, e di passare i dati al tesnore di PyTotch per il train_data normalizzato, con le seguenti istruzioni, che ne riportavano

anche il risultato, se non altro per vedere la differenza di gestione degli oggetti:

# passaggio a tnsore per train data
train_data_normalized = torch.FloatTensor(train_data_normalized).view(-1)
print('-----torch.FloatTensor--------')
print (train_data_normalized)
print('---------------------')

l’output in questo caso dovrebbe essere questo:



-----torch.FloatTensor--------
tensor([-0.9675, -1.0000, -0.9024, -0.9837, -0.9350, -0.8862, -0.7561, -0.9675,
 -0.7073, -0.9187, -0.7886, -0.8699, -0.8699, -0.7398, -0.7398, -0.9512,
 -0.8211, -0.6911, -0.6911, -0.9024, -0.9024, -0.7724, -0.7724, -0.8537,
 -0.6423, -0.8537, 0.7886, -0.7236, -0.7236, -0.7236, 0.7073, -0.9350,
 -0.5935, -0.8049, -0.8049, -0.6748, -0.6748, -0.6748, -0.4634, -0.8862,
 0.7561, -0.8862, -0.5447, -0.7561, -0.7561, -0.7561, 0.6748, -0.8374,
 -0.6260, -0.6260, -0.6260, -0.8374, -0.8374, 0.8049, 0.8049, -0.7073,
 -0.4959, -0.7073, -0.4959, -0.7073, -0.7073, 0.7236, 0.7236, -0.9187,
 -0.5772, -0.5772, -0.5772, -0.7886, -0.7886, -0.7886, 0.6423, -0.6585,
 0.8537, -0.6585, -0.7886, -0.6585, -0.6585, -0.4472, -0.4472, -0.8699,
 -0.6585, 0.7724, 0.7724, -0.8699, -0.8699, -0.5285, -0.5285, -0.7398,
 -0.5285, -0.7398, 0.4797, -0.7398, -0.7398, 0.6911, 0.6911, -0.8211,
  0.9024, -0.6098, -0.6098, -0.6098, -0.6098, -0.6098, 0.3984, -0.8211,
 -0.3984, -0.8211, 0.6098, 0.8211, 0.8211, 0.8211, 0.1057, -0.6911,
 -0.8211, -0.4797, -0.4797, -0.6911, -0.6911, -0.4797, -0.4797, -0.6911,
 0.5285, -0.6911, -0.2683, 0.7398, 0.7398, 0.7398, -0.2683, -0.9024,
 0.9512, -0.5610, -0.5610, -0.5610, -0.5610, -0.5610, -0.3496, -0.7724,
 0.4472, -0.7724, -0.3496, -0.7724, -0.7724, 0.6585, 0.6585, -0.6423,
 0.8699, 0.8699, 0.8699, -0.6423, -0.6423, -0.7724, -0.7724, -0.6423,
 -0.4309, -0.6423, 0.3659, -0.4309, -0.4309, -0.4309, -0.1382, -0.8537,
 0.5772, -0.6423, -0.6423, 0.7886, 0.7886, 0.7886, 0.0732, -0.8537,
 -0.2195, -0.8537, 1.0000, -0.5122, -0.5122, -0.5122, 0.2846, -0.7236,
 -0.5122, -0.5122, -0.5122, -0.7236, -0.7236, 0.4959, 0.4959, -0.7236,
 -0.3008])
---------------------

che, come si denota, gli elementi della “lista”, contenuta nella “tupla”, vengono troncati alla quarta cifra dopo la virgola, rispetto a quelli normalizzati in precedenza che avevano otto decimali.


Collatz, mi disse che c’era un altro passaggio delicato per far effettuare l’allenamento alla macchina sul train_data, ma siccome era solo un esperimento, aveva deciso di dividere il blocco dei dati di training in modo simile alla quantità delle sequenze di test, cioè in blocchi da 15 sequenze.

Per fare questo mi disse, utilizzerò una particolarità di elaborazione dei dati di python la “def”, con la quale ingloberò delle istruzioni che saranno in grado di elaborare l’operazione di suddivisione dei dati desiderata:


# divisione tuple di numeri
train_window = 15
# gestore di sequenza
def create_inout_sequences(input_data, tw):
  inout_seq = []
 L = len(input_data)
 for i in range(L-tw):
 train_seq = input_data[i:i+tw]
 train_label = input_data[i+tw:i+tw+1]
 inout_seq.append((train_seq ,train_label))
 return inout_seq
train_inout_seq = create_inout_sequences(train_data_normalized, train_window)
print('*** tuple di inout sequence esempio (2) *******')
print(train_inout_seq[:2])
print('*****************')

Per prima cosa, dimensionò la variabile train_window a 15 (il valore a cui far corrispondere la lunghezza delle tuple) e posi scrisse le istruzioni per la costruzione della funzione “create_inout_sequences”, che prende i dati dalla istruzione


train_inout_seq = create_inout_sequences(train_data_normalized, train_window)

dove passerà i dati alla funzione in questo modo:


input_data = train_data_normalized

e

tw = train_window)

producendo un output di esempio le prime due:



*** tuple di inout sequence esempio (2) *******
[(tensor([-0.9675, -1.0000, -0.9024, -0.9837, -0.9350, -0.8862, -0.7561, -0.9675,
 -0.7073, -0.9187, -0.7886, -0.8699, -0.8699, -0.7398, -0.7398]), tensor([-0.9512])), (tensor([-1.0000, -0.9024, -0.9837, -0.9350, -0.8862, -0.7561, -0.9675, -0.7073,
 -0.9187, -0.7886, -0.8699, -0.8699, -0.7398, -0.7398, -0.9512]), tensor([-0.8211]))]
*****************


dove, mi fece notare, che il sedicesimo elemento rappresentava l’etichetta della prima tupla, mentre la seconda tupla inizia dal secondo elemento, termina al sedicesimo e la sua etichetta è il diciasettesimo… e cosi via…


Questa cosa mi ha fatto meditare!


Poi, con un grande sorriso, Collatz mi disse “E adesso facciamo un lavoro di Classe!”.

E mi fece leggere le seguenti istruzioni:



class LSTM(nn.Module):
 def __init__(self, input_size=1, hidden_layer_size=100, output_size=1):
 super().__init__()
 self.hidden_layer_size = hidden_layer_size

 self.lstm = nn.LSTM(input_size, hidden_layer_size)

 self.linear = nn.Linear(hidden_layer_size, output_size)

 self.hidden_cell = (torch.zeros(1,1,self.hidden_layer_size),
 torch.zeros(1,1,self.hidden_layer_size))

 def forward(self, input_seq):
 lstm_out, self.hidden_cell = self.lstm(input_seq.view(len(input_seq) ,1, -1),  self.hidden_cell)
 predictions = self.linear(lstm_out.view(len(input_seq), -1))
 return predictions[-1]



al ché, li per li, dissi soltanto “WOW”… e pensai… la cosa si fa complicata!

Specialmente dopo aver letto qualche riga dal sito della documentazione di pytorch: https://pytorch.org/docs/stable/generated/torch.nn.LSTM.html


Ero rimasto particolarmente sbalordito da cosa potesse significare la seguente concettualizzazione:


LSMT pytorh

Comunque, intuitivamente, pensai: c’è in primo layer che riceve un dato alla volta, lo trasferisce ad un layer composto da 100 neuroni, il quale a sua volta elabora i dati e il risultato lo trasferisce ad un layer di output che ha un solo elemento che conterrà di volta in volta il risultato ottimizzato in relazione a delle sequenze di calcolo elaborate ricorsivamente in relazione ad uno scostamento di confronto tra il valore ottenuto e il valore di controllo.

Ma il mio amico Collatz, rincarò la dose, dicendomi, adesso che abbiamo lo scheletro del cervello, mettiamoci dentro le idee e mi fece leggere le seguenti istruzioni:


model = LSTM()
loss_function = nn.MSELoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
#------
print('----- modello ----')
print(model)

Mi disse, Python è un linguaggio che genera oggetti e LSTM è soltanto uno degli oggetti possibili che si possono generare con PyTorch, che in questo caso lo chiameremo semplicemente ”model”.

Ora, mi chiese Collatz, che cosa dovrà fare il “model”? Io ci pensai un po’ e poi risposi, dovrà “imparare” in qualche modo a selezionare i dati più simili al “target”!


Ovvero, visto che il risultato corretto di ogni operazione nel modello è già predeterminato, confrontiamo questo dato con le operazioni che la rete neurale mi fornisce come risultato “temporale”, ne identifichiamo in qualche modo l’errore e di volta in volta andiamo a correggere il “tiro”.

In altri termini, mi disse Collatz, utilizzeremo una serie di formule che, da una parte andranno a calcolare lo scostamento statistico dal target, per esempio utilizzando una funzione che si potrebbe definire “perdita di entropia incrociata”, e dall’altra, riporteremo in ingresso un valore di correzione tramite ad esempio una riparametrazione sigmoidea!


Io lo guardai e gli dissi… “Ecco… si!”. Lui sorrise e mi disse: proprio quello che è condensato in quelle di righe di programma di configurazione del “model”, che se vai a vedere l’output è semplicemente descritto così:


----- modello ----
LSTM(
 (lstm): LSTM(1, 100)
 (linear): Linear(in_features=100, out_features=1, bias=True)

Apperò, dissi io, alla faccia della sintesi! E chiesi cosa significa il termine “bias”?


Collaz mi rispose: quello è un modo che viene fornito alla rete neurale per “farsi un opinione”!


Lo guardai, sorrisi e gli dissi: anche l’intelligenza artificiale allora si basa su dei “pregiudizi”!


Si, si, rispose lui, ma facciamolo lavorare! E mi fece leggere le seguenti righe di programma:



# fase di addestramento
epochs = 150
for i in range(epochs):
for seq, labels in train_inout_seq:
optimizer.zero_grad()
model.hidden_cell = (torch.zeros(1, 1, model.hidden_layer_size),
  torch.zeros(1, 1, model.hidden_layer_size))

y_pred = model(seq)

single_loss = loss_function(y_pred, labels)
single_loss.backward()
optimizer.step()

if i%25 == 1:
 print(f'epoch: {i:3} loss: {single_loss.item():10.8f}')
print ('°°°°°°°°°°°°°°°°°')
print(f'epoch: {i:3} loss: {single_loss.item():10.10f}')
print('°°°°°°°°°°°°°°°°°°°')

Spiegandomi a grandi linee che voleva cercare di far fare la procedura di allenamento per un numero limitato di sequenze, tanto per provare, limitandola a 150.

Quindi per il numero di cicli prefissato, verrà preimpostato il gradiente a zero e verrà generato un tensore che conterrà dei valori scalari in relazione al calcolo dello scostamento per tutte le sequenze di “epochs” e per tutti i valori della tupla in esame, il risultato sarà riposto in una variabile e utilizzato ottimizzandolo, per la “back propagation”.

Per farci un idea, mi disse Collatz, si dovrebbe ottenere un risultato simile a questo ogni 25 sequenze:


epoch: 1 loss: 0.00829106
epoch: 26 loss: 0.00000873
epoch: 51 loss: 0.01431556
epoch: 76 loss: 0.02613759
epoch: 101 loss: 0.02043977
epoch: 126 loss: 0.00130326

(nota che la stampa dei dati di loss è limitata a 8 decimali)


in pratica, per ogni ciclo, l’output dovrebbe essere una cosa analoga questa (l’ultimo risultato riportato con dieci cifre significative dopo la virgola):


°°°°°°°°°°°°°°°°°
epoch: 149 loss: 0.0008416197
°°°°°°°°°°°°°°°°°°°

Ora, mi disse Collatz, la nostra rete dovrebbe avere una opinione sull’andamento dei dati e a questo punto possiamo passare al test di predizione:


#--------------
fut_pred = 15

test_inputs = train_data_normalized[-train_window:].tolist()
print(test_inputs)
#-----------------------

Per prima cosa forniamo la dimensione del test (le ultimi quindici variabili), prendiamone i dati normalizzati e vediamo a cosa corrispondono:


[1.0, -0.5121951103210449, -0.5121951103210449, -0.5121951103210449, 0.28455284237861633, -0.7235772609710693, -0.5121951103210449, -0.5121951103210449, -0.5121951103210449, -0.7235772609710693, -0.7235772609710693, 0.49593496322631836, 0.49593496322631836, -0.7235772609710693, -0.3008130192756653]

Questa lista contiene appunto i dati normalizzati da passare alla rete neurale addestrata per effettuare le previsioni, e il processo avverrà con la seguente modalità:


model.eval()

for i in range(fut_pred):
 seq = torch.FloatTensor(test_inputs[-train_window:])
 with torch.no_grad():
 model.hidden = (torch.zeros(1, 1, model.hidden_layer_size),
 torch.zeros(1, 1, model.hidden_layer_size))
 test_inputs.append(model(seq).item())
#------------
print('--- test input ---')
print(test_inputs[fut_pred:])
#--------------------

Verrà definito, nel “dominio dei nomi” il modello di valutazione e tramite un ciclo “for” operante nella lunghezza delle sequenze determinate dalla variabile “fut_pred”, All'interno del ciclo for questi 15 elementi verranno utilizzati per generare il valori di previsione sul primo elemento del set di test, ovvero l'elemento numero 185, Il valore di previsione verrà quindi aggiunto a test_imputs, mediante la funzione “append” .

Durante la seconda iterazione, di nuovo gli ultimi 15 elementi verranno utilizzati come input e verrà effettuata una nuova previsione che verrà quindi nuovamente aggiunta a test_inputs e cosi via, il ciclo for verrà eseguito per 15 volte, alla fine del ciclo il “test_inputs”, conterrà 15 elementi normalizzati:


--- test input ---
[-0.5246923565864563, -0.5111703276634216, -0.6970333456993103, -0.46873635053634644, 0.34659910202026367, 0.36807361245155334, -0.5615038275718689, 0.14165163040161133, -0.3299140930175781, -0.17069603502750397, -0.24251092970371246, -0.20503830909729004, -0.02129334583878517, -0.48219528794288635, 0.041329968720674515]

Questi valori, andranno dunque riportati alla configurazione originaria, mediante l’istruzione complementare a quella di normalizzazione:


#--------------------
actual_predictions = scaler.inverse_transform(np.array(test_inputs[train_window:] ).reshape(-1, 1))
print ('***** ACTUAL PREDICTIONS')
print(actual_predictions)
#--------------------------

che darà un risultato simile a questo:


***** ACTUAL PREDICTIONS
[[30.23142007]
 [31.06302485]
 [19.63244924]
 [33.67271444]
 [83.81584477]
 [85.13652717]
 [27.9675146 ]
 [71.21157527]
 [42.21028328]
 [52.00219385]
 [47.58557782]
 [49.89014399]
 [61.19045923]
 [32.84498979]
 [65.04179308]]

Ovviamene il risultato non sarà riportato in numeri interi, ma poco importa (se si volessero riportare i valori in numeri interi c’è una apposita istruzione di python…)

A questo punto a Collatz gli si illuminarono gli occhi e disse: finalmente adesso possiamo far disegnare i grafi con le elaborazioni fatte con il modulo di “matplotlib”, aggiunse una ultima istruzione di programma per configurare gli output,

e mi fece leggere queste ultime righe in python:

prima di stampare i grafici:


x = np.arange(185, 200, 1)

# grafi dati 
#-------vista particolare per previsione --------
plt.title('final sequences vs predictions')
plt.ylabel('final cycles')
plt.grid(True)
plt.autoscale(axis='x', tight=True)
plt.plot(dataset['n_cycle'][-train_window:])
plt.plot(x,actual_predictions)
plt.legend()
plt.savefig("Seq_predict.png")
plt.show()

Scegliemmo di visualizzare il confronto tra i dati del set di test e quelli delle previsioni ottenendo alcuni esempi:

Grafico 1

ottenuto con una configurazione impostata:


fig_size = plt.rcParams["figure.figsize"] 
fig_size[0] = 15 
fig_size[1] = 5 
plt.rcParams["figure.figsize"] = fig_size

Grafico 2

ottenuto senza la configurazione impostata, con la configurazione delle ultime righe del programma.


Fu allora che io e Collatz ci guardammo negli occhi e con una risata dicemmo:

Se questi sono i risultati, allora è meglio prevedere sempre che i clienti abbiano tanta fame, onde programmare correttamente gli acquisti della materia prima e avvisare il personale che a volte ci sarà del super lavoro…. Oppure dovremmo ripensare meglio i criteri con cui abbiamo istruito la nostra rete neurale!


p.s.

Ringrazio il mio amico “matematico” Umberto Cibien che ha destato la mia curiosità per la :


“Congettura di Collatz”

https://umbertocibien.org/math/2021/01/28/la-congettura-di-collatz/



Vuoi Scaricare il progetto Gratis ?

  1. Accedi o registrati al portale

  2. Clicca qui e scarica il progetto


Grazie mille per la lettura



3,211 visualizzazioni1 commento

Post recenti

Mostra tutti

VUOI DIVENTARE UN MEMBRO UFFICIALE DELLA NOSTRA COMMUNITY E TRARNE I SEGUENTI BENEFICI?

Forum

Accedi al forum e fai domande o crea discussioni con esperti del settore. Potrai anche fare proposte di lavoro, condividere i tuoi progetti ed altro

Sfide

Accedi a diverse e diverti sfide sulla programmazione e intelligenza artificiale. Appena completerai la sfida riverai la certificazione

Download

Potrai scaricare in modo gratuito tutti i file dei vari progetti nel portale. Ogni progetto è disponibile in formato PDF, PYTHON, NOTEBOOK

E molto altro

Appena ti sarai registrato ti arriverà un e-book in regalo e in futuro ne riceverai altri...

In oltre potresti ricevere sconti o buoni

Se ti piacciono i benefici, i regali e vuoi essere aggiornato quando escono nuovi articoli, progetti o news premi il tasto qui sotto e registrati/accedi con Google, Facebook o con la tua email

VUOI SCRIVERE ARTICOLI PER NOI.

Se vuoi scrivere articoli sul mondo dell' AI ed avere la tua visibilità, contattaci adesso.
Grazie per l'interesse dimostrato.

Segui i nostri canali social

Segui i nostri canali social 

  • YouTube Icona sociale

Youtube

  • Instagram

Instagram

  • Spotify Icona sociale

Spotify

  • White Facebook Icon

Facebook

  • Twitter Icon sociale

Twitter

©2020 Intelligenza Artificiale Italia

  • Instagram
  • Facebook Icona sociale
  • Youtube