In het Training Script-gedeelte werk je aan beeld-superresolutie met een nieuw deep learning-architectuur. De taak is een niet-lineaire mapping maken van een laagveld 3-Tesla hersen-MRI naar een hoogveld 7-Tesla hersen-MRI. In dit Testing Script-gedeelte gebruik je de gewichten die in Deel 1 zijn getraind en voorspel je op de niet-gezien getrainde data. Je leert ook hoe je de 2D-beelden uiteindelijk als één gecombineerd volume opslaat.
De tutorial is verdeeld in twee delen: in het eerste deel loop je door het trainingsproces en in het tweede deel behandel je het testproces.
Opmerking: Je bent waarschijnlijk geïnteresseerd in het lezen van de paper; je vindt het artikel hier.
Training Script
Samengevat behandel je in Deel 1 van de tutorial het volgende:
Je begint met het importeren van de benodigde modules die je nodig hebt om je deep learning-model te trainen,
Vervolgens krijg je een korte uitleg over de 3T- en 7T-MRI-dataset,
Daarna definieer je de initializers en laad je de 3T- en 7T-dataset; tijdens het laden resize je de beelden on-the-fly,
Vervolgens preprocess je de geladen data: zet de train- en testlijsten om naar numpy-matrices, zet de matrixtypen om naar float32, reschaal de matrices met een max-min-strategie, reshape de arrays en splits ten slotte de data in 80% training en 20% validatie,
Daarna maak je de 1-Encoder-3-Decoder-architectuur: met merge-verbindingen en multi-decoders,
Voordat je deze tutorial volgt, zorg dat je exact dezelfde moduleversies hebt als hieronder vermeld:
Keras==2.0.4
tensorflow==1.8.0
scipy==0.19.0
numpy==1.14.5
Pillow==4.1.1
nibabel==2.1.0
scikit_learn==0.18.1
Opmerking: Let op: het model wordt getraind op een systeem met een Nvidia 1080 Ti GPU, Xeon e5 GeForce-processor en 32GB RAM. Als je Jupyter Notebook gebruikt, moet je drie extra regels code toevoegen waarin je de CUDA-devicevolgorde en zichtbare CUDA-apparaten opgeeft met de module os.
In de onderstaande code stel je omgevingsvariabelen in de notebook in met os.environ. Het is handig dit vóór het initialiseren van Keras te doen om de Keras-backend TensorFlow te beperken tot de eerste GPU. Als de machine waarop je traint een GPU op 0 heeft, gebruik dan 0 in plaats van 1. Je kunt dit controleren met een eenvoudige terminalopdracht: bijvoorbeeld nvidia-smi
import os
os.environ["CUDA_DEVICE_ORDER"]="PCI_BUS_ID"
os.environ["CUDA_VISIBLE_DEVICES"]="0"#model will be trained on GPUMogelijk gemaakt door
Modules importeren
Eerst importeer je alle vereiste modules zoals tensorflow, numpy en vooral keras en de vereiste functies of lagen zoals Input, Conv2D,MaxPooling2D enz., omdat je al deze frameworks gaat gebruiken om het model te trainen! Om beelden in NIfTI-formaat te lezen, moet je ook een module importeren met de naam nibabel.
import os
import numpy as np
import math
import tensorflow as tf
import nibabel as nib
import numpy as np
from keras.layers import Input,Dense,merge,Reshape,Conv2D,MaxPooling2D,UpSampling2D
from keras.layers.normalization import BatchNormalization
from keras.models import Model,Sequential
from keras.callbacks import ModelCheckpoint
from keras.optimizers import RMSprop
from keras import backend as K
import scipy.misc
from sklearn.utils import shuffle
from sklearn.cross_validation import train_test_split
import matplotlib.pyplot as plt
from keras.models import model_from_json
Mogelijk gemaakt door
Using TensorFlow backend./usr/local/lib/python3.5/dist-packages/sklearn/cross_validation.py:41: DeprecationWarning: This module was deprecated in version 0.18in favor of the model_selection module into which all the refactored classes and functions are moved. Also, note that the interfaceof the newCV iterators is different from that ofthis module. This module will be removed in0.20."This module will be removed in 0.20.", DeprecationWarning)Mogelijk gemaakt door
De Brain MRI 3T- en 7T-dataset begrijpen
De 3T- en 7T-hersen-MRI-dataset bestaat uit 3D-volumes; elk volume bevat in totaal 207 slices/afbeeldingen van hersen-MRI’s, genomen op verschillende hoogten in de hersenen. Elke slice heeft een afmeting van 173 x 173. De beelden zijn enkelkanaals grijswaarden. Er zijn in totaal 39 proefpersonen, elk met de MRI-scan van een patiënt. Het bestandsformaat is geen jpeg, png, etc., maar NIfTI. In een later onderdeel zie je hoe je NIfTI-beelden leest.
De dataset bestaat uit T1-modus MR-beelden; T1-sequenties worden traditioneel als geschikt beschouwd voor de evaluatie van anatomische structuren. De dataset waarmee je vandaag werkt bestaat uit 3T- en 7T-hersen-MRI’s.
De dataset is openbaar en kan worden gedownload via deze bron.
28 proefpersonen worden gebruikt voor training, terwijl de resterende 11 voor testen worden gebruikt.
De initializers definiëren
Definieer eerst de dimensies van de data. Je resize’t de afbeeldingsdimensie van 173x173 naar 176x176 in het uitleesgedeelte. Hier definieer je ook de datamap, de batchgrootte voor training, het aantal kanalen, een Input()-laag, train- en testmatrices als lijst en laad je ten slotte voor rescaling het tekstbestand met de minimum- en maximumwaarden van de MRI-dataset.
Opmerking: Voor rescaling kun je ook het maximum en minimum van de dataset nemen, aangezien je het bestand maxANDmin.txt mogelijk niet hebt.
Vervolgens laad je de MRI-data met de nibabel-bibliotheek en resize je de beelden van 173 x 173 naar 176 x 176 door nullen te padden in de x- en y-dimensie.
Let op: wanneer je een NIfTI-volume laadt, laadt Nibabel de afbeeldingsarray niet direct. Er wordt gewacht tot je expliciet om de arraydata vraagt. De standaardmanier is de methode get_data() aanroepen.
Omdat je 2D-slices wilt in plaats van 3D, gebruik je de lijsten train en test die je eerder hebt geïnitialiseerd; telkens wanneer je een volume leest, itereer je over alle 207 slices van het 3D-volume en voeg je elke slice één voor één toe aan een lijst.
for f in folder:
temp = np.zeros([resizeTo,full_z,resizeTo])
a = nib.load(inp + f)
a = a.get_data()
temp[3:,:,3:]= a
a = temp
for j inrange(full_z):
train_matrix.append(a[:,j,:])Mogelijk gemaakt door
Laad daarna de 7T-data; je gebruikt dezelfde variabele folder voor het laden van 7T, aangezien het aantal 3T- en 7T-MR-volumes gelijk is.
for f in folder:
temp = np.zeros([resizeTo,full_z,resizeTo])
b = nib.load(out + f)
b = b.get_data()
temp[3:,:,3:]= b
b = temp
for j inrange(full_z):
test_matrix.append(b[:,j,:])Mogelijk gemaakt door
Datapreprocessing
Aangezien train- en testmatrices lijsten zijn, gebruik je numpy om de lijst naar een numpy-array om te zetten.
Verder zet je het type van de numpy-array om naar float32 en reschaal je zowel de input als de ground truth.
train_matrix = np.asarray(train_matrix)
train_matrix = train_matrix.astype('float32')
m = min_max[0]
mi = min_max[1]
train_matrix =(train_matrix - mi)/(m - mi)
test_matrix = np.asarray(test_matrix)
test_matrix = test_matrix.astype('float32')
test_matrix =(test_matrix - mi)/(m - mi)Mogelijk gemaakt door
Print snel even de shape van de train_matrix (3T) en test_matrix (7T). Ze zouden in totaal 28 x 207 = 5796 afbeeldingen moeten hebben, elk met een dimensie van 176 x 176.
Vervolgens maak je twee nieuwe variabelen augmented_images (3T/input) en Haugmented_images (7T/ground truth) met de shape van de train- en testmatrix. Dit wordt een 4D-matrix waarin de eerste dimensie het aantal afbeeldingen is, de tweede en derde de afmetingen van elke afbeelding, en de laatste het aantal kanalen, dat hier één is."
augmented_images=np.zeros(shape=[(train_matrix.shape[0]),(train_matrix.shape[1]),(train_matrix.shape[2]),(1)])
Haugmented_images=np.zeros(shape=[(train_matrix.shape[0]),(train_matrix.shape[1]),(train_matrix.shape[2]),(1)])Mogelijk gemaakt door
Daarna itereren je over alle afbeeldingen één voor één; elke keer reshape je de train- en testmatrix naar 176 x 176 en voeg je die toe aan respectievelijk augmented_images (3T/input) en Haugmented_images (7T/ground truth).
for i inrange(train_matrix.shape[0]):
augmented_images[i,:,:,0]= train_matrix[i,:,:].reshape(resizeTo,resizeTo)
Haugmented_images[i,:,:,0]= test_matrix[i,:,:].reshape(resizeTo,resizeTo)Mogelijk gemaakt door
Na dit alles is het belangrijk de data te verdelen. Om je model goed te laten generaliseren, splits je de data in een trainings- en een validatieset. Je traint je model op 80% van de data en valideert op de resterende 20%.
Dit helpt ook om overfitting te verminderen, omdat je valideert op data die het model niet tijdens training heeft gezien.
Je kunt de train_test_split-module van scikit-learn gebruiken die je in het begin hebt gedefinieerd om de data goed te verdelen:
data,Label = shuffle(augmented_images,Haugmented_images, random_state=2)
X_train, X_test, y_train, y_test = train_test_split(data, Label, test_size=0.2, random_state=2)Mogelijk gemaakt door
X_test = np.array(X_test)
y_test = np.array(y_test)
X_test = X_test.astype('float32')
y_test = y_test.astype('float32')Mogelijk gemaakt door
X_train = np.array(X_train)
y_train = np.array(y_train)
X_train = X_train.astype('float32')
y_train = y_train.astype('float32')Mogelijk gemaakt door
Vervolgens definieer je de voorgestelde architectuur met blokken van opeenvolgende filterlagen, gevolgd door een maxpooling-laag in de encodersectie, zoals in de volgende cel. Om de oorspronkelijke afbeeldingsgrootte bij de output te reconstrueren, wordt in elk decoderblok een upsampling-laag geïntroduceerd. Bij upsampling kunnen artefacten ontstaan door ontbrekende details in de gedownsamplede input van de decoder. Daarom concateneer je de input van de decoder met de opgeschaalde versie uit de encoder om de aard van opgeschaalde details te bieden voor een betere reconstructie tijdens het upsamplen in de decoder. Het toevoegen van merge-verbindingen levert inderdaad een significante PSNR-verbetering op (orde van 5 dB). Deze instelling is geïnspireerd op deze paper.
Multi-decoders
De voorgestelde aanpak gebruikt één encoder en meerdere decoders met een enkelkanaals input. In elk blok van de encoder en in alle drie de decoders worden drie convolutionele lagen gebruikt, gevolgd door batchnormalisatie om numerieke stabiliteit te behouden.
Het eerste convolutionele blok in de encoder heeft 32 filters, en het aantal filters verdubbelt na elk blok. In alle decoders heeft het eerste convolutionele blok 256 filters, en het aantal filters wordt na elk blok gehalveerd. Je gebruikt een filtergrootte van 3x3 in alle convolutionele blokken.
Rectified Linear Unit (ReLU) wordt als activatiefunctie gebruikt in alle lagen behalve de laatste. Omdat data is genormaliseerd tussen 0 en 1, wordt in de laatste laag een Sigmoid-activatie gebruikt.
Het is bekend dat lokale afbeeldingsdetails op verschillende schalen een grote rol spelen bij reconstructie. De voorgestelde architectuur bekijkt beelden op verschillende schalen met hiërarchische lagen voor downsampling (maxpooling) en upsampling (ieder met factor 2) in respectievelijk encoder en decoders. De gecodeerde representatie na drie downsamplings brengt de data van een hoge-dimensie-input naar een latente representatie.
In de bovenstaande 4 cellen definieerde je vier functies: één voor de encoder en de overige drie voor de decoders. Omdat het functies zijn, zou je de decoder()-functie kunnen definiëren en drie keer aanroepen, maar voor een beter begrip definieer je hem drie keer en om issues met de willekeur in de drie decoders te vermijden.
Verliesfunctie
Vervolgens gebruik je mean squared error waarbij je de waarden (pixels) van zowel de ground truth y_t als y_p die gelijk zijn aan nul, uitsluit.
defroot_mean_sq_GxGy(y_t, y_p):
a1=1
where = tf.not_equal(y_t,0)
a_t=tf.boolean_mask(y_t,where,name='boolean_mask')
a_p=tf.boolean_mask(y_p,where,name='boolean_mask')return a1*(K.sqrt(K.mean((K.square(a_t-a_p)))))Mogelijk gemaakt door
Modeldefinitie en compilatie
Eerst roep je de encoder-functie aan met de input. Omdat je merge-verbindingen in je architectuur gebruikt, retourneert de encoder-functie de output van vijf convolution-lagen die je vervolgens afzonderlijk merget met alle drie decoder-uitgangen.
/usr/local/lib/python3.5/dist-packages/ipykernel_launcher.py:2: UserWarning: The `merge`function is deprecated and will be removed after 08/2017. Use instead layers from`keras.layers.merge`, e.g.`add`,`concatenate`, etc./usr/local/lib/python3.5/dist-packages/keras/legacy/layers.py:460: UserWarning: The `Merge` layer is deprecated and will be removed after 08/2017. Use instead layers from`keras.layers.merge`, e.g.`add`,`concatenate`, etc.
name=name)/usr/local/lib/python3.5/dist-packages/ipykernel_launcher.py:10: UserWarning: The `merge`function is deprecated and will be removed after 08/2017. Use instead layers from`keras.layers.merge`, e.g.`add`,`concatenate`, etc.
# Remove the CWD from sys.path while we load stuff./usr/local/lib/python3.5/dist-packages/ipykernel_launcher.py:18: UserWarning: The `merge`function is deprecated and will be removed after 08/2017. Use instead layers from`keras.layers.merge`, e.g.`add`,`concatenate`, etc./usr/local/lib/python3.5/dist-packages/ipykernel_launcher.py:26: UserWarning: The `merge`function is deprecated and will be removed after 08/2017. Use instead layers from`keras.layers.merge`, e.g.`add`,`concatenate`, etc.WARNING:tensorflow:From /usr/local/lib/python3.5/dist-packages/keras/backend/tensorflow_backend.py:1257: calling reduce_mean(from tensorflow.python.ops.math_ops)with keep_dims is deprecated and will be removed in a future version.
Instructions forupdating:
keep_dims is deprecated, use keepdims instead
Mogelijk gemaakt door
Het model trainen
Je slaat de gewichten alleen op wanneer de peak signal-to-noise ratio op de validatiedata verbetert. Daarvoor definieer je een lijst psnr_gray_channel waarin je een standaardwaarde 1 toevoegt; je voegt 1 toe omdat je de lijst moet initialiseren met een willekeurig getal en de PSNR hier al snel hoger dan deze waarde zal zijn, zodat dit als dummy-waarde dient.
Je initialiseert de learning rate op 1e-3 en gebruikt een learning-rate-decaystrategie waarbij je de learning rate elke 20 epochs met 10% verlaagt vanaf de huidige waarde.
psnr_gray_channel =[]
psnr_gray_channel.append(1)
learning_rate =0.001
j=0Mogelijk gemaakt door
Opmerking: De volgende volledige code moet in één cel worden uitgevoerd, maar voor een beter begrip van het trainingsproces wordt hij in kleinere cellen opgesplitst.
Het model wordt 500 epochs getraind. De initiële learning rate is 1e-3 zoals hierboven gedefinieerd. Je slaat ook de PSNR- en MSE-waarden op na elke epoch van 7T met predicted. Je gebruikt de functie K.set_value om de learning rate van alle drie modellen met 10% te wijzigen, maar alleen na elke 20 epochs.
for jj inrange(500):
myfile_valid_psnr_7T =open('../1_encoder_3_decoders_complete_slices_single_channel/validation_psnr7T_1encoder_3decoders.txt','a')
myfile_valid_mse_7T =open('../1_encoder_3_decoders_complete_slices_single_channel/validation_mse7T_1encoder_3decoders.txt','a')
K.set_value(autoencoder_1.optimizer.lr, learning_rate)
K.set_value(autoencoder_2.optimizer.lr, learning_rate)
K.set_value(autoencoder_3.optimizer.lr, learning_rate)Mogelijk gemaakt door
Vervolgens shuffle je de input 3T- en ground truth 7T-beelden om overfitting te voorkomen, omdat niet shufflen het model dwingt de samples elke epoch in dezelfde volgorde te zien. Daarna bereken je het aantal batches op basis van de eerder gedefinieerde batch_size. Ten slotte begin je te itereren over num_batches.
train_X,train_Y = shuffle(X_train,y_train)print("Epoch is: %d\n"% j)print("Number of batches: %d\n"%int(len(train_X)/batch_size))
num_batches =int(len(train_X)/batch_size)for batch inrange(num_batches):Mogelijk gemaakt door
Naast PSNR-waarden sla je ook de losses van alle drie autoencoders en de drie decoders respectievelijk op.
myfile_ae1_loss =open('../1_encoder_3_decoders_complete_slices_single_channel/ae1_train_loss_1encoder_3decoders.txt','a')
myfile_ae2_loss =open('../1_encoder_3_decoders_complete_slices_single_channel/ae2_train_loss_1encoder_3decoders.txt','a')
myfile_ae3_loss =open('../1_encoder_3_decoders_complete_slices_single_channel/ae3_train_loss_1encoder_3decoders.txt','a')
myfile_dec1_loss =open('../1_encoder_3_decoders_complete_slices_single_channel/dec1_train_loss_1encoder_3decoders.txt','a')
myfile_dec2_loss =open('../1_encoder_3_decoders_complete_slices_single_channel/dec2_train_loss_1encoder_3decoders.txt','a')
myfile_dec3_loss =open('../1_encoder_3_decoders_complete_slices_single_channel/dec3_train_loss_1encoder_3decoders.txt','a')Mogelijk gemaakt door
Omdat je in elke batch wilt dat het model de volgende 32 (batch_size) samples ziet, regelt de volgende cel dat.
batch_train_X = train_X[batch*batch_size:min((batch+1)*batch_size,len(train_X)),:]
batch_train_Y = train_Y[batch*batch_size:min((batch+1)*batch_size,len(train_Y)),:]Mogelijk gemaakt door
Voor onze Minimum-autoencoderstrategie test je na elke epoch op de trainingsdata met de Keras-functie test_on_batch die je drie losses geeft, en die print je ten slotte.
Er kunnen zes mogelijke situaties zijn in je netwerk: loss_1: Autoencoder 1 loss loss_2: Autoencoder 2 loss loss_3: Autoencoder 3 loss
loss_1 kan groter zijn dan loss_2 en loss_3. Als dit klopt, train je alleen met Autoencoder 1. Je zet de Encoder-delen van Autoencoder 2 en 3 op False en traint alleen hun decoders, en ten slotte schrijf je alle losses naar de tekstbestanden.
loss_2 kan groter zijn dan loss_1 en loss_3. Als dit klopt, train je alleen met Autoencoder 2. Je zet de Encoder-delen van Autoencoder 1 en 3 op False en traint alleen hun decoders, en ten slotte schrijf je alle losses naar de tekstbestanden.
loss_3 kan groter zijn dan loss_1 en loss_2. Als dit klopt, train je alleen met Autoencoder 3. Je zet de Encoder-delen van Autoencoder 1 en 2 op False en traint alleen hun decoders, en ten slotte schrijf je alle losses naar de tekstbestanden.
loss_1 kan gelijk zijn aan loss_2. Als dit klopt, train je met Autoencoder 1 of 2. Je zet het Encoder-deel van Autoencoder 3 op False, samen met die van de autoencoder die je niet koos tussen 1 en 2, en traint alleen hun decoders, en ten slotte schrijf je alle losses naar de tekstbestanden.
loss_2 kan gelijk zijn aan loss_3. Als dit klopt, train je met Autoencoder 2 of 3. Je zet het Encoder-deel van Autoencoder 1 op False, samen met die van de autoencoder die je niet koos tussen 2 en 3, en traint alleen hun decoders, en ten slotte schrijf je alle losses naar de tekstbestanden.
loss_3 kan gelijk zijn aan loss_1. Als dit klopt, train je met Autoencoder 3 of 1. Je zet het Encoder-deel van Autoencoder 2 op False, samen met die van de autoencoder die je niet koos tussen 3 en 1, en traint alleen hun decoders, en ten slotte schrijf je alle losses naar de tekstbestanden.
Dit is een essentiële stap: omdat je in de bovenstaande condities enkele lagen op False hebt gezet, moet je deze lagen weer True maken, zodat alle autoencoderlagen kunnen worden gebruikt voor de functie test_on_batch en je ze niet de hele training op False laat staan.
for layer in autoencoder_1.layers[:34]:
layer.trainable =Truefor layer in autoencoder_2.layers[:34]:
layer.trainable =Truefor layer in autoencoder_3.layers[:34]:
layer.trainable =TrueMogelijk gemaakt door
Je slaat gewichten op op drie manieren; hieronder staan er twee. Eén keer sla je na elke 100 epochs op, en ten tweede sla je na elke epoch op. In feite worden ze na elke epoch overschreven.
if jj %100==0:
autoencoder_1.save_weights("../Model/CROSSVAL1/CROSSVAL1_AE1_"+str(jj)+".h5")
autoencoder_2.save_weights("../Model/CROSSVAL1/CROSSVAL1_AE2_"+str(jj)+".h5")
autoencoder_3.save_weights("../Model/CROSSVAL1/CROSSVAL1_AE3_"+str(jj)+".h5")
autoencoder_1.save_weights("../Model/CROSSVAL1/CROSSVAL1_AE1.h5")
autoencoder_2.save_weights("../Model/CROSSVAL1/CROSSVAL1_AE2.h5")
autoencoder_3.save_weights("../Model/CROSSVAL1/CROSSVAL1_AE3.h5")Mogelijk gemaakt door
Testen op validatiedata
Eerst shuffle je de validatiedata en gebruik je dezelfde 6 mogelijke condities als hierboven gedefinieerd. Welke conditie ook True is, je test je model met die specifieke autoencoder.
Daarna bereken je twee metrics MSE en PSNR tussen decoded_imgs (voorspeld) en ground truth. Tot slot sla je ze op in tekstbestanden.
Hier sla je de gewichten alleen op wanneer de PSNR tussen de voorspelde 7T en de ground truth (7T) maximaal is vergeleken met de eerdere PSNR-waarden in de lijst psnr_gray_channel.
ifmax(psnr_gray_channel)< psnr_7T:
autoencoder_1.save_weights("../Model/CROSSVAL1/CROSSVAL1_AE1_"+str(jj)+".h5")
autoencoder_2.save_weights("../Model/CROSSVAL1/CROSSVAL1_AE2_"+str(jj)+".h5")
autoencoder_3.save_weights("../Model/CROSSVAL1/CROSSVAL1_AE3_"+str(jj)+".h5")
psnr_gray_channel.append(psnr_7T)Mogelijk gemaakt door
Input, ground truth en decoded opslaan: kwantitatieve resultaten
Je definieert een numpy-matrix temp van 176 x 528, omdat er 3 afbeeldingen in één rij worden opgeslagen, elk 176 x 176. Je slaat een willekeurige afbeelding op uit de validatie-3T, 7T en predicted en vermenigvuldigt de matrix met 255 omdat de afbeeldingen geschaald waren tussen 0 en 1.
Tot slot sla je de afbeelding op met de scipy-functie.
temp = np.zeros([resizeTo,resizeTo*3])
temp[:resizeTo,:resizeTo]= X_test[0,:,:,0]
temp[:resizeTo,resizeTo:resizeTo*2]= y_test[0,:,:,0]
temp[:resizeTo,2*resizeTo:]= decoded_imgs[0,:,:,0]
temp = temp*255
scipy.misc.imsave('../Results/1_encoder_3_decoders_complete_slices_single_channel/'+str(j)+'.jpg', temp)
j +=1Mogelijk gemaakt door
Laten we de bestanden PSNR en MSE aan het eind sluiten.
Tot slot verlaag je na elke 20 epochs de learning rate met 10% van de huidige waarde.
if jj %20==0:
learning_rate = learning_rate - learning_rate *0.10Mogelijk gemaakt door
Testing Script
In deel 2 van deze tutorial behandel je het volgende:
Je begint met het importeren van de benodigde modules die je nodig hebt om je deep learning-model te trainen,
Vervolgens krijg je een korte uitleg over de 3T- en 7T-MRI-dataset,
Daarna definieer je de initializers en laad je de 3T- en 7T-testdataset; tijdens het laden resize je de beelden on-the-fly,
Vervolgens preprocess je de geladen data: zet de train- en testlijsten om naar numpy-matrices, zet de matrixtypen om naar float32, reschaal de matrices met een max-min-strategie, reshape de arrays en splits ten slotte de data in 80% training en 20% validatie,
Daarna maak je de 1-Encoder-3-Decoder-architectuur: met merge-verbindingen en multi-decoders,
Vervolgens definieer je: verliesfunctie, drie verschillende modellen en laad je de getrainde gewichten,
Tot slot is het tijd om te voorspellen met je merge- en multi-decodermodel op de niet-gezien data en zowel de kwantitatieve als kwalitatieve resultaten op te slaan. Je leert ook hoe je 2D-beelden als één gecombineerd volume opslaat met nibabel.
Python-module-afhankelijkheden
Voordat je deze tutorial volgt, zorg dat je exact dezelfde moduleversies hebt als hieronder vermeld:
Keras==2.0.4
tensorflow==1.8.0
scipy==0.19.0
numpy==1.14.5
Pillow==4.1.1
nibabel==2.1.0
scikit_learn==0.18.1
Opmerking: Let op: het model wordt getraind op een systeem met een Nvidia 1080 Ti GPU, Xeon e5 GeForce-processor en 32GB RAM. Als je Jupyter Notebook gebruikt, moet je drie extra regels code toevoegen waarin je de CUDA-devicevolgorde en zichtbare CUDA-apparaten opgeeft met de module os.
In de onderstaande code stel je in feite omgevingsvariabelen in de notebook in met os.environ. Het is handig dit vóór het initialiseren van Keras te doen om de Keras-backend TensorFlow te beperken tot de eerste GPU. Als de machine waarop je traint een GPU op 0 heeft, gebruik dan 0 in plaats van 1. Je kunt dit controleren met een eenvoudige terminalopdracht: bijvoorbeeld nvidia-smi
import os
os.environ["CUDA_DEVICE_ORDER"]="PCI_BUS_ID"
os.environ["CUDA_VISIBLE_DEVICES"]="0"#model will be trained on GPUMogelijk gemaakt door
Modules importeren
Eerst importeer je alle vereiste modules zoals tensorflow, numpy en vooral keras en de vereiste functies of lagen zoals Input, Conv2D,MaxPooling2D, enz., omdat je al deze frameworks gaat gebruiken om het model te trainen! Om beelden in NIfTI-formaat te lezen, moet je ook een module importeren met de naam nibabel.
import os
from keras.layers import Input,Dense,Flatten,Dropout,merge,Reshape,Conv2D,MaxPooling2D,UpSampling2D
from keras.layers.normalization import BatchNormalization
from keras.models import Model,Sequential
from keras.callbacks import ModelCheckpoint
from keras.optimizers import Adadelta, RMSprop,SGD,Adam
from keras import regularizers
from keras import backend as K
import numpy as np
import scipy.misc
import numpy.random as rng
from sklearn.utils import shuffle
import nibabel as nib
from sklearn.cross_validation import train_test_split
import math
Mogelijk gemaakt door
De Brain MRI 3T- en 7T-dataset begrijpen
De 3T- en 7T-hersen-MRI-dataset bestaat uit 3D-volumes; elk volume bevat in totaal 207 slices/afbeeldingen van hersen-MRI’s, genomen op verschillende hoogten in de hersenen. Elke slice heeft een afmeting van 173 x 173. De beelden zijn enkelkanaals grijswaarden. Er zijn in totaal 39 proefpersonen, elk met de MRI-scan van een patiënt. Het bestandsformaat is geen jpeg, png, etc., maar NIfTI. In een later onderdeel zie je hoe je NIfTI-beelden leest.
De dataset bestaat uit T1-modus MR-beelden; T1-sequenties worden traditioneel als geschikt beschouwd voor de evaluatie van anatomische structuren. De dataset waarmee je vandaag werkt bestaat uit 3T- en 7T-hersen-MRI’s.
De dataset is openbaar en kan worden gedownload via deze bron.
De initializers definiëren
Definieer eerst de dimensies van de data. Je resize’t de afbeeldingsdimensie van 173x173 naar 176x176 in het uitleesgedeelte. Hier definiëren we ook de datamap, batchgrootte voor het trainen van het model, aantal kanalen, een Input()-laag, train- en testmatrices als lijst en ten slotte laden we voor rescaling het tekstbestand met de minimum- en maximumwaarden van de MRI-dataset.
x,y =173,173
full_z =207
resizeTo=176
inChannel = outChannel =1
input_shape=(x,y,inChannel)
input_img = Input(shape =(resizeTo, resizeTo, inChannel))Mogelijk gemaakt door
train_matrix =[]
test_matrix =[]
ff = os.listdir("../test_crossval1")
save ="../Result_nii_crossval1"
folder_ground = os.listdir("../test_g_crossval1")
ToPredict_images=[]
predict_matrix=[]Mogelijk gemaakt door
ground_images=[]
ground_matrix=[]
min_max=np.loadtxt('../maxANDmin.txt')Mogelijk gemaakt door
De testvolumes laden
Vervolgens laden we de MRI-data met de nibabel-bibliotheek en resize’en we de beelden van 173 x 173 naar 176 x 176 door nullen te padden in de x- en y-dimensie.
Let op: wanneer je een NIfTI-volume laadt, laadt Nibabel de afbeeldingsarray niet direct. Er wordt gewacht tot je expliciet om de arraydata vraagt. De standaardmanier is de methode get_data() aanroepen.
Omdat je 2D-slices wilt in plaats van 3D, gebruik je de lijsten train en test die je eerder hebt geïnitialiseerd; telkens wanneer je een volume leest, itereer je over alle 207 slices van het 3D-volume en voeg je elke slice één voor één toe aan een lijst.
for f in ff:
temp = np.zeros([resizeTo,full_z,resizeTo])
a = nib.load("../test_crossval1"+ f)
affine = a.affine
a = a.get_data()
temp[3:,:,3:]= a
a = temp
for j inrange(full_z):
predict_matrix.append(a[:,j,:])Mogelijk gemaakt door
for f in ff:
temp = np.zeros([resizeTo,full_z,resizeTo])
a = nib.load("../test_g_crossval1"+ f)
affine = a.affine
a = a.get_data()
temp[3:,:,3:]= a
a = temp
for j inrange(full_z):
ground_matrix.append(a[:,j,:])Mogelijk gemaakt door
Datapreprocessing
Aangezien de 3T- en 7T-testmatrices lijsten zijn, gebruik je numpy om de lijst naar een numpy-array om te zetten.
Verder zet je het type van de numpy-array om naar float32 en reschaal je zowel de input als de ground truth.
ToPredict_images = np.asarray(predict_matrix)
ToPredict_images = ToPredict_images.astype('float32')
mx = min_max[0]
mn = min_max[1]
ToPredict_images[:,:,:,0]=(ToPredict_images[:,:,:,0]- mn )/(mx - mn)Mogelijk gemaakt door
ground_images = np.asarray(ground_matrix)
ground_images = ground_images.astype('float32')
ground_images[:,:,:,0]=(ground_images[:,:,:,0]- mn )/(mx - mn)Mogelijk gemaakt door
Vervolgens maak je twee nieuwe variabelen ToPredict_images (3T Test/input) en ground_images (7T Test/ground truth) met de shape van de train- en testmatrix. Dit wordt een 4D-matrix waarin de eerste dimensie het aantal afbeeldingen is, de tweede en derde de afmetingen van elke afbeelding, en de laatste het aantal kanalen, dat hier één is."
ToPredict_images=np.zeros(shape=[(ToPredict_images.shape[0]),(ToPredict_images.shape[1]),(ToPredict_images.shape[2]),(1)])
ground_images=np.zeros(shape=[(ground_images.shape[0]),(ground_images.shape[1]),(ground_images.shape[2]),(1)])Mogelijk gemaakt door
Daarna itereren je over alle afbeeldingen één voor één; elke keer reshape je de train- en testmatrix naar 176 x 176 en voeg je die toe aan respectievelijk ToPredict_images (3T Test/input) en ground_images (7T Test/ground truth).
for i inrange(ToPredict_images.shape[0]):
ToPredict_images[i,:,:,0]= ToPredict_images[i,:,:].reshape(resizeTo,resizeTo)Mogelijk gemaakt door
for i inrange(ground_images.shape[0]):
ground_images[i,:,:,0]= ground_images[i,:,:].reshape(resizeTo,resizeTo)Mogelijk gemaakt door
Vervolgens gebruik je mean squared error waarbij je de waarden (pixels) van zowel de ground truth y_t als y_p die gelijk zijn aan nul, uitsluit.
defroot_mean_sq_GxGy(y_t, y_p):
a1=1
zero = tf.constant(0, dtype=tf.float32)
where = tf.not_equal(y_t, zero)
a_t=tf.boolean_mask(y_t,where,name='boolean_mask')
a_p=tf.boolean_mask(y_p,where,name='boolean_mask')return a1*(K.sqrt(K.mean((K.square(a_t-a_p)))))Mogelijk gemaakt door
Modeldefinitie en gewichten laden in alle drie autoencoders!
Roep eerst de encoder-functie aan met de input. Omdat je merge-verbindingen gebruikt in je architectuur, retourneert de encoder-functie de output van vijf convolution-lagen die je vervolgens afzonderlijk merget met alle drie decoder-uitgangen.
Laten we nu drie verschillende modellen maken en de drie getrainde modelgewichten erin laden.
autoencoder_1 = Model(input_img, decoder(conv5,conv4,conv3,conv2,conv1))
autoencoder_1.load_weights("../Model/CROSSVAL1/CROSSVAL1_AE1.h5")Mogelijk gemaakt door
autoencoder_2 = Model(input_img, decoder(conv5,conv4,conv3,conv2,conv1))
autoencoder_2.load_weights("../Model/CROSSVAL1/CROSSVAL1_AE2.h5")Mogelijk gemaakt door
autoencoder_3 = Model(input_img, decoder(conv5,conv4,conv3,conv2,conv1))
autoencoder_3.load_weights("../Model/CROSSVAL1/CROSSVAL1_AE3.h5")Mogelijk gemaakt door
Modelvoorspelling op testvolumes: kwantitatieve en kwalitatieve resultaten!
Laten we snel twee numpy-arrays initialiseren van elk 11 x 3 x 1, waarbij de eerste dimensie het aantal volumes voor testen is. De tweede dimensie staat voor de MSE en PSNR tussen: voorspelde 7T-MR-beelden en ground truth 7T-MR-beelden, voorspelde output en input 3T-MR-beelden, input 3T-MR-beelden en ground truth 7T-MR-beelden; de derde dimensie staat voor het aantal kanalen dat je aan je model voert!
mse= np.zeros([11,3,1])
psnr= np.zeros([11,3,1])
i=0#for iterating over the slices of all the 11 volumesMogelijk gemaakt door
In het volgende deel van de cel itereer je over alle 11 volumes één voor één. In elke iteratie voorspel je elk volume met alle drie autoencoders en neem je uiteindelijk het gemiddelde van de voorspellingen.
In elk volume itereer je over het aantal kanalen van dat volume en bereken je PSNR en MSE voor drie verschillende gevallen zoals hierboven besproken.
Vervolgens sla je met de nibabel-bibliotheek de voorspelde output, input (3T) en ground truth (7T) op als .nii-bestanden: elk van de 11 volumes met 207 slices.
Ten slotte sla je de PSNR-matrix op in een tekstbestand met de numpy-bibliotheek.
Zoals vermeld in de paper helpt het middelen van de voorspelde outputs om ruiseffecten te verminderen maar lokale features in de gereconstrueerde beelden te behouden, waardoor de PSNR verbetert ten opzichte van individuele decoderoutputs.
In het Training Script-gedeelte werk je aan beeld-superresolutie met een nieuw deep learning-architectuur. De taak is een niet-lineaire mapping maken van een laagveld 3-Tesla hersen-MRI naar een hoogveld 7-Tesla hersen-MRI. In dit Testing Script-gedeelte gebruik je de gewichten die in Deel 1 zijn getraind en voorspel je op de niet-gezien getrainde data. Je leert ook hoe je de 2D-beelden uiteindelijk als één gecombineerd volume opslaat.
De tutorial is verdeeld in twee delen: in het eerste deel loop je door het trainingsproces en in het tweede deel behandel je het testproces.
Opmerking: Je bent waarschijnlijk geïnteresseerd in het lezen van de paper; je vindt het artikel hier.
Training Script
Samengevat behandel je in Deel 1 van de tutorial het volgende:
Python-module-afhankelijkheden
Voordat je deze tutorial volgt, zorg dat je exact dezelfde moduleversies hebt als hieronder vermeld:
Opmerking: Let op: het model wordt getraind op een systeem met een Nvidia 1080 Ti GPU, Xeon e5 GeForce-processor en 32GB RAM. Als je Jupyter Notebook gebruikt, moet je drie extra regels code toevoegen waarin je de CUDA-devicevolgorde en zichtbare CUDA-apparaten opgeeft met de module os.
In de onderstaande code stel je omgevingsvariabelen in de notebook in met os.environ. Het is handig dit vóór het initialiseren van Keras te doen om de Keras-backend TensorFlow te beperken tot de eerste GPU. Als de machine waarop je traint een GPU op 0 heeft, gebruik dan 0 in plaats van 1. Je kunt dit controleren met een eenvoudige terminalopdracht: bijvoorbeeld nvidia-smi
Modules importeren
Eerst importeer je alle vereiste modules zoals tensorflow, numpy en vooral keras en de vereiste functies of lagen zoals
Input,Conv2D,MaxPooling2Denz., omdat je al deze frameworks gaat gebruiken om het model te trainen!Om beelden in NIfTI-formaat te lezen, moet je ook een module importeren met de naam
nibabel.De Brain MRI 3T- en 7T-dataset begrijpen
De 3T- en 7T-hersen-MRI-dataset bestaat uit 3D-volumes; elk volume bevat in totaal 207 slices/afbeeldingen van hersen-MRI’s, genomen op verschillende hoogten in de hersenen. Elke slice heeft een afmeting van 173 x 173. De beelden zijn enkelkanaals grijswaarden. Er zijn in totaal 39 proefpersonen, elk met de MRI-scan van een patiënt. Het bestandsformaat is geen jpeg, png, etc., maar NIfTI. In een later onderdeel zie je hoe je NIfTI-beelden leest.
De dataset bestaat uit T1-modus MR-beelden; T1-sequenties worden traditioneel als geschikt beschouwd voor de evaluatie van anatomische structuren. De dataset waarmee je vandaag werkt bestaat uit 3T- en 7T-hersen-MRI’s.
De dataset is openbaar en kan worden gedownload via deze bron.
28 proefpersonen worden gebruikt voor training, terwijl de resterende 11 voor testen worden gebruikt.
De initializers definiëren
Definieer eerst de dimensies van de data. Je resize’t de afbeeldingsdimensie van 173x173 naar 176x176 in het uitleesgedeelte. Hier definieer je ook de datamap, de batchgrootte voor training, het aantal kanalen, een
Input()-laag, train- en testmatrices als lijst en laad je ten slotte voor rescaling het tekstbestand met de minimum- en maximumwaarden van de MRI-dataset.Opmerking: Voor rescaling kun je ook het maximum en minimum van de dataset nemen, aangezien je het bestand
maxANDmin.txtmogelijk niet hebt.De data laden
Vervolgens laad je de MRI-data met de
nibabel-bibliotheek en resize je de beelden van 173 x 173 naar 176 x 176 door nullen te padden in de x- en y-dimensie.Let op: wanneer je een NIfTI-volume laadt, laadt Nibabel de afbeeldingsarray niet direct. Er wordt gewacht tot je expliciet om de arraydata vraagt. De standaardmanier is de methode get_data() aanroepen.
Omdat je 2D-slices wilt in plaats van 3D, gebruik je de lijsten
train en testdie je eerder hebt geïnitialiseerd; telkens wanneer je een volume leest, itereer je over alle 207 slices van het 3D-volume en voeg je elke slice één voor één toe aan een lijst.Laten we eerst de 3T-data laden.
Laad daarna de 7T-data; je gebruikt dezelfde variabele
foldervoor het laden van 7T, aangezien het aantal 3T- en 7T-MR-volumes gelijk is.Datapreprocessing
Aangezien train- en testmatrices lijsten zijn, gebruik je numpy om de lijst naar een numpy-array om te zetten.
Verder zet je het
typevan de numpy-array om naarfloat32en reschaal je zowel deinputals deground truth.Print snel even de shape van de train_matrix (3T) en test_matrix (7T). Ze zouden in totaal 28 x 207 = 5796 afbeeldingen moeten hebben, elk met een dimensie van 176 x 176.
Vervolgens maak je twee nieuwe variabelen
augmented_images(3T/input) enHaugmented_images(7T/ground truth) met de shape van de train- en testmatrix. Dit wordt een 4D-matrix waarin de eerste dimensie het aantal afbeeldingen is, de tweede en derde de afmetingen van elke afbeelding, en de laatste het aantal kanalen, dat hier één is."Daarna itereren je over alle afbeeldingen één voor één; elke keer reshape je de train- en testmatrix naar 176 x 176 en voeg je die toe aan respectievelijk
augmented_images(3T/input) enHaugmented_images(7T/ground truth).Na dit alles is het belangrijk de data te verdelen. Om je model goed te laten generaliseren, splits je de data in een trainings- en een validatieset. Je traint je model op 80% van de data en valideert op de resterende 20%.
Dit helpt ook om overfitting te verminderen, omdat je valideert op data die het model niet tijdens training heeft gezien.
Je kunt de train_test_split-module van scikit-learn gebruiken die je in het begin hebt gedefinieerd om de data goed te verdelen:
Het model: 1-Encoder-3-Decoders!
Merge-verbindingen
Vervolgens definieer je de voorgestelde architectuur met blokken van opeenvolgende filterlagen, gevolgd door een maxpooling-laag in de encodersectie, zoals in de volgende cel. Om de oorspronkelijke afbeeldingsgrootte bij de output te reconstrueren, wordt in elk decoderblok een upsampling-laag geïntroduceerd. Bij upsampling kunnen artefacten ontstaan door ontbrekende details in de gedownsamplede input van de decoder. Daarom concateneer je de input van de decoder met de opgeschaalde versie uit de encoder om de aard van opgeschaalde details te bieden voor een betere reconstructie tijdens het upsamplen in de decoder. Het toevoegen van merge-verbindingen levert inderdaad een significante PSNR-verbetering op (orde van 5 dB). Deze instelling is geïnspireerd op deze paper.
Multi-decoders
De voorgestelde aanpak gebruikt één encoder en meerdere decoders met een enkelkanaals input. In elk blok van de encoder en in alle drie de decoders worden drie convolutionele lagen gebruikt, gevolgd door batchnormalisatie om numerieke stabiliteit te behouden.
Het eerste convolutionele blok in de encoder heeft 32 filters, en het aantal filters verdubbelt na elk blok. In alle decoders heeft het eerste convolutionele blok 256 filters, en het aantal filters wordt na elk blok gehalveerd. Je gebruikt een filtergrootte van 3x3 in alle convolutionele blokken.
Rectified Linear Unit (ReLU) wordt als activatiefunctie gebruikt in alle lagen behalve de laatste. Omdat data is genormaliseerd tussen 0 en 1, wordt in de laatste laag een Sigmoid-activatie gebruikt.
Het is bekend dat lokale afbeeldingsdetails op verschillende schalen een grote rol spelen bij reconstructie. De voorgestelde architectuur bekijkt beelden op verschillende schalen met hiërarchische lagen voor downsampling (maxpooling) en upsampling (ieder met factor 2) in respectievelijk encoder en decoders. De gecodeerde representatie na drie downsamplings brengt de data van een hoge-dimensie-input naar een latente representatie.
Encoder
Decoder 1
Decoder 2
Decoder 3
In de bovenstaande 4 cellen definieerde je vier functies: één voor de encoder en de overige drie voor de decoders. Omdat het functies zijn, zou je de
decoder()-functie kunnen definiëren en drie keer aanroepen, maar voor een beter begrip definieer je hem drie keer en om issues met de willekeur in de drie decoders te vermijden.Verliesfunctie
Vervolgens gebruik je mean squared error waarbij je de waarden (pixels) van zowel de ground truth
y_talsy_pdie gelijk zijn aan nul, uitsluit.Modeldefinitie en compilatie
Eerst roep je de
encoder-functie aan met de input. Omdat je merge-verbindingen in je architectuur gebruikt, retourneert deencoder-functie de output van vijfconvolution-lagen die je vervolgens afzonderlijkmerget met alle drie decoder-uitgangen.Het model trainen
Je slaat de gewichten alleen op wanneer de
peak signal-to-noise ratioop de validatiedata verbetert. Daarvoor definieer je een lijstpsnr_gray_channelwaarin je een standaardwaarde 1 toevoegt; je voegt 1 toe omdat je de lijst moet initialiseren met een willekeurig getal en de PSNR hier al snel hoger dan deze waarde zal zijn, zodat dit als dummy-waarde dient.Je initialiseert de learning rate op 1e-3 en gebruikt een learning-rate-decaystrategie waarbij je de learning rate elke 20 epochs met 10% verlaagt vanaf de huidige waarde.
Opmerking: De volgende volledige code moet in één cel worden uitgevoerd, maar voor een beter begrip van het trainingsproces wordt hij in kleinere cellen opgesplitst.
Het model wordt 500 epochs getraind. De initiële learning rate is 1e-3 zoals hierboven gedefinieerd. Je slaat ook de
PSNR- enMSE-waarden op na elke epoch van 7T met predicted. Je gebruikt de functieK.set_valueom de learning rate van alle drie modellen met 10% te wijzigen, maar alleen na elke 20 epochs.Vervolgens shuffle je de input 3T- en ground truth 7T-beelden om overfitting te voorkomen, omdat niet shufflen het model dwingt de samples elke epoch in dezelfde volgorde te zien. Daarna bereken je het aantal batches op basis van de eerder gedefinieerde
batch_size. Ten slotte begin je te itereren overnum_batches.Naast PSNR-waarden sla je ook de losses van alle drie autoencoders en de drie decoders respectievelijk op.
Omdat je in elke batch wilt dat het model de volgende 32 (batch_size) samples ziet, regelt de volgende cel dat.
Voor onze Minimum-autoencoderstrategie test je na elke epoch op de trainingsdata met de Keras-functie
test_on_batchdie je drie losses geeft, en die print je ten slotte.Er kunnen zes mogelijke situaties zijn in je netwerk:
loss_1: Autoencoder 1 loss
loss_2: Autoencoder 2 loss
loss_3: Autoencoder 3 loss
Dit is een essentiële stap: omdat je in de bovenstaande condities enkele lagen op False hebt gezet, moet je deze lagen weer True maken, zodat alle autoencoderlagen kunnen worden gebruikt voor de functie
test_on_batchen je ze niet de hele training op False laat staan.Je slaat gewichten op op drie manieren; hieronder staan er twee. Eén keer sla je na elke 100 epochs op, en ten tweede sla je na elke epoch op. In feite worden ze na elke epoch overschreven.
Testen op validatiedata
Eerst shuffle je de validatiedata en gebruik je dezelfde 6 mogelijke condities als hierboven gedefinieerd. Welke conditie ook True is, je
testje model met die specifieke autoencoder.Daarna bereken je twee metrics
MSEenPSNRtussen decoded_imgs (voorspeld) en ground truth. Tot slot sla je ze op in tekstbestanden.Hier sla je de gewichten alleen op wanneer de
PSNRtussen de voorspelde 7T en de ground truth (7T) maximaal is vergeleken met de eerderePSNR-waarden in de lijstpsnr_gray_channel.Input, ground truth en decoded opslaan: kwantitatieve resultaten
Je definieert een numpy-matrix
tempvan 176 x 528, omdat er 3 afbeeldingen in één rij worden opgeslagen, elk 176 x 176. Je slaat een willekeurige afbeelding op uit de validatie-3T, 7T en predicted en vermenigvuldigt de matrix met 255 omdat de afbeeldingen geschaald waren tussen 0 en 1.Tot slot sla je de afbeelding op met de
scipy-functie.Laten we de bestanden
PSNRenMSEaan het eind sluiten.Tot slot verlaag je na elke 20 epochs de learning rate met 10% van de huidige waarde.
Testing Script
In deel 2 van deze tutorial behandel je het volgende:
nibabel.Python-module-afhankelijkheden
Voordat je deze tutorial volgt, zorg dat je exact dezelfde moduleversies hebt als hieronder vermeld:
Opmerking: Let op: het model wordt getraind op een systeem met een Nvidia 1080 Ti GPU, Xeon e5 GeForce-processor en 32GB RAM. Als je Jupyter Notebook gebruikt, moet je drie extra regels code toevoegen waarin je de CUDA-devicevolgorde en zichtbare CUDA-apparaten opgeeft met de module os.
In de onderstaande code stel je in feite omgevingsvariabelen in de notebook in met os.environ. Het is handig dit vóór het initialiseren van Keras te doen om de Keras-backend TensorFlow te beperken tot de eerste GPU. Als de machine waarop je traint een GPU op 0 heeft, gebruik dan 0 in plaats van 1. Je kunt dit controleren met een eenvoudige terminalopdracht: bijvoorbeeld nvidia-smi
Modules importeren
Eerst importeer je alle vereiste modules zoals tensorflow, numpy en vooral keras en de vereiste functies of lagen zoals
Input,Conv2D,MaxPooling2D, enz., omdat je al deze frameworks gaat gebruiken om het model te trainen!Om beelden in NIfTI-formaat te lezen, moet je ook een module importeren met de naam
nibabel.De Brain MRI 3T- en 7T-dataset begrijpen
De 3T- en 7T-hersen-MRI-dataset bestaat uit 3D-volumes; elk volume bevat in totaal 207 slices/afbeeldingen van hersen-MRI’s, genomen op verschillende hoogten in de hersenen. Elke slice heeft een afmeting van 173 x 173. De beelden zijn enkelkanaals grijswaarden. Er zijn in totaal 39 proefpersonen, elk met de MRI-scan van een patiënt. Het bestandsformaat is geen jpeg, png, etc., maar NIfTI. In een later onderdeel zie je hoe je NIfTI-beelden leest.
De dataset bestaat uit T1-modus MR-beelden; T1-sequenties worden traditioneel als geschikt beschouwd voor de evaluatie van anatomische structuren. De dataset waarmee je vandaag werkt bestaat uit 3T- en 7T-hersen-MRI’s.
De dataset is openbaar en kan worden gedownload via deze bron.
De initializers definiëren
Definieer eerst de dimensies van de data. Je resize’t de afbeeldingsdimensie van 173x173 naar 176x176 in het uitleesgedeelte. Hier definiëren we ook de datamap, batchgrootte voor het trainen van het model, aantal kanalen, een
Input()-laag, train- en testmatrices als lijst en ten slotte laden we voor rescaling het tekstbestand met de minimum- en maximumwaarden van de MRI-dataset.De testvolumes laden
Vervolgens laden we de MRI-data met de
nibabel-bibliotheek en resize’en we de beelden van 173 x 173 naar 176 x 176 door nullen te padden in de x- en y-dimensie.Let op: wanneer je een NIfTI-volume laadt, laadt Nibabel de afbeeldingsarray niet direct. Er wordt gewacht tot je expliciet om de arraydata vraagt. De standaardmanier is de methode get_data() aanroepen.
Omdat je 2D-slices wilt in plaats van 3D, gebruik je de lijsten
train en testdie je eerder hebt geïnitialiseerd; telkens wanneer je een volume leest, itereer je over alle 207 slices van het 3D-volume en voeg je elke slice één voor één toe aan een lijst.Datapreprocessing
Aangezien de 3T- en 7T-testmatrices lijsten zijn, gebruik je numpy om de lijst naar een numpy-array om te zetten.
Verder zet je het
typevan de numpy-array om naarfloat32en reschaal je zowel deinputals deground truth.Vervolgens maak je twee nieuwe variabelen
ToPredict_images(3T Test/input) enground_images(7T Test/ground truth) met de shape van de train- en testmatrix. Dit wordt een 4D-matrix waarin de eerste dimensie het aantal afbeeldingen is, de tweede en derde de afmetingen van elke afbeelding, en de laatste het aantal kanalen, dat hier één is."Daarna itereren je over alle afbeeldingen één voor één; elke keer reshape je de train- en testmatrix naar 176 x 176 en voeg je die toe aan respectievelijk
ToPredict_images(3T Test/input) enground_images(7T Test/ground truth).Het model!
Encoder
Decoder
Verliesfunctie
Vervolgens gebruik je mean squared error waarbij je de waarden (pixels) van zowel de ground truth
y_talsy_pdie gelijk zijn aan nul, uitsluit.Modeldefinitie en gewichten laden in alle drie autoencoders!
Roep eerst de
encoder-functie aan met de input. Omdat je merge-verbindingen gebruikt in je architectuur, retourneert deencoder-functie de output van vijfconvolution-lagen die je vervolgens afzonderlijkmerget met alle drie decoder-uitgangen.Laten we nu drie verschillende modellen maken en de drie getrainde modelgewichten erin laden.
Modelvoorspelling op testvolumes: kwantitatieve en kwalitatieve resultaten!
Laten we snel twee numpy-arrays initialiseren van elk 11 x 3 x 1, waarbij de eerste dimensie het aantal volumes voor testen is. De tweede dimensie staat voor de
MSEenPSNRtussen: voorspelde 7T-MR-beelden en ground truth 7T-MR-beelden, voorspelde output en input 3T-MR-beelden, input 3T-MR-beelden en ground truth 7T-MR-beelden; de derde dimensie staat voor het aantal kanalen dat je aan je model voert!In het volgende deel van de cel itereer je over alle 11 volumes één voor één. In elke iteratie voorspel je elk volume met alle drie autoencoders en neem je uiteindelijk het gemiddelde van de voorspellingen.
In elk volume itereer je over het aantal kanalen van dat volume en bereken je
PSNRenMSEvoor drie verschillende gevallen zoals hierboven besproken.Vervolgens sla je met de
nibabel-bibliotheek de voorspelde output, input (3T) en ground truth (7T) op als.nii-bestanden: elk van de 11 volumes met 207 slices.Ten slotte sla je de
PSNR-matrix op in een tekstbestand met de numpy-bibliotheek.Zoals vermeld in de paper helpt het middelen van de voorspelde outputs om ruiseffecten te verminderen maar lokale features in de gereconstrueerde beelden te behouden, waardoor de PSNR verbetert ten opzichte van individuele decoderoutputs.
Wil je meer leren over Python? Volg de DataCamp-cursus Introduction to Data Visualization with Matplotlib.
Bekijk ook de Keras Tutorial: Deep Learning in Python.