Готовим нестандартные данные для нейросети |
||
МЕНЮ Главная страница Поиск Регистрация на сайте Помощь проекту Архив новостей ТЕМЫ Новости ИИ Голосовой помощник Разработка ИИГородские сумасшедшие ИИ в медицине ИИ проекты Искусственные нейросети Искусственный интеллект Слежка за людьми Угроза ИИ ИИ теория Внедрение ИИКомпьютерные науки Машинное обуч. (Ошибки) Машинное обучение Машинный перевод Нейронные сети начинающим Психология ИИ Реализация ИИ Реализация нейросетей Создание беспилотных авто Трезво про ИИ Философия ИИ Big data Работа разума и сознаниеМодель мозгаРобототехника, БПЛАТрансгуманизмОбработка текстаТеория эволюцииДополненная реальностьЖелезоКиберугрозыНаучный мирИТ индустрияРазработка ПОТеория информацииМатематикаЦифровая экономика
Генетические алгоритмы Капсульные нейросети Основы нейронных сетей Распознавание лиц Распознавание образов Распознавание речи Творчество ИИ Техническое зрение Чат-боты Авторизация |
2021-09-23 11:52 Сталкивались ли вы когда-либо с проблемой в обучении нейросетей, когда датасет слишком большой, чтобы загрузить его в оперативную память полностью и программа выдает Out-of-Memory Error? Например, при обучении классификатора изображений, у нас нет возможности загрузить все картинки в память до обучения. Даже если это и возможно для игрушечных наборов данных, в реальных задачах объёмы данных измеряются в сотнях, тысячах гигабайт. И мы не можем использовать лишь часть датасета, так как качество обученной модели тоже упадёт. Конечно, у нас есть возможность использовать готовые инструменты (например ImageDagaGenerator в библиотеке Tensorflow), но такой подход работает только если у нас стандартные данные, такие как папки с файлами jpg/png или csv файлы. А что делать, если у нас несколько различных типов данных (например, входные данные - это изображения и их текстовое описание), или большое количество табличных данных, где, например, каждый файл это данные за один день? В этих случаях для загрузки и подготовки данных на вход модели придётся писать свой собственный генератор данных. В данной статье я детально расскажу, как я создавал свой DataGenerator в Kaggle соревновании по определению наличия опухоли головного мозга по МРТ. Итак, посмотрим на данные, которые нам предоставили. Для обучения у нас имеется 585 примеров. Каждый пример представляет собой МРТ скан в четырех режимах: Fluid Attenuated Inversion Recovery (FLAIR), T1-weighted pre-contrast (T1w), T1-weighted post-contrast (T1Gd), T2-weighted (T2). Скан в каждом режиме представляет собой набор одноканальных изображений в формате DICOM. Возьмем один из примеров и посмотрим разрешение и количество файлов для каждого режима: Можно заметить, что количество изображений различно и в каждом режиме и в разных примерах. Теперь загрузим информации о классе каждого образца. Столбец ‘BraTS21ID’ означает номер образца, а ‘MGMT_value’ его класс. Добавим для удобства в качестве столбцов пути к каждому режиму образца. Эти столбцы понадобятся нам в дальнейшем. Анализируя полученную информацию, мы делаем вывод, что наш генератор должен приводить все образцы во всех режимах к одинаковому числу изображений и одинаковому разрешению. Одним из параметров генератора мы будем передавать нашу таблицу, откуда мы возьмем пути к папкам и значение (y), которое мы будем предсказывать. Теперь поговорим о генераторе данных. Согласно документации Tensorflow Keras для наиболее безопасного распараллеливания и обучения желательно использовать класс tf.keras.utils.Sequence, так как он обеспечивает то, что сеть при обучении будет использовать каждый образец за эпоху один раз. Таким образом, нужно создать свой класс, унаследованный от класса Sequence. Необходимо реализовать и методы класса Sequence __getitem__ и __len__. Метод __getitem__ должен возвращать окончательный батч для подачи в сеть. Также при желании можно реализовать метод on_epoch_end для изменения датасета между эпохами. Расмотрим код генератора для этой задачи (по этой ссылке можно найти весь код из статьи https://www.kaggle.com/fipoka2/generator-test): Задача метода __getitem__ - это выдать один батч данных в формате (x, y), где x – это наши изображения в виде numpy.array размерности [batch_size, input_depth, input_height, input_width, num_channels]. Внутри этого метода мы вызываем вспомогательный метод __get_data, задача которого из полученной части данных, сформировать наши массивы. В зависимости от параметров, мы можем использовать все режимы (каналов в этом случае будет 4) или какой-то конкретный. Создание numpy.array из одного режима происходит путем вызовов вспомогательного метода __get_input. Это ключевой метод генератора, формирующий массив для одного канала одного образца батча, остановимся на нём подробнее. Первым делом, мы упорядочиваем файлы в папке по возрастанию их номера. Это необходимо для корректного создания глубины объёмного изображения. Так как количество изображений в разных примерах и режимах различно, мы берем фиксированное параметризованное значение. Если изображений меньше фиксированного значения, мы добавляем по краям черные фоновые изображения (путем создания матрицы, заполненной нулями). Если же изображений больше, берем нужное количество из середины (по глубине) изображения. Далее начинаем работать с каждым изображением. Данная работа состоит из трех этапов.
Масштабирование необходимо, так как формат DICOM не использует стандартный масштаб пикселя от 0 до 255, как в обычных изображениях. Для этого используем вспомогательный метод rescale, который отмасштабирует каждый пиксель скана к значению 0-255. Далее нужно изменить разрешение изображения до указанного в параметрах. Для этого используем готовую функцию tf.image.resize из библиотеки TensorFlow. Последним шагом будет нормализация/стандартизация изображения. Для этого будем из каждого значения пикселя вычитать его среднее и делить на стандартное отклонение. Это важный этап, так как стандартизация входных данных может ускорить обучение и снизить вероятность застревания в локальных оптимумах. Посмотрим, как работает наш генератор. Для этого воспользуемся библиотекой imageio и склеим наши изображения в одном из образцов. Мы убедились, что из исходных данных генератор корректно собирает объёмное изображение. Теперь необходимо убедиться, что созданный нами класс корректно работает при обучении нейросети. Создадим простейшую свёрточную 3D нейросеть для классификации и используем наш генератор (из за ограничений используемого GPU возьмем только один режим FLAIR и снизим значения параметров). В результате видно, что генератор корректно работает с нейросетью, ошибок при обучении не возникает. У нас получился генератор, который можно использовать на любых объемах данных с разными размерами. Данные будут загружаться не все сразу, а по мере надобности. Слегка изменив код, можно адаптировать это генератор для загрузки датасетов, состоящих из видеофайлов. Также для более быстрой загрузки, мы можем применить код обработки изображений заранее и в процессе обучения загружать в генераторе данные, уже сохраненные как numpy массивы. Дополнительно можно добавить возможности аугментации данных (такие как сдвиги, повороты и т.д.), код которых придётся самостоятельно добавлять в наш класс. Тем не менее полученный генератор достаточно прост и эффективен, и даже в таком базовом варианте способен эффективно справляться с задачей. Источник: habr.com Комментарии: |
|