Введение

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

Малярия - это потенциально смертельное заболевание, вызываемое паразитами Plasmodium, которые передаются людям через укусы инфицированных самок комаров Anopheles, известных как переносчики малярии. Он существует 30 миллионов лет и считается основной причиной смерти древних цивилизаций по всему миру. Сегодня малярия продолжает оставаться серьезным заболеванием, и почти половина населения мира подвержена риску, хотя ВОЗ определила, что африканский регион несет непропорционально высокую долю глобального бремени малярии, при этом в этом регионе проживает 92% населения. случаев малярии и 93% смертей от малярии в 2017 г.

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

Чтобы ускорить этот процесс, мы собираемся ввести частичную автоматизацию обнаружения малярии путем создания классификатора малярии на основе CNN с использованием Keras. Набор данных, который мы будем использовать сегодня, - это набор данных Kaggle« Изображения клеток малярии », который содержит более 13 000 изображений RGB как неинфицированных, так и паразитированных клеток.

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

Реализация

Для начала давайте импортируем пакеты Python os и shutil для обработки данных и определим пути к нашим данным, а также путь к нашим рабочим каталогам. Мы разделяем наши данные на положительные и отрицательные образцы: «А» относится к инфицированным / паразитированным образцам, а «Б» относится к неинфицированным образцам.

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt


import shutil

import os
print(os.listdir("../input/cell_images/cell_images/"))
base_dir='../input/cell_images/cell_images/'
work_dir=  "work/"
os.mkdir(work_dir)
base_dir_A ='../input/cell_images/cell_images/Parasitized/'
base_dir_B ='../input/cell_images/cell_images/Uninfected/'

work_dir_A = "work/A/"
os.mkdir(work_dir_A)
work_dir_B = "work/B/"
os.mkdir(work_dir_B)

Затем мы разделяем наши данные - для обучения, проверки и тестирования. В каждой разделенной папке мы создадим две подпапки для наших выходных категорий, обозначенных как положительные (зараженные) и отрицательные (незараженные). Позже мы будем копировать изображения из обоих исходных каталогов в эти каталоги - это позволяет обойти некоторые ограничения ТОЛЬКО ДЛЯ ЧТЕНИЯ, которые влияют на определенные среды.

train_dir = os.path.join(work_dir, 'train')
os.mkdir(train_dir)

validation_dir = os.path.join(work_dir, 'validation')
os.mkdir(validation_dir)

test_dir = os.path.join(work_dir, 'test')
os.mkdir(test_dir)

print("New directories for train, validation, and test created")
train_pos_dir = os.path.join(train_dir, 'pos')
os.mkdir(train_pos_dir)
train_neg_dir = os.path.join(train_dir, 'neg')
os.mkdir(train_neg_dir)

validation_pos_dir = os.path.join(validation_dir, 'pos')
os.mkdir(validation_pos_dir)
validation_neg_dir = os.path.join(validation_dir, 'neg')
os.mkdir(validation_neg_dir)

test_pos_dir = os.path.join(test_dir, 'pos')
os.mkdir(test_pos_dir)
test_neg_dir = os.path.join(test_dir, 'neg')
os.mkdir(test_neg_dir)

print("Train, Validation, and Test folders made for both A and B datasets")

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

i = 0
      
for filename in os.listdir(base_dir_A): 
       dst ="pos" + str(i) + ".jpg"
       src =base_dir_A + filename 
       dst =work_dir_A + dst 
          
       # rename() function will 
       # rename all the files 
       shutil.copy(src, dst) 
       i += 1


       
j = 0
      
for filename in os.listdir(base_dir_B): 
       dst ="neg" + str(j) + ".jpg"
       src =base_dir_B + filename 
       dst =work_dir_B + dst 
          
       # rename() function will 
       # rename all the files 
       shutil.copy(src, dst) 
       j += 1       
        
print("Images for both categories have been copied to working directories, renamed to A & B + num")

Теперь, когда все каталоги созданы, давайте выполним вручную разделение для обучения и тестирования и скопируем наши исходные изображения в соответствующие каталоги. В нашем примере каждый класс будет иметь 3000 обучающих изображений, 1000 проверочных изображений и 500 тестовых изображений. Обучающие изображения используются для соответствия модели с использованием параметров сети, в то время как проверочные изображения используются для точной настройки указанных параметров для лучшей возможности обобщения и повышенной точности. Роль трех наборов данных примерно соответствует роли практических вопросов, практических экзаменов и выпускных экзаменов соответственно.

fnames = ['pos{}.jpg'.format(i) for i in range(3000)]
for fname in fnames:
    src = os.path.join(work_dir_A, fname)
    dst = os.path.join(train_pos_dir, fname)
    shutil.copyfile(src, dst)

fnames = ['pos{}.jpg'.format(i) for i in range(3000, 4000)]
for fname in fnames:
    src = os.path.join(work_dir_A, fname)
    dst = os.path.join(validation_pos_dir, fname)
    shutil.copyfile(src, dst)

fnames = ['pos{}.jpg'.format(i) for i in range(4000, 4500)]
for fname in fnames:
    src = os.path.join(work_dir_A, fname)
    dst = os.path.join(test_pos_dir, fname)
    shutil.copyfile(src, dst)
    


fnames = ['neg{}.jpg'.format(i) for i in range(3000)]
for fname in fnames:
    src = os.path.join(work_dir_B, fname)
    dst = os.path.join(train_neg_dir, fname)
    shutil.copyfile(src, dst)

fnames = ['neg{}.jpg'.format(i) for i in range(3000, 4000)]
for fname in fnames:
    src = os.path.join(work_dir_B, fname)
    dst = os.path.join(validation_neg_dir, fname)
    shutil.copyfile(src, dst)

fnames = ['neg{}.jpg'.format(i) for i in range(4000, 4500)]
for fname in fnames:
    src = os.path.join(work_dir_B, fname)
    dst = os.path.join(test_neg_dir, fname)
    shutil.copyfile(src, dst)
    
print("Train, validation, and test datasets split and ready for use")
print('total training pos images:', len(os.listdir(train_pos_dir)))
print('total training neg images:', len(os.listdir(train_neg_dir)))
print('total validation pos images:', len(os.listdir(validation_pos_dir)))
print('total validation neg images:', len(os.listdir(validation_neg_dir)))
print('total test pos images:', len(os.listdir(test_pos_dir)))
print('total test meg images:', len(os.listdir(test_neg_dir)))

Затем мы подготавливаем и нормализуем наши входные данные для сети. Мы можем использовать класс Keras ImageDataGenerator для автоматизации этого шага, одновременно создавая входные пакеты для обучения пакетному градиентному спуску. Подводя итог, ImageDataGenerator нормализует интенсивность пикселей до значений от 0 до 1, преобразует содержимое JPEG в тензорные карты с плавающей запятой каждого изображения и изменяет их размер до 150 x 150 пикселей. Когда мы разделяем наши данные на два класса выходных папок, мы указываем class_mode как «двоичный», и ImageDataGenerator автоматически научится связывать содержимое каждой папки с правильной меткой.

from keras.preprocessing.image import ImageDataGenerator
train_datagen = ImageDataGenerator(rescale=1./255)
test_datagen = ImageDataGenerator(rescale=1./255)
train_generator = train_datagen.flow_from_directory(
        train_dir,
        target_size=(150, 150),
        batch_size=20,
        class_mode='binary')
validation_generator = test_datagen.flow_from_directory(
        validation_dir,target_size=(150, 150),
        batch_size=20,
        class_mode='binary')

print("Image preprocessing complete")

Завершив подготовку всех данных, мы можем построить нашу сеть. Наша архитектура CNN состоит из нескольких сверточных слоев для представления признаков и полносвязных слоев, за которыми следует сигмоидный классификатор для двоичной классификации.

Вкратце, роль сверточного слоя состоит в том, чтобы изучать отличительные низкоуровневые и высокоуровневые функции путем определения важных пиксельных паттернов в областях изображения. Роль слоев максимального объединения служит для уменьшения вычислительной нагрузки путем выбора только максимального выходного значения свертки для выходной карты активации. Уменьшенный выход из окончательной свертки и максимального уровня объединения выравнивается, который затем вводится в 512-мерный полностью -связанный слой и, наконец, классификационный слой с сигмовидной активацией для получения результата классификации. В этом руководстве мы используем оптимизатор RMSprop, но читателю предлагается попробовать другие оптимизаторы в библиотеке Keras, такие как SGD и ADAM.

from keras import layers
from keras import models
model = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), activation='relu',
                        input_shape=(150, 150, 3)))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(128, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(128, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Flatten())
model.add(layers.Dropout(0.5))
model.add(layers.Dense(512, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))
model.summary()

from keras import optimizers
model.compile(loss='binary_crossentropy',
optimizer=optimizers.RMSprop(lr=1e-5),
metrics=['acc'])

print("Model created")

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

history = model.fit_generator(
        train_generator,
        steps_per_epoch=100,
        epochs=30,
        validation_data=validation_generator,
        validation_steps=200)
model.save('basic_malaria_pos_neg_v1.h5')
import matplotlib.pyplot as plt
acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(1, len(acc) + 1)
plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.legend()
plt.figure()
plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.legend()
plt.show()

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

eval_datagen = ImageDataGenerator(rescale=1./255)
eval_generator = eval_datagen.flow_from_directory(
        test_dir,target_size=(150, 150),
        batch_size=20,
        class_mode='binary')
eval_generator.reset()    
pred = model.predict_generator(eval_generator,1000,verbose=1)
print("Predictions finished")
  
import matplotlib.image as mpimg
for index, probability in enumerate(pred):
    image_path = test_dir + "/" +eval_generator.filenames[index]
    img = mpimg.imread(image_path)
    
    plt.imshow(img)
    print(eval_generator.filenames[index])
    if probability > 0.5:
        plt.title("%.2f" % (probability[0]*100) + "% B")
    else:
        plt.title("%.2f" % ((1-probability[0])*100) + "% A")
    plt.show()

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

Особое спасибо Аруши Гоэль за ее знания и вклад.

Ссылки

Чолле, Глубокое обучение с помощью Python