Gestire la risonanza magnetica e il deep learning con Python |  di Carla Pitarca Abaigar |  Dicembre 2023

 | Intelligenza-Artificiale

Una guida completa all’analisi MRI attraverso modelli di deep learning in PyTorch

fotografato da Olga Rai SU Adobe Stock.

Prima di tutto vorrei presentarmi. Mi chiamo Carla Pitarch e sono una dottoranda in AI. La mia ricerca è incentrata sullo sviluppo di un sistema automatizzato di classificazione del grado del tumore al cervello estraendo informazioni da immagini di risonanza magnetica (MRI) utilizzando modelli di Deep Learning (DL), in particolare reti neurali convoluzionali (CNN).

All’inizio del mio percorso di dottorato, immergermi nei dati MRI e nel DL era un mondo completamente nuovo. I passaggi iniziali per l’esecuzione dei modelli in questo ambito non sono stati così semplici come previsto. Nonostante abbia trascorso un po’ di tempo a fare ricerche in questo ambito, ho riscontrato la mancanza di archivi completi che guidassero l’avvio sia alla MRI che al DL. Pertanto, ho deciso di condividere alcune delle conoscenze che ho acquisito in questo periodo, sperando che rendano il tuo viaggio un po’ più agevole.

L’avvio di attività di visione artificiale (CV) tramite DL spesso implica l’utilizzo di set di dati di immagini pubbliche standard come ImageNet caratterizzato da immagini naturali RGB a 3 canali. I modelli PyTorch sono predisposti per queste specifiche, prevedendo che le immagini di input siano in questo formato. Tuttavia, quando i nostri dati di immagine provengono da un dominio distinto, come il campo medico, divergente sia nel formato che nelle caratteristiche da questi set di dati di immagini naturali, presenta delle sfide. Questo post approfondisce questo problema, sottolineando due passaggi preparatori cruciali prima dell’implementazione del modello: allineare i dati con i requisiti del modello e preparare il modello per elaborare in modo efficace i nostri dati.

Cominciamo con una breve panoramica degli aspetti fondamentali delle CNN e della MRI.

Reti neurali convoluzionali

In questa sezione, approfondiremo il regno delle CNN, presupponendo che i lettori abbiano una conoscenza fondamentale del DL. Le CNN rappresentano le architetture gold standard in CV, specializzate nell’elaborazione di dati di immagini di input 2D e 3D. Il nostro focus in questo post sarà incentrato sull’elaborazione dei dati di immagine 2D.

La classificazione delle immagini, associando classi o etichette di output alle immagini di input, è un compito fondamentale nelle CNN. L’architettura pionieristica LeNet5 introdotta da LeCun et al.¹ nel 1989 ha gettato le basi per le CNN. Tale architettura può essere così riassunta:

Architettura CNN con due strati di convoluzione, due strati di pooling e uno strato completamente connesso precedente allo strato di output.

Le architetture CNN 2D funzionano ricevendo pixel di immagine come input, aspettandosi che un’immagine sia un tensore con forma Height x Width x Channels. Le immagini a colori sono generalmente costituite da 3 canali: rosso, verde e blu (RGB), mentre le immagini in scala di grigi sono costituite da un singolo canale.

Un’operazione fondamentale nelle CNN è convoluzioneeseguito applicando una serie di filtri O noccioli in tutte le aree dei dati di input. La figura seguente mostra un esempio di come funziona la convoluzione in un contesto 2D.

Esempio di convoluzione su un’immagine 5×5 con un filtro 3×3 che produce una caratteristica convoluta 3×3.

Il processo prevede lo scorrimento del filtro sull’immagine verso destra e il calcolo della somma ponderata per ottenere una mappa delle caratteristiche convoluta. L’output rappresenterà se uno specifico modello visivo, ad esempio un bordo, viene riconosciuto in quella posizione nell’immagine di input. Dopo ogni strato convoluzionale, una funzione di attivazione introduce la non linearità. Le scelte più popolari includono: ReLU (unità lineare rettificata), Leaky ReLu, Sigmoid, Tanh e Softmax. Per ulteriori dettagli su ciascuna funzione di attivazione, questo post fornisce spiegazioni chiare Funzioni di attivazione nelle reti neurali | di SAGAR SHARMA | Verso la scienza dei dati.

Diversi tipi di livelli contribuiscono alla costruzione delle CNN, ciascuno dei quali svolge un ruolo distinto nella definizione della funzionalità della rete. Oltre agli strati convoluzionali, molti altri strati importanti utilizzati nelle CNN includono:

  • Strati di raggruppamentocome il pooling massimo o il pooling medio, riducono in modo efficace le dimensioni della mappa delle funzionalità preservando le informazioni essenziali.
  • Strati di eliminazione vengono utilizzati per prevenire l’overfitting disattivando casualmente una frazione di neuroni durante l’allenamento, migliorando così la capacità di generalizzazione della rete.
  • Strati di normalizzazione batch concentrarsi sulla standardizzazione degli input per ogni livello, il che accelera la formazione della rete.
  • Strati completamente connessi (FC). stabilire connessioni tra tutti i neuroni di uno strato e tutte le attivazioni dello strato precedente, integrando le caratteristiche apprese per facilitare le classificazioni finali.

Le CNN imparano a identificare i modelli gerarchicamente. Gli strati iniziali si concentrano su caratteristiche di basso livello, passando progressivamente a caratteristiche altamente astratte negli strati più profondi. Una volta raggiunto lo strato FC, il file Softmax la funzione di attivazione stima le probabilità della classe per l’input dell’immagine.

Al di là della nascita di LeNet, importanti architetture CNN come AlexNet², GoogLeNet³, VGGNet⁴, ResNet⁵ e la più recente Transformer⁶ hanno contribuito in modo significativo al regno del DL.

Panoramica delle immagini naturali

L’esplorazione di immagini 2D naturali fornisce una comprensione fondamentale dei dati dell’immagine. Per iniziare, approfondiremo alcuni esempi.

Per il nostro primo esempio selezioneremo un’immagine tra quelle ampiamente conosciute MNIST set di dati.

Questa forma dell’immagine è (28,28) che rappresenta un’immagine in scala di grigi con un singolo canale. Quindi, l’input dell’immagine per una rete neurale sarebbe (28*28*1).

Ora esploriamo un’immagine dal ImageNet set di dati. È possibile accedere a questo set di dati direttamente dal sito Web di ImageNet ImageNet (immagine-net.org) oppure esplora un sottoinsieme disponibile su Kaggle Sfida di localizzazione degli oggetti ImageNet | Kaggle.

Possiamo scomporre questa immagine nei suoi canali RGB:

Poiché la forma di questa immagine è (500, 402, 3) l’input dell’immagine di una rete neurale sarebbe rappresentato come (500*402*3).

Risonanza magnetica

La risonanza magnetica è l’esame più utilizzato in neurologia e neurochirurgia, poiché offre un metodo non invasivo che fornisce un buon contrasto dei tessuti molli⁷. Oltre a visualizzare i dettagli strutturali, l’imaging RM approfondisce, fornendo preziose informazioni sugli aspetti strutturali e funzionali del cervello.

Le risonanze magnetiche costituiscono volumi 3D che consentono la visualizzazione sui tre piani anatomici: assiale, coronale e sagittale. Questi volumi sono composti da cubi 3D noti come voxel, a differenza delle immagini 2D standard, che sono costituite da quadrati 2D chiamati pixel. Sebbene i volumi 3D offrano dati completi, possono anche essere scomposti in sezioni 2D.

Per la diagnosi vengono normalmente utilizzate diverse modalità o sequenze di risonanza magnetica, come T1, T1 con potenziamento del contrasto con gadolinio (T1ce), T2 e FLAIR (Fluid Attenuated Inversion Recovery). Queste sequenze consentono la differenziazione dei compartimenti tumorali offrendo intensità di segnale distinte che corrispondono a regioni o tessuti specifici. Di seguito è riportata un’illustrazione che presenta queste quattro sequenze di un singolo paziente con diagnosi di glioblastoma, noto come la forma più aggressiva tra i gliomi, i tumori cerebrali primari più diffusi.

Sfida sulla segmentazione del tumore cerebrale (BraTS). ha reso disponibile uno dei più estesi set di dati di scansioni MRI cerebrali multimodali di pazienti affetti da glioma nel periodo dal 2012 al 2023. L’obiettivo principale del concorso BraTS è valutare le metodologie all’avanguardia per la segmentazione dei tumori cerebrali in multimodali Scansioni MRI, sebbene nel tempo siano state aggiunte ulteriori attività.

Il set di dati BraTS fornisce informazioni cliniche sui tumori, inclusa un’etichetta binaria che indica il grado del tumore (basso o alto grado). Le scansioni BraTS sono disponibili come file NIfTI e descrivono le modalità T1, T1ce, T2 e Flair. Le scansioni vengono fornite dopo alcune fasi di pre-elaborazione, inclusa la co-registrazione sullo stesso modello anatomico, l’interpolazione con una risoluzione isotropa uniforme (1 mm³) e lo stripping del cranio.

In questo post utilizzeremo il set di dati 2020 di Kaggle Set di dati BraTS2020 (formazione + convalida) per classificare la risonanza magnetica del glioma in grado basso o alto.

Le seguenti immagini mostrano esempi di gliomi a basso e ad alto grado:

Modalità MRI e maschera di segmentazione per il paziente BraTS 287.
Modalità MRI e maschera di segmentazione per il paziente BraTS 006.

L’archivio Kaggle comprende 369 directory, ciascuna rappresentante un paziente e contenente i dati immagine corrispondenti. Inoltre ne contiene due .csv File: nome_mapping.csv E survival_info.csv. Per il nostro scopo, utilizzeremo nome_mapping.csvche collega i nomi dei pazienti BraTS al file TCGA-LGG E TCGA-GBM set di dati pubblici disponibili da Archivio di immagini del cancro. Questo file non solo facilita la mappatura dei nomi ma fornisce anche etichette del grado del tumore (LGG-HGG).

Esploriamo il contenuto di ciascuna cartella paziente, prendendo Paziente 006 come esempio. All’interno del ReggisenoTS20_Training_006 cartella, troviamo 5 file, ciascuno corrispondente ad una modalità MRI e alla maschera di segmentazione:

  • BraTS20_Training_006_flair.nii
  • BraTS20_Training_006_t1.nii
  • BraTS20_Training_006_t1ce.nii
  • BraTS20_Training_006_t2.nii
  • BraTS20_Training_006_seg.nii

Questi file sono in .nii formato, che rappresenta il formato NIfTI, uno dei più diffusi nel neuroimaging.

Per gestire le immagini NIfTI in Python, possiamo usare il file È Babele pacchetto. Di seguito è riportato un esempio del caricamento dei dati. Utilizzando il get_fdata() metodo possiamo interpretare l’immagine come un array numpy.

La forma dell’array, (240, 240, 155)indica un volume 3D comprendente 240 sezioni 2D nelle dimensioni xey e 155 sezioni nella dimensione z. Queste dimensioni corrispondono a prospettive anatomiche distinte: vista assiale (piano xy), vista coronale (piano xz) e vista sagittale (piano yz).

Per semplificare, utilizzeremo solo sezioni 2D sul piano assiale, quindi le immagini risultanti avranno forma (240, 240) .

Per soddisfare le specifiche dei modelli PyTorch, i tensori di input devono avere forma (batch_size, num_channels, height, width). Nei nostri dati MRI, ciascuna delle quattro modalità (FLAIR, T1ce, T1, T2) enfatizza caratteristiche distinte all’interno dell’immagine, simili ai canali in un’immagine. Per allineare il nostro formato dati con i requisiti PyTorch, impileremo queste sequenze come canali che raggiungono un (4, 240, 240) tensore.

PyTorch fornisce due utilità dati, torch.utils.data.Dataset E torch.utils.data.DataLoaderprogettato per l’iterazione su set di dati e il caricamento dei dati in batch. IL Dataset include sottoclassi per vari set di dati standard come MNIST, CIFARO ImageNet. L’importazione di questi set di dati comporta il caricamento della rispettiva classe e l’inizializzazione del file DataLoader.

Consideriamo un esempio con il MNIST insieme di dati:

Questo ci permette di ottenere il tensore finale con dimensioni (batch_size=32, num_channels=1, H=28, W=28) tensore.

Poiché disponiamo di un set di dati non banale, la creazione di un file custom Dataset class è necessario prima di utilizzare il DataLoader. Sebbene i passaggi dettagliati per la creazione di questo set di dati personalizzato non siano trattati in questo post, i lettori possono fare riferimento al tutorial su PyTorch Set di dati e caricatori di dati per una guida completa.

PyTorch è un framework DL sviluppato dai ricercatori di Facebook AI nel 2017. The torchvision il pacchetto contiene set di dati popolari, trasformazioni di immagini e architetture di modelli. In torchvision.models possiamo trovare l’implementazione di alcune architetture DL per compiti diversi, come la classificazione, la segmentazione o il rilevamento di oggetti.

Per la nostra applicazione caricheremo il file ResNet18architettura.

ResNet(
(conv1): Conv2d(3, 64, kernel_size=(7, 7), stride=(2, 2), padding=(3, 3), bias=False)
(bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
(maxpool): MaxPool2d(kernel_size=3, stride=2, padding=1, dilation=1, ceil_mode=False)
(layer1): Sequential(
(0): BasicBlock(
(conv1): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
(conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
(1): BasicBlock(
(conv1): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
(conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
)
(layer2): Sequential(
(0): BasicBlock(
(conv1): Conv2d(64, 128, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)
(bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
(conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(downsample): Sequential(
(0): Conv2d(64, 128, kernel_size=(1, 1), stride=(2, 2), bias=False)
(1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
)
(1): BasicBlock(
(conv1): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
(conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
)
(layer3): Sequential(
(0): BasicBlock(
(conv1): Conv2d(128, 256, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)
(bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
(conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(downsample): Sequential(
(0): Conv2d(128, 256, kernel_size=(1, 1), stride=(2, 2), bias=False)
(1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
)
(1): BasicBlock(
(conv1): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
(conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
)
(layer4): Sequential(
(0): BasicBlock(
(conv1): Conv2d(256, 512, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)
(bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
(conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(downsample): Sequential(
(0): Conv2d(256, 512, kernel_size=(1, 1), stride=(2, 2), bias=False)
(1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
)
(1): BasicBlock(
(conv1): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
(conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
)
(avgpool): AdaptiveAvgPool2d(output_size=(1, 1))
(fc): Linear(in_features=512, out_features=1000, bias=True)
)

In una rete neurale, i livelli di input e output sono intrinsecamente collegati al problema specifico da affrontare. I modelli PyTorch DL in genere si aspettano immagini RGB a 3 canali come input, come possiamo osservare nella configurazione del livello convoluzionale iniziale Conv2d(in_channels = 3, out_channels = 64, kernel_size=(7,7), stride=(2,2), padding=(3,3), bias=False). Inoltre, lo strato finale Linear(in_features = 512, out_features = 1000, bias = True) il valore predefinito è una dimensione di output pari a 1000, che rappresenta il numero di classi nel set di dati ImageNet.

Prima di addestrare un modello di classificazione, è essenziale adeguarlo in_channels E out_features per allinearsi al nostro set di dati specifico. Possiamo accedere al primo strato convoluzionale attraverso resnet.conv1e aggiornare il in_channels. Allo stesso modo, possiamo accedere all’ultimo livello completamente connesso tramiteresnet.fc e modificare il out_features.

ResNet(
(conv1): Conv2d(4, 64, kernel_size=(7, 7), stride=(1, 1))
(bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
(maxpool): MaxPool2d(kernel_size=3, stride=2, padding=1, dilation=1, ceil_mode=False)
(layer1): Sequential(
(0): BasicBlock(
(conv1): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
(conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
(1): BasicBlock(
(conv1): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
(conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
)
(layer2): Sequential(
(0): BasicBlock(
(conv1): Conv2d(64, 128, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)
(bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
(conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(downsample): Sequential(
(0): Conv2d(64, 128, kernel_size=(1, 1), stride=(2, 2), bias=False)
(1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
)
(1): BasicBlock(
(conv1): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
(conv2): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
)
(layer3): Sequential(
(0): BasicBlock(
(conv1): Conv2d(128, 256, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)
(bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
(conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(downsample): Sequential(
(0): Conv2d(128, 256, kernel_size=(1, 1), stride=(2, 2), bias=False)
(1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
)
(1): BasicBlock(
(conv1): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn1): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
(conv2): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
)
(layer4): Sequential(
(0): BasicBlock(
(conv1): Conv2d(256, 512, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)
(bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
(conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(downsample): Sequential(
(0): Conv2d(256, 512, kernel_size=(1, 1), stride=(2, 2), bias=False)
(1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
)
(1): BasicBlock(
(conv1): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn1): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU(inplace=True)
(conv2): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
(bn2): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
)
)
(avgpool): AdaptiveAvgPool2d(output_size=(1, 1))
(fc): Linear(in_features=512, out_features=2, bias=True)
)

Con il nostro modello e i dati preparati per la formazione, possiamo procedere alla sua applicazione pratica.

L’esempio seguente illustra come possiamo utilizzare efficacemente il nostro ResNet18 per classificare un’immagine in bassa o alta qualità. Per gestire la dimensione della dimensione batch nel nostro tensore, aggiungeremo semplicemente una dimensione unitaria (nota che questa è normalmente gestita dal dataloader).

tensor(0.5465, device='cuda:0', grad_fn=<NllLossBackward0>)

E questo ci porta alla fine del post. Spero che questo sia stato utile per coloro che si avventurano nell’intersezione tra MRI e Deep Learning. Grazie per esserti preso il tempo di leggere. Per approfondimenti sulla mia ricerca, sentiti libero di esplorare il mio recente articolo! 🙂

Pitarca, C.; Ribas, V.; Vellido, A. Classificazione del glioma basata sull’intelligenza artificiale per una diagnosi affidabile: una pipeline analitica per una migliore affidabilità. Cancri 2023, 153369. https://doi.org/10.3390/cancers15133369.

Se non diversamente specificato, tutte le immagini sono dell’autore.

Fonte: towardsdatascience.com

Lascia un commento

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