С целью выполнения семантической сегментации небольшого набора биомедицинских данных я предпринял решительную попытку демистифицировать работу U-Net с помощью Keras. Поскольку я не встречал ни одной статьи, которая систематически объясняла бы этапы обучения, мне пришла в голову мысль задокументировать это для других энтузиастов глубокого обучения. Некоторые из вас, должно быть, подумали, рассмотрел ли я теоретические аспекты этой концепции. Хотя я в первую очередь сосредоточен на реализации, я также стараюсь включать детали, относящиеся к ее пониманию. Большинство моих ссылок включают репозиторий unet zhixuhao на Github и статью U-Net: сверточные сети для сегментации биомедицинских изображений от Олаф Роннебергер и др.

О U-Net

Архитектура U-net является синонимом архитектуры кодировщика-декодера. По сути, это структура глубокого обучения, основанная на FCN; он состоит из двух частей:

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

Преимущества использования U-Net

  1. Вычислительно эффективный
  2. Обучается с небольшим набором данных
  3. Комплексное обучение
  4. Предпочтительно для биомедицинских приложений

Подождите, что такое семантическая сегментация и локализация? Проще говоря, они относятся к маркировке на уровне пикселей, т. Е. каждый пиксель изображения снабжен меткой класса. Вы получите карты сегментации, которые выглядят как на рисунке 1. До этого биомедицинские исследователи придерживались двух подходов:

  1. Классификация изображения в целом (злокачественное или доброкачественное).
  2. Разделение изображений на участки и их классификация.

Из-за увеличенного размера набора данных исправление, безусловно, было лучше, чем классификация всего изображения, однако было несколько недостатков, относящихся к тому же самому. Меньшие шаги или исправления с большим количеством перекрытий требуют больших вычислительных ресурсов и приводят к избыточной (повторяющейся) информации. Во-вторых, жизненно важно найти компромисс между контекстной информацией и локализацией. Маленькие исправления приводят к потере контекстной информации, тогда как большие исправления искажают результаты локализации. Наконец, неперекрывающиеся исправления приводят к потере контекстной информации. Основываясь на предыдущих наблюдениях, архитектура кодера-декодера дает гораздо более высокие значения пересечения по объединению (IoU), чем подача каждого пикселя в CNN для классификации.

Подготовка набора данных

Давайте погрузимся в подготовку вашего набора данных! Я напишу отдельную статью, посвященную мельчайшим деталям U-Net, так что не ломайте голову и не увязайте в ее архитектуре.

  1. Разделите исходный набор данных и соответствующие аннотации на две группы, а именно наборы для обучения и тестирования (точнее, для проверки). исходные изображения имеют RGB, а их маски - двоичные (черно-белые).
  2. Преобразуйте все данные изображения в .tif.
  3. Вам не понадобятся метки классов изображений или аннотации тестового набора (это не проблема классификации).

ПРИМЕЧАНИЕ. Размер изображения следует выбирать так, чтобы последовательные конверсии и максимальное объединение давали четные значения x и y (т. е. ширину и высота карты функций) на каждом этапе. Хотя мои изображения были 360X360 пикселей, я изменил их размер до 256X256. Обрежьте границы, чтобы получить изображение подходящего размера. Я включил код для этого.

Вам должно быть интересно, как назвать папки, yada yada yada… подождите еще несколько минут, и вы точно будете знать, где их разместить!

Предварительные условия

  • Tensorflow
  • Керас ›= 1.0
  • libtiff (необязательно)
  • OpenCV 3 (если вы пользователь MAC, следуйте этому для установки)

Кроме того, этот код должен быть совместим с Python версий 2.7–3.5.

Обучение и расширение данных

При необходимости вы можете вращать, отражать и деформировать изображения. Zhixuhao использует метод деформации, доступный здесь. Внимательно выполните следующие несколько шагов; пропуск шага может свести с ума на несколько часов! Для простоты я разделил этап обучения на две части - Часть A и Часть B.

Часть A - Изменение data.py

1. Репозиторий клона zhixuhao.

$ git clone https://github.com/zhixuhao/unet

2. Войдите в папку image, которая находится внутри папки поезда (../unet/data/train/image).

3. Включите обучающие изображения в папку изображений. Каждое изображение должно быть в формате .tif с последовательными именами, начиная с 0.tif, 1.tif… и так далее.

4. Войдите в папку label, которая находится внутри папки поезда (../unet/data/train/label). Включите соответствующие аннотации к изображениям поезда. Каждое из изображений должно быть в формате .tif с последовательными именами, , начиная с 0.tif, 1.tif… и так далее. Маркировка должна соответствовать набору обучающих изображений.

5. Войдите в тестовую папку, которая находится внутри папки данных (../unet/data/test).

6. Создайте папку под названием npydata в папке данных (../unet/data/npydata). Пусть это останется пустым; обработанный набор данных будет впоследствии сохранен в нем как 3 файла .npy.

7. Откройте файл data.py в папке unet (../unet/data.py). Шаги 8,9, 10 и 11 относятся к изменениям, которые вам нужно будет внести в этот файл для изображений RGB. Области, выделенные жирным шрифтом, соответствуют внесенным мной изменениям.

8. Измените def create_train_data (self), как показано ниже.

def create_train_data(self):
...
  imgdatas = np.ndarray((len(imgs),self.out_rows,self.out_cols,3),     dtype=np.uint8)
  imglabels =  np.ndarray((len(imgs),self.out_rows,self.out_cols,1),   dtype=np.uint8)
...
  img = load_img(self.data_path + "/" + midname) #Removed grayscale
  label = load_img(self.label_path + "/" + midname,grayscale = True)
#Correspond to lines 159-164

9. Измените def create_test_data (self), как показано ниже.

def create_test_data(self):
…
 imgdatas = np.ndarray((len(imgs),self.out_rows,self.out_cols,3), dtype=np.uint8)
 …
 img = load_img(self.test_path + "/" + midname) #Removed grayscale
#Correspond to lines 188 and 191

10. Измените imgnum на размер вашего тренировочного набора.

def doAugmentate(self, img, save_to_dir, save_prefix, batch_size=1, save_format='tif', imgnum=26): #I've considered 26 training images

11. Измените следующее в классе dataProcess (объект), указав правильные сведения о пути. Я изменил data_path, label_path, test_path и npy_path на правильный путь (соответствующий каталогам в моей системе). Вы можете попробовать отредактировать их в строке 138 в data.py. Если выскочит несколько ошибок, просмотрите мои ответы на проблему №40 на Github. Проследите, чтобы путь к папке npydata не был неправильным (это распространенная ошибка).

def __init__(self, out_rows, out_cols, data_path = "/Users/sukritipaul/Dev/newcvtestpy2/unet2/data/train/image", label_path = "/Users/sukritipaul/Dev/newcvtestpy2/unet2/data/train/label", test_path = "/Users/sukritipaul/Dev/newcvtestpy2/unet2/data/test", npy_path = "/Users/sukritipaul/Dev/newcvtestpy2/unet2/data/npydata", img_type = "tif"):
#Corresponds to line 138

12.Запустите data.py

$python data.py

Часть A - Проверка

Ваш результат должен совпадать с выводом на рисунке 3. Я распечатал размеры обучающих изображений, обучающих аннотаций и тестовых изображений. Если ваш терминал показывает правильное количество обучающих и тестовых изображений (в данном случае 8 и 4), то все готово! Если он показывает 0, значит ваши файлы не были включены в файлы .npy. Проверьте, есть ли у вас в / unet / data / npydata следующие файлы:

  1. imgs_mask_train.npy
  2. imgs_test.npy
  3. imgs_train.npy

Wohooo! Вы закончили с битом подготовки данных :)

Часть B - Изменение unet.py

  1. Создайте папку results в папке unet (../unet/results). Если вы думаете, зачем вы это создали - вы сразу поймете, почему!

2. Откройте unet.py (. ./unet/unet.py).

3. Измените количество строк и столбцов в следующей строке. Мои изображения имели размеры 256X256.

def __init__(self, img_rows = 256, img_cols = 256):
 #Corresponds to line 13

4. Измените следующее для трехканального входа в get_unet (self), как показано ниже.

def get_unet(self):
 inputs = Input((self.img_rows, self.img_cols,3))
 #Corresponds to line 27

5. Измените следующие строки в train (self).

def train(self):
...
 np.save('/Users/sukritipaul/Dev/newcvtestpy2/unet2/results/imgs_mask_test.npy', imgs_mask_test)
...
#Note that the address to the results directory must be provided
##Corresponds to line 164

6. Измените def save_img (self), учитывая адрес каталога результатов, как указано на шаге 4.

def save_img(self):
 imgs = np.load('/Users/sukritipaul/Dev/newcvtestpy2/unet2/results/imgs_mask_test.npy') # Use the same address as above
 img.save("/Users/sukritipaul/Dev/newcvtestpy2/unet2/results/%d.jpg"%(i)) #Saves the resulting segmented maps in /results
##Corresponds to lines 169 and 173 

7. Запустите unet.py и подождите пару минут (ваше обучение может занять несколько часов в зависимости от размера набора данных и оборудования системы).

$python unet.py

Часть B - Проверка

Посетите / unet / results и альт! Вы можете найти сгенерированные маски изображений или сегментированные карты функций в оттенках серого :)

В заключение я получил общую точность 90,71% на 65 обучающих изображениях и 10 проверочных изображениях размером 256X256. Они использовали двоичную кросс-энтропию в качестве функции потерь. Мне потребовалось около 3 часов, чтобы тренироваться на MacBook Air 8 ГБ с процессором Intel Core i5 1,8 ГГц.

Не стесняйтесь включать свои запросы в качестве комментариев! :)

Также прочтите

Получайте лучшие предложения по программному обеспечению прямо в свой почтовый ящик