Domande di Intervista per Ingegneri di Computer Vision
15 domande selezionate di intervista sulla visione artificiale raggruppate per livello di anzianità. Usale per ripassare i fondamenti, i compromessi pratici e il ragionamento di produzione a livello senior.
1Cos'è una convoluzione discreta 2D su un'immagine e come si relaziona a kernel, filtri e cross-correlazione?
La convoluzione discreta 2D su un'immagine è un'operazione di filtraggio spaziale lineare in cui una matrice di pesi (un kernel) scorre sull'immagine di input. In ogni posizione spaziale, calcola un prodotto elemento per elemento tra i pesi del kernel e la patch di immagine locale sovrapposta (campo ricettivo) e somma i risultati (spesso aggiungendo un termine di bias) per produrre un singolo valore di pixel nella feature map di output. Nella matematica classica e nell'elaborazione del segnale, la vera convoluzione implica il ribaltamento (rotazione di 180 gradi) del kernel orizzontalmente e verticalmente prima di calcolare il prodotto interno scorrevole: $(I * K)(i, j) = \sum_m \sum_n I(i - m, j - n) K(m, n)$. Al contrario, la cross-correlazione calcola direttamente il prodotto scalare scorrevole senza ribaltare il kernel: $(I \star K)(i, j) = \sum_m \sum_n I(i + m, j + n) K(m, n)$. Nei framework di deep learning come PyTorch e TensorFlow, l'operazione implementata sotto il nome di "convoluzione" è in realtà una cross-correlazione. Poiché i pesi del kernel sono parametri apprendibili ottimizzati direttamente tramite backpropagation, la rete apprende semplicemente i pesi opportunamente orientati, rendendo il ribaltamento matematico del kernel computazionalmente ridondante sia durante l'addestramento che durante l'inferenza.
2Quando useresti le rappresentazioni RGB, HSV, Lab, YCbCr o in scala di grigi in una pipeline di visione?
Spazi colore diversi disaccoppiano proprietà fisiche, percettive e statistiche specifiche dei dati visivi, rendendoli adatti a compiti di visione specifici:
1. **RGB / BGR**: Formato standard per i sensori delle telecamere e l'hardware di visualizzazione che rappresenta i colori primari additivi (Rosso, Verde, Blu). I canali colore sono altamente correlati con la luminanza (i cambiamenti di illuminazione influenzano tutti e tre i canali contemporaneamente). È l'input standard per le reti neurali profonde (CNN (Convolutional Neural Network), Vision Transformers). Un problema di produzione comune è lo scambio di canali RGB vs BGR (ad es., OpenCV carica BGR, mentre PIL/PyTorch si aspettano RGB), che riduce silenziosamente l'accuratezza del modello.
2. **HSV / HSL**: Disaccoppia esplicitamente la cromaticità (Tonalità = tinta del colore, Saturazione = purezza del colore) dall'intensità (Valore/Luminosità). È ideale per la sogliatura classica del colore, il tracciamento di oggetti per colore e la segmentazione del colore sotto illuminazione variabile, poiché la Tonalità è relativamente invariante a ombre e cambiamenti di luminosità.
3. **CIE Lab**: Uno spazio colore percettivamente uniforme dove la distanza euclidea tra due punti ($\Delta E$) riflette direttamente la differenza percettiva umana. Separa la Luminosità ($L^*$) dagli assi dei colori opposti ($a^*$ verde-rosso, $b^*$ blu-giallo). È utilizzato nell'ispezione della differenza di colore, nella correzione del colore, nella valutazione della qualità dell'immagine e nei compiti di colorazione.
4. **YCbCr**: Separa la luminanza ($Y$) dalle componenti cromatiche di differenza blu ($Cb$) e differenza rossa ($Cr$). È la spina dorsale degli standard di compressione video e immagine (JPEG, MPEG, H.264/H.265) perché il sistema visivo umano è meno sensibile ai dettagli cromatici, consentendo il sottocampionamento della croma (e.g., 4:2:0) per ridurre la larghezza di banda e il calcolo.
5. **Scala di grigi (Grayscale)**: Rappresentazione dell'intensità a canale singolo ($Y \approx 0.299R + 0.587G + 0.114B$). È ideale per compiti basati su geometria e texture dove il colore è irrilevante (ad es., optical flow, SLAM (Simultaneous Localization and Mapping), estrazione di feature classiche come SIFT/ORB, rilevamento dei bordi) per risparmiare il 66% di memoria e calcolo. Tuttavia, non dovrebbe essere usato quando il colore è un indizio discriminante chiave (ad es., classificazione dello stato del semaforo).
import cv2
import numpy as np
# Sample image: saturated red under normal and shaded lighting
img_bgr = np.zeros((100, 100, 3), dtype=np.uint8)
img_bgr[:, :50] = [0, 0, 255] # Bright Red in BGR
img_bgr[:, 50:] = [0, 0, 120] # Shaded/Dark Red in BGR
# Convert to HSV (OpenCV H: 0-179, S: 0-255, V: 0-255)
img_hsv = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2HSV)
# Red Hue wraps around 0/180; track red with single Hue range [0, 10] & high saturation
lower_red = np.array([0, 100, 50])
upper_red = np.array([10, 255, 255])
mask_hsv = cv2.inRange(img_hsv, lower_red, upper_red)
print("HSV segmented pixels:", np.count_nonzero(mask_hsv), "out of", mask_hsv.size)
3Perché la normalizzazione delle immagini canale per canale, la normalizzazione per immagine e la scalatura consistente dei pixel sono importanti nei sistemi di visione?
La normalizzazione delle immagini è vitale nelle pipeline di visione per mantenere la stabilità numerica, accelerare la convergenza del gradiente e prevenire lo spostamento di distribuzione tra training e serving (train-serving distribution shift):
1. **Scalatura dei Pixel ([0, 1] o [-1, 1])**: I pixel grezzi delle immagini sono memorizzati come interi non firmati a 8 bit (`uint8` $\in [0, 255]$). Alimentare valori grezzi $[0, 255]$ nelle reti neurali causa attivazioni iniziali estremamente grandi ed esplosione o saturazione del gradiente. Scalare a $[0.0, 1.0]$ (dividendo per 255.0) o $[-1.0, 1.0]$ centra gli input attorno allo zero, allineandosi con i metodi standard di inizializzazione dei pesi (He, Xavier).
2. **Normalizzazione per Canale a Livello di Dataset (Standardizzazione Media-Deviazione Standard)**: Standardizza gli input tramite $x_{norm} = \frac{x - \mu_c}{\sigma_c}$ usando statistiche globali precalcolate per canale (ad esempio, media ImageNet `[0.485, 0.456, 0.406]` e deviazione standard `[0.229, 0.224, 0.225]`). Ciò centra ogni canale attorno a una media zero con varianza unitaria attraverso il dataset, garantendo un flusso di gradiente bilanciato tra i canali. Un fallimento operativo critico è lo **skew train-serving**: se l'inferenza omette la divisione per 255, utilizza costanti di media/deviazione standard non corrispondenti o le applica nell'ordine sbagliato dei canali (RGB vs BGR), le prestazioni del modello diminuiscono gravemente.
3. **Normalizzazione per Immagine (Normalizzazione dell'Istanza / Min-Max / Z-score)**: Calcola la media e la deviazione standard per ogni singola immagine: $x_{norm} = \frac{x - \mu_{img}}{\sigma_{img}}$. Questo rimuove le variazioni globali di contrasto e illuminazione tra diverse condizioni di acquisizione. Sebbene efficace nel trasferimento di stile, nell'imaging medico MRI/CT o nelle immagini satellitari, può essere dannoso quando l'intensità assoluta dei pixel ha un significato fisico (ad esempio, classificazione giorno/notte, riflettanza del materiale o rilevamento di difetti sotto illuminazione calibrata).
import torch
import torchvision.transforms as T
from PIL import Image
import numpy as np
# Create a mock uint8 RGB image
raw_img = Image.fromarray(np.random.randint(0, 256, (224, 224, 3), dtype=np.uint8))
# Standard transform pipeline:
# 1. ToTensor scales uint8 [0, 255] -> float32 [0.0, 1.0] and permutes HWC -> CHW
# 2. Normalize standardizes per-channel using dataset mean and std
preprocess = T.Compose([
T.ToTensor(),
T.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])
tensor_img = preprocess(raw_img)
print("Tensor shape:", tensor_img.shape)
print("Tensor dtype:", tensor_img.dtype)
print("Channel 0 min/max:", round(tensor_img[0].min().item(), 2), round(tensor_img[0].max().item(), 2))
4In che modo gli aumenti geometrici e fotometrici incoraggiano l'invarianza, e come si decide se un aumento preserva le etichette?
Gli aumenti geometrici (come rotazione, traslazione, scalatura e ribaltamento) alterano la mappatura delle coordinate spaziali dei pixel, mentre gli aumenti fotometrici (come luminosità, contrasto, variazione di tonalità (hue jitter) e sfocatura (blur)) alterano l'intensità dei pixel e i canali di colore senza modificare le coordinate spaziali. Nella classificazione, entrambe le categorie espongono il modello a variazioni realistiche, incoraggiandolo ad apprendere rappresentazioni invarianti in cui le previsioni rimangono stabili nonostante i cambiamenti di posa, punto di vista o condizioni di illuminazione. Nei compiti di predizione spaziale (rilevamento e segmentazione), le trasformazioni geometriche richiedono trasformazioni corrispondenti ai bounding box o alle maschere ground-truth per preservare l'equivarianza. Decidere se un aumento preserva le etichette richiede la valutazione delle semantiche del dominio e dei priori fisici. Una trasformazione preserva le etichette se l'immagine risultante rimane un'istanza plausibile della classe target originale senza alterarne l'identità semantica. Ad esempio, il ribaltamento orizzontale preserva l'identità per oggetti generici (come auto o animali), ma altera l'identità o invalida i caratteri nel riconoscimento ottico dei caratteri (OCR) e nella classificazione di cifre. Allo stesso modo, una rotazione completa di 360 gradi è valida in istologia o nelle immagini satellitari dove non esiste un orientamento canonico, ma innaturale nella guida autonoma dove gli oggetti capovolti violano i vincoli fisici del mondo reale.
5Cosa sono l'interpolazione per nearest-neighbor, bilineare, bicubica e di area, e come il ridimensionamento può introdurre aliasing o sfocatura?
I metodi di interpolazione stimano le intensità dei pixel in coordinate continue quando si ricampiona su una nuova griglia discreta:
- **Nearest-Neighbor:** Assegna l'intensità del pixel discreto più vicino. È computazionalmente $O(1)$ e preserva i valori originali esatti, ma crea artefatti a blocchi durante l'upsampling e bordi frastagliati evidenti durante il downsampling.
- **Interpolazione Bilineare:** Calcola una media pesata dalla distanza dei $2 \times 2$ (4 più vicini) pixel vicini utilizzando l'interpolazione lineare lungo entrambi gli assi, producendo transizioni fluide con una sfocatura moderata.
- **Interpolazione Bicubica:** Campiona un vicinato di $4 \times 4$ (16 più vicini) utilizzando kernel polinomiali cubici. Cattura i gradienti di intensità locali, producendo bordi più netti e curve più lisce rispetto alla bilineare, ma può produrre artefatti di overshoot/ringing intorno a transizioni nette.
- **Interpolazione di Area (Box):** Calcola la sovrapposizione frazionaria dell'area dei pixel di destinazione proiettati sui pixel di origine e ne fa la media delle intensità della sorgente sottostante. È particolarmente efficace per il downsampling senza effetto moiré.
**Meccanismi di Aliasing vs. Sfocatura:**
- **L'Aliasing** si verifica durante il downsampling quando il contenuto ad alta frequenza supera la frequenza di Nyquist ($f_s / 2$) della nuova frequenza di campionamento senza un sufficiente filtraggio passa-basso. Le alte frequenze si ripiegano in bande di frequenza inferiori, producendo pattern falsi, effetto moiré e bordi frastagliati.
- **La Sfocatura** si verifica quando i kernel di interpolazione agiscono come filtri di smussamento passa-basso che attenuano le legittime alte frequenze (ad esempio, smussamento bilineare) o quando i dettagli ad alta frequenza vengono mediati durante il downsampling.
6Contro quali modalità di guasto visivo nel mondo reale dovrebbe essere valutato un sistema di visione, e come queste spostano la distribuzione dell'input?
Le modalità di guasto visivo nel mondo reale rientrano in diverse ampie categorie: condizioni ambientali (scarsa illuminazione, abbagliamento diretto, pioggia, neve, nebbia), effetti del sensore e ottici (sfocatura da movimento (motion blur), sfocatura da defocalizzazione (defocus blur), rumore del sensore ad alto ISO, distorsione da rolling shutter), degrado da compressione e trasmissione (blocchi JPEG, limitazione del bitrate video, downsampling) e spostamenti semantici/spaziali (occlusione parziale, troncamento, pose di oggetti fuori distribuzione). Queste modalità di guasto spostano la distribuzione dell'input lontano dai domini di addestramento nominali a più livelli. A livello di pixel, alterano gli istogrammi di luminanza e crominanza, distruggono il contrasto locale, riducono il rapporto segnale/rumore (SNR - Signal-to-Noise Ratio) e introducono artefatti artificiali ad alta frequenza o kernel di sfocatura. A livello strutturale e semantico, attenuano i gradienti dei bordi, oscurano le caratteristiche visive chiave, distorcono le silhouette e nascondono regioni diagnostiche critiche. Quando i modelli addestrati su dati puliti incontrano questi input spostati, gli estrattori di feature convoluzionali o di attenzione di basso livello non si attivano correttamente, producendo rilevamenti mancati, falsi positivi allucinati su pattern di rumore ed errori con eccessiva confidenza.
7Come i descrittori classici come l'HOG rappresentano la struttura dell'immagine, e quali limitazioni presentano rispetto alle feature apprese delle CNN?
L'HOG (Histogram of Oriented Gradients) è un descrittore di feature classico artigianale progettato per catturare la forma e l'aspetto locale degli oggetti attraverso la distribuzione degli orientamenti del gradiente di intensità. La pipeline di calcolo HOG prevede: (1) il calcolo dei gradienti orizzontali e verticali dell'immagine (ad esempio, con filtri derivativi 1D `[-1, 0, 1]`) per ottenere magnitudine e orientamento del gradiente; (2) la divisione dell'immagine in piccole regioni spaziali chiamate 'celle' (come 8x8 pixel) e l'accumulo di istogrammi 1D di orientamenti del gradiente pesati dalla magnitudine del gradiente; (3) il raggruppamento di celle adiacenti in 'blocchi' più grandi e sovrapposti (come 2x2 celle) e la normalizzazione dei vettori di feature dei blocchi (usando la norma L2 o L1-sqrt) per ottenere robustezza contro variazioni locali di illuminazione, contrasto e ombreggiamento; e (4) la concatenazione dei vettori di blocco normalizzati in una rappresentazione finale 1D delle feature, storicamente abbinata a SVM (Support Vector Machine) lineari per il rilevamento di pedoni e oggetti. Rispetto alle feature apprese delle CNN (Convolutional Neural Network), l'HOG presenta limitazioni maggiori: in primo luogo, le feature HOG sono fisse e artigianali, catturando solo statistiche di orientamento dei bordi locali di basso livello senza la capacità di apprendere rappresentazioni gerarchiche specifiche del compito (come texture, parti di oggetti e concetti semantici). In secondo luogo, l'HOG fornisce un'invarianza geometrica molto limitata: mentre la normalizzazione dei blocchi gestisce cambiamenti di illuminazione monotoni e piccoli spostamenti spaziali all'interno delle celle, l'HOG è fragile sotto rotazioni 3D fuori dal piano, variazioni significative di scala, articolazioni di posa non rigide e forte disordine di sfondo. Al contrario, le CNN apprendono astrazioni non lineari multistrato ottimizzate end-to-end per l'obiettivo target.
8In che modo lo stride, il padding, la dilatazione e la dimensione del kernel determinano la dimensione dell'output della convoluzione e il campo ricettivo?
Le dimensioni spaziali dell'output di un layer convoluzionale e il suo campo ricettivo cumulativo sono determinati dalla dimensione del kernel $k$, dallo stride $s$, dal padding $p$ e dalla dilatazione $d$:
1. **Dimensione Spaziale dell'Output**:
La dilatazione introduce $(d - 1)$ spazi tra gli elementi del kernel, dando una dimensione effettiva del kernel di $k' = d(k - 1) + 1$. La dimensione dell'output lungo un asse è data da:
$$O = \left\lfloor \frac{I + 2p - k'}{s} \right\rfloor + 1 = \left\lfloor \frac{I + 2p - d(k - 1) - 1}{s} \right\rfloor + 1$$
Qui, lo stride sottocampiona la risoluzione avanzando il kernel di $s$ pixel per passo, il padding aggiunge pixel di bordo virtuali per preservare o regolare le dimensioni spaziali, e la dilatazione espande l'impronta del filtro senza aggiungere parametri extra.
2. **Campo Ricettivo (RF)**:
Il campo ricettivo teorico descrive l'estensione spaziale nell'immagine di input che influenza una particolare attivazione. Attraverso layer impilati, il campo ricettivo $RF_l$ e lo stride cumulativo (salto $j_l$) si aggiornano ricorsivamente:
* Salto: $j_l = j_{l-1} \cdot s_l$, con $j_0 = 1$
* Campo Ricettivo: $RF_l = RF_{l-1} + (k'_l - 1) \cdot j_{l-1}$, con $RF_0 = 1$
Gli stride e il pooling espandono il campo ricettivo in modo moltiplicativo in profondità perché i passi del kernel dei layer successivi corrispondono a salti di pixel maggiori nello spazio delle coordinate di input originale. La dilatazione espande il campo ricettivo in modo additivo all'interno di un singolo layer aumentando $k'_l$ senza sottocampionamento spaziale.
9In che modo un kernel di convoluzione corrisponde a un filtro immagine lineare classico, e quando un filtro 2D può essere reso separabile?
Nella classica elaborazione lineare di segnali e immagini, un kernel di convoluzione rappresenta la risposta all'impulso spaziale (funzione di diffusione del punto) di un sistema Lineare Invariante allo Spostamento (LSI). L'applicazione del kernel è un'operazione di filtraggio lineare nel dominio spaziale che modella la risposta in frequenza dell'immagine (la sua trasformata di Fourier 2D). I kernel passa-basso (ad esempio, Gaussiano, box blur) attenuano le alte frequenze spaziali per sopprimere il rumore e appianare le texture, mentre i kernel passa-alto o passa-banda (ad esempio, Sobel, Laplaciano, Prewitt) amplificano le alte frequenze per rilevare bordi e gradienti. Un kernel di filtro 2D $K \in \mathbb{R}^{M \times N}$ è spazialmente separabile se può essere scomposto nel prodotto esterno di due filtri 1D: $K = u \cdot v^T$, dove $u \in \mathbb{R}^{M \times 1}$ e $v \in \mathbb{R}^{N \times 1}$. In termini di algebra lineare, una matrice 2D è separabile se e solo se il suo rango è 1. Questo può essere verificato tramite la Decomposizione ai Valori Singolari (SVD), dove esattamente un valore singolare è non nullo ($ \sigma_1 > 0, \sigma_2 = \dots = 0$). La separabilità riduce significativamente la complessità computazionale. Applicare un kernel $K \times K$ non separabile a un'immagine $H \times W$ richiede $O(H \cdot W \cdot K^2)$ moltiplicazioni e addizioni. Quando scomposto in due passaggi 1D consecutivi (orizzontale e poi verticale), la complessità si riduce a $O(H \cdot W \cdot 2K)$. Per un kernel $15 \times 15$, questo fornisce all'incirca una velocizzazione di $7.5\times$.
10In che modo il ridimensionamento, il ritaglio centrale (center cropping), il ritaglio casuale ridimensionato (random-resized cropping), l'allungamento e il letterboxing modificano la distribuzione dell'input?
Le diverse strategie di ridimensionamento e ritaglio modificano la distribuzione dei dati di input in termini di geometria, scala, copertura del contenuto e confini spaziali:
1. **Ridimensionamento Diretto (Allungamento/Schiacciamento):** Ridimensiona l'immagine in modo non isotropo a una dimensione target $(H, W)$, distorcendo i rapporti d'aspetto originali. Questo forza il modello a elaborare forme di oggetti deformate (es. oggetti circolari diventano ellissi allungate).
2. **Ritaglio Centrale (Center Cropping):** Estrae un ritaglio centrale di dimensione o rapporto fisso. Preserva il rapporto d'aspetto nativo e la scala locale, ma introduce un forte bias centrale (presupponendo che gli obiettivi siano inquadrati centralmente) e scarta il contesto periferico o taglia gli oggetti posizionati ai margini.
3. **Ritaglio Casuale Ridimensionato (RRC - Random-Resized Cropping):** Estrae sotto-regioni casuali su varie scale di area e rapporti d'aspetto, quindi le ridimensiona a una dimensione fissa. Questo amplia la distribuzione della scala di addestramento e incoraggia l'apprendimento di feature basate su parti, ma ritagli estremi potrebbero escludere completamente l'oggetto target.
4. **Letterboxing (Ridimensionamento Isotropo con Riempimento):** Scala l'immagine in modo uniforme fino a quando la sua dimensione più lunga si adatta alla dimensione target, quindi riempie i bordi rimanenti con un valore costante. Mantiene le vere proporzioni e rapporti d'aspetto degli oggetti, ma introduce bordi artificiali ad alto contrasto e pixel di riempimento non informativi.
11Quali modalità di rumore di annotazione si verificano nei dataset di visione e come si possono rilevare o mitigare?
I dataset di visione artificiale mostrano comunemente quattro principali modalità di rumore di annotazione:
1. **Rumore di etichetta categoriale (Categorical Label Noise)**: A un'immagine o a un oggetto viene assegnata l'etichetta di classe sbagliata (ad esempio, classificare erroneamente un gatto come un cane, o confondere classi sottili e a grana fine).
2. **Annotazioni mancanti (Rumore da omissione) (Missing Annotations / Omission Noise)**: Oggetti in primo piano validi vengono lasciati senza annotazione. Nel rilevamento di oggetti, i target omessi sono trattati come negativi di sfondo, penalizzando direttamente le rilevazioni corrette del modello durante l'addestramento.
3. **Jitter del Bounding Box / Keypoint (Rumore di localizzazione) (Bounding-Box / Keypoint Jitter / Localization Noise)**: Coordinate imprecise, larghe o spostate dei bounding box e posizioni dei punti di riferimento (landmark) causate dall'incoerenza dell'annotatore umano.
4. **Ambiguità del bordo della maschera (Mask Boundary Ambiguity)**: Contorni di segmentazione incoerenti o grossolani lungo bordi complessi o sfocati (ad esempio, capelli, superfici semitrasparenti, sfocatura di movimento).
**Strategie di rilevamento e mitigazione:**
- **Tracciamento degli outlier di perdita e Confident Learning**: Il tracciamento della perdita di un campione attraverso le epoche di addestramento identifica gli outlier persistenti con alta perdita, che spesso indicano istanze etichettate erroneamente o omesse. Algoritmi come Confident Learning stimano le distribuzioni di rumore per eliminare o correggere gli errori.
- **Discrepanze nelle previsioni Out-of-Fold (OOF)**: L'addestramento di modelli di cross-validazione e il confronto delle previsioni di holdout con la ground truth evidenziano immagini etichettate erroneamente e bounding box mancanti.
- **Accordo Inter-Annotatore (IAA) e Mediazione**: La misurazione di metriche come la Kappa di Cohen (classificazione) o l'Intersection-over-Union (IoU) media (localizzazione) tra più annotatori segnala campioni con basso accordo per una revisione consensuale.
- **Formulazioni di funzione di perdita robuste**: L'uso del label smoothing, di funzioni di perdita di classificazione tolleranti al rumore o di funzioni di perdita di regressione robuste basate su IoU riduce la sensibilità al jitter e all'etichettatura errata.
import torch
import torch.nn.functional as F
def find_label_noise_candidates(model, dataloader, device, top_k=50):
model.eval()
sample_losses = []
with torch.no_grad():
for batch_idx, (images, targets, sample_ids) in enumerate(dataloader):
images, targets = images.to(device), targets.to(device)
logits = model(images)
loss_per_sample = F.cross_entropy(logits, targets, reduction='none')
for sid, l, target, pred in zip(sample_ids, loss_per_sample.cpu(), targets.cpu(), logits.argmax(dim=-1).cpu()):
sample_losses.append({'id': sid, 'loss': l.item(), 'target': target.item(), 'pred': pred.item()})
sample_losses.sort(key=lambda x: x['loss'], reverse=True)
return sample_losses[:top_k]
12In che modo lo squilibrio di classe (class imbalance) influisce sull'addestramento della classificazione di immagini, del rilevamento e della previsione densa?
Lo squilibrio di classe influisce sulle dinamiche di ottimizzazione e sui paesaggi di perdita (loss landscapes) attraverso i compiti di visione artificiale:
1. **Classificazione di Immagini:** Quando le classi maggioritarie superano in numero quelle minoritarie, la minimizzazione del rischio empirico (empirical risk minimization) fa sì che gli aggiornamenti dei parametri siano dominati dai gradienti delle classi maggioritarie. Il modello apprende il priori della classe empirica $P(Y)$ e ne influenza il confine di decisione (decision boundary) contro le classi rare. Questo porta a un'elevata accuratezza top-1 complessiva, ma soffre di un grave collasso del recall per le classi minoritarie.
2. **Rilevamento di Oggetti:**
* **Squilibrio Tra Primo Piano e Sfondo (Foreground-Background Imbalance):** Nei rilevatori densi a stadio singolo e basati su anchor, centinaia di migliaia di posizioni candidate vengono valutate per immagine, dove più del 99% sono sfondo. Anche se i singoli anchor di sfondo semplici generano piccole perdite, il loro vasto gradiente aggregato sommerge i segnali di gradiente informativi dagli oggetti in primo piano sparsi.
* **Squilibrio delle Classi in Primo Piano (Foreground Class Imbalance):** Le categorie di oggetti comuni appaiono ordini di grandezza più frequentemente rispetto alle categorie rare, riducendo la dimensione effettiva del campione e il segnale di apprendimento per le classi rare.
3. **Previsione Densa (Segmentazione):** Lo squilibrio a livello di pixel è grave perché le grandi classi di sfondo e 'stuff' (ad esempio, strada, cielo) occupano milioni di pixel, mentre le piccole classi di 'thing' (ad esempio, segnali stradali, pedoni) possono occupare meno dello 0,1% dei pixel dell'immagine. La funzione di perdita (loss) di entropia incrociata standard per pixel è dominata dalle classi di area ampia, portando a una sotto-segmentazione o alla completa scomparsa di strutture piccole e sottili.
13Come si esegue il debug di NaN (Not a Number) o di una perdita instabile durante l'addestramento di visione distribuito su larga scala?
Eseguire il debug di NaN (Not a Number) o dell'instabilità della perdita nell'addestramento di visione su larga scala con DDP (Distributed Data Parallel) richiede di isolare se la causa principale derivi da input dati errati, overflow/underflow numerico in precisione mista (AMP - Automatic Mixed Precision) o instabilità dell'ottimizzazione tra i worker. In primo luogo, stabilire il determinismo e i "safety hooks": abilitare il rilevamento delle anomalie (`torch.autograd.set_detect_anomaly(True)`), registrare gli "hooks" di gradiente/attivazione per individuare l'esatto layer in cui appaiono i NaN e aggiungere una rigorosa validazione dei dati nel DataLoader (asserendo valori finiti, controllando immagini corrotte a 0 byte, bounding box vuote o divisori di normalizzazione a varianza zero). Il logging a livello di minibatch dovrebbe tracciare la perdita per rango, gli URI di input, la norma del gradiente prima del clipping e i fattori di scala del GradScaler. In secondo luogo, ispezionare l'AMP (Automatic Mixed Precision) e lo scaling dinamico della perdita: in FP16, i gradienti grandi possono facilmente causare overflow (`> 65504`), facendo sì che il "loss scaler" salti i passaggi e dimezzi ripetutamente il suo fattore di scala finché la scala non raggiunge zero; passare le operazioni instabili (ad esempio, softmax, LayerNorm, esponenti della "focal loss" o denominatori IoU della "bounding box") a FP32 o adottare BF16 (che corrisponde al range dinamico di FP32) di solito stabilizza l'addestramento. Infine, controllare le insidie specifiche di DDP come le operazioni "all-reduce" che propagano i NaN da un singolo worker a tutti i ranghi, lo "learning rate warmup scaling" (ad esempio, la regola di scaling lineare con grandi dimensioni di batch globali) e il "gradient clipping".
import torch
from torch.cuda.amp import autocast, GradScaler
scaler = GradScaler()
optimizer = torch.optim.AdamW(model.parameters(), lr=1e-4)
for step, (images, targets, uris) in enumerate(dataloader):
# 1. Input sanitization
if not torch.isfinite(images).all():
print(f"Corrupt input detected from URIs: {uris}")
continue
optimizer.zero_grad(set_to_none=True)
with autocast(dtype=torch.float16):
outputs = model(images)
loss = criterion(outputs, targets)
if not torch.isfinite(loss):
print(f"NaN/Inf loss at step {step} on rank {torch.distributed.get_rank()}; skipping step.")
continue
scaler.scale(loss).backward()
# Unscale before clipping to inspect true gradient norms
scaler.unscale_(optimizer)
grad_norm = torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
if torch.isnan(grad_norm) or torch.isinf(grad_norm):
print(f"Warning: Grad norm is {grad_norm}. Scaler will adjust.")
scaler.step(optimizer)
scaler.update()
14Come progetteresti un componente di stabilizzazione video digitale online per una telecamera con `frame jitter` (jittter del fotogramma)?
La progettazione di una pipeline di stabilizzazione video digitale online per il `jitter` della telecamera in tempo reale comporta quattro fasi principali: stima del movimento, smussamento del movimento, compensazione del movimento (`warping`) e gestione dei bordi. 1. **Stima del Movimento Inter-Fotogramma:** Estrazione di `keypoint` sparsi 2D tra fotogrammi consecutivi utilizzando `feature detector` veloci (ad es., ORB, FAST o angoli Shi-Tomasi) e calcolo delle corrispondenze usando il flusso ottico Lucas-Kanade o la corrispondenza di descrittori di `feature`. Stimare una trasformazione geometrica inter-fotogramma (come un modello Affine o Omografia) usando RANSAC per rifiutare le corrispondenze anomale causate da oggetti in primo piano che si muovono indipendentemente. 2. **Accumulo della Traiettoria di Movimento e Smussamento Online:** Integrare le trasformazioni fotogramma per fotogramma nel tempo per mantenere il percorso cumulativo della telecamera $P_t = P_{t-1} \cdot H_t$. Applicare un filtro di smussamento online, come un filtro di Kalman 1D/2D o una media mobile causale a finestra corta, per separare il `jitter` involontario ad alta frequenza dal `panning` intenzionale della telecamera a bassa frequenza. 3. **Compensazione e `Image Warping`:** Calcolare la trasformazione di correzione $C_t = S_t \cdot P_t^{-1}$ (dove $S_t$ è il percorso smussato) e deformare il fotogramma corrente usando l'interpolazione bilineare/bicubica. 4. **Gestione dei Bordi e Vincoli di Latenza:** Il `warping` introduce pixel mancanti ai bordi / bordi neri; risolvere questo applicando un fattore di `crop` dinamico fisso (ad es., zoom 5-10%) con estrapolazione dei bordi o scala adattiva. In un'impostazione online, mantenere un buffering minimo (1–3 fotogrammi di `lookahead`) per limitare la latenza di elaborazione.
15Come si ottimizzerebbe una pipeline di visione in tempo reale quando il rilevatore è più lento della frequenza dei fotogrammi target?
Quando un rilevatore di oggetti non riesce a sostenere la frequenza dei fotogrammi target (ad esempio, funziona a 15-20 FPS su un flusso video a 60 FPS), una robusta architettura di produzione disaccoppia il rilevamento dal ciclo di presentazione in tempo reale utilizzando una pipeline multi-thread asincrona che combina un rilevamento "pesante" con un tracciamento "leggero".
In questa architettura ibrida, un thread di acquisizione cattura continuamente i fotogrammi video. Il rilevatore "pesante" viene eseguito in modo asincrono come un "rilevatore di keyframe" su un thread worker in background. Nel frattempo, un tracker "leggero" (come il flusso ottico Lucas-Kanade, ByteTrack/BoT-SORT, o un filtro di correlazione/filtro di Kalman veloce) viene eseguito sincronicamente su ogni fotogramma a 60 FPS completi, mantenendo lo stato dell'oggetto, l'identità e traiettorie fluide dei bounding box.
Per gestire il ritardo (lag) e la contropressione (backpressure) senza introdurre latenza illimitata o fotogrammi obsoleti, vengono utilizzati buffer ad anello con dimensione fissa (bounded ring buffers) e politiche di scarto dell'ultimo fotogramma (latest-frame drop policies). Quando il rilevatore completa un fotogramma T_0 al tempo T_curr, il suo output è obsoleto. La pipeline esegue un riallineamento delle coordinate/back-projection: associa i risultati di rilevamento ritardati con lo stato storico del tracklet a T_0, aggiorna le identità e le tracce perse, e propaga le correzioni in avanti a T_curr tramite i vettori di movimento del tracciamento o i passi di predizione del filtro di Kalman.
import queue
import threading
frame_queue = queue.Queue(maxsize=1) # Drop stale frames, keep latest
det_result_queue = queue.Queue()
def detector_worker():
while True:
frame, frame_id, timestamp = frame_queue.get()
boxes, scores, classes = heavy_detector.infer(frame)
det_result_queue.put({'frame_id': frame_id, 'boxes': boxes, 'timestamp': timestamp})
def realtime_pipeline(video_stream):
tracker = FastTracker() # e.g. Optical flow or Kalman tracker
for frame, frame_id, timestamp in video_stream:
if frame_queue.empty():
frame_queue.put((frame, frame_id, timestamp))
if not det_result_queue.empty():
det_result = det_result_queue.get()
tracker.reconcile_and_correct(det_result, current_frame_id=frame_id)
active_tracks = tracker.update(frame)
display_or_downstream(frame, active_tracks)