Русский
Русский
English
Статистика
Реклама

Gan

Как мы обучили нейросеть генерировать тени на фотографии

25.12.2020 10:05:30 | Автор: admin

Привет, Хабр!

Я работаю Computer Vision Engineer в Everypixel и сегодня расскажу вам, как мы учили генеративно-состязательную сеть создавать тени на изображении.

Разрабатывать GAN не так трудно, как кажется на первый взгляд. В научном мире существует множество статей и публикаций на тему генеративно-состязательных сетей. В этой статье я покажу вам, как можно реализовать архитектуру нейросети и решение, предложенное в одной из научных статей. В качестве опорной статьи я выбрал ARShadowGAN публикация о GAN, генерирующей реалистичные тени для нового, вставленного в изображение объекта. Поскольку от оригинальной архитектуры я буду отклоняться, то дальше я буду называть своё решение ARShadowGAN-like.

Пример работы нейронной сети ARShadowGAN-likeПример работы нейронной сети ARShadowGAN-like

Вот что вам понадобится:

  • браузер;

  • опыт работы с Python;

  • гугл-аккаунт для того, чтобы работать в среде Google Colaboratory.

Описание генеративно-состязательной сети

Напомню, что генеративно-состязательная сеть состоит из двух сетей:

  • генератора, создающего изображение из входного шума (у нас генератор будет создавать тень, принимая изображение без тени и маску вставленного объекта);

  • дискриминатора, различающего настоящее изображение от поддельного, полученного от генератора.

Упрощённая схема ARShadowGAN-likeУпрощённая схема ARShadowGAN-like

Генератор и дискриминатор работают вместе. Генератор учится всё лучше и лучше генерировать тень, обманывать дискриминатор. Дискриминатор же учится качественно отвечать на вопрос, настоящее ли изображение.

Основная задача научить генератор создавать качественную тень. Дискриминатор нужен только для более качественного обучения, а в дальнейших этапах (тестирование, инференс, продакшн и т.д.) он участвовать не будет.

Генератор

Генератор ARShadowGAN-like состоит из двух основных блоков: attention и shadow generation (SG).

Схема генератораСхема генератора

Одна из фишек для улучшения качества результатов использование механизма внимания. Карты внимания это, иначе говоря, маски сегментации, состоящие из нулей (чёрный цвет) и единиц (белый цвет, область интереса).

Attention блок генерирует так называемые карты внимания карты тех областей изображения, на которые сети нужно обращать больше внимания. Здесь в качестве таких карт будут выступать маска соседних объектов (окклюдеров) и маска падающих от них теней. Так мы будто бы указываем сети, как нужно генерировать тень, она ориентируется, используя в качестве подсказки тени от соседних объектов. Подход очень похож на то, как человек действовал бы в жизни при построении тени вручную, в фотошопе.

Архитектура модуля: U-Net, в котором 4 канала на входе (RGB-изображение без тени и маска вставленного объекта) и 2 канала на выходе (маска окклюдеров и соответствующих им теням).

Shadow generation самый важный блок в архитектуре всей сети. Его цель: создание 3-канальной маски тени. Он, аналогично attention, имеет U-Net-архитектуру с дополнительным блоком уточнения тени на выходе (refinement). На вход блоку поступает вся известная на данный момент информация: исходное изображение без тени (3 канала), маска вставленного объекта (1 канал) и выход attention блока маска соседних объектов (1 канал) и маска теней от них (1 канал). Таким образом, на вход модулю приходит 6-канальный тензор. На выходе 3 канала цветная маска тени для вставленного объекта.

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

Дискриминатор

В качестве дискриминатора возьмем дискриминатор от SRGAN. Он привлек своей небольшой, но достаточно мощной архитектурой, а также простотой реализации.

Таким образом, полная схема ARShadowGAN-like будет выглядеть примерно так (да, мелко, но крупным планом отдельные кусочки были показаны выше ):

Полная схема обучения ARShadowGAN-likeПолная схема обучения ARShadowGAN-like

О датасете

Обучение генеративно-состязательных сетей обычно бывает paired и unpaired.

С парными данными (paired) всё достаточно прозрачно: используется подход обучения с учителем, то есть имеется правильный ответ (ground truth), с которым можно сравнить выход генератора. Для обучения сети составляются пары изображений: исходное изображение измененное исходное изображение. Нейронная сеть учится генерировать из исходного изображения его модифицированную версию.

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

Пример Style TransferПример Style Transfer

Изображение взято здесь.

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

Как же собрать такой датасет?

Вариантов здесь достаточно много, приведу некоторые из них:

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

  • Альтернативным подходом я вижу сбор датасета из других изображений с тенями. Логика такая: возьмем изображение с тенью и тень удалим. Отсюда вытекает другая, не менее лёгкая задача Image Inpainting восстановление вырезанных мест в изображении, либо опять же ручная работа в фотошопе. Кроме того, сеть может легко переобучиться на таком датасете, поскольку могут обнаружиться артефакты, которые не видны человеческому глазу, но заметны на более глубоком семантическом уровне.

  • Еще один способ сбор синтетического датасета с помощью 3D. Авторы ARShadowGAN пошли по этому пути и собрали ShadowAR-dataset. Идея следующая: сперва авторы выбрали несколько 3D-моделей из известной библиотеки ShapeNet, затем эти модели фиксировались в правильном положении относительно сцены. Далее запускался рендер этих объектов на прозрачном фоне с включенным источником освещения и выключенным с тенью и без тени. После этого рендеры выбранных объектов просто вставлялись на 2D-изображения сцен без дополнительных обработок. Так получили пары: исходное изображение без тени (noshadow) и ground truth изображение с тенью (shadow). Подробнее о сборе ShadowAR-dataset можно почитать в оригинальной статье.

Итак, пары изображений noshadow и shadow у нас есть. Откуда берутся маски?

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

Пример Shadow-AR датасета.Пример Shadow-AR датасета.

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

Attention

В этом месте отклонимся от статьи возьмем функцию потерь для решения задачи сегментации.

Генерацию карт внимания (масок) можно рассматривать как классическую задачу сегментации изображений. В качестве функции потерь возьмем Dice Loss. Она хорошо устойчива по отношению к несбалансированным данным.

В качестве метрики возьмем IoU (Intersection over Union).

Подробнее о Dice Loss и IoU можно посмотреть здесь.

Shadow generation

Функцию потерь для блока генерации возьмем подобной той, что приведена в оригинальной статье. Она будет состоять из взвешенной суммы трёх функций потерь: L2, Lper и Ladv:

L2 будет оценивать расстояние от ground truth изображения до сгенерированных (до и после refinement-блока, обозначенного как R).

Lper (perceptual loss) функция потерь, вычисляющая расстояние между картами признаков сети VGG16 при прогоне через неё изображений. Разница считается стандартным MSE между ground truth изображением с тенью и сгенерированными изображениями до и после refinement-блока соответственно.

Ladv стандартный adversarial лосс, который учитывает соревновательный момент между генератором и дискриминатором. Здесь D(.) вероятность принадлежности к классу настоящее изображение. В ходе обучения генератор пытается минимизировать Ladv, в то время как дискриминатор, наоборот, пытается его максимизировать.

Подготовка

Установка необходимых модулей

Для реализации ARShadowGAN-like будет использоваться библиотека глубокого обучения для Python pytorch.

Используемые библиотеки: что для чего?

Работу начнём с установки необходимых модулей:

  • segmentation-models-pytorch для импорта U-Net архитектуры;

  • albumentations для аугментаций;

  • piq для импорта необходимой функции потерь;

  • matplotlib для отрисовки изображений внутри ноутбуков;

  • numpy для работы с массивами;

  • opencv-python для работы с изображениями;

  • tensorboard для визуализации графиков обучения;

  • torch для нейронных сетей и глубокого обучения;

  • torchvision для импорта моделей, для глубокого обучения;

  • tqdm для progress bar визуализации.

pip install segmentation-models-pytorch==0.1.0pip install albumentations==0.5.1pip install piq==0.5.1pip install matplotlib==3.2.1pip install numpy==1.18.4pip install opencv-python>=3.4.5.20pip install tensorboard==2.2.1pip install torch>=1.5.0pip install torchvision>=0.6.0pip install tqdm>=4.41.1

Датасет

Датасет: структура, скачивание, распаковка

Для обучения и тестирования я буду использовать готовый датасет. В нём данные уже разбиты на train и test выборки. Скачаем и распакуем его.

unzip shadow_ar_dataset.zip

Структура папок в наборе данных следующая.

Каждая из выборок содержит 5 папок с изображениями:
- noshadow (изображения без теней);
- shadow (изображения с тенями);
- mask (маски вставленных объектов);
- robject (соседние объекты или окклюдеры);
- rshadow (тени от соседних объектов).

dataset train    noshadow  example1.png, ...    shadow  example1.png, ...    mask  example1.png, ...    robject  example1.png, ...    rshadow  example1.png, ... test     noshadow  example2.png, ...     shadow  example2.png, ...     mask  example2.png, ...     robject  example2.png, ...     rshadow  example2.png, ...

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

Итак, подготовим класс ARDataset для обработки изображений и выдачи i-ой порции данных по запросу.

Импорт библиотек
import osimport os.path as ospimport cv2import randomimport numpy as npimport albumentations as albuimport torchimport torch.nn as nnfrom torch.utils.data import Dataset, DataLoaderfrom torch.autograd import Variablefrom piq import ContentLossimport segmentation_models_pytorch as smp

Далее определим сам класс. Основная функция в классе __getitem__() . Она возвращает i-ое изображение и соответствующую ему маску по запросу.

Класс ARDataset
class ARDataset(Dataset):    def __init__(self, dataset_path, augmentation=None, \                 augmentation_images=None, preprocessing=None, \                 is_train=True, ):        """ Инициализация параметров датасета        dataset_path - путь до папки train или test        augmentation - аугментации, применяемые как к изображениям, так и                       к маскам        augmentation_images - аугментации, применяемые только к         изображениям        preprocessing - предобработка изображений        is_train - флаг [True - режим обучения / False - режим предсказания]        """        noshadow_path = os.path.join(dataset_path, 'noshadow')        mask_path = os.path.join(dataset_path, 'mask')        # соберём пути до файлов        self.noshadow_paths = []; self.mask_paths = [];        self.rshadow_paths = []; self.robject_paths = [];        self.shadow_paths = [];        if is_train:            rshadow_path = osp.join(dataset_path, 'rshadow')            robject_path = osp.join(dataset_path, 'robject')            shadow_path = osp.join(dataset_path, 'shadow')        files_names_list = sorted(os.listdir(noshadow_path))        for file_name in files_names_list:            self.noshadow_paths.append(osp.join(noshadow_path, file_name))            self.mask_paths.append(osp.join(mask_path, file_name))            if is_train:                self.rshadow_paths.append(osp.join(rshadow_path, file_name))                self.robject_paths.append(osp.join(robject_path, file_name))                self.shadow_paths.append(osp.join(shadow_path, file_name))        self.augmentation = augmentation        self.augmentation_images = augmentation_images        self.preprocessing = preprocessing        self.is_train = is_train    def __getitem__(self, i):        """ Получение i-го набора из датасета.        i - индекс        Возвращает:        image - изображение с нормализацией для attention блока        mask - маска с нормализацией для attention блока        image1 - изображение с нормализацией для shadow generation блока        mask1 - маска с нормализацией для shadow generaion блока        """        # исходное изображение        image = cv2.imread(self.noshadow_paths[i])        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)        # маска вставленного объекта        mask = cv2.imread(self.mask_paths[i], 0)        if self.is_train:            # маска соседних объектов            robject_mask = cv2.imread(self.robject_paths[i], 0)            # маска теней от соседних объектов            rshadow_mask = cv2.imread(self.rshadow_paths[i], 0)            # результирующее изображение            res_image = cv2.imread(self.shadow_paths[i])            res_image = cv2.cvtColor(res_image, cv2.COLOR_BGR2RGB)            # применяем аугментации отдельно к изображениям            if self.augmentation_images:                sample = self.augmentation_images(                  image=image,                   image1=res_image                )                image = sample['image']                res_image = sample['image1']            # соберём маски в одну переменную для применения аугментаций            mask = np.stack([robject_mask, rshadow_mask, mask], axis=-1)            mask = mask.astype('float')            # аналогично с изображениями            image = np.concatenate([image, res_image], axis=2)            image = image.astype('float')        # применяем аугментации        if self.augmentation:            sample = self.augmentation(image=image, mask=mask)            image, mask = sample['image'], sample['mask']        # нормализация масок        mask[mask >= 128] = 255; mask[mask < 128] = 0        # нормализация для shadow generation блока        image1, mask1 = image.astype(np.float) / 127.5 - 1.0, \                        mask.astype(np.float) / 127.5 - 1.0        # нормализация для attention блока        image, mask = image.astype(np.float) / 255.0, \                      mask.astype(np.float) / 255.0        # применяем препроцессинг        if self.preprocessing:            sample = self.preprocessing(image=image, mask=mask)            image, mask = sample['image'], sample['mask']            sample = self.preprocessing(image=image1, mask=mask1)            image1, mask1 = sample['image'], sample['mask']        return image, mask, image1, mask1    def __len__(self):        """ Возвращает длину датасета"""        return len(self.noshadow_paths)

Объявим аугментации и функции для обработки данных. Аугментации будем брать из репозитория albumentations.

Аугментации и предобработка
def get_training_augmentation():    """ Аугментации для всех изображений, тренировочная выборка. """    train_transform = [        albu.Resize(256,256),        albu.HorizontalFlip(p=0.5),        albu.Rotate(p=0.3, limit=(-10, 10), interpolation=3, border_mode=2),    ]    return albu.Compose(train_transform)def get_validation_augmentation():    """ Аугментации для всех изображений, валидационная / тестовая выборка """    test_transform = [        albu.Resize(256,256),    ]    return albu.Compose(test_transform)def get_image_augmentation():    """ Аугментации только для изображений (не для масок). """    image_transform = [        albu.OneOf([          albu.Blur(p=0.2, blur_limit=(3, 5)),          albu.GaussNoise(p=0.2, var_limit=(10.0, 50.0)),          albu.ISONoise(p=0.2, intensity=(0.1, 0.5), \                        color_shift=(0.01, 0.05)),          albu.ImageCompression(p=0.2, quality_lower=90, quality_upper=100, \                                compression_type=0),          albu.MultiplicativeNoise(p=0.2, multiplier=(0.9, 1.1), \                                   per_channel=True, \                                   elementwise=True),        ], p=1),        albu.OneOf([          albu.HueSaturationValue(p=0.2, hue_shift_limit=(-10, 10), \                                  sat_shift_limit=(-10, 10), \                                  val_shift_limit=(-10, 10)),          albu.RandomBrightness(p=0.3, limit=(-0.1, 0.1)),          albu.RandomGamma(p=0.3, gamma_limit=(80, 100), eps=1e-07),          albu.ToGray(p=0.1),          albu.ToSepia(p=0.1),        ], p=1)    ]    return albu.Compose(image_transform, additional_targets={        'image1': 'image',        'image2': 'image'    })def get_preprocessing():    """ Препроцессинг """    _transform = [        albu.Lambda(image=to_tensor, mask=to_tensor),    ]    return albu.Compose(_transform)def to_tensor(x, **kwargs):    """ Приводит изображение в формат: [channels, width, height] """    return x.transpose(2, 0, 1).astype('float32')

Обучение

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

Датасеты, даталоадеры, девайс
# число изображений, прогоняемых через нейросеть за один разbatch_size = 8dataset_path = '/path/to/your/dataset'train_path = osp.join(dataset_path, 'train')test_path = osp.join(dataset_path, 'test')# объявим датасетыtrain_dataset = ARDataset(train_path,\                          augmentation=get_training_augmentation(),\                          preprocessing=get_preprocessing(),)valid_dataset = ARDataset(test_path, \                          augmentation=get_validation_augmentation(),\                          preprocessing=get_preprocessing(),)# объявим даталоадерыtrain_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)valid_loader = DataLoader(valid_dataset, batch_size=batch_size, shuffle=False)

Определим устройство, на котором будем учить сеть:

device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')

Будем учить attention и shadow generation блоки по отдельности.

Обучение attention блока

В качестве модели attention блока возьмём U-Net. Архитектуру импортируем из репозитория segmentation_models.pytorch. Для повышения качества работы сети заменим стандартную кодирующую часть U-Net на сеть-классификатор resnet34.

Поскольку на вход attention блок принимает изображение без тени и маску вставленного объекта, то заменим первый сверточный слой в модели: на вход модулю поступает 4-канальный тензор (3 цветных канала + 1 черно-белый).

# объявим модель Unet с 2 классами на выходе - 2 маски (соседние объекты и их тени)model = smp.Unet(encoder_name='resnet34', classes=2, activation='sigmoid',)# заменим в модели первый сверточный слой - на входе должно быть 4 каналаmodel.encoder.conv1 = nn.Conv2d(4, 64, kernel_size=(7, 7), stride=(2, 2), \                                padding=(3, 3), bias=False)

Объявим функцию потерь, метрику и оптимизатор.

loss = smp.utils.losses.DiceLoss()metric = smp.utils.metrics.IoU(threshold=0.5)optimizer = torch.optim.Adam([dict(params=model.parameters(), lr=1e-4),])

Создадим функцию для обучения attention блока. Обучение стандартное, состоит из трех циклов: цикла по эпохам, тренировочного цикла по батчам и валидационного цикла по батчам.

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

Функция для обучения attention и её вызов
def train(n_epoch, train_loader, valid_loader, model_path, model, loss,\          metric, optimizer, device):    """ Функция обучения сети.    n_epoch -- число эпох    train_loader -- даталоадер для тренировочной выборки    valid_loader -- даталоадер для валидационной выборки    model_path -- путь для сохранения модели    model -- предварительно объявленная модель    loss -- функция потерь    metric -- метрика    optimizer -- оптимизатор    device -- определенный torch.device    """    model.to(device)    max_score = 0    total_train_steps = len(train_loader)    total_valid_steps = len(valid_loader)    # запускаем цикл обучения    print('Start training!')    for epoch in range(n_epoch):        # переведём модель в режим тренировки        model.train()        train_loss = 0.0        train_metric = 0.0        # тренировочный цикл по батчам        for data in train_loader:            noshadow_image = data[0][:, :3].to(device)            robject_mask = torch.unsqueeze(data[1][:, 0], 1).to(device)            rshadow_mask = torch.unsqueeze(data[1][:, 1], 1).to(device)            mask = torch.unsqueeze(data[1][:, 2], 1).to(device)            # прогоним через модель            model_input = torch.cat((noshadow_image, mask), axis=1)            model_output = model(model_input)            # сравним выход модели с ground truth данными            ground_truth = torch.cat((robject_mask, rshadow_mask), axis=1)            loss_result = loss(ground_truth, model_output)            train_metric += metric(ground_truth, model_output).item()            optimizer.zero_grad()            loss_result.backward()            optimizer.step()            train_loss += loss_result.item()        # переведём модель в eval-режим        model.eval()        valid_loss = 0.0        valid_metric = 0.0        # валидационный цикл по батчам        for data in valid_loader:            noshadow_image = data[0][:, :3].to(device)            robject_mask = torch.unsqueeze(data[1][:, 0], 1).to(device)            rshadow_mask = torch.unsqueeze(data[1][:, 1], 1).to(device)            mask = torch.unsqueeze(data[1][:, 2], 1).to(device)            # прогоним через модель            model_input = torch.cat((noshadow_image, mask), axis=1)            with torch.no_grad():                model_output = model(model_input)            # сравним выход модели с ground truth данными            ground_truth = torch.cat((robject_mask, rshadow_mask), axis=1)            loss_result = loss(ground_truth, model_output)            valid_metric += metric(ground_truth, model_output).item()            valid_loss += loss_result.item()        train_loss = train_loss / total_train_steps        train_metric = train_metric / total_train_steps        valid_loss = valid_loss / total_valid_steps        valid_metric = valid_metric / total_valid_steps        print(f'\nEpoch {epoch}, train_loss: {train_loss}, train_metric: {train_metric}, valid_loss: {valid_loss}, valid_metric: {valid_metric}')        # если получили новый максимум по точности - сохраняем модель        if max_score < valid_metric:            max_score = valid_metric            torch.save(model.state_dict(), model_path)            print('Model saved!')# вызовем функцию:# число эпохn_epoch = 10# путь для сохранения моделиmodel_path = '/path/for/model/saving' train(n_epoch=n_epoch,      train_loader=train_loader,      valid_loader=valid_loader,      model_path=model_path,      model=model,      loss=loss,      metric=metric,      optimizer=optimizer,      device=device)

После того, как обучение attention блока закончено, приступим к основной части сети.

Обучение shadow generation блока

В качестве модели shadow generation блока аналогично возьмём U-Net, только в качестве кодировщика возьмем сеть полегче resnet18.

Поскольку на вход shadow generation блок принимает изображение без тени и 3 маски (маску вставленного объекта, маску соседних объектов и маску теней от них), заменим первый сверточный слой в модели: на вход модулю поступает 6-канальный тензор (3 цветных канала + 3 черно-белых).

После U-Net добавим в конце 4 refinement-блока. Один такой блок состоит из последовательности: BatchNorm2d, ReLU и Conv2d.

Объявим класс генератор.

Класс генератор
class Generator_with_Refin(nn.Module):    def __init__(self, encoder):        """ Инициализация генератора."""        super(Generator_with_Refin, self).__init__()        self.generator = smp.Unet(            encoder_name=encoder,            classes=1,            activation='identity',        )        self.generator.encoder.conv1 = nn.Conv2d(6, 64, kernel_size=(7, 7), \                                                 stride=(2, 2), padding=(3, 3), \                                                 bias=False)        self.generator.segmentation_head = nn.Identity()        self.SG_head = nn.Conv2d(in_channels=16, out_channels=3, \                                 kernel_size=3, stride=1, padding=1)        self.refinement = torch.nn.Sequential()        for i in range(4):            self.refinement.add_module(f'refinement{3*i+1}', nn.BatchNorm2d(16))            self.refinement.add_module(f'refinement{3*i+2}', nn.ReLU())            refinement3 = nn.Conv2d(in_channels=16, out_channels=16, \                                    kernel_size=3, stride=1, padding=1)            self.refinement.add_module(f'refinement{3*i+3}', refinement3)        self.output1 = nn.Conv2d(in_channels=16, out_channels=3, kernel_size=3, \                                 stride=1, padding=1)    def forward(self, x):      """ Прямой проход данных через сеть."""        x = self.generator(x)        out1 = self.SG_head(x)        x = self.refinement(x)        x = self.output1(x)        return out1, x

Объявим класс дискриминатор.

Класс дискриминатор
class Discriminator(nn.Module):    def __init__(self, input_shape):        super(Discriminator, self).__init__()        self.input_shape = input_shape        in_channels, in_height, in_width = self.input_shape        patch_h, patch_w = int(in_height / 2 ** 4), int(in_width / 2 ** 4)        self.output_shape = (1, patch_h, patch_w)        def discriminator_block(in_filters, out_filters, first_block=False):            layers = []            layers.append(nn.Conv2d(in_filters, out_filters, kernel_size=3, \                                    stride=1, padding=1))            if not first_block:                layers.append(nn.BatchNorm2d(out_filters))            layers.append(nn.LeakyReLU(0.2, inplace=True))            layers.append(nn.Conv2d(out_filters, out_filters, kernel_size=3, \                                    stride=2, padding=1))            layers.append(nn.BatchNorm2d(out_filters))            layers.append(nn.LeakyReLU(0.2, inplace=True))            return layers        layers = []        in_filters = in_channels        for i, out_filters in enumerate([64, 128, 256, 512]):            layers.extend(discriminator_block(in_filters, out_filters, \                                              first_block=(i == 0)))            in_filters = out_filters        layers.append(nn.Conv2d(out_filters, 1, kernel_size=3, stride=1, \                                padding=1))        self.model = nn.Sequential(*layers)    def forward(self, img):        return self.model(img)

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

Генератор, дискриминатор, функции потерь, оптимизаторы
generator = Generator_with_Refin('resnet18')discriminator = Discriminator(input_shape=(3,256,256))l2loss = nn.MSELoss()perloss = ContentLoss(feature_extractor="vgg16", layers=("relu3_3", ))GANloss = nn.MSELoss()optimizer_G = torch.optim.Adam([dict(params=generator.parameters(), lr=1e-4),])optimizer_D = torch.optim.Adam([dict(params=discriminator.parameters(), lr=1e-6),])

Всё готово для обучения, определим функцию для обучения SG блока. Её вызов будет аналогичен вызову функции обучения attention.

Функция для обучения SG блока
def train(generator, discriminator, device, n_epoch, optimizer_G, optimizer_D, train_loader, valid_loader, scheduler, losses, models_paths, bettas, writer):    """Функция для обучения SG-блока        generator: модель-генератор        discriminator: модель-дискриминатор        device: torch-device для обучения        n_epoch: количество эпох        optimizer_G: оптимизатор для модели-генератора        optimizer_D: оптимизатор для модели-дискриминатора        train_loader: даталоадер для тренировочной выборки        valid_loader: даталоадер для валидационной выборки        scheduler: шедуллер для изменения скорости обучения        losses:  список функций потерь        models_paths: список путей для сохранения моделей        bettas: список коэффициентов для функций потерь        writer: tensorboard writer    """    # перенесем модели на ГПУ    generator.to(device)    discriminator.to(device)    # для валидационного минимума    val_common_min = np.inf    print('Запускаем обучение!')    for epoch in range(n_epoch):        # переводим модели в режим обучения        generator.train()        discriminator.train()        # списки для значений функций потерь        train_l2_loss = []; train_per_loss = []; train_common_loss = [];         train_D_loss = []; valid_l2_loss = []; valid_per_loss = [];         valid_common_loss = [];        print('Цикл по батчам (пакетам):')        for batch_i, data in enumerate(tqdm(train_loader)):            noshadow_image = data[2][:, :3].to(device)            shadow_image = data[2][:, 3:].to(device)            robject_mask = torch.unsqueeze(data[3][:, 0], 1).to(device)            rshadow_mask = torch.unsqueeze(data[3][:, 1], 1).to(device)            mask = torch.unsqueeze(data[3][:, 2], 1).to(device)            # подготовим входной тензор для модели            model_input = torch.cat((noshadow_image, mask, robject_mask, rshadow_mask), axis=1)            # ------------ учим генератор -------------------------------------            shadow_mask_tensor1, shadow_mask_tensor2 = generator(model_input)            result_nn_tensor1 = torch.add(noshadow_image, shadow_mask_tensor1)            result_nn_tensor2 = torch.add(noshadow_image, shadow_mask_tensor2)            for_per_shadow_image_tensor = torch.sigmoid(shadow_image)            for_per_result_nn_tensor1 = torch.sigmoid(result_nn_tensor1)            for_per_result_nn_tensor2 = torch.sigmoid(result_nn_tensor2)            # Adversarial ground truths            valid = Variable(torch.cuda.FloatTensor(np.ones((data[2].size(0), *discriminator.output_shape))), requires_grad=False)            fake = Variable(torch.cuda.FloatTensor(np.zeros((data[2].size(0), *discriminator.output_shape))), requires_grad=False)            # вычисляем функции потерь            l2_loss = losses[0](shadow_image, result_nn_tensor1) + losses[0](shadow_image, result_nn_tensor2)            per_loss = losses[1](for_per_shadow_image_tensor, for_per_result_nn_tensor1) + losses[1](for_per_shadow_image_tensor, for_per_result_nn_tensor2)            gan_loss = losses[2](discriminator(result_nn_tensor2), valid)            common_loss = bettas[0] * l2_loss + bettas[1] * per_loss + bettas[2] * gan_loss            optimizer_G.zero_grad()            common_loss.backward()            optimizer_G.step()            # ------------ учим дискриминатор ---------------------------------            optimizer_D.zero_grad()            loss_real = losses[2](discriminator(shadow_image), valid)            loss_fake = losses[2](discriminator(result_nn_tensor2.detach()), fake)            loss_D = (loss_real + loss_fake) / 2            loss_D.backward()            optimizer_D.step()            # ------------------------------------------------------------------            train_l2_loss.append((bettas[0] * l2_loss).item())            train_per_loss.append((bettas[1] * per_loss).item())            train_D_loss.append((bettas[2] * loss_D).item())            train_common_loss.append(common_loss.item())        # переводим generator в eval-режим        generator.eval()        # валидация        for batch_i, data in enumerate(valid_loader):            noshadow_image = data[2][:, :3].to(device)            shadow_image = data[2][:, 3:].to(device)            robject_mask = torch.unsqueeze(data[3][:, 0], 1).to(device)            rshadow_mask = torch.unsqueeze(data[3][:, 1], 1).to(device)            mask = torch.unsqueeze(data[3][:, 2], 1).to(device)            # подготовим вход в для модели            model_input = torch.cat((noshadow_image, mask, robject_mask, rshadow_mask), axis=1)            with torch.no_grad():                shadow_mask_tensor1, shadow_mask_tensor2 = generator(model_input)            result_nn_tensor1 = torch.add(noshadow_image, shadow_mask_tensor1)            result_nn_tensor2 = torch.add(noshadow_image, shadow_mask_tensor2)            for_per_result_shadow_image_tensor = torch.sigmoid(shadow_image)            for_per_result_nn_tensor1 = torch.sigmoid(result_nn_tensor1)            for_per_result_nn_tensor2 = torch.sigmoid(result_nn_tensor2)            # вычисляем функции потерь            l2_loss = losses[0](shadow_image, result_nn_tensor1) + losses[0](shadow_image, result_nn_tensor2)            per_loss = losses[1](for_per_result_shadow_image_tensor, for_per_result_nn_tensor1) + losses[1](for_per_result_shadow_image_tensor, for_per_result_nn_tensor2)            common_loss = bettas[0] * l2_loss + bettas[1] * per_loss            valid_per_loss.append((bettas[1] * per_loss).item())            valid_l2_loss.append((bettas[0] * l2_loss).item())            valid_common_loss.append(common_loss.item())        # усредняем значения функций потерь        tr_l2_loss = np.mean(train_l2_loss)        val_l2_loss = np.mean(valid_l2_loss)        tr_per_loss = np.mean(train_per_loss)        val_per_loss = np.mean(valid_per_loss)        tr_common_loss = np.mean(train_common_loss)        val_common_loss = np.mean(valid_common_loss)        tr_D_loss = np.mean(train_D_loss)        # добавляем результаты в tensorboard        writer.add_scalar('tr_l2_loss', tr_l2_loss, epoch)        writer.add_scalar('val_l2_loss', val_l2_loss, epoch)        writer.add_scalar('tr_per_loss', tr_per_loss, epoch)        writer.add_scalar('val_per_loss', val_per_loss, epoch)        writer.add_scalar('tr_common_loss', tr_common_loss, epoch)        writer.add_scalar('val_common_loss', val_common_loss, epoch)        writer.add_scalar('tr_D_loss', tr_D_loss, epoch)        # печатаем информацию        print(f'\nEpoch {epoch}, tr_common loss: {tr_common_loss:.4f}, val_common loss: {val_common_loss:.4f}, D_loss {tr_D_loss:.4f}')        if val_common_loss <= val_common_min:            # сохраняем лучшую модель            torch.save(generator.state_dict(), models_paths[0])            torch.save(discriminator.state_dict(), models_paths[1])            val_common_min = val_common_loss            print(f'Model saved!')        # делаем шаг шедуллера        scheduler.step(val_common_loss)

Процесс обучения

Визуализация процесса обучения Визуализация процесса обучения

Графики, общая информация

Для обучения я использовал видеокарту GTX 1080Ti на сервере hostkey. В процессе я отслеживал изменение функций потерь по построенным графикам с помощью утилиты tensorboard. Ниже, на рисунках, представлены графики обучения на тренировочной и валидационной выборке.

Графики обучения тренировочная выборкаГрафики обучения тренировочная выборка

Особенно полезен второй рисунок, поскольку валидационная выборка не участвует в процессе обучения генератора и является независимой. По графикам обучения видно, что выход на плато произошел в районе 200-250-й эпохи. Здесь можно было уже тормозить обучение генератора, поскольку монотонность у функции потерь отсутствует.

Однако полезно также смотреть на графики обучения в логарифмической шкале она более наглядно показывает монотонность графика. По графику логарифма валидационной функции потерь видим, что обучение в районе 200-250-й эпохи останавливать рановато, можно было сделать это позже, на 400-й эпохе.

Графики обучения валидационная выборкаГрафики обучения валидационная выборка

Для наглядности эксперимента периодически происходило сохранение предсказанной картинки (см. гифку визуализации процесса обучения выше).

Некоторые трудности

В процессе обучения пришлось решить достаточно простую проблему неправильное взвешивание функций потерь.

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

При неправильной балансировке лосс-функций мы можем получить неудовлетворительные результаты, например, если для L2 задать слишком сильный вклад, то обучение нейронной сети может и вовсе застопориться. L2 достаточно быстро сходится, но при этом совсем убирать её из общей суммы тоже нежелательно выходная тень будет получаться менее реалистичной, менее консистентной по цвету и прозрачности.

Пример сгенерированной тени в случае отсутствия вклада L2-лоссаПример сгенерированной тени в случае отсутствия вклада L2-лосса

На картинке слева ground truth изображение, справа сгенерированное изображение.

Инференс

Для предсказания и тестирования объединим модели attention и SG в один класс ARShadowGAN.

Класс ARShadowGAN, объединяющий attention и shadow generation блоки
class ARShadowGAN(nn.Module):    def __init__(self, model_path_attention, model_path_SG, encoder_att='resnet34', \                 encoder_SG='resnet18', device='cuda:0'):        super(ARShadowGAN, self).__init__()        self.device = torch.device(device)        self.model_att = smp.Unet(            classes=2,            encoder_name=encoder_att,            activation='sigmoid'        )        self.model_att.encoder.conv1 = nn.Conv2d(4, 64, kernel_size=(7,7), stride=(2,2), padding=(3,3), bias=False)        self.model_att.load_state_dict(torch.load(model_path_attention))        self.model_att.to(device)        self.model_SG = Generator_with_Refin(encoder_SG)        self.model_SG.load_state_dict(torch.load(model_path_SG))        self.model_SG.to(device)    def forward(self, tensor_att, tensor_SG):        self.model_att.eval()        with torch.no_grad():            robject_rshadow_tensor = self.model_att(tensor_att)        robject_rshadow_np = robject_rshadow_tensor.cpu().numpy()        robject_rshadow_np[robject_rshadow_np >= 0.5] = 1        robject_rshadow_np[robject_rshadow_np < 0.5] = 0        robject_rshadow_np = 2 * (robject_rshadow_np - 0.5)        robject_rshadow_tensor = torch.cuda.FloatTensor(robject_rshadow_np)        tensor_SG = torch.cat((tensor_SG, robject_rshadow_tensor), axis=1)        self.model_SG.eval()        with torch.no_grad():            output_mask1, output_mask2 = self.model_SG(tensor_SG)        result = torch.add(tensor_SG[:,:3, ...], output_mask2)        return result, output_mask2

Далее приведём сам код инференса.

Инференс
# укажем пути до данных и чекпоинтовdataset_path = '/content/arshadowgan/uploaded'result_path = '/content/arshadowgan/uploaded/shadow'path_att = '/content/drive/MyDrive/ARShadowGAN-like/attention.pth'path_SG = '/content/drive/MyDrive/ARShadowGAN-like/SG_generator.pth'# объявим датасет и даталоадерdataset = ARDataset(dataset_path, augmentation=get_validation_augmentation(256), preprocessing=get_preprocessing(), is_train=False)dataloader = DataLoader(dataset, batch_size=1, shuffle=False, num_workers=0)# определим устройствоdevice = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')# объявим полную модельmodel = ARShadowGAN(    encoder_att='resnet34',    encoder_SG='resnet18',    model_path_attention=path_att,    model_path_SG=path_SG,    device=device)# переведем ее в режим тестированияmodel.eval()# предсказаниеfor i, data in enumerate(dataloader):    tensor_att = torch.cat((data[0][:, :3], torch.unsqueeze(data[1][:, -1], axis=1)), axis=1).to(device)    tensor_SG = torch.cat((data[2][:, :3], torch.unsqueeze(data[3][:, -1], axis=1)), axis=1).to(device)    with torch.no_grad():        result, shadow_mask = model(tensor_att, tensor_SG)        shadow_mask = np.uint8(127.5*shadow_mask[0].cpu().numpy().transpose((1,2,0)) + 1.0)        output_image = np.uint8(127.5 * (result.cpu().numpy()[0].transpose(1,2,0) + 1.0))        cv2.imwrite(osp.join(result_path, 'test.png'), output_image)        print('Результат сохранен: ' + result_path + '/test.png')

Заключение

В данной статье рассмотрена генеративно-состязательная сеть на примере решения одной из амбициозных и непростых задач на стыке Augmented Reality и Computer Vision. В целом полученная модель умеет генерировать тени, пусть и не всегда идеально.

Отмечу, что GAN это не единственный способ генерации тени, существуют и другие подходы, в которых, например, используются техники 3D-реконструкции объекта, дифференцированный рендеринг и т.п.

Весь приведенный код в репозитории, примеры запуска в Google Colab ноутбуке.

P.S. Буду рад открытой дискуссии, каким-либо замечаниям и предложениям.

Спасибо за внимание!

Подробнее..

Перевод Генеративная зоология с нейронными сетями

05.07.2020 14:08:58 | Автор: admin
image

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

image

Примеры изображений, созданных GAN

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

Некоторое время спустя в моем любимом подкасте была упомянута PhyloPic база данных силуэтных изображений животных, растений и других жизненных форм. Размышляя над этими строками, я задался вопросом что получится, если обучить систему, вроде той, что описана в статье Прогрессивные GAN, на очень разнообразном наборе подобных данных? Получится множество разновидностей нескольких известных типов животных, или мы получим множество вариаций, которое породит спекулятивную зоологию, управляемую нейронными сетями? Как бы все ни складывалось, я был уверен, что смогу получить из этого несколько хороших рисунков для моей учебной стены, поэтому я решил удовлетворить свое любопытство экспериментом.

Your browser does not support HTML5 video.

Я адаптировал код из статьи о прогрессивных GAN и обучил модель с помощью 12000 итераций, используя мощности Google Cloud (8 графических процессоров NVIDA K80) и весь набор данных PhyloPic. Общее время обучения, включая некоторые ошибки и эксперименты, составило 4 дня. Я использовал окончательную обученную модель для создания 50-килобайтных отдельных изображений, а затем потратил часы на просмотр результатов, категоризацию, фильтрацию и сопоставление изображений. Я также немного редактировал некоторые изображения, повернув их, чтобы все существа были направлены в одну и ту же сторону (чтобы добиться визуального удовлетворения). Этот практический подход означает, что то, что вы видите ниже это своего рода коллаборация между мной и нейронной сетью это была творческая работа, и я вносил в нее свои правки.

image

Летающие насекомые

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

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

image

Птицы

image

Четвероногие

image

Динозавры

image

Рыбы

image

Жуки

image

Гоминиды

Странные вещи



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

image

Чудища

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

image

Абстрактные существа

image

Неидентифицируемые

Случайный отбор


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

image

image

Узнайте подробности, как получить востребованную профессию с нуля или Level Up по навыкам и зарплате, пройдя платные онлайн-курсы SkillFactory:



Читать еще


Подробнее..

Перевод Как удалить татуировку с помощью глубокого обучения

20.04.2021 16:13:49 | Автор: admin

Глубокое обучение интересная тема и моя любимая область исследований. Мне очень нравится играть с новыми исследовательскими разработками специалистов по глубокому обучению. Я только что наткнулся на удивительный репозиторий GitHub одного из моих товарищей по группе компьютерного зрения. Мне он так понравился, что я решил поделиться им. Основа репозитория генеративно-состязательная сеть (GAN), которая способна удалять татуировки с тела. Я расскажу вам шаг за шагом, как применять упомянутый репозиторий на примере фотографии в Pexels.


Запуск Google Colab

Google Colab бесплатно предоставляет мощные возможности графического процессора, чтобы выполнять или обучать наши модели глубокого обучения за меньшее время. Введите в браузере следующий URL-адрес и нажмите клавишу Enter:

https://colab.research.google.com/

После запуска войдите в свою учётную запись Google. Если вы уже вошли, платформа просто выберет основную учётную запись для входа. Не беспокойтесь! Ваши данные здесь в безопасности. После входа перейдите к файлу и откройте новую записную книжку.

Клонирование репозитория GitHub

Теперь в только что созданной записной книжке мы должны выполнить такую команду:

!git clone https://github.com/vijishmadhavan/SkinDeep.git SkinDeep
Эта команда клонирует код GitHub в вашу среду Colab.Эта команда клонирует код GitHub в вашу среду Colab.

Теперь, на следующем шаге, мы должны использовать клонированный репозиторий. Для этого в соседней ячейке записной книжки выполните эту команду:

cd SkinDeep

Установка библиотек

Чтобы установить все необходимые библиотеки, в очередной ячейке выполните:

!pip install -r colab_requirements.txt

Определение архитектуры модели

Теперь настало время инициализировать архитектуру модели. Архитектура доступна в том же репозитории GitHub, который мы клонировали. Чтобы инициализировать модель, в соседней ячейке выполните следующий код:

import fastaifrom fastai.vision import *from fastai.utils.mem import *from fastai.vision import open_image, load_learner, image, torchimport numpy as npimport urllib.requestimport PIL.Imagefrom io import BytesIOimport torchvision.transforms as Tfrom PIL import Imageimport requestsfrom io import BytesIOimport fastaifrom fastai.vision import *from fastai.utils.mem import *from fastai.vision import open_image, load_learner, image, torchimport numpy as npimport urllib.requestimport PIL.Imagefrom io import BytesIOimport torchvision.transforms as Tclass FeatureLoss(nn.Module):    def __init__(self, m_feat, layer_ids, layer_wgts):        super().__init__()        self.m_feat = m_feat        self.loss_features = [self.m_feat[i] for i in layer_ids]        self.hooks = hook_outputs(self.loss_features, detach=False)        self.wgts = layer_wgts        self.metric_names = ['pixel',] + [f'feat_{i}' for i in range(len(layer_ids))              ] + [f'gram_{i}' for i in range(len(layer_ids))]    def make_features(self, x, clone=False):        self.m_feat(x)        return [(o.clone() if clone else o) for o in self.hooks.stored]        def forward(self, input, target):        out_feat = self.make_features(target, clone=True)        in_feat = self.make_features(input)        self.feat_losses = [base_loss(input,target)]        self.feat_losses += [base_loss(f_in, f_out)*w                             for f_in, f_out, w in zip(in_feat, out_feat, self.wgts)]        self.feat_losses += [base_loss(gram_matrix(f_in), gram_matrix(f_out))*w**2 * 5e3                             for f_in, f_out, w in zip(in_feat, out_feat, self.wgts)]        self.metrics = dict(zip(self.metric_names, self.feat_losses))        return sum(self.feat_losses)        def __del__(self): self.hooks.remove()

Загрузка файла модели

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

MODEL_URL = "https://www.dropbox.com/s/vxgw0s7ktpla4dk/SkinDeep2.pkl?dl=1"urllib.request.urlretrieve(MODEL_URL, "SkinDeep2.pkl")path = Path(".")learn=load_learner(path, 'SkinDeep2.pkl')

Входное изображение

Наконец, можно определить своё входное изображение для тестирования. В приведённом ниже сегменте кода подставьте URL-адрес изображения.

url = 'https://images.pexels.com/photos/5045947/pexels-photo-5045947.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260' #@param {type:"string"}response = requests.get(url)img = PIL.Image.open(BytesIO(response.content)).convert("RGB")img_t = T.ToTensor()(img)img_fast = Image(img_t)show_image(img_fast, figsize=(8,8), interpolation='nearest');

Тестирование модели и получение результатов

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

p,img_hr,b = learn.predict(img_fast)Image(img_hr).show(figsize=(8,8))

Заключение

Вот и всё. Мы обсудили пошаговое реальное применение модели SkinDeep для удаления татуировок с кожи. Подобные забавы лишь малая демонстрация потенциала глубокого обучение. Оно способно способно генерировать новые функции без вмешательства человека, из ограниченного набора функций, расположенных в наборе учебных данных. Для специалистов это означает, что они могут использовать более сложные наборы функций по сравнению с традиционным ПО для машинного обучения. Если вас заинтересовала эта сфера ждем вас на расширенном курсе Machine Learning и Deep Learning, в котором мы совместили изучение DL с классическим курсом по ML, чтобы студент начал с основ и постепенно перешел к более сложным вещам.

Узнайте, как прокачаться и в других специальностях или освоить их с нуля:

Другие профессии и курсы
Подробнее..

Нейродайджест главное из области машинного обучения за май 2021

03.06.2021 14:23:54 | Автор: admin

Управляемые складки одежды и морщины, фотореалистичные симуляции вождения, естественное освещение объектов при смене фона, китайский аналог DALL-E и многое другое: встречайте подборку самых интересных исследований и нейросетевых моделей, которые появились в прошедшем месяце.

DECA

Доступность: страница проекта / статья / репозиторий

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

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

Garment Collision Handling

Доступность: страница проекта / статья

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

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

DriveGAN

Доступность: страница проекта / статья

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

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

Enhancing Photorealism Enhancement

Доступность: страница проекта / статья / репозиторий

Пока приходится ждать симулятор езды от NVIDIA, можно развлечь себя с помощью разработок от исследователей из Intel. Они представили подход к повышению реалистичности синтетических изображений. Сверточная нейросеть использует промежуточные представления, созданные обычными пайплайнами рендеринга, что позволяет добиться фотореалистичной картинки в GTA V.

CogView

Доступность: онлайн-демо / статья / репозиторий

Новая нейросеть для перевода текста в изображение. В основе модели трансформер на 4 миллиарда параметров и токенизатор VQ-VAE. Создатели утверждают, что их модель работает лучше DALL-E от OpenAI, и в статье также делятся подходом к файнтюнингу модели для решения других задач вроде обучения стилю, улучшению разрешения, а также стабилизации предварительного обучения.

Попробовать модель можно уже сейчас, правда онлайн-демо пока понимает только текст на китайском.

Expire-Span

Доступность: публикация в блоге / статья / репозиторий

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

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

Wav2Vec-U

Доступность: публикация в блоге / статья / репозиторий

Есть много моделей распознавания речи, которые превосходно справляются с распространенными языками. Но множество диалектов все еще не поддерживаются этими технологиями. Это связано с тем, что высококачественные системы необходимо обучать с использованием большого количества размещенных аудиозаписей. Исследователи FAIR представили версию модели wav2vec-U, которая обучается без учителя и вообще не требуют размеченных данных.

Rethinking Style Transfer

Доступность: страница проекта / статья / репозиторий

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

Relit

Доступность: страница проекта / статья

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

Total Relighting

Доступность: страница проекта / статья

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

Omnimatte

Доступность: страница проекта / статья

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

DeepFaceEditing

Доступность: страница проекта / репозиторий

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

StyleMapGAN

Доступность: репозиторий

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

GPEN

Доступность: онлайн-демо / статья / репозиторий

Китайские исследователи из Alibaba представили модель для реставрации размытых фото низкого качества, который в отличие от методов на основе GAN, создает не чрезмерно сглаженные изображения. Для этого модель использует GAN, чтобы сгенерировать высококачественное изображения лица, которое предварительно декодируется с помощью U-образной DNN.

CodeNet

Доступность: репозиторий

Исследователи из IBM представили крупнейший открытый датасет для проведения бенчмарков с участием программного кода. Набор данных содержит 500 миллионов строк кода на 55 языках программирования, включая C ++, Java, Python, Go, COBOL, Pascal и FORTRAN. CodeNet фокусируется на обнаружении сходств и отличий кода, чтобы продвигать разработку систем, которые смогут автоматически переводить код с одного языка программирования на другой.

DatasetGAN

Доступность: страница проекта / статья

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

Golos

Доступность: репозиторий

Исследователи из Сбера опубликовали датасет с русским корпусом, подходящий для исследования речи. Набор данных в основном состоит из записанных и вручную размеченных аудиофайлов. Общая продолжительность аудиозаписи около 1240 часов.

В мае стали доступны:

На этом все, спасибо за внимание и до встречи в следующем месяце!

Подробнее..

Лучшие гаджеты CES 2021, часть 1. Собираем умный дом

28.01.2021 12:17:06 | Автор: admin

Коронавирус внес свои коррективы во все. Вот и ежегодная выставка потребительской электроники Consumer Electronics Show в 2021, впервые в истории, прошла не живьем, а в интернете.

Для CES это, скорее, недостаток. Магия шоу заключается именно в живых демонстрациях продуктов: когда можно надеть VR-шлем на голову и протестировать виртуальность, поездить на самоходном чемодане, потрогать прототипы устройств другой набор ощущений. Проще понять, что взлетит, а что нет. Красивые презентации научились делать все, а хорошие продукты создают единицы.

Тем не менее, команда Madrobots годами охотится за гаджетами по всему миру. Сменой формата нас не остановить. Мы взяли виртуальные билеты на CES 2021 и нашли несколько десятков интересных стартапов и проектов.

Умный диспенсер для вина Albi Home M+

  • подает вино идеальной температуры;

  • позволяет использовать открытую бутылку вина до 6 месяцев;

  • управляется со смартфона.

  • совместим с голосовыми ассистентами.

Итальянский стартап Albicchiere виртуально привез умный диспенсер для вина Albi Home M+. Работает так: переливаешь вино из открытой бутылки в умный пакет, умный пакет помещается в специальный гаджет, который охлаждает пакет до идеальной температуры подачи. Затем вино можно наливать из диспенсера в бокал. Пакет вакуумный, внутрь не попадает кислород, так что вино остается в первозданном состоянии 6 месяцев. К диспенсеру прилагаются расходники запечатанные пакеты с вином.

У гаджета есть приложение, которое управляет им и предлагает, что подать к вину. Гаджет подключается к амазоновской Алексе. То есть, теперь можно сказать внимательной виртуальной помощнице: Алекса, налей мне тещиного погребка, пожалуйста!, и она послушается.

Сайт: albicchiere.com

Робот-пивоварня Beermkr

  • автоматизирует пивоварение и ферментацию;

  • требует всего 10 минут для запуска;

  • готовый напиток за 1012 дней;

  • управляется со смартфона.

В России никого не удивишь самодельным алкоголем, но Beermkr, пожалуй, порадовал бы и продвинутых мастеров со сложными установками для ферментации и перегонки. Потому что работает просто и выглядит, как кофемашина. Аппарат можно поставить на кухне, он не привлечет лишнего внимания. Загружаешь заготовленные либо свои ингредиенты, ждешь чуть больше недели, и можно наливать бокал своего, свежего, особенного. Beermkr управляется со смартфона и все процессы проходят внутри автономно. Два в одном: и удобно, и есть поле для экспериментов.

Цена вопроса: 579 $.

Сайт: beermkr.com

Умный интерком Buzr

  • устанавливается за 15 минут без лишних сложностей;

  • работает через приложение из любой точки планеты;

  • выдает виртуальные ключи для друзей, нянек, сантехника и так далее;

  • интегрируется с сервисами доставки, вы не пропустите курьера;

  • записывает каждого гостя

Buzr крайне полезный гаджет. Это умный интерком, который интегрируется с существующими системами интеркомов и позволяет удаленно открывать двери родным, друзьям, курьерам, нянькам и так далее. Открывать дверь можно прямо со смартфона из любого места, где есть интернет, и со смартфона же говорить с гостями. Создатели утверждают, что установить обновку можно за 15 минут, не трогая существующие системы. Buzr работает более чем с 2 000 тысячами моделей глупых интеркомов. Мы пока не знаем, можно ли его подружить с российскими домофонами и интеркомами. Возможно, это только мечта о будущем, которую кому-то предстоит воплотить.

Цена вопроса: 229 $.

Сайт: getbuzr.com

AiS All-in-Sensor сенсор все в одном для умного дома

  • 12 типов датчиков в одном корпусе, от влажности и дыма до наклона и разбивания стекла;

  • защищает данные от утечек;

  • напрямую подключается к смартфону;

  • работает без интернета и внешних сервисов.

Что может быть лучше умного датчика для умного дома? Такой умный датчик, который не отдает ваши личные данные, куда попало, и работает, не подключаясь к плохо защищенным серверами в Китае. Это AiS All-in-Sensor, а еще к его достоинствам относится широкий спектр того, что он умеет обнаруживать:

  • возгорание;

  • утечку газа;

  • влажность;

  • вибрацию;

  • попытку открыть или разбить окно;

  • освещенность.

Он умеет подключаться к смартфону и умному дому. И умеет управлять электрическими приборами. Звучит как волшебство, но это просто сенсор-мультитул.

Цена вопроса: 198 $

Сайт: direct-smarter.com/en/

Умный навесной замок Igloohome Smart Padlock

  • подойдет для велосипедов, ворот и так далее;

  • работает без ключей, с ПИН-кодами;

  • можно выдавать временный доступ другим людям;

  • доступ выдается удаленно, через приложение или SMS;

  • замок водостойкий, не боится дождя.

Умные замки вещь удобная, особенно когда их создатели подумали, как и в каких условиях замками пользуются. Инженеры стартапа Igloocompany подумали! Поэтому навесной замок Igloohome Smart Padlock это мощный, водо- и пылестойкий, защищенный от попыток вскрыть отсек с батарейкой и вскрыть себя девайс. Замок открывается пин-кодом, который нужно набрать кнопками на замке, либо приложением со смартфона. Коды можно генерировать для близких, гостей или сантехников с курьерами. Они будут временными или постоянными, по желанию владельца замка, и их можно запросить СМС-кой, а не через приложение. На одной батарейке CR2 замок продержится год.

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

Цена вопроса: 129 $

Сайт: igloohome.co

Переработчик мусора Lasso

  • перерабатывает мусор прямо на дому;

  • моет перерабатываемое;

  • перемалывает и хранит в готовом к повторному использованию виде;

  • работает со стеклом, металлом и пластиком.

В мире бесконечных свалок каждый обязан сделать свой вклад в сохранение окружающей среды или хотя бы сэкономить деньги на вывозе мусора. Альдус Хикс, сооснователь Lasso Loop Recycling, уверен, что именно стоимость материалов, которые мы беспечно возим на свалки, сыграет роль в том, чтобы они исчезли. Lasso это домашняя мусороперерабатывающая станция, которая сортирует, очищает, а затем измельчает мусор. На выходе получаются готовые к использованию в производстве гранулы. Проект пока что находится на стадии многообещающего, работающего прототипа. Чем скорее он станет общедоступным, тем лучше.

Сайт: lassoloop.com

Умный супермощный блендер Millo

  • приложение и прошивка обновляются и расширяют функционал прибора;

  • компактный и не громоздкий, легко моется и раскладывается;

  • беспроводной, работает от встроенной батареи, можно свободно перемещаться по кухне;

  • тихий.

Блендер Milo похож на летающую тарелку, поэтому на кухне будет смотреться в самый раз. К беспроводной подставке примагничиваются сменные чаши с лезвиями. Затем магнитный двигатель приводит в движение лезвия, разгоняя их до 14 000 оборотов в минуту. С Milo можно делать все то же самое, что и с обычным блендером: смузи, колоть лед, готовить ингредиенты для блюд. Удобнее мыть, хранить и использовать.

Millo Appliances литовский стартап, и сборка первых блендеров осуществляется на территории страны.

Цена вопроса: 399

Сайт: getmillo.com

Умный датчик дыма, угарного газа, движения, шума и влажности Owl Home

  • решение все-в-одном: один датчик вместо нескольких;

  • сообщает о возгораниях и проникновениях в дом;

  • сигнализация и резервное питание;

  • совместим с системами умного дома.

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

Цена вопроса: 139 $

Сайт: https://getowlhome.com/

Умная колыбель Cradlewise

  • анализирует паттерны сна;

  • автоматически укачивает проснувшегося ребенка;

  • встроенный детский монитор;

  • есть приложение;

  • в приложении доступна аналитика сна;

  • подходит для малышей от рождения до 2 лет.

Умная колыбель Cradlewise ответ на колыбель SNOO, победившую на CES-2017. Это тоже умная детская мебель, которая укачивает ребенка, однако делает это если верить создателям лучше и эффективнее. Cradlewise подходит для детей до 2 лет, ребенка не надо пеленать, чтобы он мог в ней качаться, а еще она удаленно отслеживает состояние младенца.

Стоит очень дорого. Однако гонка начата, и теперь дело времени, пока та же Xiaomi выпустит похожий, доступный продукт.

Цена вопроса: 1999$

Сайт: cradlewise.com

Сверхмощные зарядки Omega ChargeAsap

  • ЗУ второго поколения на основе нитрида галлия (GaN);

  • три размера: 65W, 100W & 200W

  • поддерживают стандарты быстрой зарядки всех производителей;

  • можно одновременно заряжать два Макбука на полной скорости;

  • графеновая мембрана для теплоотвода;

  • в комплекте есть переходники для розеток основных мировых стандартов;

  • порты USB-C/USB-A.

Кажется, лишь недавно нитрид галлия был поводом для журналистов словить хайп: мини-революция в полупроводниках, как-никак! А теперь это уже вполне себе отработанная технология. Omega ChargeAsap одна из ласточек новой посткремниевой эры, маленькая, но мощная зарядка. Воткнул такую в одну розетку, и заряжаешь сразу все ноутбуки и смартфоны в доме.

И коврики и кредлы для беспроводных зарядок тоже.

Цена вопроса: от 99$.

Сайт: chargeasap.com

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

Подробнее..

Как группа энтузиастов и нейросеть StyleGAN2 сделали выставку современного искусства в Петербурге

12.04.2021 14:15:58 | Автор: admin
Привет, Хабр! До 15 апреля в Центре современного искусства имени Сергея Курехина проходит выставка Ивана Плюща После всего / После всех. Часть 1. После стула. В соавторы художник взял нейросеть GAN. Под катом рассказываем, как мы делали эту выставку. Осторожно, много картинок.




Зачем делали выставку


Нейросеть всего лишь алгоритм, который написан людьми для выполнения определенных целей и задач, но людям, далеким от IT, это не так хорошо понятно.

Вокруг термина Искусственный интеллект сейчас много хайпа. Медиа подливают масла в огонь заголовками вроде AI is the future of everything, AI will change everything или Искусственный интеллект захватит мир. Этим проектом мы хотели, среди прочего, повысить осведомленность людей. Подчеркнуть, что искусственный интеллект это не угроза и не магия, которая выдает абсолютно случайный результат или, напротив, единственное верное решение. В текстах о выставке мы обязательно говорим, что используем не просто искусственный интеллект, а генеративно-состязательную нейросеть (Generative adversarial network, сокращённо GAN) и по возможности стараемся объяснить общие принципы ее работы.

Современный человек часто сталкивается с применениями machine learning. Взять те же соцсети, которые изучают наше поведение и показывают то, что, по их мнению, мы хотели бы видеть. Выглядит безобидно, но рождает реальные социальные проблемы. Подробнее например, в этой документалке: The Social Dilemma (Netflix, 2020 год).

Мы считаем, что подобные вещи нужно осмыслять, обсуждать и совместно вырабатывать новые формы цифровой этики. И для обсуждения нужны все! Ничего не получится, пока широкая аудитория представляет себе угрожающий black box, в котором творится что-то, и который скоро нас всех заменит. Искусство один из способов показать, что из себя представляет этот загадочный искусственный интеллект.

Чем вдохновлялись


Конечно, не нам первым пришло в голову использовать GAN. В 2018 году на аукционе Christies за $432,500 был продан Портрет Эдмонда Бэлами, созданный нейросетью.



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

Есть и другие проекты, их много. Лично нам больше всего нравятся работы Тревора Паглена. Всем этим заинтересовался петербургский художник Иван Плющ. Совместно мы подобрали датасеты картинок на разные темы и прогнали их через StyleGAN2. Иван смотрел на то, что получается, использовал эти результаты как эскизы и создавал живопись на их основе.

Теперь подробнее.

Как создавали работы


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



Получалось примерно так:


Для создания работ из этой серии использовали StyleGAN2 и, с недавних пор, StyleGAN2-ada: генеративно-состязательные сети, разработанные компанией nVidia и дающие на текущий момент одни из лучших результатов. Вещь не новая. Те, кто следит за развитием нейросетей или за медиа-искусством наверняка натыкались как минимум на проект https://thispersondoesnotexist.com/, который работает на основе этих сеток.

На результаты смотрел Иван Плющ и рисовал на холсте такое:


Холст, масло, акрил. 159 x 129 см.


Холст, масло, акрил. 180 x 180 см


Холст, масло, акрил. 85 x 85 см

Всего сделали несколько серий: После стула (сюда относятся три предыдущие картинки), После меня, После тебя, После войны, После оружия, После религии, После политики и другое.

Вот картина из серии После порно:

Холст, акрил, масло. 180 x 180 см



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


Как подготовили помещение


Найти помещение нам помогли связи куратора и тот факт, что Иван Плющ достаточно известный художник (его работы сейчас показывают, например, в Русском музее на выставке Поколение тридцатилетних). Выставку договорились провести в Центре современного искусства имени Сергея Курёхина. Это авторитетная в арт-тусовке организация.

Под выставку предоставили 6 комнат общей площадью около 400 квадратных метров. Там мы показываем 25 произведений Ивана и отдельно результаты работы StyleGAN2. Часть картинок, созданных нейросетью, распечатали и развесили на экспозиции, часть собрали в видео, которое проецируется на стены.


Полное название выставки После всего / После всех. Часть 1. После стула. Стул это нечто понятное, знакомое и максимально простое. На этом примере было удобно объяснять, как работали над проектом. А еще такая тема отсылает к знаковому для современного искусства произведению Джозефа Кошута, который показал на выставке настоящий стул, фотографию этого стула и определение слова стул из словаря. На этом примере обычно объясняют, что такое концептуализм. Почитать можно здесь.

Планируем после Петербурга показать проект в Москве и в Нижнем Новгороде: это будут следующие части проекта.


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


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

Вряд ли посетитель уйдет с выставки с ощущением гармонии и спокойствия, которые может подарить классическое искусство. Скорее всего, возникнут вопросы Как это понять и интерпретировать?. Прямо как с нейронными сетями. Если зритель унесет с собой любопытство, легкое беспокойство, а в идеале пойдет гуглить про GAN, мы будем считать, что нам удалось воплотить то, что мы задумали.

Выставка работает в Центре современного искусства имени Сергея Курехина (Санкт-Петербург, Лиговский пр., 73, 4 этаж) до 15 апреля. Приходите!

Художник: Иван Плющ
Куратор выставки: Марина Альвитр
Продюсер: Иван Пузырев
Техническая поддержка: Артем Коневских
Помогали: Анна Падаляко, Дарья Елизарова, Евгений Новиков
Написала статью: Дарья Яковенко
Подробнее..

Deepfake. Реализация технологии замены лиц в видео. Часть 1

25.05.2021 18:15:50 | Автор: admin

Всем привет! Я работаю с генеративными моделями, одним из моих проектов является разработка DeepFake. Планирую создать несколько статей про этот проект. Данная статья является первой, в ней я рассмотрю архитектуры которые можно использовать, их достоинства и недостатки. Из существующих подходов к созданию DeepFake можно выделить следующие:

  • Архитектура основанная на схеме кодер-декодер

  • Генеративно-состязательные сети(GAN)

Энкодер-декодер архитектура

К данной подходу можно отнести методы генерации основанные на автоэнкодерах. Их объединяет использование пиксельных(изображение на входе попиксельно сравнивается с изображением на выходе, оптимизируется MSE,MAE и т.п. функции потерь) потерь, что определяет достоинства и недостатки и данной схемы. Плюс в том, что автоэнкодеры относительно просто(в сравнении с GAN) обучать. Минус - оптимизация по пиксельным метрикам не позволяет добиться фотореалистичности, сравнимой с другими методами(опять же в сравнении GAN). Первым вариантом, который я рассмотрел, была схема с двумя декодерами. Подробнее можно ознакомиться здесь .Архитектура представлена на рисунке ниже.

Идея данного подхода в следующем: используется энкодер-декодер схема, при этом энкодер один, а декодеров используется несколько, причем каждый декодер может создавать изображения только одного человека. Данный подход огранивает использование модели, требуя обучать отдельный декодер для каждого нового человека которого мы хотим сгенерировать. Для некоторых сценариев это не критично, например при производстве рекламных роликов это не будет проблемой. Но например для приложения, где пользователь хочет создать DeepFake с своим участием, такая схема не подходит, т.к. вряд ли пользователю понравится ждать несколько часов, пока под него обучается отдельный декодер. К достоинствам можно отнести относительную компактность - в отличие от других подходов, не нужно создавать и обучать(или искать готовые и адаптировать) дополнительные сетки для кодирования внешности. Но ограничение "каждому человеку - отдельный декодер" ограничивает применение данной архитектуры, поэтому рассмотрим следующую.

Condtional Autoencoder

Схема представлена на рисунке ниже. Больше информации можно найти здесь.

Conditional VAE architectureConditional VAE architecture

Идея данного подхода в следующем: на вход энкодера подается изображение и вектор атрибутов. На вход декодера поступает вектор размерности latent_dim являющейся выходом энкодера и вектор атрибутов. Атрибутом может быть любой признак характеризующий изображение, например наличие/отсутствие улыбки(и других эмоций), повернута голова вправо или влево, внешность человека и т.п. Т.к. вектор атрибутов поступает в декодер напрямую, энкодеру нет смысла извлекать из входного изображения эту информацию. Т.е. например, если в atrributes vectors задать внешность человека, то энкодер будет кодировать только свойства изображения не связанные с внешностью, такие как поворот и наклон головы, эмоции, освещение и т.п. И если к выходу энкодера добавить atrributes vectors другого человека, декодер восстановит картинку, где поворот головы и эмоции будут соответствовать исходному изображению, а внешность будет от другого человека. В данной схеме не требуется обучать отдельный декодер для каждого нового человека, которого мы хотим генерировать, т.к. замена внешности реализуется изменением atrributes vectors подаваемого в декодер. Примеры восстановленных изображений с помощью данного подхода можно увидеть на рисунке ниже.

Autoencoder reconstructionAutoencoder reconstruction

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

Conditional GAN

В GAN генератор получает на вход случайный вектор, и возвращает сгенерированное изображение. Т.к. в DeepFake изображение должно обладать рядом заданных свойств(обычно - внешность от одного человека, поворот головы и эмоции от другого), рассмотрим Conditional GAN. Сравнение GAN и Conditional GAN архитектур представлено на рисунке ниже. Рисунок взят из этой статьи, там можно найти больше информации.

GAN vs Conditional GANGAN vs Conditional GAN

В классическом GAN, генератор получает на вход случайный вектор и генерирует изображение. При этом изображение является случайным, т.е. неизвестно сгенерируется женщина или мужчина, будет смотреть прямо или направо, будет улыбаться или грустить. В Conditional Gan на вход генератору подается два вектора - случайный вектор Z и вектор атрибутов(на приведенной схеме обозначен label). Дискриминатор получает на вход изображение и соответствующий вектор атрибутов. В процессе обучения генератор учиться создавать не только реалистичные изображения, но и соответствующие вектору атрибутов. Результаты которые я получил с использованием Conditional GAN представлены на рисунке ниже.

Conditional GANConditional GAN

Изображения сгруппированы по парам(слева - сгенерированные, справа - оригинал). В attributes vectors задаются поворот и наклон головы, эмоции и внешность человека. Xорошо видна способность GAN генерировать фотореалистичные изображения. Если сравнить сгенерированные картинки с рассмотренными ранее результатами автоэнкодеров, создается впечатление, что GAN выигрывает в одни ворота. Но не нужно забывать, что конечная цель - DeepFake, а это генерация не одной картинки, а видео. В следующей части я расскажу как перейти от генерации отдельных изображений к созданию видео. Последняя сборка DeepFake на основе GAN выглядит следующим образом:

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

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru