Введение

Запуск вашего первого проекта по машинному обучению (ML) может оказаться сложной и непосильной задачей. Это руководство было создано, чтобы помочь вам разработать собственный проект машинного обучения специально в области медицинской визуализации. Мы, Солас и Бен, студенты третьего курса бакалавриата, изучающие инженерные науки в Оксфордском университете. Этим летом нам дали возможность поработать месяц с нашим наставником Bartek Papiez над разработкой Рентгеновского классификатора для обнаружения посторонних предметов. К концу этого руководства у вас будут все инструменты, необходимые для создания классификатора изображений любого типа для участия в следующем конкурсе MICCAI, MIDL или ISBI (или для вашего собственного приложения). Мы надеемся, что вы обнаружите, что вам не нужно быть экспертом, чтобы получить очень хорошие результаты!

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

Первые шаги

Чтобы лучше понять основы, необходимые для нашего проекта, мы начали с руководства: Глубокое обучение с PyTorch: 60-минутный блиц. Важно попытаться понять шаги, которые они предпринимают, но не все элементы одинаково важны для начала. Например, мы считаем важным понимать, что такое Автоград и для чего он используется, однако детали реализации как строится динамический вычислительный граф не важны для разработки классификатора. PyTorch великолепен, потому что есть так много примеров кода и руководств, поэтому вам не нужно разбираться во всех тонкостях каждой строки кода, чтобы иметь возможность создавать работающую программу с хорошими результатами.

Документация PyTorch очень подробная, и на любые ваши вопросы, скорее всего, уже есть ответы на StackExchange или других подобных форумах.

Рекомендуемое программное обеспечение / IDE

Для нашего проекта мы использовали Google Colaboratory в качестве основного программного обеспечения для кодирования. Google Colaboratory (Colab) — это онлайн-исполнитель Python, его использование очень интуитивно понятно, и при правильном использовании он может быть очень мощным инструментом. Основным преимуществом является свободный доступ к графическому процессору, поскольку у большинства из нас ограниченная вычислительная мощность на наших домашних машинах. Графические процессоры важны, потому что они более эффективны, чем ЦП, для глубокого обучения, особенно для обучения, которое может занять очень много времени.

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

Основное преимущество использования Colab заключается в простоте импорта набора данных и результатов между вашим диском и файлом Colab.

Импорт изображений и файлов CSV

Вы можете импортировать и сохранять файлы на своем диске Google, подключив диск Google к ноутбуку Colab.

from google.colab import drive
drive.mount('/content/drive')

Мы организовали наши папки следующим образом:

# ─── DATA_DIR
# ├── train
# │ ├── #####.jpg
# │ └── …
# ├── dev
# │ ├── #####.jpg
# │ └── …
# ├── test
# │ ├── #####.jpg
# │ └── …
# ├── train.csv
# └── dev.csv
# └── test.csv

Внутри файлов CSV были имена изображений и аннотации местонахождения посторонних предметов. Для чтения файлов CSV вы можете использовать Pandas DataFrames. Затем их можно преобразовать в словари.

HOW TO IMPORT CSV FILES
import pandas as pd
#drive path to the DATA_DIR folder
data_dir = '/content/drive/My Drive/DATA_DIR/'
#import the csv files
labels_tr = pd.read_csv(data_dir + 'train.csv', na_filter=False)
labels_dev = pd.read_csv(data_dir + 'dev.csv', na_filter=False)
labels_test = pd.read_csv(data_dir + 'test.csv', na_filter=False)
#Convert DataFrames into dictionaries
img_class_dict_tr = dict(zip(labels_tr.image_name, labels_tr.annotation))
img_class_dict_dev = dict(zip(labels_dev.image_name, labels_dev.annotation))

Каждая из этих трех папок — train, dev (также называемая validation/val) и test — имеют разные цели. Обучающие данные — это то, на чем учится модель, и они используются для подгонки модели. Данные проверки используются для оценки модели во время обучения. Один из способов сделать это — после каждой эпохи повторно сохранять модель только в том случае, если текущее значение оцениваемого параметра лучше, чем предыдущее наилучшее значение. Параметрами оценки могут быть, например, потери, AUC — подробнее об этом позже — или точность, и они рассчитываются с использованием данных проверки. Вы делаете это, потому что не хотите, чтобы ваша модель соответствовала вашему набору обучающих данных. Тестовые данные используются для объективной оценки окончательной модели. Важно, чтобы вы не использовали свои тестовые данные во время обучения и не использовали набор обучающих данных для тестирования. С первым вы будете смещать свою модель, чтобы она соответствовала результатам тестирования, даже если это предназначено для независимого способа оценки вашей модели, чтобы имитировать использование в реальных приложениях. В последнем случае ваша модель будет работать намного лучше, если использовать модель, обученную на этих точных данных. Для получения дополнительной информации см. эту статью.

Ограничения

Есть также несколько иногда разочаровывающих ограничений Colab. Поскольку Colab — бесплатный сервис, доступный каждому, существуют ограничения на время, память и использование графического процессора.

Colab ограничивает продолжительность активного времени выполнения до 12 часов, это означает, что вы можете одновременно обучать свою сеть только в течение 12 часов. Это может быть серьезным препятствием, если ваш набор данных большой или вам нужно обучать его несколько раз. Обойти это можно, сохранив сеть во время обучения, например, после каждой эпохи. Таким образом, если среда выполнения выключится, вы сможете продолжить с того места, на котором остановились, снова загрузить сеть и продолжить обучение сети.

HOW TO SAVE YOUR MODEL AFTER TRAINING AND LOAD YOUR SAVED MODEL
import torch
#saving your model to drive path model_dir
model_dir = '/content/drive/My Drive/.../classification_model.pt'
torch.save(model.state_dict(), model_dir)
#loading a saved model after having retrieved appropriate model architecture from torchvision
model.load_state_dict(torch.load(model_dir))

При обучении особенно тяжелых классификаторов вы можете получить сообщение об ошибке«В настоящее время вы не можете подключиться к графическому процессору из-за ограничений использования в Colab». К сожалению, после получения этого сообщения невозможно продолжить обучение вашей сети. Есть два варианта: вы можете создать новую учетную запись Google и поделиться проектом с этой новой учетной записью, или вам нужно дождаться сброса использования, чтобы продолжить обучение вашей сети. Время, необходимое для этого, зависит от мирового спроса, но, по нашему опыту, может занять около 12 часов. С памятью немного проще, вам нужно «Сбросить время выполнения к заводским настройкам» (в раскрывающемся меню «Время выполнения») и перезагрузить страницу, чтобы очистить память и вернуться к обучению вашей сети!

Исследование набора данных

Мы использовали набор данных из Вызова Object-CXR: автоматическое обнаружение посторонних предметов на рентгенограммах грудной клетки. Он состоит из 10 000 изображений: 8000 обучающих, 1000 проверочных и 1000 тестовых, причем половина каждого из этих наборов данных содержит изображения с посторонними объектами. Каждое изображение аннотируется координатами посторонних предметов.

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

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

Просмотр изображений с аннотациями в Pytorch

HOW TO VIEW IMAGES WITH ANNOTATIONS
from PIL import Image, ImageDraw
ANNOTATION_SEP = ';'
OBJECT_SEP = ','
#Function to draw the annotations on images
def draw_annotation(im, anno_str, fill=(255, 63, 63, 40)):
    draw = ImageDraw.Draw(im, mode="RGBA")
    #if multiple annotations in the images, split them up then draw
    for anno in anno_str.split(OBJECT_SEP):
        anno = list(map(int, anno.split(ANNOTATION_SEP)))
        if anno[0] == 0:
            draw.rectangle(anno[1:], fill=fill)
        elif anno[0] == 1:
            draw.ellipse(anno[1:], fill=fill)
    else:
        draw.polygon(anno[1:], fill=fill)

#Create a subplot of 4 example images
fig, axs = plt.subplots(nrows=1, ncols=4, subplot_kw=dict(xticks=[], yticks=[]), figsize=(24, 6))
#Display images 00001.jpg, 00002.jpg, 00003.jpg, 00004.jpg
example_idxes = [0, 1, 2, 3] 
for row, ax in zip(labels_tr.iloc[example_idxes].itertuples(index=False), axs):
    im = Image.open(data_dir + "train/" + row.image_name).convert("RGB")
    if row.annotation:
        draw_annotation(im, row.annotation)
    ax.imshow(im)
    ax.set_title(f"{row.image_name}")

В наборе данных Object-CXR мы обнаружили 9 изображений из первых 100 изображений «dev» с частично отсутствующими аннотациями, одно из которых можно увидеть слева на рис. 1. С бинарной классификацией это не было проблемой, поскольку изображение все еще было классифицируется как содержащий посторонние предметы из-за других аннотаций на изображении. Однако было 2 изображения, которые не имели других аннотаций, как видно справа на рис. 1, и поэтому были неправильно классифицированы.

Предварительная обработка данных

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

Нормализация данных

Набор данных содержал много изображений разного размера, поэтому мы изменили изображения до 800x800 пикселей. Причина, по которой мы выбрали этот размер, заключается в том, что ограничения на размер входных данных для архитектуры, используемой в базовой линии для Object-CXR Challenge, составляли минимум 800 и максимум 1333. Из-за ограничений в Colab, чтобы максимизировать количество изображений, мы можно тренироваться сразу и минимизировать время обучения, мы выбрали наименьший возможный размер ввода 800x800. Поскольку мы наблюдали хорошие результаты с этим размером изображения, мы оставили его таким же в наших архитектурах. Мы также нормализовали значения пикселей до стандартных значений, среднее значение = [0,485, 0,456, 0,406], стандартное отклонение = [0,229, 0,224, 0,225], чтобы каждый вход имел одинаковое распределение данных. Смотрите здесь для получения дополнительной информации о том, почему это важно.

Увеличение данных

Мы реализовали несколько преобразований, чтобы повысить точность нашей модели и предотвратить ее простое изучение обучающего набора данных. Увеличение данных — это процесс увеличения объема обучающих данных путем применения различных преобразований к существующим данным.

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

Мы сделали аугментации: случайные горизонтальные перевороты с вероятностью 0,5, случайные вертикальные перевороты с вероятностью 0,5, случайные повороты между -15 и 15 градусами и дрожание яркости между 0,85 и 1,05.

HOW TO IMPLEMENT DATA AUGMENTATION AND NORMALISATION
from torchvision import transforms
input_size = 800
data_transforms = {
    'train': transforms.Compose([
        transforms.RandomHorizontalFlip(),
        transforms.RandomVerticalFlip(),
        transforms.RandomRotation(15),
        transforms.Resize((input_size,input_size)), 
        transforms.ColorJitter(brightness=(0.85,1.05)),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
]),
    'val': transforms.Compose([
        transforms.Resize((input_size,input_size)),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) 
]),
    'test': transforms.Compose([
        transforms.Resize((input_size,input_size)), 
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])}

Данные проверки и тестирования не нужно дополнять, поскольку дополненные данные используются для улучшения обучения вашей сети.

Импорт вашей сети

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

Теперь возникает вопрос, какую модель выбрать. Существует так много разных вариантов, даже в пределах каждой общей архитектуры. Так как же выбрать? Что ж, это довольно сложный выбор, который зависит от многих факторов, но хорошее эмпирическое правило заключается в следующем. Если у вас небольшой набор данных, вам понадобится небольшая модель (например, ResNet18), чтобы избежать подгонки модели к этому конкретному набору данных, теряя обобщение; если у вас большой набор данных и много времени, выберите более крупную модель (например, ResNet101, DenseNet или InceptionV3). Если вы планируете использовать это для разработки мобильного приложения, SqueezeNet — это то, что вам нужно. Существует множество различных моделей, каждая из которых имеет свои преимущества и недостатки. Если вы не уверены, какую из них использовать, ResNet50 обычно является отличным местом для начала.

В любой сети классификатора есть две основные части: первая часть сети называется сверточной нейронной сетью (CNN). CNN очень гибкие и показали отличную производительность для множества задач, изучая все, от очень простых функций, таких как границы и общие формы, до конкретных функций, относящихся к набору данных. Вторая часть (верхние слои) сети называется полносвязной сетью (FCN), эти верхние слои отвечают за окончательную классификацию. При загрузке вашей модели вам нужно изменить последний слой FC, поскольку ваша модель должна иметь соответствующее количество выходов для вашей конкретной задачи, в нашем случае это было 2.

Поскольку Pytorch предлагает «предварительно обученные» сети, наш вопрос в этот момент заключался в том, что вообще означает «предварительно обученная» модель? И зачем нам вообще импортировать модель, обученную на другом наборе данных? Идея, почему это работает, называется трансферным обучением. Поскольку верхние слои классифицируют изображение на основе признаков, извлеченных из CNN, импорт предварительно обученной модели позволяет нам использовать знания, полученные моделью в другом наборе данных. Даже если набор данных отличается, сеть будет более или менее знать, какие функции искать.

Последний вопрос: какие слои вы тренируете? Что ж, это интересный вопрос! Обновляете ли вы только верхний слой сети, который выполняет окончательную классификацию, или вы точно настраиваете всю модель и обновляете все слои? Предварительно обученная модель, которую вы сейчас загружаете в свою сеть, обучается на наборе данных ImageNet. Несмотря на то, что трансферное обучение дает много преимуществ, изображения из ImageNet сильно отличаются от рентгеновских изображений как по характеристикам, так и по цветам. Возможно, вам потребуется настроить все слои для сети, чтобы изучить особенности, важные для рентгеновских лучей. Ниже вы можете найти фрагмент кода для импорта предварительно обученной сети DenseNet, если для переменной feature_extracting установлено значение Истина, вся сеть точно настроена.

IMPORTING A NETWORK AND DETERMINING WHICH LAYERS ARE TRAINED
#determine which if all or only some of the layers will be trained
def set_parameter_requires_grad(model, feature_extracting):
    if feature_extracting:
        for param in model.parameters():
            param.requires_grad = False
#importing densenet and edit the final layer to the correct number of outputs (num_classes) in our case num_classes = 2
def _get_model(num_classes,feature_extract):
    model = models.densenet121(pretrained=True)
    set_parameter_requires_grad(model, feature_extract)
    num_ftrs = model.classifier.in_features
    model.classifier = nn.Linear(num_ftrs, num_classes)
    return model

Тензорборд

TensorBoard позволяет визуализировать ваш тренировочный процесс. Вы можете строить графики потерь, точности и AUC.

LAUNCHING THE TENSORBOARD
# Load the TensorBoard notebook extension
%load_ext tensorboard
# Define place to store data
from torch.utils.tensorboard import SummaryWriter
writer = SummaryWriter(save_folder + 'runs')
#Launch the TensorBoard. It opens all TensorBoard files inside the path given (even if in subfolders)
tensorboard --logdir=drive/My\ Drive/.../model_v1

WRITING TO THE TENSORBOARD
#During training, add this line to your code to plot the loss
writer.add_scalars('Losses',{'Training':epoch_loss_tr,'Validation':epoch_loss}, epoch)

На рисунке 2 видно, что хотя потери в обучающем наборе данных уменьшаются, потери в проверочных данных стабильны/растут. Это связано с тем, что сеть может начать изучать набор данных, а не особенности посторонних объектов. Это особенно заметно в крупных сетях, таких как ResNet101 и DenseNet, по сравнению с небольшими сетями. Хотя мы реализовали увеличение данных, чтобы смягчить это, оно не останавливается полностью. Вот почему мы сохраняем лучший AUC, а не модель с лучшими потерями.

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

Подробнее см. в этом руководстве по PyTorch.

Оценка

При изучении бинарной классификации в области медицины очень важным инструментом оценки является кривая рабочих характеристик приемника (ROC). Кривая ROC представляет собой график «доли истинных положительных результатов» (tpr) по сравнению с «долей ложных срабатываний» (fpr). Мы используем этот график для оценки наших данных путем расчета площади под кривой (AUC). Чем выше AUC, тем лучше с теоретическим максимумом 1, а все, что ниже 0,5, менее точно, чем подбрасывание монеты — при условии, что вы выполняете двоичную классификацию.

CALCULATING AND PLOTTING ROC AUC
#calculating the AUC of your classifier, both gt (ground truth) and pred_prob (outputs of from the model) must be in a list format
from sklearn.metrics import roc_auc_score, roc_curve, auc
fpr, tpr, _ = roc_curve(gt, pred_prob)
roc_auc = auc(fpr, tpr)
#the ROC curve can also be plotted using
import matplotlib.pyplot as plt
fig, ax = plt.subplots(subplot_kw=dict(xlim=[0, 1], ylim=[0, 1], aspect='equal'))
ax.plot(fpr, tpr, label= f'AUC: {roc_auc:.03}')
_ = ax.legend(loc="lower right")
_ = ax.set_title('ROC curve')

В дополнение к ROC AUC мы также можем использовать точность для общей проверки производительности нашего классификатора. Точность — хороший способ проверить вашу модель, особенно полезный при обучении небинарного классификатора.

Модели ансамбля

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

Тренировочный процесс остается прежним. Скажем, вы обучаете две модели, одну с архитектурой A, а другую с архитектурой B. Обе модели выводят вероятности каждого изображения, содержащего посторонний объект. Существует несколько способов, которыми вы можете затем объединить вероятности из моделей A и B, чтобы улучшить окончательное значение AUC. Мы внедрили три из них: голосование по большинству, среднее и средневзвешенное значение, основанное на Softmax отдельного AUC каждой модели.

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

Голосование большинством

ENSEMBLE A MODEL USING MAJORITY VOTING
import itertools
import collections
#Create empty dictionaries to store values
final_dict = collections.defaultdict(float)
vote_dict = collections.defaultdict(float)
novote_dict = collections.defaultdict(float)
vote_prob_dict = collections.defaultdict(float)
novote_prob_dict = collections.defaultdict(float)
counter_dict =  collections.defaultdict(float)
# Iterating key, val with chain()
# If the value is above 0.5 then store the probability in vote_prob_dict, if it is below store it in novote_prob_dict. Take the average of whichever has more entries per image. 
#Each dictionary contains the image names as keys and probability of the image containing a foreign object as items. 
for key, val in itertools.chain(vgg_dict.items(),squeezenet_dict.items(),
                resnet101_dict.items(), resnet50_dict.items(),             
                inception_dict.items(), densenet_dict.items()):
counter_dict[key] += 1
if val > 0.5:
    vote_dict[key] +=1
    vote_prob_dict[key] += val
else:
    novote_dict[key] +=1
    novote_prob_dict[key] += val
if vote_dict[key] / counter_dict[key] > 0.5:
    final_dict[key] = vote_prob_dict[key] / vote_dict[key]
else:
    final_dict[key] = novote_prob_dict[key] / novote_dict[key]
preds_prob = list(final_dict.values())

В среднем

ENSEMBLE A MODEL USING AVERAGING
import itertools
import collections
store_dict = collections.defaultdict(float)
numofmodels = 6
# iterating key, val with chain(). For each key (image_name) add teh value (probability) from each dictionary 
for key, val in itertools.chain(vgg_dict.items(),squeezenet_dict.items(),
                resnet101_dict.items(), resnet50_dict.items(),             
                inception_dict.items(), densenet_dict.items()):
     store_dict[key] += val / numofmodels
preds_prob = list(store_dict.values())

Средневзвешенное значение

ENSEMBLE A MODEL USING WEIGHTED AVERAGING
import itertools
import collections
import math
#Manually input the AUC values. Make sure they are in the same order as you have listed them in intertools.chain(...)
AUC = torch.tensor([vgg_auc, squeezenet_auc, 
                    resnet101_auc, resnet50_auc, 
                    inception_auc, densenet_auc])
outputs = torch.nn.Softmax(dim=0)(AUC * 125)
store_dict = collections.defaultdict(float)
n = 0
num_models = 6
num_images = 1000 * num_models
# iterating key, val with chain()
for key, val in itertools.chain(vgg_dict.items(),squeezenet_dict.items(),
                resnet101_dict.items(), resnet50_dict.items(),             
                inception_dict.items(), densenet_dict.items()):
    #Determine the model using n as intertools is in order
    model = math.floor(n/(num_images))
    n += 1
    store_dict[key] += val * outputs[model]
    
preds_prob_values = list(store_dict.values())
preds_prob = []
#Convert list of tensors to list of values
for i in range(len(preds_prob_values)):
    preds_prob.append(preds_prob_values[i].item())

Найдя новый preds_prob, пересчитайте AUC, как описано в разделе «Оценка».

Заключительные комментарии

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

За 4 недели работы над проектом мы набрали балл, который поставил бы нас на 10-е место в Object-CXR Challenge, однако мы начали этот проект после того, как прошла дата подачи заявки. И вот наши результаты:

После всего обучения сетей ResNet50, ResNet101, SqueezeNet, VGG, InceptionV3 и DenseNet и проверки всех возможных комбинаций, чтобы собрать их вместе, мы получили эти окончательные результаты [рис. 3.1, 3.2].

Для отдельных моделей наши лучшие результаты были получены с моделью ResNet101 с AUC 0,948 при проверке и AUC 0,943 при тестировании.

В целом наш лучший результат был получен при объединении всех моделей со средневзвешенным значением с использованием Softmax, мы достигли AUC проверки 0,952 и тестовой AUC 0,951.

В разделе «Исследование набора данных» мы выразили обеспокоенность по поводу неправильной маркировки некоторых изображений. Наша ансамблевая модель предсказала, что вероятность того, что изображение 08026.jpg, правильное изображение на рис. 1, содержит посторонний предмет, составляет 0,987. Однако посторонний объект на этом изображении не был идентифицирован в наборе данных. Такие случаи, как тезисы, повлияют на ваши результаты.

Если вам интересно поближе познакомиться с нашим кодом, его можно найти в нашем репозитории GitHub.

Дополнительные материалы

Сюрприз! Это еще не все, что нужно знать о машинном обучении в области медицины. Вот еще несколько ссылок, которые нам пригодились, пока мы разбирались в этом беспорядке: