EDI+VA · Esercizio di Programmazione

EP09_01 — 🟢 Convoluzione 2D Manuale ( Forward di un Livello Appreso)

9.10.1 EP09_01 🟢 Convoluzione 2D Manuale (Forward di un Livello Appreso)

Il PyTorch, presentato in questo capitolo, esegue nn.Conv2d(x) in un’unica chiamata — ma dietro di essa c’è solo la correlazione incrociata tra un kernel (già addestrato) e un intorno dell’input, seguita dalla somma di un bias e di un’attivazione, esattamente come formalizzato nella Sezione “Livello Convoluzionale”. La differenza essenziale rispetto alla convoluzione con kernel fissi del Capitolo 3 è che, qui, i valori del kernel e del bias sono già pronti (come se fossero stati appresi per gradiente), e spetta a te riprodurre manualmente il passaggio diretto (forward pass) che il framework esegue internamente.

Prima di addestrare una vera CNN, ti è stato affidato il compito di implementare questo passaggio diretto da zero, per un singolo livello convoluzionale con un singolo canale di input e un singolo filtro di output, incluso il supporto a padding e stride arbitrari.

9.10.1.1 📋 Linee Guida di Implementazione

  1. Input: Leggere le dimensioni \(H \times W\) della mappa delle caratteristiche di input e, successivamente, i suoi \(H \times W\) valori reali.

  2. Kernel e bias: Leggere le dimensioni \(k_h \times k_w\) del kernel (già addestrato), i suoi valori reali, e il bias \(b\) (reale, scalare).

  3. Iperparametri: Leggere il padding \(p\) (intero, numero di zeri aggiunti su ciascun bordo) e lo stride \(s\) (intero, passo dello scorrimento).

  4. Riempimento: Aggiungere \(p\) zeri su ciascuno dei quattro bordi della mappa di input prima della correlazione.

  5. Correlazione incrociata: Per ogni posizione di output \((i, j)\), calcolare \[ z(i,j) = b + \sum_{u=0}^{k_h-1} \sum_{v=0}^{k_w-1} K(u,v) \cdot X_{pad}(i \cdot s + u,\; j \cdot s + v), \] scorrendo l’input senza invertire il kernel (convenzione dei framework di deep learning, diversa dalla convoluzione matematica classica).

  6. Attivazione: Applicare ReLU a ogni valore: \(a(i,j) = \max(0, z(i,j))\).

  7. Dimensioni di output: \(O_h = \lfloor (H + 2p - k_h)/s \rfloor + 1\) e \(O_w = \lfloor (W + 2p - k_w)/s \rfloor + 1\).

  8. Output: Stampare \(O_h\) e \(O_w\) nella prima riga, seguiti da \(O_h\) righe con \(O_w\) valori reali ciascuna (la mappa delle caratteristiche di output, già con ReLU applicata), formattati con 4 cifre decimali.

9.10.1.2 📌 Vincoli Computazionali

  • Un canale di input, un filtro di output: non è necessario gestire più canali o più filtri in questa versione semplificata.
  • Senza inversione del kernel: implementare la correlazione incrociata, non la convoluzione matematica classica con kernel invertito — è questa l’operazione che PyTorch (e la maggior parte dei framework) chiama “convoluzione”.
  • Riempimento con zeri: i \(p\) pixel aggiunti su ciascun bordo valgono sempre \(0\).
  • Formattazione: tutti i valori di output devono avere esattamente 4 cifre decimali, anche quando il valore è un intero (es.: 2.0000).

9.10.1.3 🧠 Fondamenti Teorici

Elemento Ruolo nel livello convoluzionale
Kernel \(K\) Parametri appresi per gradiente, analoghi ai coefficienti di un filtro fisso del Capitolo 3, ma regolati tramite backpropagation
Bias \(b\) Offset appreso, sommato dopo la correlazione — consente al neurone di “attivarsi” anche con input nullo
Padding Controlla la dimensione spaziale dell’output e previene la perdita di informazioni ai bordi a ogni livello
Stride Controlla il passo dello scorrimento; valori \(> 1\) riducono la risoluzione spaziale, come una forma di sottocampionamento integrato nella convoluzione stessa
ReLU Introduce non linearità dopo la combinazione lineare, esattamente come nella Sezione “Funzione di Attivazione”

9.10.1.4 🧩 Metodi di morph.py che possono aiutare

  • mm.readImg(h, w, dtype='float') — legge direttamente una matrice \(h \times w\) di valori reali dall’input standard, evitando il parsing manuale della mappa delle caratteristiche e del kernel.
  • mm.correlacao0(f, kernel, bias) — implementa la stessa somma di correlazione incrociata + bias che dovrai calcolare a mano, ma senza supporto per padding o stride, e converte il risultato in uint8 (tronca valori negativi e decimali). Può servire come riferimento concettuale o per verificare il caso più semplice (\(p=0\), \(s=1\)), ma non sostituisce la tua implementazione completa — che deve preservare segno, cifre decimali, padding, stride e ReLU.

9.10.1.5 📦 Specifica di Input e Output (VPL)

Input:

  • Riga 1: Interi \(H\) e \(W\).
  • Prossime \(H\) righe: \(W\) valori reali ciascuna (mappa di input).
  • Riga successiva: Interi \(k_h\) e \(k_w\).
  • Prossime \(k_h\) righe: \(k_w\) valori reali ciascuna (kernel).
  • Riga successiva: Reale \(b\) (bias).
  • Riga successiva: Interi \(p\) e \(s\).

Output:

  • Riga 1: Interi \(O_h\) e \(O_w\).
  • Prossime \(O_h\) righe: \(O_w\) valori reali ciascuna, con 4 cifre decimali.

9.10.1.6 📌 Esempi

Input Output Osservazione
3 3
1 2 0
0 1 2
1 0 1
2 2
1 1
1 1
-2
0 1
2 2
2.0000 3.0000
0.0000 2.0000
Padding 0, stride 1: output \(2\times2\) senza riempimento.
3 3
1 2 0
0 1 2
1 0 1
2 2
1 0
0 1
0
1 2
2 2
1.0000 0.0000
1.0000 2.0000
Padding 1, stride 2: input riempito con zeri prima della correlazione.
🎮 Simulatore: Convoluzione 2D Manuale 🟢 correlazione incrociata + bias + ReLU

Ingresso 4×4 fisso, kernel 2×2 fisso (evidenziato in blu) — regola padding (p), stride (s) e bias (b), esattamente i parametri che l'EP09_01 richiede in ingresso, e osserva come cambiano la dimensione e i valori dell'uscita.

Padding (p)
Stride (s)
Bias (b)
(0,0)
Ingresso X imbottito (con padding)
originale padding (0) finestra corrente
Kernel K (2×2)
Uscita Y = ReLU(X⊛K + b)

💡 Ogni posizione dello slider rivela una cella della matrice di uscita. Percorri tutte le posizioni per completare la mappa di uscita. Cambiare p, s o b riavvia l'esplorazione, perché la mappa di uscita cambia dimensione e/o valori.

Figura 9.43: Simulatore EP09_01: Convoluzione 2D Manuale (correlazione incrociata + bias + ReLU, con padding e stride regolabili)
%%writefile EP09_01.py
# Codice Python
Overwriting EP09_01.py
TestSuite("EP09_01.py").run()
✔️ EP09_01.cases esiste già in casos/
📋 3 caso/i caricato/i da casos/EP09_01.cases

🔍 Test di Python: EP09_01.py
⚠️ EP09_01.py: file vuoto (meno di 3 righe). Test saltati.