Введение

Благодаря медицинскому и техническому прогрессу продолжительность жизни увеличивается. Конечно, это здорово, но это палка о двух концах. По мере увеличения продолжительности жизни все больше людей будут доживать до 80-х и 90-х годов, что неизбежно приведет к увеличению распространенности деменции.

Вот статистика CDC относительно деменции:

По оценкам, в 2014 году из тех, кому исполнилось 65 лет, насчитывалось 5,0 миллионов взрослых с деменцией, а к 2060 году, по прогнозам, их будет почти 14 миллионов.

Источник: https://www.cdc.gov/aging/dementia/index.html

Это увеличение станет проблемой для медицинских систем и специалистов, работающих в этой области. Достижения в области технологий и медицины будут иметь решающее значение для раннего выявления случаев деменции и их лечения.

В свете этих фактов в настоящей статье будет рассмотрен процесс разработки инструмента для прогнозирования деменции при горизонтальном МРТ-сканировании головного мозга пациентов.

Данные

Вот ссылка на страницу kaggle: https://www.kaggle.com/datasets/sachinkumar413/alzheimer-mri-dataset. Каждое изображение МРТ имеет размер 128x128 пикселей. Набор данных был разбит на 4 класса: легкая деменция, умеренная деменция, отсутствие деменции и очень легкая деменция.

Вот пример одного изображения для каждой категории:

#Visualize images
dem_types = ['Mild_Demented',
'Moderate_Demented',
'Non_Demented',
'Very_Mild_Demented']
examples = ['Mild_Demented/mild_2.jpg', 'Moderate_Demented/moderate_2.jpg',
'Non_Demented/non_2.jpg',
'Very_Mild_Demented/verymild_2.jpg']
for i in range(len(dem_types)):
   plt.plot(128,128)
   plt.figure(i+1)
   plt.title(dem_types[i])
   plt.xlabel("X pixel scaling")
   plt.ylabel("Y pixels scaling")
   plt.imshow(image.imread('Dataset/'+ examples[i]))
plt.show()

Выход:

Подготовка данных

Данные разделены на 4 папки, каждая папка включает МРТ-изображения класса. Чтобы создать разделы для обучения, тестирования и проверки, мы будем использовать модуль python splitfolders.

import splitfolders
splitfolders.ratio('../Alzheimer_classification/Dataset', output="output", seed=1345, ratio=(.8, 0.1,0.1))

Обратите внимание, что разделение обучения, теста и проверки передается в аргументе ratio. В этом случае наш сплит будет составлять 80% от общего количества данных для обучения, 10% для тестирования и 10% для проверки.

Теперь давайте инициализируем данные обучения, тестирования и проверки:

#Height and width in pixels
img_height = 128
img_width = 128
data_splits = []
direct = ['output/train', 'output/test', 'output/val']
for d in direct:
   var_name = tf.keras.utils.image_dataset_from_directory(
       d,
       batch_size=64,
       image_size=(img_height, img_width),
       seed=123,
       )
   data_splits.append(var_name)

Большой! Данные готовы для подачи в модель. Давайте построим!

Модель

Учитывая, что мы имеем дело с проблемой классификации изображений, мы будем использовать сверточную нейронную сеть (CNN). Я использовал популярную библиотеку tensorflow, Keras, для построения модели. После тестирования различных архитектур я решил использовать модифицированную версию архитектуры LeNet-5, разработанную Яном ЛеКуном и Йошуа Бенджио.

Что я сохранил из их архитектуры (примечание: я не буду вдаваться в технические термины, но сошлюсь на некоторые полезные ресурсы):

Что я изменил:

Вот график оригинального LeNet-5:

Вот моя версия (примечание: добавлен масштабирующий слой для получения «съедобного» ввода):

#LeNet-5 CNN Architecture Modified (MaxPool and Dropout 25%)
model = keras.Sequential()
model.add(Rescaling(1./255, input_shape=(128, 128, 3)))
#First convolutional layer
model.add(layers.Conv2D(filters=16, kernel_size=(3, 3), padding='same', activation = 'relu', input_shape = (128, 128, 3), kernel_initializer="he_normal"))
#First pooling layer
model.add(layers.MaxPooling2D())
#Second convolutional layer
model.add(layers.Conv2D(filters=32, kernel_size=(3, 3), padding='same', activation = 'relu', input_shape = (128, 128, 3), kernel_initializer="he_normal"))
#Second pooling layer
model.add(layers.MaxPooling2D())
#Dropout 1/4 of nodes
model.add(Dropout(0.25))
model.add(layers.Flatten())
model.add(layers.Dense(units = 128, activation = 'relu',kernel_initializer="he_normal"))
model.add(layers.Dense(units = 64, activation = 'relu'))
#Softmax for multiclass classifying
model.add(layers.Dense(units = 4, activation = 'softmax'))
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', 
metrics = ['accuracy'])

Давайте обучим модель:

#Reference the initialization, data_splits[0] is the training data, #and data_splits[2] is the validation data
hist=model.fit(data_splits[0], 
               epochs=50,
               validation_data=data_splits[2])

Теперь давайте посмотрим, является ли модель переобученной, построив функции точности и потерь.

Для точности:

За потерю:

Поскольку функции проверки и обучения, похоже, сходятся в течение 50 эпох, мы можем сделать вывод, что модель не является переобученной. Он должен хорошо работать с невидимыми данными.

Давайте оценим или смоделируем:

#data_splits[1] being our test data
model.evaluate(data_splits[1])

Выход:

Наша модель имеет точность 0,994. Довольно хорошо.

Давайте вычислим отчет о классификации и матрицу путаницы соответственно:

preds = np.array([])
labels =  np.array([])
for x, y in data_splits[1]:
   preds = np.concatenate([preds, np.argmax(model.predict(x), axis = -1)])
   labels = np.concatenate([labels, np.argmax(y.numpy(), axis=-1)])
   print(classification_report(labels,preds))
   print(confusion_matrix(labels,preds))

Давайте проверим это на МРТ-изображении:

LeNet5 = load_model('LeNet-5e50Mod.h5')
img = load_img("output/test/Very_Mild_Demented/verymild_1737.jpg")
img = img.resize((128, 128))
img = img_to_array(img)
img = img.reshape(-1,128, 128,3)
#print out the predicted class
print(np.argmax(LeNet5.predict(img)))

Выход:

Он классифицировал МРТ-изображение очень легкой деменции как 3 (из 0, 1, 2, 3 возможных категорий), что является правильной классификацией, если мы посмотрим на порядок исходных папок:

Большой! Теперь давайте разработаем веб-приложение для его развертывания.

Веб приложение

Мы будем использовать gradio, библиотеку Python для веб-приложений с низким кодом, для развертывания модели.

Вот импорт:

import gradio as gr
import numpy as np
import tensorflow as tf
from tensorflow import keras
from keras.models import load_model

Во-первых, нам нужно создать словарь для индексации названия категории:

#Dictionary to index a category
res_dict = {
   0: "Mild dementia detected",
   1: "Moderate dementia detected",
   2: "No dementia detected",
   3: "Very mild dementia detected"
}

Теперь давайте определим функцию, которая будет принимать МРТ-изображение мозга в качестве входных данных, предварительно обрабатывать изображение, чтобы оно правильно подходило к модели, и возвращать прогноз:

def classify_image(img):
   #Preprocessing image
   scan = tf.keras.preprocessing.image.img_to_array(img)
   scan = scan.reshape(-1,128, 128,3)
   LeNet5 = load_model('LeNet-5e50Mod.h5')
   return res_dict[np.argmax(LeNet5.predict(scan))]

Теперь давайте создадим интерфейс:

app = gr.Interface(title="Input your horizontal section MRI scan",
      #Calling the function to predict MRI input 
      fn=classify_image,
      #Creating the input slot for the image
      inputs=gr.inputs.Image(shape=(128, 128)),
      #Creating the ouput slot
      outputs=gr.outputs.Label(num_top_classes=4),
      #Setting out a few examples to "test drive" the app
                #Mild dementia MRI
      examples=["output/test/Mild_Demented/mild_2.jpg",
                #Moderate dementia MRI              
                "output/test/Moderate_Demented/moderate_7.jpg",
                #No dementia MRI    
                "output/test/Non_Demented/non_10.jpg",
                #Very mild dementia MRI
                "output/test/Very_Mild_Demented/verymild_3.jpg"
              ]
       )
app.launch()

И вот мы идем! У нас есть наше веб-приложение.

Развертывание

Это приложение было развернуто на Heroku. Чтобы развернуть приложение gradio на Heroku, вам нужно добавить 3 файла в репозиторий Github, чтобы правильно развернуть его.

  • setup.sh
  • Procfile (команды, выполняемые при запуске)
  • requirements.txt (список всех ваших зависимостей

Вот мой файл setup.sh:

export GRADIO_SERVER_NAME=0.0.0.0                              export GRADIO_SERVER_PORT="$PORT"

Вот мой файл Procfile (убедитесь, что «p» — заглавная!):

web: source setup.sh && python WebApp.py

И, наконец, вот мой файл requirements.txt:

numpy                             
pandas
gunicorn                             
rq                             
tensorflow-cpu
keras
gradio

Убедитесь, что у вас есть tensorflow-cpu, а не tensorflow, так как он слишком велик и превысит ограничение в 500 МБ.

Тестирование

Вот демонстрация приложения:

Как мы видим, модель не предсказала деменцию для нового изображения МРТ (найдено в Google Images). Деменция напрямую связана с объемом белого и серого вещества головного мозга. Классификация верна, так как серое и белое вещество в этом случае кажется неповрежденным (а также тот факт, что я специально искал МРТ здорового мозга)!

Проверьте приложение на себе! Ссылка на приложение

Спасибо за чтение!

Вот ссылка на репозиторий github (который включает в себя приложение, предварительную обработку данных и разработку модели: https://github.com/KamenDamov/Alzheimer_classification