В декабре прошлого года Standford ML Group выпустила набор данных MURA, большой набор данных рентгенограмм опорно-двигательного аппарата, содержащий 40 895 изображений из 14 982 исследований, где каждое исследование вручную помечено радиологами как нормальное или ненормальное. Один из крупнейших в своем роде. Они также разработали плотную сверточную нейронную сеть из 169 слоев для обнаружения и локализации аномалий. Модель достигла производительности, сравнимой с показателями сертифицированных радиологов.

Информация о проекте MURA здесь и исследовательском документе: Набор данных MURA: на пути к обнаружению аномалий на уровне рентгенолога на рентгенограммах опорно-двигательного аппарата. В исследовательской статье дается представление об архитектуре модели, алгоритме оптимизации, скорости обучения и ее эффективности в различных типах исследований. Они выпустят код в этом месяце.

В этом посте я буду имитировать модель, реализованную в документе MURA, с помощью PyTorch. Код размещен на GitHub здесь.

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

Исследовательский анализ данных

MURA - это набор данных рентгенограмм опорно-двигательного аппарата, состоящий из 14 982 studies из 12 251 patients, всего 40 895 multi-view radiographic images. Каждый study относится к одному из семи стандартных рентгенографических study types верхних конечностей: локоть, палец, предплечье, кисть, плечевая кость, плечо и запястье.

Набор данных MURA поставляется с папками train, valid и test, содержащими соответствующие наборы данных, train.csv и valid.csv содержат пути radiographic images и их метки. Каждое изображение помечается как 1 (ненормальное) или 0 (нормальное) в зависимости от того, является ли соответствующее исследование отрицательным или положительным, соответственно. Иногда эти рентгенографические изображения также обозначаются как views.

Компоненты набора train и valid:

  • train набор состоит из семи study types а именно: XR_ELBOW XR_FINGER XR_FOREARM XR_HAND XR_HUMERUS XR_SHOULDER XR_WRIST
  • Каждый study type содержит несколько папок с названиями типа: patient12104 patient12110 patient12116 patient12122 patient12128 ...
  • Эти папки названы в честь идентификаторов пациентов, каждая из этих папок содержит один или несколько study с такими названиями: study1_negative study2_negative study3_positive ...
  • Каждый из этих studys содержит одну или несколько рентгенограмм (ракурсов или изображений), названных так: image1.png image2.png ...
  • Каждое представление (изображение) является RGB с диапазоном пикселей [0, 255] и различается по размерам.

Все вышеперечисленные пункты верны для набора test, за исключением третьего пункта, папка study называется так: study1 study2 ..

Прочтите мой полный отчет EDA здесь.

Создание конвейера данных:

Согласно статье MURA:

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

Таким образом, нам необходимо спрогнозировать вероятность отклонения от нормы на уровне исследования. Если вы читаете отчет EDA, вы знаете, что каждый study может иметь одно или несколько просмотров (изображений). Теперь нам нужен конвейер данных уровня исследования, который возвращает все изображения исследования для загрузки в модель и соответствующую метку исследования. Давайте посмотрим на необходимое увеличение данных, согласно статье:

Перед подачей изображений в сеть мы нормализовали каждое изображение, чтобы оно имело такое же среднее значение и стандартное отклонение изображений в обучающем наборе ImageNet. Затем мы масштабировали изображения переменного размера до 224 × 224. Мы расширили данные во время обучения, применив случайные боковые инверсии и вращения.

К счастью, PyTorch предоставляет простые в использовании модули данных и расширения данных Dataset и Dataloader.

Пока мы будем использовать только данные исследования запястья. проверьте get_study_level_data, чтобы узнать больше.

ImageDataSet готовит набор данных, эта __getitem__ функция вызывается каждый раз, когда мы пытаемся выполнить итерацию по конвейеру. Он берет исследование, складывает все его изображения в tensor и возвращает его в dict вместе с меткой соответствующего исследования. Для реализации увеличения данных мы используем модуль PyTorch transform. Мы изменяем размер изображения до 224x224, делаем случайные горизонтальные перевороты, вращаем изображение (‹10), преобразуем его в тензор и затем нормализуем в соответствии со средним значением и стандартным отклонением набора данных ImageNet. get_dataloaders верните нам dataloaders для train и valid, установленных в dict.

Построение модели:

Мы использовали сверточную нейронную сеть с 169 уровнями, чтобы предсказать вероятность отклонения от нормы для каждого изображения в исследовании. В сети используется архитектура плотной сверточной сети, подробно описанная в Huang et al. (2016) - который связывает каждый уровень с каждым другим слоем с прямой связью, чтобы сделать оптимизацию глубоких сетей управляемой. Мы заменили последний полностью связанный слой на слой с единственным выходом, после чего применили сигмовидную нелинейность.

Веса сети были инициализированы весами из модели, предварительно обученной в ImageNet (Deng et al., 2009).

По умолчанию PyTorch имеет реализацию DenseNet, но для того, чтобы заменить последний полностью связанный слой на слой с одним выходом и инициализировать модель с весами из модели, предварительно обученной в ImageNet, нам необходимо изменить реализацию DenseNet по умолчанию. Модифицированный DenseNet (169 слоев Dense CNN) можно найти здесь.

Функция потерь:

Для каждого изображения X типа исследования T в обучающей выборке мы оптимизировали взвешенную двоичную кросс-энтропийную потерю.

L (X, y) = - wT, 1 · y log p (Y = 1 | X) −wT, 0 · (1 - y) log p (Y = 0 | X),

где y - метка исследования, p (Y = i | X) - вероятность того, что сеть присвоит метку i, wT, 1 = | NT | / (| AT | + | NT |) и wT, 0 = | AT | / (| AT | + | NT |), где | AT | и | NT | - количество аномальных изображений и нормальных изображений типа исследования T в обучающей выборке, соответственно.

Мы можем создать собственный класс Loss для нашей модели, используя torch.nn.modules.Molude PyTorch.

Обучение модели:

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

Сеть была обучена от начала до конца с использованием Адама с параметрами по умолчанию β1 = 0,9 и β2 = 0,999 (Kingma & Ba, 2014). Мы обучили модель с использованием мини-пакетов размера 8. Мы использовали начальную скорость обучения 0,0001, которая снижается в 10 раз каждый раз, когда после эпохи плато с потерями при проверке, и выбрали модель с наименьшими потерями при проверке.

Pytorch предоставляет ReduceLROnPlateau, мы можем отслеживать потери при проверке и снижать скорость обучения в 10 раз каждый раз, когда происходит плато потерь при проверке.

Функция обучения реализована здесь.

У меня NVIDIA Tesla K80, и на это уходит около 30 минут на изучение запястья.

Весь код имеет открытый исходный код, и его можно найти здесь:



Я попытался дать общий обзор своего кода, дайте мне знать в разделе комментариев, если у вас есть какие-либо сомнения.

Нажмите 👏, если вам понравился пост. Наслаждаться :)