Время прочтения: 4 мин.

В данный момент можно найти много подготовленных наборов данных для машинного обучения. Но что делать, если нужного под именно ваши задачи нет? Здесь приходится собирать данные вручную. Я столкнулся с такой ситуацией. Мне нужно обучить нейронную сеть распознавать сигналы светофора, а для этого соответственно мне нужны сами фотографии этих светофоров. Как бы странно это не казалось, но в интернете я не смог найти датасет с фотографиями светофоров. Максимум, который я откопал, на сайте Kaggle был датасет с пешеходными светофорами, в котором как побочный пример были представлены искомые изображения светофоров. Но этого все равно явно недостаточно, потому что на класс приходится примерно по 100 фотографий. Добавил еще несколько собственных фоток, но принципиально ничего не изменилось. Смешно, учитывая, что обычно нейросети обучаются на тысячах фотографий.

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

1. RandomContrast

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

data_augmentation = tf.keras.Sequential([
  layers.RandomContrast([1.0, 10.0]),
])

2. RandomFlip

С его помощью можно изменять угол поворота изображения или его отражение по вертикали и горизонтали. Соответственно, в качестве параметров принимает аргумент как вращать: по вертикали, горизонтали или в двух плоскостях сразу. С добавлением к нему RandomRotation можно управлять углом поворота. Я выбрал 0.2, что оптимально для моего примера.

data_augmentation = tf.keras.Sequential([
  layers.RandomFlip("horizontal_and_vertical"),
  layers.RandomRotation(0.2),
])

3. RandomZoom

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

data_augmentation = tf.keras.Sequential([
  layers.RandomZoom(.5, .2, interpolation='nearest')
])

4. RandomTranslation

Рандомное смещение перемещает фото внутри заданного размера, как бы смещая его. Все оставленные области закрашиваются черным. Параметры height_factor=0.2, width_factor=0.2 задают допустимый процент смещения по высоте и ширине соответственно.

data_augmentation = tf.keras.Sequential([
  layers.RandomTranslation(height_factor=0.2, width_factor=0.2, fill_mode="constant")
])

5. RandomCrop

Обрезает случайно выбранную область фото. Размер обрезаемой области задается в аргументах к функции. Первые два параметра отвечают за количество пикселей в кропнутой области. Можно регулировать, приближая совсем маленькие области, что вряд ли подойдет для реальных задач. Я взял значение 500, что соответствует половине от изначального размера моего изображения и получилось довольно неплохо. Почти на всех изображениях светофор попал в кропнутую область.

data_augmentation = tf.keras.Sequential([
  layers.RandomCrop(500, 500, 1),
])

Также в Tensorflow есть модуль tf.image с помощью которого можно делать различные преобразования с изображениями. Передо мной не стоит задачи изменять размер или делать другие преобразования, поэтому я лишь в скользь упомяну о такой возможности. Также можно преобразовывать форматы изображений из RGB в HSV и YIQ, но для этих целей лично мне привычнее использовать OpenCV в котором гораздо больше цветовых пространств и вариаций. Ну и операции рандомного приближения и переворачивания также есть в этом модуле. Но использовать их я не стал, так как они повторяют уже проделанные мной манипуляции. Но возможно вам будет удобнее пользоваться именно tf.image. Ниже перечислю часть функций модуля tf.image.

                tf.image.flip_left_right
		tf.image.flip_up_down
		tf.image.random_flip_left_right
		tf.image.random_flip_up_down
		tf.image.rot90
		tf.image.transpose

Такими нехитрыми способами я смог увеличить свой датасет до вполне приемлемых размеров. Можно варьировать количество изображений на выходе каждого слоя и подбирать наиболее оптимальное для конкретной задачи. Из одного входного изображения мне удалось получить порядка 20-30 различных выходных изображений. Получается, что 100 экземпляров каждого класса легко превращаются в 2-3 тысячи. А это уже тот размер, который обычно используется в тренировочных наборах данных. Думаю, этого будет достаточно для корректного обучения модели нейронной сети.