Введение
Благодаря медицинскому и техническому прогрессу продолжительность жизни увеличивается. Конечно, это здорово, но это палка о двух концах. По мере увеличения продолжительности жизни все больше людей будут доживать до 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, разработанную Яном ЛеКуном и Йошуа Бенджио.
Что я сохранил из их архитектуры (примечание: я не буду вдаваться в технические термины, но сошлюсь на некоторые полезные ресурсы):
- Два сверточных слоя и два объединяющих слоя (ссылка: https://deepai.org/machine-learning-glossary-and-terms/convolutional-neural-network).
- Rectified Linear Unit как функция активации для внутренних слоев (список всех функций активации: https://en.wikipedia.org/wiki/Activation_function).
Что я изменил:
- Слои объединения представляют собой максимальное объединение вместо среднего объединения (ссылка: https://medium.com/@bdhuma/what-pooling-method-is-better-maxpooling-vs-minpooling-vs-average-pooling -95fb03f45a9).
- Перед выравниванием добавлено удаление одной четверти (0,25) узлов (ссылка: https://medium.com/@amarbudhiraja/https-medium-com-amarbudhiraja-learning-less-to-learn- лучшее отсев-в-глубоком машинном обучении-74334da4bfc5).
Вот график оригинального 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