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

Согласно этой статье, больницы производят 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 Learninghistory = 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.