• Non ci sono risultati.

Moduli Utils e GPUAccelerated

4.2 Realizzazione

4.2.7 Moduli Utils e GPUAccelerated

Per motivi di precisione di macchina, il tipo floatprecision per i pesi delle matrici, in caso di accelerazione grafica, `e sempre float32.

Si focalizzi l’attenzione sulla classe ESN e sull’accelerazione grafica delle operazioni ma- triciali coinvolte. L’obiettivo principale `e quello di avere un’unica implementazione per ogni metodo della classe ESN, cio`e ognuno deve usare matrici e array in modo indipen- dente dalla memoria in cui sono memorizzati; pertanto essi non devono conoscere la natura di tali matrici e (dunque) delle operazioni matriciali.

In particolare ogni operazione matriciale `e implementata tramite due funzioni, a se- conda della memoria in cui le matrici sono memorizzate:

• una funzione `e implementata in un modulo chiamato Utils, in cui le matrici sono delle istanze della classe matrix;

• l’altra funzione `e implementata in un modulo chiamato GPUAccelerated, in cui le matrici sono delle istanze della classe CUDAMatrix.

Il modulo EchoStateNetwork deve in ogni caso importare i namespace relativi ai due moduli. Il costruttore di ESN prende in input anche l’identificativo gpu id della scheda grafica da utilizzare, il quale, come detto in Sezione 4.1.8, determina anche se utilizzare o meno la GPU. Per conseguire l’obiettivo, per ogni operazione matriciale i suoi due metodi devono avere la stessa interfaccia, e il metodo di ESN deve usare un riferimento, denominato mod e memorizzato come attributo di ESN, che

• se gpu id > -1, memorizza il riferimento al modulo GPUAccelerated, • altrimenti memorizza il riferimento al modulo Utils.

Ad esempio l’operazione che effettua il prodotto interno tra due matrici `e implementata tramite due funzioni che si chiamano dot, prendono in input due oggetti di tipo x e restituiscono un oggetto dello stesso tipo. L’operazione effettuata da una funzione di attivazione `e implementata tramite due funzioni che prendono in input un oggetto di tipo x e ne restituiscono un altro dello stesso tipo. Il tipo x `e matrix o CUDAMatrix a seconda del modulo in cui le funzioni sono definite.

Se si considera ad esempio il metodo compute, esso effettua la seguente operazione per calcolare le attivazioni di output:

readout.outputvector = readout.f(mod.dot(readout.Wout,

readout.concatenation_vector_for_output))

La funzione f e gli array Wout e concatenation vector for output hanno un tipo stabilito in fase di istanziazione della classe ESN, e non `e conosciuto a tempo di compi- lazione dal metodo compute. Tuttavia siccome il costruttore di tale classe assume che le

istanze relative ai tre strati siano state gi`a create (e quindi con esse anche le rispettive matrici e gli array), `e necessario durante l’inizializzazione delle matrici, conoscere il loro tipo.

La soluzione `e quella di inizializzare tutte le matrici nella memoria centrale utilizzando la classe matrix. Peraltro in questo modo si aggira il problema della non disponibilit`a in CUDAMat di alcune operazioni durante la creazione delle matrici, come ad esem- pio il calcolo degli autovalori. Una volta passate le tre istanze al costruttore di ESN e memorizzate come suoi attributi, se gpu id > -1 si utilizza, per ogni attributo di tipo matrix di ogni strato, la funzione toGPUArray del modulo GPUAccelerated, che data una matrice di tipo matrix, ne restituisce una equivalente ma di tipo CUDAMatrix. Il seguente esempio trasferisce nella memoria della GPU la matrice Wout:

readout.Wout = GPUAccelerated.toGPUArray(readout.Wout)

Si noti che grazie alla natura non tipizzata di Python si possono riassegnare le matrici di tipo CUDAMatrix agli stessi attributi della classe ESN. Il metodo toGPUArray `e definito come segue:

def toGPUArray(mat):

return cudamat.CUDAMatrix(numpy.asarray(mat))

Il costruttore di ESN deve adattare anche le funzioni di attivazione del reservoir e del readout. A tale scopo il modulo GPUAccelerated fornisce la funzione toGPUFunction. Ad esempio l’istruzione seguente adatta la funzione di attivazione del readout.

readout.f = GPUAccelerated.toGPUFunction(readout.activation_function_string)

dove l’attributo activation function string di readout memorizza la stringa pas- sata come parametro al suo costruttore, indicante la funzione di attivazione che si vuole utilizzare per questo strato. La funzione toGPUFunction `e definita come segue:

def toGPUFunction(which_function): if which_function == "tanh":

f = tanh

elif which_function == "logistic": f = logistic_function

elif which_function == "rectifier": f = rectifier_function

elif which_function == "softplus": f = softplus_function

elif which_function == "identity": f = identity_function

elif which_function == "pseudotanh": f = pseudotanh_function

elif which_function == "sign": f = sign_function

return f

In definitiva, il costruttore della classe ESN in base al valore di gpu id indicato, deter- mina se mantenere tutti i dati nella memoria centrale, o trasferirli nella memoria della GPU.

La metodologia attuata riguardante il design dei metodi della classe ESN, `e applicata in tutti gli altri moduli. In particolare per quanto riguarda il modulo ESNTraining, l’unica eccezione `e la seguente: dato che la classe CUDAMat non mette a disposizione operatori pi`u complessi come il calcolo dell’inversa di una matrice o della sua pseudo- inversa, il calcolo della matrice readout.Wout tramite ridge regression e pseudo-inversa viene svolto sempre sulla CPU tramite NumPy.

Isolando tutti gli aspetti riguardanti l’accelerazione grafica in un modulo dedicato, `e possibile sostituire CUDAMat con un’altra libreria, senza intaccare il design dei metodi e delle classi presenti negli altri moduli, che fanno uso di tale modulo. In particolare, `e sufficiente cambiare solo la definizione dei wrapper presenti nel modulo GPUAccelerated e mantenere la stessa interfaccia.