Nel deep learning, è comune vedere molte discussioni sui tensori come struttura dati fondamentale. Tensor compare persino nel nome della libreria di machine learning di punta di Google: “TensorFlow”. I tensori sono un tipo di struttura dati usata in algebra lineare e, come vettori e matrici, puoi eseguire operazioni aritmetiche con i tensori.
PyTorch è invece un pacchetto Python sviluppato da Facebook che offre due funzionalità di alto livello: 1) Calcolo con tensori (come NumPy) con potente accelerazione GPU e 2) Reti neurali profonde costruite su un sistema di differenziazione automatica basato su “tape”.
In questo tutorial scoprirai cosa sono i tensori e come manipolarli in Python con PyTorch.
Quindi, senza ulteriori indugi, iniziamo con l’introduzione ai tensori.
Introduzione ai tensori
Un tensore è una generalizzazione di vettori e matrici e si può intendere facilmente come un array multidimensionale. Secondo il noto libro di deep learning “Deep Learning” (Goodfellow et al.) -
"Nel caso generale, un array di numeri disposto su una griglia regolare con un numero variabile di assi è noto come tensore."
Uno scalare è un tensore di ordine zero (o rango zero). Un vettore è un tensore monodimensionale o di primo ordine, e una matrice è un tensore bidimensionale o di secondo ordine.
La seguente infografica descrive i tensori in modo molto intuitivo:
Costruiamo ora l’intuizione dietro i tensori in modo ancora più chiaro.
Un tensore è il mattone di base del machine learning moderno. Alla base è un contenitore di dati. Per lo più contiene numeri. A volte include anche stringhe, ma è raro. Quindi pensalo come a un secchio di numeri.
Spesso però si confondono i tensori con gli array multidimensionali. Come dice StackExchange:
I tensori e gli array multidimensionali sono tipi di oggetti diversi. Il primo è un tipo di funzione. Il secondo è una struttura dati adatta a rappresentare un tensore in un sistema di coordinate.
Matematicamente, i tensori sono definiti come funzioni multilineari. Una funzione multilineare consiste di varie variabili vettoriali. Un campo tensoriale è una funzione a valori tensoriali. Per una spiegazione matematica rigorosa, puoi leggere qui.
Dunque, i tensori sono funzioni o contenitori che devi definire. Il calcolo vero e proprio avviene quando vengono forniti i dati. Quello che vedi come array o multidimensionale (1D, 2D, …, ND) può essere considerato come tensori generici.
Parliamo ora un po’ della notazione dei tensori.
La notazione dei tensori assomiglia a quella delle matrici: una lettera maiuscola rappresenta un tensore e lettere minuscole con indici interi rappresentano i valori scalari all’interno del tensore.
Molte delle operazioni che si possono eseguire con scalari, vettori e matrici possono essere riformulate per essere eseguite con i tensori.
Come strumento, i tensori e l’algebra tensoriale sono ampiamente usati in fisica e ingegneria. Il termine e l’insieme di tecniche note nel machine learning per l’addestramento e l’uso di modelli di deep learning possono essere descritti in termini di tensori.
Introduzione a PyTorch
PyTorch è un pacchetto di calcolo scientifico basato su Python pensato per:
Una sostituzione di NumPy per sfruttare la potenza delle GPU.
Una piattaforma di ricerca sul deep learning che offre la massima flessibilità e velocità.
Riassumiamo rapidamente le caratteristiche uniche di PyTorch:
PyTorch offre un’ampia varietà di routine sui tensori per accelerare e soddisfare le tue esigenze di calcolo scientifico, come slicing, indicizzazione, operazioni matematiche, algebra lineare, riduzioni. E sono veloci.
PyTorch ha un modo unico di costruire reti neurali: usando e rigiocando un registratore a nastro.
La maggior parte dei framework come TensorFlow, Theano, Caffe e CNTK hanno una visione statica del mondo. Bisogna costruire una rete neurale e riutilizzare la stessa struttura più e più volte. Cambiare il comportamento della rete significa dover ripartire da zero.
PyTorch usa una tecnica chiamata auto-differenziazione in modalità inversa (Reverse-mode), che ti permette di cambiare arbitrariamente il comportamento della tua rete con latenza o overhead nulli. La nostra ispirazione arriva da diversi articoli di ricerca su questo tema, oltre che da lavori passati e presenti come autograd, autograd, Chainer, ecc.
(Sebbene questa tecnica non sia unica di PyTorch, la sua è una delle implementazioni più veloci finora. Ottieni il meglio in termini di velocità e flessibilità per le tue ricerche più ardite.)
PyTorch ha un overhead di framework minimo. Integriamo librerie di accelerazione come Intel MKL e NVIDIA (CuDNN, NCCL) per massimizzare la velocità. Al nucleo, i backend per Tensori su CPU e GPU e Reti Neurali (TH, THC, THNN, THCUNN) sono scritti come librerie indipendenti con API C99. Sono maturi e testati da anni.
(Perciò, PyTorch è piuttosto veloce — che tu esegua reti neurali piccole o grandi.)
L’uso della memoria in PyTorch è estremamente efficiente rispetto a Torch o ad alcune alternative. I creatori di PyTorch hanno scritto allocator di memoria personalizzati per la GPU per garantire che i tuoi modelli di deep learning siano massimamente efficienti in memoria. Questo ti consente di addestrare modelli di deep learning più grandi di prima.
Questo blog mette molto bene a confronto PyTorch e TensorFlow.
L’installazione di PyTorch è piuttosto semplice. Poiché PyTorch supporta un calcolo GPU efficiente, comunica in modo efficace con i driver Cuda e lavora più velocemente.
Per usare PyTorch ti serviranno torch e torchvision. Installiamoli in ambiente Windows. Più avanti vedrai i passaggi anche per Linux e Mac.
Io ho Python 3.5 con Anaconda configurato. Inoltre, non ho CUDA. Esegui i seguenti comandi per installare torch e torchvision:
Se hai capito bene i tensori, dimmi nei commenti che tipo di tensore è x!
Puoi creare un tensore a partire da un tensore esistente. Questi metodi riutilizzeranno le proprietà del tensore di input, ad esempio il dtype (tipo di dato), a meno che l’utente non fornisca nuovi valori:
x = x.new_ones(5,3, dtype=torch.double)print(x)
x = torch.randn_like(x, dtype=torch.float)print(x)Offerto da
tensor([[1.,1.,1.],[1.,1.,1.],[1.,1.,1.],[1.,1.,1.],[1.,1.,1.]], dtype=torch.float64)tensor([[-1.2174,1.1807,1.4249],[-1.1114,-0.8098,0.4003],[0.0780,-0.5011,-1.0985],[1.8160,-0.3778,-0.8610],[-0.7109,-2.0509,-1.2079]])Offerto da
Nota che torch.Size è in realtà una tupla, quindi supporta tutte le operazioni sulle tuple.
Ora facciamo un’operazione di somma sui tensori.
Addizione di tensori
L’addizione elemento per elemento di due tensori con le stesse dimensioni produce un nuovo tensore con le stesse dimensioni in cui ogni valore scalare è la somma elemento per elemento degli scalari nei tensori di partenza.
# Sintassi 1 per l'addizione di tensori in PyTorch
y = torch.rand(5,3)print(x)print(y)print(x + y)Offerto da
tensor([[-1.2174,1.1807,1.4249],[-1.1114,-0.8098,0.4003],[0.0780,-0.5011,-1.0985],[1.8160,-0.3778,-0.8610],[-0.7109,-2.0509,-1.2079]])tensor([[0.8285,0.7619,0.1147],[0.1624,0.8994,0.6119],[0.2802,0.2950,0.7098],[0.8132,0.3382,0.4383],[0.6738,0.2022,0.3264]])tensor([[-0.3889,1.9426,1.5396],[-0.9490,0.0897,1.0122],[0.3583,-0.2061,-0.3887],[2.6292,-0.0396,-0.4227],[-0.0371,-1.8487,-0.8815]])Offerto da
# Sintassi 2 per l'addizione di tensori in PyTorchprint(torch.add(x, y))Offerto da
tensor([[-0.3889,1.9426,1.5396],[-0.9490,0.0897,1.0122],[0.3583,-0.2061,-0.3887],[2.6292,-0.0396,-0.4227],[-0.0371,-1.8487,-0.8815]])Offerto da
Facciamo ora la sottrazione tra tensori.
Sottrazione di tensori
La sottrazione elemento per elemento di un tensore da un altro con le stesse dimensioni produce un nuovo tensore con le stesse dimensioni in cui ogni valore scalare è la sottrazione elemento per elemento degli scalari nei tensori di partenza4.
Ora diamo un’occhiata al prodotto tra tensori:
Prodotto tra tensori
Esegue una moltiplicazione tra matrici su mat1 e mat2.
Se mat1 è un tensore (n×m), mat2 è un tensore (m×p), l’output sarà un tensore (n×p).
In PyTorch fai i prodotti tra tensori come segue:
mat1 = torch.randn(2,3)
mat2 = torch.randn(3,3)print(mat1)print(mat2)print(torch.mm(mat1, mat2))Offerto da
tensor([[1.9490,-0.6503,-1.9448],[-0.7126,1.0519,-0.4250]])tensor([[0.0846,0.4410,-0.0625],[-1.3264,-0.5265,0.2575],[-1.3324,0.6644,0.3528]])tensor([[3.6185,-0.0901,-0.9753],[-0.8892,-1.1504,0.1654]])Offerto da
Nota che torch.mm() non effettua broadcasting. Parliamo un po’ di broadcasting.
Broadcasting
Il termine broadcasting descrive come vengono trattati gli array con forme diverse durante le operazioni aritmetiche. Soggetto a determinate condizioni, l’array più piccolo viene “esteso” sull’array più grande in modo che abbiano forme compatibili. Il broadcasting offre un modo per vettorializzare le operazioni sugli array così che i cicli avvengano in C invece che in Python. Lo fa senza creare copie inutili dei dati e solitamente porta a implementazioni efficienti degli algoritmi. Ci sono però casi in cui il broadcasting è una cattiva idea perché porta a un uso inefficiente della memoria che rallenta il calcolo.
Due tensori sono “broadcastable” se valgono le seguenti regole:
Ogni tensore ha almeno una dimensione.
Iterando sulle dimensioni a partire da quella di coda, le dimensioni devono essere uguali, oppure una delle due è 1, oppure una delle due non esiste.
Capiremo questo con PyTorch usando il seguente snippet di codice:
x=torch.empty(5,7,3)
y=torch.empty(5,7,3)# same shapes are always broadcastable (i.e. the above rules always hold)Offerto da
x=torch.empty((0,))
y=torch.empty(2,2)# x and y are not broadcastable, because x does not have at least 1 dimensionOfferto da
# can line up trailing dimensions
x=torch.empty(5,3,4,1)
y=torch.empty(3,1,1)# x and y are broadcastable.# 1st trailing dimension: both have size 1# 2nd trailing dimension: y has size 1# 3rd trailing dimension: x size == y size# 4th trailing dimension: y dimension doesn't existOfferto da
# but:>>> x=torch.empty(5,2,4,1)>>> y=torch.empty(3,1,1)# x and y are not broadcastable, because in the 3rd trailing dimension 2 != 3Offerto da
Ora che hai un’idea chiara del broadcasting, vediamo, se due tensori sono “broadcastable”, quale sarà il tensore risultante.
Se due tensori x, y sono “broadcastable”, la dimensione del tensore risultante si calcola così:
Se il numero di dimensioni di x e y non è uguale, si premette 1 alle dimensioni del tensore con meno dimensioni per renderle di uguale lunghezza. Poi, per ogni dimensione, la dimensione risultante è il massimo tra le dimensioni di x e y lungo quella dimensione. Per esempio:
# can line up trailing dimensions to make reading easier
x=torch.empty(5,1,4,1)
y=torch.empty(3,1,1)(x+y).size()
torch.Size([5,3,4,1])Offerto da
x=torch.empty(5,2,4,1)
y=torch.empty(3,1,1)(x+y).size()Offerto da
---------------------------------------------------------------------------
RuntimeError Traceback(most recent call last)<ipython-input-17-72fb34250db7>in<module>()1 x=torch.empty(5,2,4,1)2 y=torch.empty(3,1,1)---->3(x+y).size()RuntimeError: The size of tensor a(2) must match the size of tensor b(3) at non-singleton dimension 1Offerto da
Ora il concetto ti è chiaro!
Il prodotto tensoriale è la forma più comune di moltiplicazione tra tensori che potresti incontrare, ma esistono molti altri tipi di moltiplicazioni tra tensori, come il prodotto scalare tra tensori e la contrazione di tensori.
Convertire un tensore Torch in un array NumPy e viceversa è un gioco da ragazzi. Il concetto è chiamato Numpy Bridge. Diamo un’occhiata.
Numpy Bridge
Il tensore Torch e l’array NumPy condivideranno la stessa area di memoria sottostante, e cambiare uno cambierà anche l’altro.
Conversione di un tensore Torch in un array NumPy.
# A 1D tensor of 5 ones
a = torch.ones(5)print(a)Offerto da
Quindi, nelle sezioni sopra, hai fatto alcune operazioni di base con i tensori come addizione, sottrazione e prodotti tra tensori. Ora, nella sezione seguente, implementerai una semplice rete neurale in PyTorch.
Implementare una semplice rete neurale con PyTorch
Se ti serve un ripasso sulle reti neurali, allora questo articolo di DataCamp è il posto migliore da cui partire. Potresti anche voler consultare i seguenti link:
Prima di implementare, discutiamo un po’ di un concetto chiamato differenziazione automatica che è centrale per tutte le reti neurali in PyTorch. È particolarmente utile per calcolare i gradienti durante la backpropagation.
Il pacchetto autograd fornisce la differenziazione automatica per tutte le operazioni sui tensori. È un framework “define-by-run”, il che significa che la tua backpropagation è definita da come viene eseguito il tuo codice e che ogni singola iterazione può essere diversa.
Vediamo la differenziazione automatica in azione con un esempio di codice molto semplice.
# Create a Rank-2 tensor of all ones
x = torch.ones(2,2, requires_grad=True)print(x)Offerto da
Ora creerai alcuni dati di input fittizi x e alcuni dati target fittizi y. Userai i tensori di PyTorch per archiviare questi dati. I tensori di PyTorch possono essere usati e manipolati proprio come gli array NumPy, con il vantaggio aggiuntivo che possono essere eseguiti sulle GPU. In questo tutorial li eseguirai semplicemente sulla CPU. Anche se, trasferirli su una GPU è piuttosto semplice.
x = torch.randn(batch_size, n_in)
y = torch.tensor([[1.0],[0.0],[0.0],[1.0],[1.0],[1.0],[0.0],[0.0],[1.0],[1.0]])Offerto da
E ora definirai il modello in una riga di codice.
model = nn.Sequential(nn.Linear(n_in, n_h),
nn.ReLU(),
nn.Linear(n_h, n_out),
nn.Sigmoid())Offerto da
Questo crea un modello che appare come input -> linear -> relu -> linear -> sigmoid. Esiste un altro modo per definire i modelli, usato per modelli più complessi e personalizzati. Si fa definendo il modello in una classe. Puoi leggerne qui.
Ora è il momento di costruire la funzione di perdita. Userai la Mean Squared Error Loss.
Inoltre, non dimenticare di definire l’ottimizzatore. In questo esempio userai il potente Stochastic Gradient Descent e un learning rate di 0.01. model.parameters() restituisce un iteratore sui parametri del modello (pesi e bias).
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)Offerto da
Ora eseguirai la discesa del gradiente per 50 epoche. Questo esegue, in sequenza, forward propagation, calcolo della loss, backward propagation e aggiornamento dei parametri.
for epoch inrange(50):# Forward Propagation
y_pred = model(x)# Compute and print loss
loss = criterion(y_pred, y)print('epoch: ', epoch,' loss: ', loss.item())# Zero the gradients
optimizer.zero_grad()# perform a backward pass (backpropagation)
loss.backward()# Update the parameters
optimizer.step()Offerto da
epoch:0loss:0.2399429827928543epoch:1loss:0.23988191783428192epoch:2loss:0.23982088267803192epoch:3loss:0.2397598922252655epoch:4loss:0.23969893157482147epoch:5loss:0.23963800072669983epoch:6loss:0.23957709968090057epoch:7loss:0.23951618373394012epoch:8loss:0.23945537209510803epoch:9loss:0.23939454555511475epoch:10loss:0.23933371901512146epoch:11loss:0.23927298188209534epoch:12loss:0.23921218514442444epoch:13loss:0.23915143311023712epoch:14loss:0.2390907108783722epoch:15loss:0.23903003334999084epoch:16loss:0.23896940052509308epoch:17loss:0.23890872299671173epoch:18loss:0.23884813487529755epoch:19loss:0.23878750205039978epoch:20loss:0.23872694373130798epoch:21loss:0.2386663407087326epoch:22loss:0.2386058121919632epoch:23loss:0.23854532837867737epoch:24loss:0.23848481476306915epoch:25loss:0.23842433094978333epoch:26loss:0.2383638620376587epoch:27loss:0.23830339312553406epoch:28loss:0.2382429838180542epoch:29loss:0.23818258941173553epoch:30loss:0.2381247729063034epoch:31loss:0.2380656749010086epoch:32loss:0.23800739645957947epoch:33loss:0.2379491776227951epoch:34loss:0.2378900945186615epoch:35loss:0.23783239722251892epoch:36loss:0.23777374625205994epoch:37loss:0.23771481215953827epoch:38loss:0.23765745759010315epoch:39loss:0.23759838938713074epoch:40loss:0.23753997683525085epoch:41loss:0.2374821901321411epoch:42loss:0.23742322623729706epoch:43loss:0.23736533522605896epoch:44loss:0.23730707168579102epoch:45loss:0.23724813759326935epoch:46loss:0.23719079792499542epoch:47loss:0.23713204264640808epoch:48loss:0.23707345128059387epoch:49loss:0.2370160073041916Offerto da
y_pred ottiene i valori predetti da un forward pass del modello. Passi questo, insieme ai valori target y, al criterion che calcola la loss.
Poi, optimizer.zero_grad() azzera tutti i gradienti. Devi farlo per evitare che i gradienti precedenti continuino ad accumularsi.
Quindi, loss.backward() è la magia principale di PyTorch che usa la funzione Autograd. Autograd calcola automaticamente tutti i gradienti rispetto a tutti i parametri in base al grafo computazionale che crea dinamicamente. In pratica, esegue il backward pass (backpropagation) della discesa del gradiente.
Infine, chiami optimizer.step() che esegue un singolo aggiornamento di tutti i parametri usando i nuovi gradienti.
Sei arrivato alla fine. In questo post hai coperto un bel po’ di argomenti partendo dai tensori fino alla differenziazione automatica e altro ancora! Hai anche implementato una semplice rete neurale usando PyTorch e il suo sistema di tensori.
Se vuoi saperne di più su PyTorch e approfondire, dai un’occhiata alla documentazione e ai tutorial ufficiali di PyTorch. Sono scritti davvero bene. Li trovi qui.
Di seguito alcune referenze che mi hanno aiutato a scrivere questo tutorial:
A Student’s Guide to Vectors and Tensors, 2011. link
Documentazione ufficiale di PyTorch (ho già menzionato il link).
Scopri i 15 migliori server MCP remoti che stanno trasformando lo sviluppo AI nel 2026. Scopri come migliorano automazione, ragionamento, sicurezza e velocità dei workflow.
Nel deep learning, è comune vedere molte discussioni sui tensori come struttura dati fondamentale. Tensor compare persino nel nome della libreria di machine learning di punta di Google: “TensorFlow”. I tensori sono un tipo di struttura dati usata in algebra lineare e, come vettori e matrici, puoi eseguire operazioni aritmetiche con i tensori.
PyTorch è invece un pacchetto Python sviluppato da Facebook che offre due funzionalità di alto livello: 1) Calcolo con tensori (come NumPy) con potente accelerazione GPU e 2) Reti neurali profonde costruite su un sistema di differenziazione automatica basato su “tape”.
In questo tutorial scoprirai cosa sono i tensori e come manipolarli in Python con PyTorch.
Quindi, senza ulteriori indugi, iniziamo con l’introduzione ai tensori.
Introduzione ai tensori
Un tensore è una generalizzazione di vettori e matrici e si può intendere facilmente come un array multidimensionale. Secondo il noto libro di deep learning “Deep Learning” (Goodfellow et al.) -
"Nel caso generale, un array di numeri disposto su una griglia regolare con un numero variabile di assi è noto come tensore."
Uno scalare è un tensore di ordine zero (o rango zero). Un vettore è un tensore monodimensionale o di primo ordine, e una matrice è un tensore bidimensionale o di secondo ordine.
La seguente infografica descrive i tensori in modo molto intuitivo:
Costruiamo ora l’intuizione dietro i tensori in modo ancora più chiaro.
Un tensore è il mattone di base del machine learning moderno. Alla base è un contenitore di dati. Per lo più contiene numeri. A volte include anche stringhe, ma è raro. Quindi pensalo come a un secchio di numeri.
Spesso però si confondono i tensori con gli array multidimensionali. Come dice StackExchange:
I tensori e gli array multidimensionali sono tipi di oggetti diversi. Il primo è un tipo di funzione. Il secondo è una struttura dati adatta a rappresentare un tensore in un sistema di coordinate.
Matematicamente, i tensori sono definiti come funzioni multilineari. Una funzione multilineare consiste di varie variabili vettoriali. Un campo tensoriale è una funzione a valori tensoriali. Per una spiegazione matematica rigorosa, puoi leggere qui.
Dunque, i tensori sono funzioni o contenitori che devi definire. Il calcolo vero e proprio avviene quando vengono forniti i dati. Quello che vedi come array o multidimensionale (1D, 2D, …, ND) può essere considerato come tensori generici.
Parliamo ora un po’ della notazione dei tensori.
La notazione dei tensori assomiglia a quella delle matrici: una lettera maiuscola rappresenta un tensore e lettere minuscole con indici interi rappresentano i valori scalari all’interno del tensore.
Molte delle operazioni che si possono eseguire con scalari, vettori e matrici possono essere riformulate per essere eseguite con i tensori.
Come strumento, i tensori e l’algebra tensoriale sono ampiamente usati in fisica e ingegneria. Il termine e l’insieme di tecniche note nel machine learning per l’addestramento e l’uso di modelli di deep learning possono essere descritti in termini di tensori.
Introduzione a PyTorch
PyTorch è un pacchetto di calcolo scientifico basato su Python pensato per:
Riassumiamo rapidamente le caratteristiche uniche di PyTorch:
PyTorch offre un’ampia varietà di routine sui tensori per accelerare e soddisfare le tue esigenze di calcolo scientifico, come slicing, indicizzazione, operazioni matematiche, algebra lineare, riduzioni. E sono veloci.
PyTorch ha un modo unico di costruire reti neurali: usando e rigiocando un registratore a nastro.
La maggior parte dei framework come TensorFlow, Theano, Caffe e CNTK hanno una visione statica del mondo. Bisogna costruire una rete neurale e riutilizzare la stessa struttura più e più volte. Cambiare il comportamento della rete significa dover ripartire da zero.
PyTorch usa una tecnica chiamata auto-differenziazione in modalità inversa (Reverse-mode), che ti permette di cambiare arbitrariamente il comportamento della tua rete con latenza o overhead nulli. La nostra ispirazione arriva da diversi articoli di ricerca su questo tema, oltre che da lavori passati e presenti come autograd, autograd, Chainer, ecc.
(Sebbene questa tecnica non sia unica di PyTorch, la sua è una delle implementazioni più veloci finora. Ottieni il meglio in termini di velocità e flessibilità per le tue ricerche più ardite.)
(Perciò, PyTorch è piuttosto veloce — che tu esegua reti neurali piccole o grandi.)
Questo blog mette molto bene a confronto PyTorch e TensorFlow.
P.S.: Questo blog di DataCamp è un ottimo punto di partenza per iniziare con TensorFlow.
Installare PyTorch
L’installazione di PyTorch è piuttosto semplice. Poiché PyTorch supporta un calcolo GPU efficiente, comunica in modo efficace con i driver Cuda e lavora più velocemente.
Per usare PyTorch ti serviranno torch e torchvision. Installiamoli in ambiente Windows. Più avanti vedrai i passaggi anche per Linux e Mac.
Io ho Python 3.5 con Anaconda configurato. Inoltre, non ho CUDA. Esegui i seguenti comandi per installare torch e torchvision:
(Tieni presente che PyTorch non supporta Python 2.7. Quindi è necessario avere una versione di Python => 3.5.)
Se hai CUDA abilitato sulla tua macchina, esegui pure quanto segue (questo è per CUDA 9.0):
Vediamo ora i passaggi di installazione per un ambiente Linux con Python 3.5 e senza supporto CUDA:
Sì, l’hai indovinato! È uguale a quello per Windows.
Ora, se hai il supporto CUDA (9.0), allora il passaggio sarà:
Per un ambiente Mac con Python 3.5 e senza supporto CUDA i passaggi saranno:
E, con supporto CUDA (9.0):
(I binari MacOS non supportano CUDA, installa dai sorgenti se ti serve CUDA)
Spero che a questo punto tu abbia completato l’installazione di PyTorch!
Ora tuffiamoci direttamente in un po’ di aritmetica con i tensori in PyTorch.
Aritmetica dei tensori con PyTorch
Per prima cosa, importiamo tutte le librerie necessarie.
Se l’installazione di PyTorch è andata a buon fine, l’esecuzione delle righe di codice sopra non darà errori.
Ora, costruiamo una matrice 5x3, non inizializzata:
Costruiamo una matrice piena di zeri e di tipo long:
Costruiamo un tensore direttamente dai dati:
Se hai capito bene i tensori, dimmi nei commenti che tipo di tensore è x!
Puoi creare un tensore a partire da un tensore esistente. Questi metodi riutilizzeranno le proprietà del tensore di input, ad esempio il dtype (tipo di dato), a meno che l’utente non fornisca nuovi valori:
Ottieni la sua dimensione:
Nota che
torch.Sizeè in realtà una tupla, quindi supporta tutte le operazioni sulle tuple.Ora facciamo un’operazione di somma sui tensori.
Addizione di tensori
L’addizione elemento per elemento di due tensori con le stesse dimensioni produce un nuovo tensore con le stesse dimensioni in cui ogni valore scalare è la somma elemento per elemento degli scalari nei tensori di partenza.
Facciamo ora la sottrazione tra tensori.
Sottrazione di tensori
La sottrazione elemento per elemento di un tensore da un altro con le stesse dimensioni produce un nuovo tensore con le stesse dimensioni in cui ogni valore scalare è la sottrazione elemento per elemento degli scalari nei tensori di partenza4.
Ora diamo un’occhiata al prodotto tra tensori:
Prodotto tra tensori
Esegue una moltiplicazione tra matrici su mat1 e mat2.
Se mat1 è un tensore (n×m), mat2 è un tensore (m×p), l’output sarà un tensore (n×p).
In PyTorch fai i prodotti tra tensori come segue:
Nota che torch.mm() non effettua broadcasting. Parliamo un po’ di broadcasting.
Broadcasting
Il termine broadcasting descrive come vengono trattati gli array con forme diverse durante le operazioni aritmetiche. Soggetto a determinate condizioni, l’array più piccolo viene “esteso” sull’array più grande in modo che abbiano forme compatibili. Il broadcasting offre un modo per vettorializzare le operazioni sugli array così che i cicli avvengano in C invece che in Python. Lo fa senza creare copie inutili dei dati e solitamente porta a implementazioni efficienti degli algoritmi. Ci sono però casi in cui il broadcasting è una cattiva idea perché porta a un uso inefficiente della memoria che rallenta il calcolo.
Due tensori sono “broadcastable” se valgono le seguenti regole:
Capiremo questo con PyTorch usando il seguente snippet di codice:
Ora che hai un’idea chiara del broadcasting, vediamo, se due tensori sono “broadcastable”, quale sarà il tensore risultante.
Se due tensori x, y sono “broadcastable”, la dimensione del tensore risultante si calcola così:
Se il numero di dimensioni di x e y non è uguale, si premette 1 alle dimensioni del tensore con meno dimensioni per renderle di uguale lunghezza. Poi, per ogni dimensione, la dimensione risultante è il massimo tra le dimensioni di x e y lungo quella dimensione. Per esempio:
Ora il concetto ti è chiaro!
Il prodotto tensoriale è la forma più comune di moltiplicazione tra tensori che potresti incontrare, ma esistono molti altri tipi di moltiplicazioni tra tensori, come il prodotto scalare tra tensori e la contrazione di tensori.
Convertire un tensore Torch in un array NumPy e viceversa è un gioco da ragazzi. Il concetto è chiamato Numpy Bridge. Diamo un’occhiata.
Numpy Bridge
Il tensore Torch e l’array NumPy condivideranno la stessa area di memoria sottostante, e cambiare uno cambierà anche l’altro.
Conversione di un tensore Torch in un array NumPy.
Guarda come l’array NumPy cambia di valore.
Quindi, nelle sezioni sopra, hai fatto alcune operazioni di base con i tensori come addizione, sottrazione e prodotti tra tensori. Ora, nella sezione seguente, implementerai una semplice rete neurale in PyTorch.
Implementare una semplice rete neurale con PyTorch
Se ti serve un ripasso sulle reti neurali, allora questo articolo di DataCamp è il posto migliore da cui partire. Potresti anche voler consultare i seguenti link:
Prima di implementare, discutiamo un po’ di un concetto chiamato differenziazione automatica che è centrale per tutte le reti neurali in PyTorch. È particolarmente utile per calcolare i gradienti durante la backpropagation.
Il pacchetto autograd fornisce la differenziazione automatica per tutte le operazioni sui tensori. È un framework “define-by-run”, il che significa che la tua backpropagation è definita da come viene eseguito il tuo codice e che ogni singola iterazione può essere diversa.
Vediamo la differenziazione automatica in azione con un esempio di codice molto semplice.
Esegui un’operazione di addizione.
Esegui qualche altra operazione su y.
Facciamo ora la backprop, perché out contiene uno scalare, out.backward() è equivalente a out.backward(torch.tensor(1)).
Ora conosci la differenziazione automatica e come PyTorch la gestisce. Adesso coderai una semplice rete neurale usando PyTorch.
Creerai una semplice rete neurale con uno strato nascosto e una singola unità di output. Userai l’attivazione ReLU nello strato nascosto e l’attivazione sigmoid nello strato di output.
Per prima cosa devi importare la libreria PyTorch. Le reti neurali possono essere costruite usando il pacchetto torch.nn.
Poi definisci le dimensioni di tutti gli strati e la dimensione del batch:
Ora creerai alcuni dati di input fittizi x e alcuni dati target fittizi y. Userai i tensori di PyTorch per archiviare questi dati. I tensori di PyTorch possono essere usati e manipolati proprio come gli array NumPy, con il vantaggio aggiuntivo che possono essere eseguiti sulle GPU. In questo tutorial li eseguirai semplicemente sulla CPU. Anche se, trasferirli su una GPU è piuttosto semplice.
E ora definirai il modello in una riga di codice.
Questo crea un modello che appare come input -> linear -> relu -> linear -> sigmoid. Esiste un altro modo per definire i modelli, usato per modelli più complessi e personalizzati. Si fa definendo il modello in una classe. Puoi leggerne qui.
Ora è il momento di costruire la funzione di perdita. Userai la Mean Squared Error Loss.
Inoltre, non dimenticare di definire l’ottimizzatore. In questo esempio userai il potente Stochastic Gradient Descent e un learning rate di 0.01. model.parameters() restituisce un iteratore sui parametri del modello (pesi e bias).
Ora eseguirai la discesa del gradiente per 50 epoche. Questo esegue, in sequenza, forward propagation, calcolo della loss, backward propagation e aggiornamento dei parametri.
y_pred ottiene i valori predetti da un forward pass del modello. Passi questo, insieme ai valori target y, al criterion che calcola la loss.
Poi, optimizer.zero_grad() azzera tutti i gradienti. Devi farlo per evitare che i gradienti precedenti continuino ad accumularsi.
Quindi, loss.backward() è la magia principale di PyTorch che usa la funzione Autograd. Autograd calcola automaticamente tutti i gradienti rispetto a tutti i parametri in base al grafo computazionale che crea dinamicamente. In pratica, esegue il backward pass (backpropagation) della discesa del gradiente.
Infine, chiami optimizer.step() che esegue un singolo aggiornamento di tutti i parametri usando i nuovi gradienti.
Sei arrivato alla fine. In questo post hai coperto un bel po’ di argomenti partendo dai tensori fino alla differenziazione automatica e altro ancora! Hai anche implementato una semplice rete neurale usando PyTorch e il suo sistema di tensori.
Se vuoi saperne di più su PyTorch e approfondire, dai un’occhiata alla documentazione e ai tutorial ufficiali di PyTorch. Sono scritti davvero bene. Li trovi qui.
Di seguito alcune referenze che mi hanno aiutato a scrivere questo tutorial:
Fammi sapere se hai qualcosa da chiedere o discutere nei commenti!
Se ti interessa imparare di più su Python, segui il corso di DataCamp Statistical Thinking in Python (Parte 1).