Chatbots, chatbots, chatbots… Parte 6

Enter the Gordon Ramsay of data

Cosimo Iaia
4 min readMar 7, 2019

Nelle storie precedenti dedicate alla creazione del nostro Chatbot abbiamo visto come mettere insieme un dataset di frasi da far studiare al nostro modello di rete neurale, come ‘pulirlo’ da eventuali caratteri e numeri inutili e come metterlo sotto forma di tensori, una struttura appetibile e digeribile dal nostro modello scritto con Tensorflow. Quello che ci ritroviamo è quindi un grosso file di numeri che costituiscono le nostre frasi mappate da un dizionario, un’enorme sequenza di dati che però il nostro chatbot non potrà certo digerire tutto in una volta, ma andrà suddiviso in batches, lotti, o portate, per far sì che il modello di rete possa studiarlo ben bene e, di conseguenza, gustarselo appieno.

È il momento di tagliarlo ed impiattarlo con finezza.

Per ottenere le nostre portate creiamo una classe apposita, che useremo per dividere il nostro dataset in tagli di dimensioni definite dal batch_size (uno degli hyperparameters che abbiamo visto nell'articolo precedente) e inserirli in tensori di input compatibili con le dimensioni della nostra rete, grazie all'aiuto di Numpy.

Innanzitutto creiamo la classe reader che molto meccanicamente affetterà il nostro dataset in dimensioni del batch_size, li metterà in appositi bucket, e, con il metodo next_batch, ci restituirà in ordine di volta in volta la porzione di dati relativa, che quindi andremo poi man man ad inserire nei nostri tensori di encoder, decoder e target e che utilizzeremo per ‘nutrire’ il nostro modello durante la fase di training.

class reader:def __init__(self, file_name, batch_size, buckets, bucket_option, signal=False, clean_mode=False):  self.epoch = 1
self.batch_size = batch_size
self.file_name = file_name
self.buckets = buckets
self.bucket_option = bucket_option
self.output = []
self.bucket_list = []
self.clean_stock = False
self.clean_mode = clean_mode
self.bucket_dict = self.build_bucket_dict()
for i in xrange (len(buckets)):
self.bucket_list.append([])
self.file = open(file_name + "/text.txt", "r")
with open(file_name + "/dict.json", "r") as f:
self.dict = json.load(f)
if signal:
with open(file_name + "/signal.json", "r") as f:
self.signal_dict = json.load(f)
self.id_dict = {}
for key in self.dict:
self.id_dict[self.dict[key]]=key
#make batch for the model
def next_batch(self):
# normal mode
if not self.clean_stock:
index = self.fill_bucket()
if index >= 0:
output = self.bucket_list[index]
#clean the bucket
self.bucket_list[index] = []
return output, index
else:
self.clean_stock = True
for i in xrange(len(self.bucket_list)):
if len(self.bucket_list[i]) > 0:
output = self.bucket_list[i]
self.bucket_list[i] = []
return output, i
# clean the stock
elif self.clean_mode:
for i in xrange(len(self.bucket_list)):
if len(self.bucket_list[i]) > 0:
output = self.bucket_list[i]
self.bucket_list[i] = []
return output, i
#if all bucket are cleaned, reset the batch
self.clean_stock = False
self.reset()
return self.next_batch()
# ignore the stock
else:
for i in xrange(len(self.bucket_list)):
self.bucket_list[i] = []
self.clean_stock = False
self.reset()
return self.next_batch()
#reset when epoch finished
def reset(self):
self.epoch += 1
self.file.close()
self.file = open(self.file_name + "/text.txt", "r")
def build_bucket_dict(self):
bucket_dict={}
for i in xrange (1, self.bucket_option[-1]+1):
count = len(self.bucket_option)-1
for options in reversed(self.bucket_option):
if options >= i:
bucket_dict[i]=count
count = count - 1
return bucket_dict
#pick bucket
def check_bucket(self, pair):
best_i = self.bucket_dict[len(pair[0])]
best_j = self.bucket_dict[len(pair[1])]
return best_i * len(self.bucket_option) + best_j
#fill bucket
def fill_bucket(self):
while True:
line = self.file.readline()
if not line:
break
pair = json.loads(line)
index = self.check_bucket(pair)
#if size exceed all buckets, continue
if index == -1:
continue
self.bucket_list[index].append(pair)
if len(self.bucket_list[index]) == self.batch_size:
return index
#if hit the end of epoch
return -1

Per creare e riempire i nostri tensori enc_imp, dec_imp e dec_tar, utilizziamo una funzione apposita che chiameremo, data_processing che con numpy useremo per inizializzarli e i popolarli di volta in volta, con la porzione di dati relativa per il training:

def data_processing(data, size, batch_size):
enc_len = size[0]
dec_len = size[1]
enc_inp = np.zeros((enc_len, batch_size))
dec_inp = np.zeros((dec_len, batch_size))
dec_tar = np.zeros((dec_len, batch_size))
for i in xrange(len(data)):
pair = data[i]
# copy data to np-array
enc_inp[enc_len-len(pair[0]):enc_len,i] = pair[0][::-1]
dec_inp[1:len(pair[1])+1,i] = pair[1]
dec_tar[0:len(pair[1]),i] = pair[1]
# add start end token
dec_inp[0,i]=2
dec_tar[len(pair[1]),i]=3
return enc_inp, dec_inp, dec_tar

I tensori enc_inp, dec_inp, dec_tar avranno quindi come dimensioni la lunghezza della porzione dei nostri dati e la lunghezza dei layer della nostra rete.

Per avere un’esempio visivo dell’intero processo immaginate metaforicamente gli step che avvengono in una cucina di un ristorante d’alta classe per la preparazione di un piatto, la pietanza viene cotta a seconda dell’ordine del cliente (le frasi vengono indicizzate in un dizionario), passata ad un’altra postazione (la classe reader)per essere tagliata in porzioni da fine dining (il medoto next_batch della classe reader), impiattate nei singoli piatti (i bucket della classe reader) e quindi speziati e decorati con i relativi contorni (i nostri tensori inizializzati con numpy nella funziona data_processing).

Qui potete trovare il codice completo della nostra classe:

https://github.com/cosimoiaia/Tensorflow-Chatbot-Italian/blob/master/util/s2s_reader.py

L’impiattamento è servito, possiamo ora chiamare il cameriere e portare in ordine le nostre portate alla nostra rete neurale.

Nel prossimo articolo daremo un’occhiata sotto il cofano andando più in dettaglio nel funzionamento interno della nostra rete per capire perché il training e il modello finale funzionano, vedremo finalmente come effettuare il training e cercheremo di capire come interpretare quanto bene il nostro modello sta imparando durante questo processo!

Se questo articolo vi è piaciuto, volete restare aggiornati su questa serie e leggerne altre, seguitemi qui su medium, premete sul tasto 👏, per domande, richieste, critiche (e/o complimenti) scrivetemelo nei commenti e, se volete, andate a leggere anche gli altri miei altri articoli/tutorial sul Machine Learning e l’Intelligenza Artificiale. Tutti possiamo diventare insegnanti di macchine!

Stay tuned!

AGGIORNAMENTO:

Qui potete trovare la continuazione di questa serie:

--

--

Cosimo Iaia

Creatore di Loquace, una famiglia di Large Language Model Italiani. Fondatore di ExMentisLab, il Laboratorio di Intelligenza Artificiale. www.exmentislab.it