Глубокое обучение

Проблема

Сегодня более 700 000 американцев живут с опухолью головного мозга, и исследования показывают, что в 2021 году первичная опухоль головного мозга будет диагностирована более чем у 84 000 человек [1]. Диагностика опухоли головного мозга обычно начинается с магнитно-резонансной томографии (МРТ). Затем результаты анализирует невролог, чтобы определить, есть ли опухоль в головном мозге.

Использование искусственного интеллекта для обнаружения опухоли головного мозга с помощью МРТ позволит сэкономить деньги и, самое главное, время. Мало того, это также может уменьшить человеческую ошибку при обнаружении опухоли. В условиях постоянно растущего населения сегодня крайне важно, чтобы врачи использовали технологии для своевременного определения результатов сканирования мозга. В этой статье мы построим модель сверточной нейронной сети (CNN) для классификации МРТ-сканов мозга.

Анализ

Набор данных

Для поставленной задачи мы решили использовать набор данных МРТ-изображения мозга для обнаружения опухоли головного мозга от Kaggle. Этот набор данных содержит 155 изображений МРТ с опухолями головного мозга и 98 изображений без опухолей. Снимки опухоли головного мозга находятся в папке с пометкой да, а изображения здорового мозга — в папке нет.

Ниже приведены образцы сканов, показывающих мозг без опухоли (слева) и мозг с опухолью (справа).

Предварительная обработка данных

Все изображения имеют оттенки серого, поэтому при импорте они будут иметь одинаковое значение для каналов R, G и B. Мы можем использовать любой канал для нашей модели. Однако все изображения не имеют одинаковых размеров и, следовательно, должны быть изменены или дополнены перед передачей их в модель нейронной сети. Кроме того, мы нормализуем изображения, масштабируя значения пикселей в диапазоне от 0 до 255 до диапазона от 0 до 1.

Характеристики и ярлыки

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

Мы определили функцию preprocess_data, которая принимает путь, по которому хранятся изображения, и желаемую форму для изменения размера изображений. После чтения, изменения размера и масштабирования изображений он сохраняет их в массиве X (функции) и соответствующие им метки в массиве y (метки).

def preprocess_data(path, img_size):
'''
Reads in images classified into folders, resizes and scales them. Returns
those processed images as features and their associated labels as well.
Arguments:
path (str) - path to classified image folders
img_size (tuple) - tuple containing resized image height and width
Returns:
X (array) - features (brain scan images)
y (array) - feature labels (0 - no tumor, 1 - tumor)
'''
unsuccessful_files = {}
X = []
y = []
for folder_name in os.listdir(path):
if folder_name == 'no':
label = 0
else:
label = 1
folder_path = os.path.join(path, folder_name)
for fname in os.listdir(folder_path):
fpath = os.path.join(folder_path, fname)
try:
img = cv2.imread(fpath)
img = cv2.resize(img, img_size)
img = img / 255.0
X.append(img)
y.append(label)
except Exception as e:
unsuccessful_files[fname] = e
if unsuccessful_files:
print(f'Error processing the following files:\n')
for index, key in enumerate(unsuccessful_files, 1):
print(f'{index}. {key} - {unsuccessful_files[key]}')
else:
print('Successfully processed all images.')
X = np.array(X)
y = np.array(y)
return X, y

Алгоритмы

Цель этого проекта — построить модель сверточной нейронной сети (CNN), которая точно классифицирует МРТ-сканирование мозга как наличие опухоли или ее отсутствие. В качестве эталона мы сравним производительность нашей модели нейронной сети с классификатором изображений машины опорных векторов. Участник этого набора данных на Kaggle не указывает источник данных, поэтому мы не сможем сравнить производительность нашей модели с опубликованным эталоном.

Тренировочный тестовый сплит

Мы используем разделение тестов поезда sklearn, чтобы разделить данные на обучающие (75%), проверочные (12,5%) и тестовые (12,5%) наборы.

# split data into train, validation and test sets
from sklearn.model_selection import train_test_split

X_train, X_test_val, y_train, y_test_val = train_test_split(X, y, test_size=0.25, random_state=42)
X_test, X_val, y_test, y_val = train_test_split(X_test_val, y_test_val, test_size=0.5, random_state=42)

Визуализация первых 9 изображений в нашем тренировочном наборе вместе с их метками:

Выполнение

Сверточная нейронная сеть

  • Построить модель

Мы строим последовательную модель, состоящую из трех блоков свертки (16, 32 и 64 единицы соответственно) с максимальным слоем пула в каждом из них. Эти слои действуют как экстракторы признаков. Вывод этих слоев после перехода в выпадающий слой выравнивается, а затем проходит через полностью связанный слой со 128 единицами, который активируется функцией активации relu. Наконец, у нас есть плотный слой с сигмовидной активацией, который выводит вероятность обнаружения опухоли.

# create the model
model = Sequential([
layers.Input((img_height, img_width, 3)),
layers.Conv2D(16, 3, padding='same', activation='relu'),
layers.MaxPooling2D(),
layers.Conv2D(32, 3, padding='same', activation='relu'),
layers.MaxPooling2D(),
layers.Conv2D(64, 3, padding='same', activation='relu'),
layers.MaxPooling2D(),
layers.Dropout(0.2),
layers.Flatten(),
layers.Dense(128, activation='relu'),
layers.Dense(1, activation='sigmoid')
])
  • Скомпилируйте модель

Затем мы компилируем модель, используя оптимизатор Адама, бинарную перекрестную энтропийную потерю и точность в качестве метрики.

# compile the model
model.compile(optimizer='adam',
loss=tf.keras.losses.BinaryCrossentropy(),
metrics=['accuracy'])
  • Обучите модель

Мы используем размер партии 32 и 20 эпох для обучения модели. Мы добавляем обратный вызов для ранней остановки, чтобы предотвратить переобучение данных. Кроме того, слой отсева в нашей последовательной модели также должен помочь предотвратить переоснащение.

# train the model
early_stop = tf.keras.callbacks.EarlyStopping(monitor='val_loss', mode='min', verbose=1, patience=4)
history = model.fit(
X_train,
y_train,
batch_size=32,
validation_data=(X_val, y_val),
epochs=20,
callbacks=[early_stop]
)

  • Протестируйте модель

Мы тестируем модель, делая прогнозы на нашем тестовом наборе. Обратите внимание, что модель выводит вероятность от 0 до 1, поэтому мы округляем это значение, чтобы получить классифицированную метку для каждого из прогнозов.

# make predictions on the test set
y_pred = model.predict(X_test)
y_pred = np.squeeze(y_pred).round().astype(int)
# classification report
from sklearn.metrics import classification_report , confusion_matrix
print(classification_report(y_test, y_pred))

Глядя на отчет о классификации, наша модель дала нам 75% точности и 75% средневзвешенного отзыва. Производительность не самая высокая, в основном из-за того, что у нас недостаточно выборок в нашем наборе данных. Всего в нашем наборе данных 253 изображения.

  • Увеличение данных

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

# define augmentation layer
data_augmentation = tf.keras.Sequential([
layers.experimental.preprocessing.RandomFlip("horizontal_and_vertical"),
layers.experimental.preprocessing.RandomRotation(0.2),
layers.experimental.preprocessing.RandomZoom(0.1)])

Затем мы определяем функцию augment_image, которая принимает входное изображение и возвращает список указанного количества дополненных изображений.

def augment_image(image, n_augmented_images):
'''
Returns a list of augmented images for the given input image
Arguments:
image (array) - input image
number_of_images (int) - number of augmented images to return
Returns:
images (list) - list of augmented images
'''
image = tf.expand_dims(image, 0)
images = []
for i in range(n_augmented_images):
augmented_image = data_augmentation(image)
images.append(np.array(augmented_image[0]))
return images

Мы генерируем 12 дополненных изображений для каждого изображения в нашем наборе данных, в результате чего общее количество образцов изображений составляет 3289. Это намного больше, чем было в нашем исходном наборе данных. Визуализируя первые 9 изображений в нашем тренировочном наборе, мы можем увидеть смесь исходных и дополненных изображений.

После обучения и тестирования модели с использованием расширенного набора данных мы получаем точность 86% и средневзвешенную полноту 86%. Это на 15% больше производительности модели.

  • Настройка гиперпараметров

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

После построения, обучения и тестирования модели с использованием лучших гиперпараметров мы получили точность 91% и отзыв 91%. Это улучшение примерно на 6% по сравнению с нашей предыдущей моделью.

Классификатор опорных векторов

Мы сравним производительность нашей модели с классификатором машины опорных векторов (SVM) в качестве эталона. Мы используем расширенный набор данных здесь. Чтобы использовать SVM, нужно немного изменить форму функций. Изображения размером (128, 128, 3) необходимо сгладить перед установкой SVM.

Как только это будет сделано, мы создадим классификатор SVM и обучим его.

from sklearn import svm
# Create a classifier: a support vector classifier
clf = svm.SVC(gamma=0.001)
# Learn the digits on the train subset
clf.fit(X_train, y_train)

Проверка классификаций SVM в нашем тесте привела к точности и средневзвешенному отзыву 79%.

Результаты

Подводя итог нашей работе, мы начали с исходного набора данных и обучили модель CNN классифицировать сканирование мозга с опухолями и без них. Мы быстро поняли, что производительность модели была не самой лучшей. Это было связано с небольшим размером выборки исходного набора данных. Затем мы использовали увеличение изображения для создания дополненных изображений для каждого изображения в исходном наборе данных, что дало 3289 образцов по сравнению с 253 изображениями, которые у нас были в исходном наборе данных.

Затем мы дополнительно оптимизировали производительность нашей модели, используя настройку гиперпараметров, чтобы найти лучшие гиперпараметры.

Наконец, мы построили и обучили классификатор SVM в качестве эталона. Поскольку мы не использовали поиск по сетке для настройки гиперпараметров классификатора SVM, мы сравним его производительность с нашей моделью CNN до настройки гиперпараметров. Обратите внимание, что в обоих случаях использовалась аугментация данных.

Хотя мы получили хорошую точность и полноту 91% с нашей настроенной моделью CNN, есть возможности для улучшения. Вот несколько советов по улучшению производительности:

  1. Обрезайте дополненные изображения, чтобы удалить ненужные артефакты. Мы можем использовать OpenCV, чтобы найти самый большой контур и обрезать остальные.
  2. Используйте лучший набор данных с большим количеством изображений сканирования мозга, чтобы нам не приходилось сильно полагаться на увеличение данных.
  3. Мы только провели поиск, чтобы найти оптимальное количество единиц в каждом слое. Получите оптимальное количество эпох, размер пакета и количество слоев свертки, а также с помощью настройки гиперпараметров.

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

использованная литература

  1. Портер К.Р., Маккарти Б.Дж., Фрилз С., Ким И., Дэвис Ф.Г. Оценки распространенности первичных опухолей головного мозга в США по возрасту, полу, поведению и гистологии. Нейроонкология 12(6):520–527, 2010.

В Раке есть «может», потому что мы МОЖЕМ победить его!