Классификация 12 видов микробов с помощью библиотеки fast.ai

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

Данные: DIBaS (http://misztal.edu.pl/software/databases/dibas/)

Полный набор данных содержит 660 изображений 33 различных родов и видов. В качестве доказательства концептуального проекта сегодня я буду работать только с 12 различными классами: Acinetobacter baumanii, Actinomyces israelii, Bacteroides fragilis, Candida albicans, Clostridium perfringens, Enterococcus faecalis, Escherichia coli, Listeria monocytogenes, Neisseaeria gonorrhoe spp., Pseudomonas aeruginosa, Staphylococcus aureus

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

Каждый класс включает примерно 20 изображений с разрешением 2048 x 1532 пикселей. Образцы окрашивали по Граму и оценивали с использованием 100-кратного объектива в масляной иммерсии. Изображения были получены с помощью микроскопа Olympus CX31, оснащенного камерой SC30.

Настройка:

  • Блокнот Jupyter с библиотекой fasti.ai v1
  • Модель ResNet-50
  • NVIDIA RTX 2060 6 ГБ

Результат: точность 99,6%.

Часть 1. Обработка изображений

Я решил разбить изображения на более мелкие блоки, так как исходный размер 2048 x 1532 довольно велик. Например, взгляните на исходное изображение ниже. Микробы физически малы, и если мы изменим их размер до 224 x 224, важные детали, такие как края и скопления, могут быть потеряны.

Я написал специальную функцию, чтобы разбить изображение на блоки 224 x 224.

def process_image(im):
    imarray = np.array(im)
    im_h, im_w = imarray.shape[:2]
    block_h, block_w = 224, 224
    
    for row in np.arange(im_h - block_h +1, step = block_h):
        for col in np.arange(im_w - block_w +1, step = block_w):
            im1 = imarray[row:row+block_h, col:col+block_w, :]
            im1 = Image.fromarray(im1)
            global i
            global path
            im1.save(path + "\img" + f"{i}" + ".png")
            i+=1
    print("completed")

Я запустил код для каждого класса, при необходимости изменив имя папки.

from PIL import Image
import numpy as np
import os
i=0
path = r"C:\bacteria\Staphylococcus.aureus\edited"
for file in os.listdir(r"C:\bacteria\Staphylococcus.aureus\raw"):
    filename = r"C:\bacteria\Staphylococcus.aureus\raw" + f"\{file}"
    im = Image.open(filename)
    process_image(im)

Конечный продукт выглядит так:

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

Теперь мы готовы к построению модели и обучению.

Часть 2: Обучение

Сначала импортируем необходимые модули.

from fastai.tabular import *
from fastai.vision import *
path = r"C:\bacteria\data"

Моя структура папок: родительская папка (C: \ бактерии \ данные) → подпапки с именами классов (C: \ бактерии \ данные \ кандида) → изображения

np.random.seed(42)
data = ImageDataBunch.from_folder(path, valid_pct=0.2,
        ds_tfms=get_transforms(), size=224, num_workers=4, bs=32).normalize(imagenet_stats)
data.classes, data.c, len(data.train_ds), len(data.valid_ds)

У нас есть 12 различных классов с 10234 обучающими изображениями и 2558 проверочными изображениями.

learn = cnn_learner(data, models.resnet50, metrics=accuracy).to_fp16()

Fasti.ai поддерживает обучение со смешанной точностью, и это так же просто, как добавить .to_fp16 () при создании учащегося. Для тех, у кого есть графические карты NVIDIA RTX, смешанная точность значительно ускоряет обучение и вдвое снижает потребность в памяти. У Эрика есть отличная статья по этому поводу, и вы можете прочитать ее здесь: https://towardsdatascience.com/rtx-2060-vs-gtx-1080ti-in-deep-learning-gpu-benchmarks-cheapest-rtx-vs-most -дорогой-gtx-card-cd47cd9931d2

learn.fit_one_cycle(4)

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

learn.unfreeze()
learn.lr_find()
learn.recorder.plot()

Я обучил всю модель дальше с пониженной скоростью обучения.

learn.fit_one_cycle(10, max_lr=slice(7e-5, 9e-4))

В конце 10 эпох мы достигли точности 99,6%. Нет значительного переобучения, поскольку потери при обучении и проверке одинаковы. Давайте посмотрим на матрицу путаницы.

interp = ClassificationInterpretation.from_learner(learn)
interp.plot_confusion_matrix(figsize=(10,10), dpi=100)

Основные ошибки, по-видимому, связаны с протеусом против псевдомонад и кишечной палочкой против протеуса. Это понятно, поскольку все 3 вида представляют собой грамотрицательные палочки, которые окрашиваются и выглядят одинаково под световым микроскопом. Давайте посмотрим на изображения с наибольшими потерями.

interp.plot_top_losses(6, figsize=(15,15))

В заключение, мы достигли замечательной точности 99,6% в этой довольно сложной задаче. На самом деле, я довольно удивлен производительностью, так как визуально отличить некоторые классы чрезвычайно сложно. Например, вы можете определить стафилококк. aureus и Enterococcus. fecalis отдельно?

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

Спасибо за прочтение!