Prevenire la cecità degli occhi prevedendo le fasi della retinopatia diabetica

Preventing blindness by predicting stages of diabetic retinopathy

Introduzione

La retinopatia diabetica è una condizione degli occhi che provoca cambiamenti ai vasi sanguigni della retina. Quando viene lasciata senza trattamento, porta alla perdita della vista. Quindi, rilevare le fasi della retinopatia diabetica è cruciale per prevenire la cecità. Questo studio di caso riguarda la rilevazione della cecità degli occhi dai sintomi della gravità della retinopatia diabetica per prevenire la cecità della persona. Qui i dati sono stati raccolti da aree rurali da vari esperti clinici addestrati utilizzando telecamere per il fondo dell’occhio (telecamere che fotografano la parte posteriore dell’occhio). Queste foto sono state scattate in diverse condizioni di imaging. Nel 2019 Kaggle ha organizzato una competizione (APTOS 2019 Blindness Detection) per rilevare le fasi della retinopatia diabetica; i nostri dati sono stati presi dalla stessa competizione Kaggle. La rilevazione precoce di questa retinopatia diabetica può aiutare ad accelerare il trattamento e ridurre significativamente il rischio di perdita della vista.

L’intervento manuale di esperti clinici addestrati richiede tempo e sforzo, specialmente nei paesi sottosviluppati. Pertanto, l’obiettivo principale di questo studio di caso è utilizzare tecnologie efficienti per rilevare la gravità della condizione al fine di prevenire la cecità. Implementiamo tecniche di apprendimento profondo per ottenere risultati efficaci nella classificazione della gravità della condizione.

Obiettivi di Apprendimento

  • Comprensione della Retinopatia Diabetica: Apprendere sulla condizione degli occhi e il suo impatto sulla vista, sottolineando l’importanza della rilevazione precoce.
  • Fondamenti dell’Apprendimento Profondo: Esplorare le basi dell’apprendimento profondo e la sua rilevanza nella diagnosi della retinopatia diabetica.
  • Preelaborazione e Aumento dei Dati: Comprendere come preparare ed migliorare in modo efficace il set di dati per l’addestramento di modelli di apprendimento profondo.
  • Selezione e Valutazione del Modello: Imparare a scegliere e valutare le prestazioni dei modelli di apprendimento profondo per la classificazione della gravità.
  • Implementazione Pratica: Scoprire l’implementazione del miglior modello utilizzando Flask per le previsioni nel mondo reale.

Questo articolo è stato pubblicato come parte del Data Science Blogathon.

Problema Aziendale

In questo caso, la gravità della condizione della persona viene classificata in cinque categorie, cioè una classificazione multi-classe in quanto la persona può essere riconosciuta con un solo livello di gravità.

Vincoli Aziendali

La precisione e l’interpretabilità sono estremamente importanti nel campo medico. Poiché previsioni errate possono portare all’ignoranza e mettere a rischio la vita di una persona, non abbiamo particolari problemi di latenza, ma dobbiamo essere precisi nei risultati.

Descrizione del Set di Dati

Il set di dati include 3.662 immagini etichettate della retina di pazienti clinici, per le quali esperti clinici addestrati categorizzano ogni immagine in base alla gravità della retinopatia diabetica come segue.

0 — Nessuna retinopatia diabetica,

1 — Lieve

2 — Moderata

3 — Grave

4 — Retinopatia diabetica proliferativa.

La tabella sopra indica le immagini del nostro set di dati identificate con una delle fasi della retinopatia diabetica.

Metrica di Prestazione

Utilizziamo il Kappa ponderato quadratico e la matrice di confusione come metriche di valutazione per la nostra classificazione multi-classe.

Il Kappa misura l’accordo (similitudine) tra le etichette effettive e le etichette previste. Un punteggio Kappa di 1.0 indica che le previsioni e le etichette effettive sono le stesse, mentre un punteggio Kappa di -1 indica che le previsioni sono molto lontane dalle etichette effettive. Il nostro obiettivo per questa metrica è ottenere un punteggio Kappa superiore a 0,6.

La metrica Kappa svolge un ruolo cruciale nella diagnosi medica perché, nel caso di un punteggio -1, indica quanto sono simili i due valutatori (valutatori previsti ed effettivi) e impone una penalità per la divergenza. Ad esempio, nel nostro caso, se la nostra etichetta prevista è 0 e l’etichetta effettiva è 1, ciò rappresenta un problema grave per il paziente, in quanto questo caso verrebbe ignorato e non sarebbe consigliata una diagnosi successiva per il paziente.

Come sappiamo, la matrice di confusione viene utilizzata per valutare le prestazioni di un modello di classificazione. La matrice confronta i valori target effettivi con quelli previsti dal nostro modello. Ciò ci offre una visione completa di quanto bene si comporta il nostro modello di classificazione e che tipo di errori commette.

Analisi esplorativa dei dati e preelaborazione

Verifichiamo la distribuzione dei nostri dati tracciando il grafico a barre.

v=df['diagnosi'].value_counts().plot(kind='bar')

Dal grafico sopra, possiamo dedurre che i nostri dati sono chiaramente sbilanciati. Dobbiamo assicurarci di bilanciare i nostri dati per evitare risultati inaccurati.

Possiamo applicare i pesi delle classi per mantenere l’uniformità nel set di dati e ottenere una distribuzione uniforme nei dati.

Il nostro set di dati di addestramento contiene solo 3.662 immagini, quindi il Dataset fornito da Kaggle è molto piccolo. È auspicabile l’overfitting a causa dell’addestramento sul piccolo set di dati, quindi qui la preelaborazione svolge un ruolo cruciale per una migliore performance aumentando i nostri dataset. Quindi ci dirigiamo verso l’aumento dei dati per migliorare i nostri dataset. Prima dell’aumento dei dati, dobbiamo verificare le condizioni delle immagini, poiché i dati sono stati raccolti da varie fonti, ad esempio se le immagini sono molto scure, hanno uno sfondo nero extra e sono immagini di diverse dimensioni. Pertanto, dobbiamo applicare tecniche di smoothing alle immagini per mantenere l’uniformità nella qualità dell’immagine ritagliando sfondi neri extra, ridimensionando le immagini a una dimensione tipica dell’immagine, ecc.

Dal precedente, possiamo osservare che il nostro set di dati contiene immagini di diverse dimensioni con ritaglio orizzontale, ritaglio verticale e regioni nere extra.

Applichiamo le seguenti tecniche di smoothing per ottenere tutte le immagini con una qualità uniforme.

Funzione di ritaglio

→ Funzione di ritaglio – usata per rimuovere parti scure extra intorno all’immagine

def crop(img,tol=7):
 # qui tol sta per tolleranza  
  '''
  questa funzione di ritaglio viene utilizzata per rimuovere parti scure intorno all'immagine
  '''

  if img.ndim==2:
# questo ciclo viene utilizzato per ritagliare le immagini GRAY
    mask=img>tol
    return img[np.ix_(mask.any(1),mask.any(0))]
  elif img.ndim==3:
# questo ciclo viene utilizzato per ritagliare le immagini a colori    
    grayimg=cv2.cvtColor(img,cv2.COLOR_RGB2GRAY)
    mask=grayimg>tol
    shap=img[:,:,0][np.ix_(mask.any(1),mask.any(0))].shape[0]
    if shap==0:
# l'immagine è troppo scura quindi ritagliamo tutto      
      return img
    else:
      img0=img[:,:,0][np.ix_(mask.any(1),mask.any(0))]
      img1=img[:,:,1][np.ix_(mask.any(1),mask.any(0))]
      img2=img[:,:,2][np.ix_(mask.any(1),mask.any(0))]
      img=np.stack([img0,img1,img2],axis=-1)
    return img #import csv

La figura seguente indica che dopo l’applicazione della funzione, otteniamo immagini ritagliando parti scure intorno ad esse.

→ La funzione Circle Crop ritaglia un’immagine in modo circolare prendendo riferimento dal centro.

def circlecrop(img):
  '''
  usato per ritagliare l'immagine in modo circolare dal centro dell'immagine
  '''

  h,w,d= img.shape
  x = int(w/2)
  y = int(h/2)
  r = np.amin((x,y))
  circle_img = np.zeros((h,w), np.uint8)
  cv2.circle(circle_img, (x,y), int(r), 1, thickness=-1)
  img = cv2.bitwise_and(img, img, mask=circle_img) 
  return img

La figura seguente è ottenuta dopo l’applicazione della funzione di ritaglio circolare

Funzione di Ben Graham

→ Funzione di Ben Graham – questa funzione viene applicata per migliorare la luminosità dell’immagine

def ben(img,sigmaX=10):
  '''
  Metodo di Ben Graham per migliorare le condizioni di illuminazione.

  '''
  image=cv2.addWeighted( img,4, cv2.GaussianBlur( img , (0,0) , sigmaX) ,-4 ,128)
  return image

La figura sottostante indica che dopo l’applicazione della funzione di Ben Graham, abbiamo migliorato la condizione di illuminazione dell’immagine.

Verifichiamo le immagini della retina preelaborate dopo l’applicazione delle funzioni sopra descritte.

Dato che il nostro dataset è molto piccolo, potremmo incorrere nel sovradattamento. Per superare questa situazione, è necessario aumentare i dati di addestramento. Aumentiamo i dati utilizzando tecniche di aumentazione come il ribaltamento orizzontale, il ribaltamento verticale, la rotazione delle immagini, lo zoom e la regolazione della luminosità.

from keras_preprocessing.image import ImageDataGenerator

datagen=ImageDataGenerator(horizontal_flip=True,vertical_flip=True,rotation_range=360,
                           brightness_range=[0.5, 1],
                           zoom_range = 0.2,rescale=1./255.,validation_split=0.25)
validation_datagen = ImageDataGenerator(rescale = 1./255)

train_generator=datagen.flow_from_dataframe(
dataframe=df,
directory="prep",
x_col="add",
y_col="diagnosis",
subset="training",
batch_size=12,
seed=42,
shuffle=True,
class_mode="categorical",
target_size=(256, 256))
     

Dato che abbiamo utilizzato una divisione di validazione del 0,25, otteniamo 2.747 immagini di addestramento e 915 immagini di validazione.

In questo caso, ogni immagine viene replicata cinque volte, in quanto abbiamo utilizzato cinque tecniche: ribaltamento orizzontale, ribaltamento verticale, intervallo di rotazione, intervallo di luminosità e intervallo di zoom.

Modello di Deep Learning

Iniziamo costruendo un modello di base con una semplice architettura CNN.

inp=Input(shape=(256,256,3))
x=Conv2D(32,(3,3),activation='relu')(inp)
x=MaxPooling2D(pool_size=(2, 2))(x)
x=Dropout(0.5)(x)
x=Flatten()(x)
x=BatchNormalization()(x)
x=Dense(5, activation='softmax')(x)

Per il modello sopra, otteniamo un punteggio kappa di 0,554, che non è accettabile per la previsione dello stadio della condizione.

Ricorriamo al transfer learning per utilizzare un modello preaddestrato al fine di ottenere un alto punteggio kappa.

VGG-16

model_vgg16= VGG16(weights='imagenet', include_top=False,input_shape=(256,256, 3))
x=GlobalAveragePooling2D()(model_vgg16.layers[-1].output)
x=Dropout(0.5)(x)
x=Dense(5, activation='softmax')(x)

→Punteggio kappa di addestramento: 0,913

→Punteggio di accuratezza di addestramento: 0,817

Dal modello sopra, otteniamo un punteggio kappa di 0,913

DENSENET

modeldense=DenseNet121(weights='imagenet', include_top=False,input_shape=(256,256, 3))
x=GlobalAveragePooling2D()(modeldense.layers[-1].output)
x=Dropout(0.5)(x)
x=Dense(5, activation='softmax')(x)

→Punteggio kappa di addestramento: 0,933

→Punteggio di accuratezza di addestramento: 0,884

Dal modello sopra, otteniamo un punteggio kappa di 0,933

RESNET

modelres152=ResNet152(weights='imagenet', include_top=False,input_shape=(256,256, 3))
x=GlobalAveragePooling2D()(modelres152.layers[-1].output)
x=Dropout(0.5)(x)
x=Dense(5, activation='softmax')(x)

→Punteggio kappa di addestramento: 0,910

→Punteggio di accuratezza di addestramento: 0,844

Dal modello sopra, otteniamo un punteggio kappa di 0,91

EFFICIENTNET

Dopo aver implementato vari modelli efficienti come EEfficientNetB0, B3, B4 e B7, possiamo ottenere risultati migliori utilizzando EfficientNetB7.

modeleffB7=EfficientNetB7(weights='imagenet', include_top=False,input_shape=(256,256, 3))
x=GlobalAveragePooling2D()(modeleffB7.layers[-1].output)
x=Dropout(0.5)(x)
x=Flatten()(x)
x=Dense(5, activation='softmax')(x)

→Punteggio Cohen Kappa di allenamento: 0.877

→Punteggio di accuratezza di allenamento: 0.838

Dal modello sopra, otteniamo un punteggio kappa di 0.877

XCEPTION

modelxcep=Xception(weights='imagenet', include_top=False,input_shape=(256,256, 3))
x=GlobalAveragePooling2D()(modelxcep.layers[-1].output)
x=Dropout(0.5)(x)
x=Flatten()(x)
x=Dense(5, activation='softmax')(x)

→Punteggio Cohen Kappa di allenamento: 0.925

→Punteggio di accuratezza di allenamento: 0.854

Dal modello sopra, otteniamo un punteggio kappa di 0.925

Dal modello sopra, osserviamo che il modello Denset ottiene un punteggio Kappa migliore. Quindi scegliamo il nostro modello Denset come il migliore e procediamo per la predizione dello stadio.

Predizione Utilizzando il Nostro Miglior Modello

Predizione degli stadi utilizzando il nostro miglior modello:

X='/content/00a8624548a9.png'
img = cv2.imread(X)
img=crop(img)
img = cv2.resize(img,(256,256),interpolation=cv2.INTER_AREA)
img=circlecrop(img)
img=ben(img)
img = np.reshape(img,[1,256,256,3])
cd = ImageDataGenerator(horizontal_flip=True,vertical_flip=True,
                          rotation_range=360,brightness_range=[0.5, 1],
                           zoom_range = 0.2,rescale=1./255)
cg = cd.flow(img,batch_size=1)
tp = model.predict(cg)
op=np.argmax(tp)
if op==0:
  matter="Stadio 0 - Nessuna Retinopatia Diabetica"
elif op==1:
  matter="Stadio 1 - Lieve"
elif op==2:
  matter="Stadio 2 - Moderato"
elif op==3:
  matter="Stadio 3 - Grave"
elif op==4:
  matter="Stadio 4 - Retinopatia Diabetica Proliferativa"
print(matter)

Dall’immagine sopra, possiamo osservare che il nostro miglior modello predice gli stadi della retinopatia diabetica.

Deployment del Nostro Modello Utilizzando Flask

Ho utilizzato Flask per distribuire il mio modello in modo che possiamo predire lo stadio della retinopatia diabetica della nostra immagine della retina caricata. Di seguito è riportato il video delle istanze in esecuzione del mio modello distribuito.

Conclusione

In conclusione, questo blog ha mostrato il potere trasformativo del deep learning nella rilevazione della retinopatia diabetica e nella prevenzione della perdita della vista. Con una diagnosi precoce e una classificazione accurata della gravità, l’IA può migliorare significativamente i risultati dei pazienti. L’applicabilità nel mondo reale di queste tecniche attraverso la distribuzione del modello utilizzando Flask ne evidenzia la praticità nelle impostazioni sanitarie.

La ricerca continua sulle tecniche di augmentazione e sul perfezionamento del modello aumenterà ulteriormente le capacità diagnostiche. Sfruttando il potenziale dell’IA, possiamo rivoluzionare la diagnosi medica e aprire la strada a un futuro più sano.

  • I modelli di deep learning, come VGG-16, DENSENET, RESNET, EFFICIENTNET e XCEPTION, hanno classificato efficacemente la gravità della retinopatia diabetica.
  • Il modello con le migliori prestazioni, DENSENET, ha ottenuto alti punteggi Kappa, dimostrando la sua capacità di effettuare previsioni accurate.
  • La pre-elaborazione dei dati e l’augmentazione sono fondamentali per migliorare le prestazioni e la generalizzabilità del modello.
  • La distribuzione di Flask mostra l’applicabilità pratica del deep learning in scenari reali, facilitando una diagnosi e un trattamento efficienti.
  • La ricerca continua sulle tecniche di augmentazione e sul perfezionamento del modello ha il potenziale per migliorare ulteriormente l’accuratezza diagnostica e far progredire la diagnosi medica utilizzando l’IA.

Lavori Futuri

  • Possiamo implementare ulteriori tecniche di augmentazione.
  • Possiamo provare varie strati convoluzionali sui nostri modelli.
  • È necessario ottenere più immagini della retina per l’allenamento.

Domande frequenti

Riferimenti

  • https://github.com/btgraham/SparseConvNet/blob/kaggle_Diabetic_Retinopathy_competition/competitionreport.pdf
  • https://arxiv.org/abs/1905.11946
  • https://arxiv.org/abs/0704.1028
  • https://www.kaggle.com/xhlulu/aptos-2019-densenet-keras-starter
  • https://www.kaggle.com/c/aptos2019-blindness-detection/discussion/108065

I media mostrati in questo articolo non sono di proprietà di Analytics Vidhya e vengono utilizzati a discrezione dell’autore.