Рост медицинской визуализации способствовал раннему выявлению заболеваний, что привело к раннему вмешательству. Это также привело к сокращению использования инвазивных процедур. И с увеличением данных возрастает нагрузка на медицинских экспертов, изучающих эти данные.
Согласно этой статье, больницы производят 50 петабайт данных в год, что составляет 90% всех медицинских данных. И в этой статье говорится: Количество медицинских изображений, которые рентгенологам отделения неотложной помощи приходится анализировать, может быть ошеломляющим, при этом каждое медицинское исследование включает до 3000 изображений, занимающих 250 ГБ данных. был быстрый рост получения медицинских изображений, а также их сложный и интересный анализ. Кроме того, огромное количество данных и необходимость быстрого анализа — это прямой путь к ошибке.
Именно здесь такие технологии, как машинное обучение и глубокое обучение, могут помочь снизить нагрузку на техников и врачей. Эти технологии также могут быть полезными, если будет продемонстрировано, что они способны находить закономерности в изображениях, которые нелегко обнаружить при осмотре человеком.
Мы предлагаем изучить использование алгоритмов Machine Learning
и Deep Learning
для облегчения анализа медицинских изображений. В частности, эти усилия будут сосредоточены на анализе рентгеновских снимков грудной клетки пациентов, чтобы определить, была ли у них пневмония.
Подход, который мы используем, заключается в создании нескольких моделей разных типов и настроек, которые затем сравниваются, чтобы определить наиболее эффективную модель. Модели построены с использованием сверточных нейронных сетей (CNN) на базе фреймворка Keras/Tensorflow.
Модели оцениваются по тому, насколько точно они предсказывают заболевание. В этом случае желательно иметь высокую скорость True Positives
и низкую скорость False Negative
. Это связано с тем, что чем выше False Negative
, тем больше число пропущенных случаев заболевания. Предпочтительнее неправильно диагностировать заболевание, что приведет к дальнейшему анализу, чем пропустить заболевание. Позднее может привести к потере драгоценного времени в лечении.
Поэтому используемая нами метрика должна учитывать высокую стоимость, связанную с False Negative
. Вот обзор вариантов.
В центре внимания точности стоит цена высоких False Positive
результатов. Он измеряет стоимость пациентов, у которых неправильно диагностировано заболевание. Хотя это проблема, она не так значительна, как ошибочный диагноз, как отсутствие заболевания.
Напомним, основное внимание уделяется стоимости False Negative
результатов. Казалось бы, это метрика, по которой должны оцениваться наши модели. Уменьшив количество False Negative
, модель уменьшит количество больных пациентов, которые пропущены в диагностике.
Это определяется как среднее гармоническое между precision
и recall
. Поскольку он учитывает обе скорости, он считается более надежным, чем точность в случае несбалансированных классов. Однако средняя ставка сама по себе не говорит нам, какая индивидуальная ставка вносит больший вклад.
Эта метрика возвращает долю истинных прогнозов среди всех прогнозов. Это может ввести в заблуждение в случае несбалансированных классов, где важны точность и полнота.
Исходя из вышеизложенного, становится ясно, что отзыв — это метрика, которая лучше всего решает проблему. Это приводит к более высокой стоимости False Negative
результатов, а это означает, что меньшему количеству пациентов с заболеванием будет поставлен неверный диагноз.
Набор данных получен от Kermany et al. на Mendeley.com Также есть версия на Kaggle, которая является подмножеством набора данных Mendeley.
Простые модели Keras
В этом эксперименте мы создаем две модели Keras/Tensorflow CNN. Они очень простые и отличаются только количеством слоев.
Мы используем Keras ImageDataGenerator для увеличения данных.
def batch_make(path, classes, batch_size=10, shuffle=True): batches = ImageDataGenerator( rescale=1./255, rotation_range=10, samplewise_center=True, samplewise_std_normalization=True, width_shift_range=0.2, height_shift_range=0.2, shear_range=0.2, zoom_range=0.2, fill_mode="nearest", cval=0.0, horizontal_flip=True).flow_from_directory( directory=path, target_size=(224,224), classes=classes, batch_size=batch_size, shuffle=shuffle ) return batches
train_batch = batch_make(train_path, ['NORMAL', 'PNEUMONIA'], batch_size=20) valid_batch = batch_make(valid_path, ['NORMAL', 'PNEUMONIA'], batch_size=20) test_batch = batch_make(test_path, ['NORMAL', 'PNEUMONIA'], batch_size=20, shuffle=False)
Модель 1
Мы создали простую сверточную нейронную сеть, состоящую из двух сверточных слоев с максимальным объединением и выходного слоя с двумя узлами. Ниже приведена конфигурация.
model = Sequential([
Conv2D(filters=32, kernel_size=(3, 3), activation='relu', padding = 'same', input_shape=(224,224,3)),
MaxPool2D(pool_size=(2, 2), strides=2),
Conv2D(filters=64, kernel_size=(3, 3), activation='relu', padding = 'same'),
MaxPool2D(pool_size=(2, 2), strides=2),
Flatten(),
Dense(units=2, activation='softmax')
])
Скомпилируйте и обучите модель
Модель обучается на 100 эпох.
model.compile(optimizer=Adam(learning_rate=0.0001), loss='categorical_crossentropy', metrics=['accuracy'])
history_1 = model.fit(x=train_batch,
steps_per_epoch=len(train_batch),
validation_data=valid_batch,
validation_steps=len(valid_batch),
epochs=100,
verbose=2
)
Epoch 1/100
210/210 - 1532s - loss: 0.3867 - accuracy: 0.8232 - val_loss: 0.2814 - val_accuracy: 0.8701
Epoch 2/100
210/210 - 104s - loss: 0.2613 - accuracy: 0.8908 - val_loss: 0.2403 - val_accuracy: 0.9064
Epoch 3/100
210/210 - 104s - loss: 0.2472 - accuracy: 0.8906 - val_loss: 0.2281 - val_accuracy: 0.9007
Epoch 4/100
............
Epoch 97/100
210/210 - 100s - loss: 0.1254 - accuracy: 0.9527 - val_loss: 0.1229 - val_accuracy: 0.9551
Epoch 98/100
210/210 - 100s - loss: 0.1166 - accuracy: 0.9553 - val_loss: 0.1201 - val_accuracy: 0.9475
Epoch 99/100
210/210 - 101s - loss: 0.1254 - accuracy: 0.9503 - val_loss: 0.1167 - val_accuracy: 0.9494
Epoch 100/100
210/210 - 100s - loss: 0.1119 - accuracy: 0.9584 - val_loss: 0.1157 - val_accuracy: 0.9570
Результаты обучения
Мы видим, что точность обучения и проверки сходятся. Это признак того, что модель может быть слишком обобщающей. Добавление большей сложности может решить эту проблему. И мы можем видеть то же самое для потерь. Точность проверки составляет почти 0,096 для обоих наборов данных.
Теперь мы посмотрим, как модель делает прогнозы на тестовом наборе.
predictions = model.predict(x=test_batch, verbose=0)
Мы строим матрицу путаницы, чтобы получить представление о том, насколько хорошо работает модель.
Используя матрицу путаницы, мы рассчитаем различные показатели для модели. Целью прогноза является класс пневмонии. Итак, если пневмония предсказана и верна, то это True Positive
.
Хотя accuracy
в модели предполагает, что это относительно слабая модель, помните, что наиболее важной метрикой для этого бизнес-кейса является recall
. Модель набрала очень приличные 0.97
для recall
, что означает, что она правильно предсказала пневмонию для 97%
всех изображений, помеченных как больные. Тем не менее, пропустить 3% случаев пневмонии по-прежнему проблематично.
Модель 2
Далее мы воссоздаем эксперимент, но с двумя дополнительными слоями.
model_2 = Sequential([ Conv2D(filters=32, kernel_size=(3, 3), activation='relu', padding = 'same', input_shape=(224,224,3)), MaxPool2D(pool_size=(2, 2), strides=2), Conv2D(filters=32, kernel_size=(3, 3), activation='relu', padding = 'same'), MaxPool2D(pool_size=(2, 2), strides=2), Conv2D(filters=32, kernel_size=(3, 3), activation='relu', padding = 'same'), MaxPool2D(pool_size=(2, 2), strides=2), Conv2D(filters=64, kernel_size=(3, 3), activation='relu', padding = 'same'), MaxPool2D(pool_size=(2, 2), strides=2), Flatten(), Dense(units=2, activation='softmax') ])
Скомпилируйте и обучите модель
Мы обучаем эту модель на 50 эпох.
model.compile(optimizer=Adam(learning_rate=0.0001), loss='categorical_crossentropy', metrics=['accuracy']) history_2 = model_2.fit(x=train_batch, steps_per_epoch=len(train_batch), validation_data=valid_batch, validation_steps=len(valid_batch), epochs=50, verbose=2 ) 210/210 - 400s - loss: 0.1723 - accuracy: 0.9321 - val_loss: 0.1863 - val_accuracy: 0.9150 Epoch 22/50 210/210 - 399s - loss: 0.1844 - accuracy: 0.9300 - val_loss: 0.1924 - val_accuracy: 0.9274 Epoch 23/50 210/210 - 405s - loss: 0.1674 - accuracy: 0.9381 - val_loss: 0.1508 - val_accuracy: 0.9379 Epoch 24/50 210/210 - 408s - loss: 0.1625 - accuracy: 0.9357 - val_loss: 0.1789 - val_accuracy: 0.9284 .................. Epoch 44/50 210/210 - 407s - loss: 0.1431 - accuracy: 0.9465 - val_loss: 0.1259 - val_accuracy: 0.9532 Epoch 45/50 210/210 - 404s - loss: 0.1344 - accuracy: 0.9513 - val_loss: 0.1245 - val_accuracy: 0.9551 Epoch 46/50 210/210 - 398s - loss: 0.1303 - accuracy: 0.9489 - val_loss: 0.1177 - val_accuracy: 0.9666 Epoch 47/50 210/210 - 399s - loss: 0.1417 - accuracy: 0.9472 - val_loss: 0.1288 - val_accuracy: 0.9551 Epoch 48/50 210/210 - 397s - loss: 0.1302 - accuracy: 0.9493 - val_loss: 0.1269 - val_accuracy: 0.9542 Epoch 49/50 210/210 - 407s - loss: 0.1182 - accuracy: 0.9532 - val_loss: 0.1139 - val_accuracy: 0.9599 Epoch 50/50 210/210 - 395s - loss: 0.1223 - accuracy: 0.9503 - val_loss: 0.1183 - val_accuracy: 0.9542
Результаты обучения
Результаты такие же, как и для предыдущей модели. Эта модель, как и последняя, кажется чрезмерно обобщенной (недостаточно подходящей).
predictions_2 = model_2.predict(x=test_batch, verbose=0)
Оценки для этой модели очень похожи на предыдущую модель. На самом деле, когда мы рассматриваем recall
, разницы нет. Можно было бы заключить, что, учитывая дополнительную сложность, нет причин выбирать эту модель по сравнению с предыдущей.
Предварительно обученные модели с VGG16
Следующие два эксперимента используют известную модель VGG 16, предложенную К. Симоняном и А. Зиссерманом из Оксфордского университета в статье «Очень глубокие сверточные сети для крупномасштабного распознавания изображений». Вместо того, чтобы полагаться исключительно на наши данные для обучения модели, мы используем эту уже обученную модель, а затем добавляем наши данные, чтобы уточнить обучение для нашей цели.
ВГГ16 модель 1
В этом эксперименте мы импортируем модель VGG 16
с помощью Keras. Затем мы будем использовать Keras
для создания нашей собственной модели поиска. VGG 16 — это functional model
, но мы будем перебирать слои модели VGG 16
, чтобы наслоить нашу модель. По сути, мы преобразуем функциональную модель в последовательную.
Здесь мы загружаем модель VGG 16, а затем печатаем сводку.
vgg16_model = tf.keras.applications.vgg16.VGG16() vgg16_model.summary() Downloading data from https://storage.googleapis.com/tensorflow/keras-applications/vgg16/vgg16_weights_tf_dim_ordering_tf_kernels.h5 553467904/553467096 [==============================] - 5s 0us/step Model: "vgg16" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= input_1 (InputLayer) [(None, 224, 224, 3)] 0 _________________________________________________________________ block1_conv1 (Conv2D) (None, 224, 224, 64) 1792 _________________________________________________________________ block1_conv2 (Conv2D) (None, 224, 224, 64) 36928 _________________________________________________________________ block1_pool (MaxPooling2D) (None, 112, 112, 64) 0 _________________________________________________________________ block2_conv1 (Conv2D) (None, 112, 112, 128) 73856 _________________________________________________________________ block2_conv2 (Conv2D) (None, 112, 112, 128) 147584 _________________________________________________________________ block2_pool (MaxPooling2D) (None, 56, 56, 128) 0 _________________________________________________________________ block3_conv1 (Conv2D) (None, 56, 56, 256) 295168 _________________________________________________________________ block3_conv2 (Conv2D) (None, 56, 56, 256) 590080 _________________________________________________________________ block3_conv3 (Conv2D) (None, 56, 56, 256) 590080 _________________________________________________________________ block3_pool (MaxPooling2D) (None, 28, 28, 256) 0 _________________________________________________________________ block4_conv1 (Conv2D) (None, 28, 28, 512) 1180160 _________________________________________________________________ block4_conv2 (Conv2D) (None, 28, 28, 512) 2359808 _________________________________________________________________ block4_conv3 (Conv2D) (None, 28, 28, 512) 2359808 _________________________________________________________________ block4_pool (MaxPooling2D) (None, 14, 14, 512) 0 _________________________________________________________________ block5_conv1 (Conv2D) (None, 14, 14, 512) 2359808 _________________________________________________________________ block5_conv2 (Conv2D) (None, 14, 14, 512) 2359808 _________________________________________________________________ block5_conv3 (Conv2D) (None, 14, 14, 512) 2359808 _________________________________________________________________ block5_pool (MaxPooling2D) (None, 7, 7, 512) 0 _________________________________________________________________ flatten (Flatten) (None, 25088) 0 _________________________________________________________________ fc1 (Dense) (None, 4096) 102764544 _________________________________________________________________ fc2 (Dense) (None, 4096) 16781312 _________________________________________________________________ predictions (Dense) (None, 1000) 4097000 ================================================================= Total params: 138,357,544 Trainable params: 138,357,544 Non-trainable params: 0
Сводка показывает, что существует 138 257 544 обучаемых параметра и 1000 различных классов.
Создать VGG 16 модель 1
Мы создаем последовательную модель Keras, а затем перебираем VGG 16, добавляя каждый слой в новую модель. Добавляем все, кроме последней модели. Это будет заменено нашим собственным выходным слоем. Наконец, мы перебираем слои последовательной модели и замораживаем все слои. Это позволит нам сохранить всю подготовку, унаследованную от модели VGG 16.
# Create a Keras sequential model
seq_vgg16_model = Sequential()
# Iterrrate over the vgg16 model and add layers to the new model. Exclude final layer.
for layer in vgg16_model.layers[:-1]:
seq_vgg16_model.add(layer)
# Iterrate over sequential model and freeze all the layers.
for layer in seq_vgg16_model.layers:
layer.trainable = False
Теперь мы добавляем выходной слой. Напомним, исходный VGG 16
имел 1000 узлов в финальном (выходном) слое. Мы заменяем его выходным слоем с двумя узлами, поскольку нам нужно рассмотреть только два класса. Обратите внимание, что мы могли бы также использовать один двоичный узел. Давайте также посмотрим, как модель выглядит сейчас.
seq_vgg16_model.add(Dense(units=2, activation='softmax')) seq_vgg16_model.summary() Model: "sequential" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= block1_conv1 (Conv2D) (None, 224, 224, 64) 1792 _________________________________________________________________ block1_conv2 (Conv2D) (None, 224, 224, 64) 36928 _________________________________________________________________ block1_pool (MaxPooling2D) (None, 112, 112, 64) 0 _________________________________________________________________ block2_conv1 (Conv2D) (None, 112, 112, 128) 73856 _________________________________________________________________ block2_conv2 (Conv2D) (None, 112, 112, 128) 147584 _________________________________________________________________ block2_pool (MaxPooling2D) (None, 56, 56, 128) 0 _________________________________________________________________ block3_conv1 (Conv2D) (None, 56, 56, 256) 295168 _________________________________________________________________ block3_conv2 (Conv2D) (None, 56, 56, 256) 590080 _________________________________________________________________ block3_conv3 (Conv2D) (None, 56, 56, 256) 590080 _________________________________________________________________ block3_pool (MaxPooling2D) (None, 28, 28, 256) 0 _________________________________________________________________ block4_conv1 (Conv2D) (None, 28, 28, 512) 1180160 _________________________________________________________________ block4_conv2 (Conv2D) (None, 28, 28, 512) 2359808 _________________________________________________________________ block4_conv3 (Conv2D) (None, 28, 28, 512) 2359808 _________________________________________________________________ block4_pool (MaxPooling2D) (None, 14, 14, 512) 0 _________________________________________________________________ block5_conv1 (Conv2D) (None, 14, 14, 512) 2359808 _________________________________________________________________ block5_conv2 (Conv2D) (None, 14, 14, 512) 2359808 _________________________________________________________________ block5_conv3 (Conv2D) (None, 14, 14, 512) 2359808 _________________________________________________________________ block5_pool (MaxPooling2D) (None, 7, 7, 512) 0 _________________________________________________________________ flatten (Flatten) (None, 25088) 0 _________________________________________________________________ fc1 (Dense) (None, 4096) 102764544 _________________________________________________________________ fc2 (Dense) (None, 4096) 16781312 _________________________________________________________________ dense (Dense) (None, 2) 8194 ================================================================= Total params: 134,268,738 Trainable params: 8,194 Non-trainable params: 134,260,544
Обратите внимание, что теперь у нас есть 8,194
обучаемых параметра вместо 134,268,738
. Это потому, что когда мы сделали все эти слои необучаемыми, мы заморозили 134,260,544
параметров.
Скомпилируйте и обучите модель
Мы будем обучать эту модель в течение 10 эпох
seq_vgg16_model.compile(optimizer=Adam(learning_rate=0.0001), loss='categorical_crossentropy', metrics=['accuracy']) mod_history = seq_vgg16_model.fit(x=train_batch, steps_per_epoch=len(train_batch), validation_data=valid_batch, validation_steps=len(valid_batch), epochs=10, verbose=2 ) Epoch 1/10 419/419 - 1970s - loss: 0.2405 - accuracy: 0.8973 - val_loss: 0.1568 - val_accuracy: 0.9417 Epoch 2/10 419/419 - 66s - loss: 0.1164 - accuracy: 0.9579 - val_loss: 0.1343 - val_accuracy: 0.9465 Epoch 3/10 419/419 - 67s - loss: 0.0948 - accuracy: 0.9661 - val_loss: 0.1304 - val_accuracy: 0.9484 Epoch 4/10 419/419 - 67s - loss: 0.0835 - accuracy: 0.9668 - val_loss: 0.1191 - val_accuracy: 0.9513 Epoch 5/10 419/419 - 67s - loss: 0.0759 - accuracy: 0.9744 - val_loss: 0.1172 - val_accuracy: 0.9561 Epoch 6/10 419/419 - 67s - loss: 0.0689 - accuracy: 0.9742 - val_loss: 0.1218 - val_accuracy: 0.9513 Epoch 7/10 419/419 - 66s - loss: 0.0650 - accuracy: 0.9775 - val_loss: 0.1105 - val_accuracy: 0.9532 Epoch 8/10 419/419 - 67s - loss: 0.0587 - accuracy: 0.9797 - val_loss: 0.1111 - val_accuracy: 0.9551 Epoch 9/10 419/419 - 68s - loss: 0.0546 - accuracy: 0.9816 - val_loss: 0.1079 - val_accuracy: 0.9570 Epoch 10/10 419/419 - 68s - loss: 0.0512 - accuracy: 0.9816 - val_loss: 0.1096 - val_accuracy: 0.9551
Результаты обучения
Примечательно, что эта модель начинается с более высокой точности и меньших потерь, чем последние две. Это можно объяснить тем, что уже усвоил многие особенности в своем предыдущем обучении. Эта модель очень хорошо обобщает.
С recall
оценкой 0.98
эта модель работает очень хорошо. Точность и точность страдают из-за числа False Positive
. Конечно, когда на карту поставлены жизни, коэффициент 1.8%
из False Negative
по-прежнему относительно высок. Кроме того, около 40_ из тех, у кого нет болезни, были классифицированы как больные.
Создать VGG 16 модель 2
В этом эксперименте мы возьмем предыдущую модель и заморозим на два слоя меньше и посмотрим, будет ли это иметь значение. Результатом этого является то, что теперь будет гораздо больше обучаемых параметров.
Это практически то же самое, что и предыдущая модель, за исключением второго цикла for. Здесь последние два слоя используются для обучения, тогда как в предыдущей модели мы использовали только последний.
# Create a Keras sequential model seq_vgg16_model_2 = Sequential() # Iterrrate over the vgg16 model and add layers to the new model. Exclude final layer. for layer in vgg16_model.layers[:-1]: seq_vgg16_model_2.add(layer) # Freeze all but the last two layers for layer in seq_vgg16_model_2.layers[:-2]: layer.trainable = False
Добавьте выходной слой
Это точно такой же выходной слой, как и в предыдущей модели.
seq_vgg16_model_2.add(Dense(units=2, activation='softmax')) seq_vgg16_model_2.summary() Model: "sequential" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= block1_conv1 (Conv2D) (None, 224, 224, 64) 1792 _________________________________________________________________ block1_conv2 (Conv2D) (None, 224, 224, 64) 36928 _________________________________________________________________ block1_pool (MaxPooling2D) (None, 112, 112, 64) 0 _________________________________________________________________ block2_conv1 (Conv2D) (None, 112, 112, 128) 73856 _________________________________________________________________ block2_conv2 (Conv2D) (None, 112, 112, 128) 147584 _________________________________________________________________ block2_pool (MaxPooling2D) (None, 56, 56, 128) 0 _________________________________________________________________ block3_conv1 (Conv2D) (None, 56, 56, 256) 295168 _________________________________________________________________ block3_conv2 (Conv2D) (None, 56, 56, 256) 590080 _________________________________________________________________ block3_conv3 (Conv2D) (None, 56, 56, 256) 590080 _________________________________________________________________ block3_pool (MaxPooling2D) (None, 28, 28, 256) 0 _________________________________________________________________ block4_conv1 (Conv2D) (None, 28, 28, 512) 1180160 _________________________________________________________________ block4_conv2 (Conv2D) (None, 28, 28, 512) 2359808 _________________________________________________________________ block4_conv3 (Conv2D) (None, 28, 28, 512) 2359808 _________________________________________________________________ block4_pool (MaxPooling2D) (None, 14, 14, 512) 0 _________________________________________________________________ block5_conv1 (Conv2D) (None, 14, 14, 512) 2359808 _________________________________________________________________ block5_conv2 (Conv2D) (None, 14, 14, 512) 2359808 _________________________________________________________________ block5_conv3 (Conv2D) (None, 14, 14, 512) 2359808 _________________________________________________________________ block5_pool (MaxPooling2D) (None, 7, 7, 512) 0 _________________________________________________________________ flatten (Flatten) (None, 25088) 0 _________________________________________________________________ fc1 (Dense) (None, 4096) 102764544 _________________________________________________________________ fc2 (Dense) (None, 4096) 16781312 _________________________________________________________________ dense (Dense) (None, 2) 8194 ================================================================= Total params: 134,268,738 Trainable params: 119,554,050 Non-trainable params: 14,714,688
Результатом нашего изменения стало то, что теперь имеется 119 554 050 обучаемых параметров.
Скомпилируйте и обучите модель
Тренируемся на 10 эпох.
seq_vgg16_model_2.compile(optimizer=Adam(learning_rate=0.0001), loss='categorical_crossentropy', metrics=['accuracy'])
modDeep Learning
history = seq_vgg16_model_2.fit(x=train_batch,
steps_per_epoch=len(train_batch),
validation_data=valid_batch,
validation_steps=len(valid_batch),
epochs=10,
verbose=2
)
Epoch 1/10
419/419 - 2343s - loss: 0.1728 - accuracy: 0.9589 - val_loss: 0.0613 - val_accuracy: 0.9733
Epoch 2/10
419/419 - 93s - loss: 0.0702 - accuracy: 0.9823 - val_loss: 1.4422 - val_accuracy: 0.9112
Epoch 3/10
419/419 - 93s - loss: 0.0968 - accuracy: 0.9859 - val_loss: 0.2311 - val_accuracy: 0.9513
Epoch 4/10
419/419 - 93s - loss: 0.0113 - accuracy: 0.9967 - val_loss: 0.1169 - val_accuracy: 0.9819
Epoch 5/10
419/419 - 93s - loss: 3.1420e-04 - accuracy: 1.0000 - val_loss: 0.1316 - val_accuracy: 0.9838
Epoch 6/10
419/419 - 93s - loss: 1.7572e-05 - accuracy: 1.0000 - val_loss: 0.1285 - val_accuracy: 0.9847
Epoch 7/10
419/419 - 93s - loss: 1.4997e-05 - accuracy: 1.0000 - val_loss: 0.1449 - val_accuracy: 0.9847
Epoch 8/10
419/419 - 93s - loss: 1.0747e-06 - accuracy: 1.0000 - val_loss: 0.1481 - val_accuracy: 0.9866
Epoch 9/10
419/419 - 93s - loss: 4.6145e-08 - accuracy: 1.0000 - val_loss: 0.1498 - val_accuracy: 0.9866
Epoch 10/10
419/419 - 93s - loss: 3.3356e-08 - accuracy: 1.0000 - val_loss: 0.1509 - val_accuracy: 0.9857
Результаты обучения
Предварительно обученных параметров значительно меньше, и это видно по первым четырем эпохам. Как видно ниже, точность проверки падает, прежде чем начинает расти. И в это время происходит большой всплеск убытков. После этого модель стабилизируется. Модели, по-видимому, довольно хорошо обобщаются, но очевидно, что проверочный набор демонстрирует большие потери.
Предсказать тест
predictions_2 = seq_vgg16_model_2.predict(x=test_batch, steps=len(test_batch), verbose=0)
На этот раз показатель отзыва равен 0.99
(на самом деле близко к 0,995). На этот раз количество пациентов с пневмонией, которые неправильно классифицированы, меньше .5%
. Это намного ближе к тому, что можно было бы считать приемлемым. По-прежнему насчитывается около 30%
здоровых людей, которые классифицируются как больные.
Резюме
|--------------------------------|------|------|------|--------| |Model |Recall| Prec | F1 |Accuracy| |--------------------------------|------|------|------|--------| |Basic CNN with two conv layers | 0.97 | 0.84 | 0.90 | 0.87 | |Basic CNN with four conv layers | 0.97 | 0.85 | 0.91 | 0.87 | |VGG 16 Model One | 0.98 | 0.84 | 0.90 | 0.88 | |VGG 16 Model Two | 0.99 | 0.85 | 0.91 | 0.88 | |--------------------------------------------------------------|
Мы использовали фреймворк Keras/Tensorflow для создания четырех моделей CNN. Первые две модели были созданы с нуля и обучены только на имеющихся данных. Затем мы использовали модель VGG 16 для создания двух дополнительных моделей. Преимущество модели VGG 16 в том, что она уже обучена на очень большом наборе данных из 1000 различных классов. К этой предварительно обученной модели мы добавляем дополнительное обучение для включения наших изображений. Мы решили использовать отзыв в качестве показателя для оценки производительности моделей, поскольку он учитывает стоимость результатов False Negative
.
Мы видим, что две базовые модели CNN показали себя довольно хорошо — обе набрали 0,97 балла по показателю отзыва. Идеальный результат 1,0 означает, что False Negative
прогнозов нет. Чем ниже балл, тем выше FN
прогнозов. Хотя 0,97 кажется впечатляющим, мы должны помнить, что любой прогноз FN
приводит к тому, что пациент не получает необходимого лечения.
В VGG 16 Model One все слои, кроме последнего, были заморожены, чтобы использовать то, чему модель научилась при первоначальном обучении на массивном наборе данных. Это означало, что было 8 194 параметра, которые можно было обучить. Как видно выше, эта модель набрала 0.98
баллов при отзыве. Это улучшение, но, вероятно, все же меньше, чем можно было бы ожидать, когда на карту потенциально поставлена жизнь.
VGG 16 Model Two набрала 0.99
баллов при отзыве. Но на самом деле это было ближе к 0.995
. У этой модели было два дополнительных слоя, настроенных на обучение. Это означало, что было 119,554,050
обучаемых параметров. Это означает большую гибкость для изучения нашего набора данных.
VGG 16 Model Two явно работает лучше, чем три другие модели, основываясь на нашей метрике оценки производительности отзыва. С низким баллом 0.99
это очень близко к тому типу чисел, которые хотелось бы видеть в производстве. Я считаю, что с некоторой тонкой настройкой и доработкой эту модель можно улучшить.
Будущая работа
Работа никогда не делается. Есть еще ряд вещей, которые нужно сделать.
Показатели точности оказались гораздо ниже, чем хотелось бы. Никто не хочет слышать, что у него может быть болезнь, когда он на самом деле здоров. Важно выяснить, что вызывает относительно низкие оценки. Может быть, на «нормальных» изображениях есть что-то, что интерпретируется как возможный признак пневмонии? Может ли это быть связано с эффектами увеличения изображения? Были ли эти пациенты с легкими случаями, которых не забрал техник? Понимание ответа и необходимые корректировки (данные, модель или и то, и другое) приведут к более надежному решению.
Также хотелось бы попробовать другие модели и посмотреть, как они соотносятся с этими.
Говорят, что модели глубокого обучения — это черные ящики. Что мы просто знаем, что они работают, но не имеем никакого представления о том, что они делают. Сегодня это менее верно. Я хотел бы дополнительно оценить эти модели, используя activation maps
. Activation maps
дают нам представление о том, какие функции интересны модели.
Первоначально опубликовано на https://github.com.