La creazione di tensori

Autore: Andrea Mercuri

Come negli altri framework per il deep learning, anche in PyTorch il tipo fondamentale è il tensore. Formulando i calcoli che devono essere effettuati da una rete neurale come operazioni tensoriali otteniamo due vantaggi: usiamo un formalismo estremamente compatto e rendiamo possibile alla GPU parallelizzare i calcoli.
I tensori possono risiedere nella RAM principale del computer ed essere processati dalla CPU o nella RAM di una scheda video ed essere processati da una GPU. Questi ultimi sono identificati da un tipo differente dai primi. Segue l’elenco di tutti i tipi di tensori presenti in PyTorch.

Tipo Tensore CPU Tensore GPU
32-bit virgola mobile torch.FloatTensor torch.cuda.FloatTensor
64-bit virgola mobile torch.DoubleTensor torch.cuda.DoubleTensor
16-bit virgola mobile torch.HalfTensor torch.cuda.HalfTensor
8-bit intero (senza segno) torch.ByteTensor torch.cuda.ByteTensor
8-bit intero (con segno) torch.CharTensor torch.cuda.CharTensor
16-bit intero (con segno) torch.ShortTensor torch.cuda.ShortTensor
32-bit intero (con segno) torch.IntTensor torch.cuda.IntTensor
64-bit intero (con segno) torch.LongTensor torch.cuda.LongTensor

Come prima cosa importiamo PyTorch.

import torch

Possiamo creare un tensore non inizializzato invocando il costruttore di uno dei tipi elencati sopra.

x = torch.FloatTensor(3, 2)
print(x)
1.00000e-25 *
&nbsp&nbsp9.9872  0.0000
&nbsp&nbsp9.9872  0.0000
&nbsp&nbsp0.0000  0.0000
&nbsp&nbsp[torch.FloatTensor of size 3x2]

In questo caso il tensore è creato nella RAM principale. Se invece vogliamo creare un tensore nella GPU corrente dobbiamo utilizzare uno dei tipi cuda.

x = torch.cuda.FloatTensor(3, 2)
print(x)
nan nan
nan nan
nan nan
[torch.cuda.FloatTensor of size 3x2 (GPU 0)]

In questo caso veniamo informati che il tensore si trova sulla prima GPU. Le GPU presenti
sulla macchina sono numerate con numeri interi a partire da 0.
Possiamo creare tensori da liste Python

torch.FloatTensor([[1,2,3],[4,5,6]])
1  2  3
4  5  6
[torch.FloatTensor of size 2x3]

o da array numpy.

x_np = np.array([1,2,3,4], dtype=np.float32)
x = torch.FloatTensor(x_np)

Otteniamo lo stesso risultato col metodo from_numpy.

x = torch.from_numpy(x_np)

Notiamo che l’array numpy e il tensore PyTorch condividono i dati. Se modifichiamo uno dei due, l’altro subisce le medesime modifiche.

x[0] = 0
print(x_np)
[ 0.,  2.,  3.,  4.]
print(x)
0
2
3
4
[torch.FloatTensor of size 4]

Possiamo creare tensori da altri tensori.

y = torch.FloatTensor(x)
print(y)
0
2
3
4

Anche in questo caso il tensore creato condivide i dati con il tensore originale.
Possiamo creare tensori di zeri.

torch.zeros(3,2)
0  0
0  0
0  0
[torch.FloatTensor of size 3x2]

Possiamo creare tensori di numeri casuali estratti da una certa distribuzione, ad esempio uniforme sull’intervallo [0,1].

torch.rand(2, 3)
0.1256  0.0406  0.2072
0.2479  0.0515  0.093
[torch.FloatTensor of size 2x3]

Ogni tensore vive nella memoria principale o in quella di una scheda video. Due tensori possono essere gli operandi di una medesima operazione solo se risiedono nella stessa memoria (e in questo caso il risultato risiede ancora nella stessa memoria). Se proviamo, invece, a combinare (ad esempio sommandoli) un tensore che risiede nella RAM principale e uno che sta su una scheda video (o due tensori in due schede video differenti) otteniamo un’eccezione.

xcpu = torch.FloatTensor(3,2)
xgpu = torch.cuda.FloatTensor(3,2)
xcpu + xgpu
TypeError             Traceback (most recent call last)
in ()
----> 1 xcpu + xgpu
…

Se vogliamo ottenere una copia di un tensore x sulla prima GPU possiamo utilizzare il metodo cuda.

y = x.cuda(device=0)

Se il tensore è già sulla prima GPU viene ritornato il tensore originale.
Invece per ottenere una copia di un tensore x sulla RAM principale utilizziamo il metodo cpu.

y = x.cpu()

Possiamo convertire un tensore ad un tipo differente passando al metodo type il tipo di destinazione.

y = x.type(torch.ByteTensor)

Otteniamo lo stesso risultato chiamando uno specifico metodo di conversione.

y = x.byte()

Se oltre a cambiare il tipo vogliamo copiare il tensore sulla GPU corrente dobbiamo passare al metodo type un tipo cuda

y = x.type(torch.cuda.ByteTensor)

oppure scrivere

y = x.byte().cuda()

Per rendere corrente la seconda GPU utilizziamo set_device.

torch.cuda.set_device(1)

Se ora scriviamo

torch.cuda.current_device()

otteniamo 1 come valore di ritorno. Questo significa che ora la GPU corrente è la seconda e non più la prima e che, ad esempio, quando utilizziamo il metodo cuda su un tensore ne creiamo una copia sulla seconda GPU invece che sulla prima.
È anche possibile utilizzare un context manager per cambiare temporaneamente la GPU corrente.
Ad esempio scrivendo

with torch.cuda.device(1):
&nbsp&nbspx1 = torch.cuda.FloatTensor(2,3)
x2 = torch.cuda.FloatTensor(2,3)

quando la GPU corrente è quella di indice 0, x1 viene creato sulla seconda GPU (indice 1), x2 sulla prima (indice 0).
Tutte le funzionalità relative alla creazione di tensori sono contenute nei package torch, torch.cuda e nella classe torch.Tensor. Nei prossimi tutorial continueremo l'esplorazione dei tensori.
Riferimenti

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *