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

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

Мы обсудим увеличение данных и его преимущества. Мы также будем держать вещи практичными. То есть, пройдясь по коду Python, используемому для увеличения изображений. В частности, мы обсудим, как сделать эти дополнения:

  • Переворачивание изображений
  • Регулировка яркости
  • Случайное цветовое дрожание
  • Случайный шум (гауссовский, соль и перец, удаление)

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

Вариант использования — автоматизированный автомобиль

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

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

Сбор надежного набора данных

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

Что такое надежный набор данных?

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

Надежный набор данных = надежная модель

Хорошим примером является наш опыт работы с автоматизированным автомобилем. Мы собрали данные, обучили модель и развернули ее. Это сработало отлично! Пока мы не открыли жалюзи…

Солнечные лучи отражались от трассы и «смущали» модель. В этих условиях он не мог делать точных прогнозов. Другими словами, модель не была устойчива к изменениям условий освещения.

Как собрать надежный набор данных

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

  • Включать и выключать свет
  • Открывать и закрывать жалюзи на окнах
  • Сбор данных в разное время суток

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

  • Собирайте данные в разных комнатах
  • Перемещение различных объектов на задний план

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

Преимущества увеличения данных

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

Создание надежного набора данных

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

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

Уменьшите переоснащение до набора условий

С хорошими дополнениями мы можем уменьшить переоснащение. Чтобы было ясно, это не то же самое, что подгонка к тренировочному набору. Возьмем, к примеру, Рисунок 3. Допустим, мы собрали данные только в одной комнате. В результате модель связала объект на заднем плане с предсказанием поворота налево.

Объект будет на всех изображениях, которые мы собрали. Это означает, что это будет в обучении, проверке и даже в тестовом наборе. Модель может хорошо работать на всех этих наборах, но все еще плохо работает в производстве. Например, если мы удалили объект, он может «запутаться» и не повернуть налево. Другими словами, модель была приспособлена к условиям, отраженным в нашем наборе данных.

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

Сходимость моделей

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

Дополнение данных с помощью Python

Хорошо, имея все это в виду, давайте перейдем к фактическому дополнению данных. Мы пройдемся по коду, а также вы можете найти проект на GitHub.

Для начала мы будем использовать импорт ниже. У нас есть несколько стандартных пакетов (строки 2–3). Glob используется для обработки путей к файлам (строка 5). Также у нас есть несколько пакетов для работы с изображениями (строки 8–11).

#Imports 
import numpy as np
import matplotlib.pyplot as plt

import glob
import random

import torchvision.transforms as transforms
import matplotlib.image as mpimg
from PIL import Image, ImageEnhance
import cv2

Как уже упоминалось, мы будем дополнять изображения, используемые для питания автоматизированного автомобиля. Вы можете найти такие примеры на Kaggle. Все эти изображения имеют размер 224 x 224 пикселя. Мы отображаем один из них с кодом ниже.

Обратите внимание на название изображения (строка 3). Первые два числа — это координаты x и y в кадре 224 x 224. На Рис. 4 вы можете видеть, что мы отобразили эти координаты с помощью зеленого круга (строка 11).

read_path = "../../data/images/"

name = "32_50_c78164b4-40d2-11ed-a47b-a46bb6070c92.jpg"

#Get x,y coordinates from name
x = int(name.split("_")[0])
y = int(name.split("_")[1])

#Load image and add circle for coordinates
img = mpimg.imread(read_path + name)
cv2.circle(img, (x, y), 8, (0, 255, 0), 3)

plt.imshow(img)

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

Переворачивание изображений

Предположим, мы собрали кучу изображений в направлении против часовой стрелки (т.е. только повороты влево). Если мы хотим, чтобы машина поворачивала направо, нам нужно собрать кучу данных. В качестве альтернативы, поскольку наша дорожка симметрична, мы могли бы отразить изображения по оси X.

Мы делаем это с помощью функции flip_img. Имейте в виду, что при переворачивании по горизонтальной оси координата x также должна быть скорректирована. Мы делаем это в строке 9, вычитая текущую координату из 224 (ширины изображения). Вы можете увидеть результат этой функции на Рис. 6.

def flip_img(name,img):
    """Invert image and target on x axis"""
    
    # flip image
    img = cv2.flip(img,1)
    
    # flip target variable
    s = name.split("_")
    s[0] = str(224 - int(s[0]))
    name = "_".join(s)
    
    return name, img   

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

Для некоторых приложений это может иметь смысл. Для нашего автоматизированного автомобиля… не так уж и много. Взгляните на Рисунок 7. Вертикальное переворачивание подразумевает, что машина будет ехать по потолку. Если мы не летаем в космосе, это не то условие, которого мы ожидаем в производстве.

Отрегулировать яркость

С помощью adjust_brightness мы можем использовать параметр factor для изменения яркости изображения. Глядя на Рисунок 8, если мы увеличим коэффициент (1,5), изображение станет ярче. Точно так же с коэффициентом меньше 1 изображение будет темнее.

def adjust_brightness(img,factor=1):
    """
    Invert image on x axis
        factor: <1 will decrease brightness and >1 will increase brightness
    """
    img = Image.fromarray(img)

    enhancer = ImageEnhance.Brightness(img)
    img = enhancer.enhance(factor)

    return img

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

Цветовой джиттер

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

def jitter(img, b=0.2, c=0.2, s=0.2, h=0.1):
    """
    Randomly alter brightness, contrast, saturation, hue within given range
    """
    
    img = Image.fromarray(img)
    
    transform = transforms.ColorJitter(
    brightness=b, contrast=c, saturation=s, hue=h)
  
    # apply transform
    img = transform(img)
    
    return img

Опять же, вам нужно подумать о том, имеют ли смысл эти дополнения для вашего приложения. Вы можете видеть, что по умолчанию мы установили коэффициент оттенка на 0,1 (т. е. h = 0,1). Как видно на Рис. 10, более высокий коэффициент оттенка возвращает изображения с разными цветовыми дорожками. Тем не менее, в производстве наш трек всегда будет оранжевым.

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

Входной шум

Менее систематический подход заключается в случайном введении шума. Некоторые примеры приведены на Рис. 11. В каждом случае мы можем регулировать количество вносимого шума.

При выполнении этих дополнений помните, что каждый пиксель в нашем изображении 224 x 224 будет иметь 3 канала — R, G, B. Каждый канал может принимать значение от 0 до 255. Они определяют цвет пикселя.

Первая строка на Рис. 11 была создана с помощью функции gaussian_noise. Мы создаем массив случайных шумов того же размера (224 x 224 x 3), что и изображение (строки 4–5). Каждый элемент в этом массиве будет выбран из нормального распределения со средним значением 0 и заданной дисперсией (var). Добавление этого к изображению отрегулирует каналы R, G, B случайным образом.

def gaussian_noise(img,var=5):
    """Add guassian noise to image"""
    
    dims = np.shape(img)
    noise = np.random.normal(0,var,size=dims).astype("uint8")

    img = img + noise
    
    return img

Аналогичным образом работает функция sp_noise. За исключением того, что теперь мы случайным образом меняем пиксели с заданной вероятностью (вероятность) на черные или белые. Вы можете увидеть это во второй строке на Рис. 11.

def sp_noise(img,prob=0.1):
    """Add salt and pepper noise to image"""
    
    height,width,channels = np.shape(img)
    img = np.array(img)
    
    #Iterate over all pixels
    for i in range(height):
        for j in range(width):
            #Randomly change pixel values
            if random.random()<prob:
                if random.random() < 0.5:
                    img[i][j] = np.array([255,255,255]) #white
                else:
                    img[i][j] = np.array([0,0,0]) #black
                    
    img = Image.fromarray(img)
        
    return img

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

Функция delete_square — это другой подход к добавлению шума. Он работает путем удаления больших кусков изображения. В частности, он превращает случайный квадрат с заданным размером (пиксели) в черный. Примеры приведены в последней строке Рис. 11.

def delete_square(img,pixels=20):
    """Delete random square from image"""
    
    img = np.array(img)
    h,w,channels = np.shape(img)
    
    #Random starting pixel
    rh = random.randint(0,h)
    rw = random.randint(0,w)
  
    sub = round(pixels/2)
    add = pixels-sub
    
    #Boundries for square
    hmin = max(rh-sub,0)
    hmax = min(rh+add,h-1)
    vmin = max(rw-sub,0)
    vmax = min(rw+add,w-1)
    
    # Turn pixel within range black
    img[hmin:hmax,vmin:vmax] = np.array([0,0,0])

    img = Image.fromarray(img)
    return img

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

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

Измерение эффекта увеличения

Итак, мы видели различные типы аугментаций. Мы также видели, как мы можем варьировать уровень некоторых из них. Действительно, типы и уровни можно рассматривать как гиперпараметр. При их настройке важно помнить о некоторых вещах.

Не увеличивать тестовый набор

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

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

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

Все условия не отражены в тестовом наборе

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

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

Надеюсь, вам понравилась эта статья! Вы можете поддержать меня, став одним из моих приглашенных участников :)



| Твиттер | Ютуб | Информационный бюллетень — подпишитесь на БЕСПЛАТНЫЙ доступ к Курсу Python SHAP

Набор данных

Изображения JatRacer (CC0: Public Domain) https://www.kaggle.com/datasets/conorsully1/jatracer-images

Рекомендации

Х. Навид, Опрос: смешивание и удаление изображений для увеличения данных (2021 г.) https://arxiv.org/abs/2106.07085

Gaudenz Boesch, Увеличение данных изображения для компьютерного зрения в 2022 году (Руководство)«https://viso.ai/computer-vision/image-data-augmentation-for-computer -зрение"

Как случайным образом изменить яркость, контрастность, насыщенность и оттенок изображения в PyTorch (2022) https://www.geeksforgeeks.org/how-to-randomly-change-the-brightness- контраст-насыщенность-и-оттенок-изображения-в-pytorch/

К. Шортен и Т.М. Хошгофтаар, Опрос по расширению данных изображений для глубокого обучения (2019 г.) https://journalofbigdata.springeropen.com/articles/10.1186/s40537-019-0197-0

Джейсон Браунли, Обучение нейронных сетей с помощью шума для уменьшения переобучения (2019 г.) https://machinelearningmastery.com/train-neural-networks-with-noise-to-reduce-overfitting/