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

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

Для сбора данных я использовал объединение изображений FUNDUS — сканов сетчатки — из общедоступных источников. Они были отсортированы на диабетическую ретинопатию, глаукому, здоровые глаза и выбросы. Обработка выбросов была особенно важна для классификации — если глаза не принадлежат к трем другим классификациям (возможно, это совершенно другое заболевание) — тогда модель будет вынуждена сделать неверный прогноз.

Я использовал Roboflow для автоматической организации, разделения, дополнения и преобразования набора данных. Большинство этих функций выполняются на веб-сайте Roboflow, поэтому их нет в коде. Затем я использовал fast.ai для быстрого обучения и оценки моделей.

data = ImageDataBunch.from_folder(path, size=224, num_workers=4).normalize(imagenet_stats)

Проверить классы:

>>> data.classes
['Bad eyes', 'DR1', 'Glaucoma', 'Good eyes', 'Outliers']

Затем я загрузил модель с помощью fastai.

from fastai.metrics import error_rate # 1 - accuracy
learn = create_cnn(data, models.resnet34, metrics=error_rate)

Печать сетевых слоев:

>>>learn
Learner(data=ImageDataBunch;

Train: LabelList (2463 items)
x: ImageList
Image (3, 224, 224),Image (3, 224, 224),Image (3, 224, 224),Image (3, 224, 224),Image (3, 224, 224)
y: CategoryList
Bad eyes,Bad eyes,Bad eyes,Bad eyes,Bad eyes
Path: /content;

Valid: LabelList (97 items)
x: ImageList
Image (3, 224, 224),Image (3, 224, 224),Image (3, 224, 224),Image (3, 224, 224),Image (3, 224, 224)
y: CategoryList
Bad eyes,Bad eyes,Bad eyes,Bad eyes,Bad eyes
Path: /content;

Test: None, model=Sequential(
  (0): Sequential(
    (0): Conv2d(3, 64, kernel_size=(7, 7), stride=(2, 2), padding=(3, 3), bias=False)
    (1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (2): ReLU(inplace=True)
    (3): MaxPool2d(kernel_size=3, stride=2, padding=1, dilation=1, ceil_mode=False)
    (4): Sequential(
      (0): BasicBlock(
        (conv1): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
        (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
        (relu): ReLU(inplace=True)
        (conv2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
        (bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
      )
... (More layers)

Разморозьте модель, чтобы ее можно было обучить:

learn.unfreeze()

Поиск скорости обучения:

def find_appropriate_lr(model:Learner, lr_diff:int = 15, loss_threshold:float = .05, adjust_value:float = 1, plot:bool = False) -> float:
#Run the Learning Rate Finder
model.lr_find()
#Get loss values and their corresponding gradients, and get lr values
losses = np.array(model.recorder.losses)
min_loss_index = np.argmin(losses)
#loss_grad = np.gradient(losses)
lrs = model.recorder.lrs
#return the learning rate that produces the minimum loss divide by 10
return lrs[min_loss_index] / 10

Тренироваться:

learn.fit_one_cycle(1, max_lr=slice(optimal_lr/10, optimal_lr), callbacks=[early_stop, save_best_model])

Матрица путаницы сюжета:

interp = ClassificationInterpretation.from_learner(learn)
interp.plot_confusion_matrix()

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