Aprendizaje de representación no supervisado con adversario generativo convolucional profundo (小白 学 GAN 六)

Enlace original: https://arxiv.org/pdf/1511.06434.pdf

Introducción

Idea central : todas las GAN de la serie anterior utilizaron la estructura DNN para generar datos, pero este artículo comenzó a introducir la estructura de convolución en el procesamiento de imágenes .

Como se muestra en la figura anterior, el generador GAN de este artículo no usa FC (Capa completamente conectada), pero usa la deconvolución para expandir gradualmente las dimensiones H y W, y reducir gradualmente la dimensión C para generar el gráfico de resultado final. Entre ellos, el primer paso es proyectar y deformar un ruido aleatorio unidimensional en un mapa de características de 4x4x1024. El método específico es usar la capa FC para convertir el vector cuya salida se establece en 8192 en un mapa de características de 4x4x1024. En segundo lugar, este artículo también mencionó el uso de GAN en métodos de aprendizaje no supervisados, y exploró la relación entre el ruido insertado por el generador y los resultados generados, y también visualizó las características internas del discriminador.

estructura basica

Upsampling

En el campo del aprendizaje profundo aplicado en la visión por computadora, debido a que la imagen de entrada es extraída por la red neuronal convolucional (CNN), el tamaño de salida tiende a reducirse y, en ocasiones, es necesario restaurar la imagen a su tamaño original para realizar más cálculos. Esta operación de expandir el tamaño de la imagen para realizar el mapeo de la imagen desde una resolución pequeña a una resolución grande se llama Upsample.

Deconvolución

La deconvolución también se llama convolución transpuesta. No es un proceso inverso completo de convolución directa. Se explica en una frase: la
deconvolución es una convolución directa especial, que primero se llena con 0 según una cierta proporción. Para expandir el tamaño de la imagen de entrada, luego gire el núcleo de convolución y luego realice la convolución hacia adelante.

H_ {out} = (H_ {in} -1) stride [0] -2padding [0] + kernelsize [0] + outputpadding [0]

 

Aprendizaje sin supervisión

Este artículo concibe una estrategia de aprendizaje no supervisada basada en la naturaleza de las redes GAN que no requieren etiquetas: cuando el generador de la red GAN puede generar datos más realistas, también demuestra que su discriminador también ha aprendido la representación relacionada con los datos, entonces Eliminamos la última capa FC del discriminador para obtener un extractor de características de datos. Para verificar si este extractor de características puede extraer de manera efectiva las características de los datos, podemos usar las características extraídas para clasificar y luego comparar las etiquetas de clasificación reales para evaluar el rendimiento del extractor de características. En el artículo, el autor utilizó los conjuntos de datos CIFAR-10 y SVHN para realizar experimentos, y los resultados son los siguientes:

Visualización

Investigación sobre el estado oculto del generador

El autor observa la forma de la imagen final generada cambiando algunos dígitos en el ruido de entrada zy utilizando el método de variable controlada. Se encuentra que los dígitos específicos pueden controlar la representación morfológica específica en la imagen generada. Según el cambio en el tamaño del valor digital, el resultado de la imagen generada es el siguiente:

Incluso para el vector de entrada, la "operación" de la imagen se puede controlar mediante "operación":

Visualización de discriminador

Como se muestra en la figura anterior, el mapa de características de la izquierda es la visualización de la convolución interna del discriminador sin entrenamiento, y el mapa de características de la derecha es la visualización de la convolución interna del discriminador después del entrenamiento. Se puede ver claramente que el discriminador sí ha aprendido las características de los datos, lo que también confirma que la red GAN se puede utilizar en el aprendizaje no supervisado.

Código y práctica

Enlace de referencia ( https://github.com/eriklindernoren/PyTorch-GAN/blob/master/implementations/dcgan/dcgan.py )

import argparse
import os
import numpy as np
import math

import torchvision.transforms as transforms
from torchvision.utils import save_image

from torch.utils.data import DataLoader
from torchvision import datasets
from torch.autograd import Variable

import torch.nn as nn
import torch.nn.functional as F
import torch

os.makedirs("images", exist_ok=True)

parser = argparse.ArgumentParser()
parser.add_argument("--n_epochs", type=int, default=200, help="number of epochs of training")
parser.add_argument("--batch_size", type=int, default=64, help="size of the batches")
parser.add_argument("--lr", type=float, default=0.0002, help="adam: learning rate")
parser.add_argument("--b1", type=float, default=0.5, help="adam: decay of first order momentum of gradient")
parser.add_argument("--b2", type=float, default=0.999, help="adam: decay of first order momentum of gradient")
parser.add_argument("--n_cpu", type=int, default=8, help="number of cpu threads to use during batch generation")
parser.add_argument("--latent_dim", type=int, default=100, help="dimensionality of the latent space")
parser.add_argument("--img_size", type=int, default=32, help="size of each image dimension")
parser.add_argument("--channels", type=int, default=1, help="number of image channels")
parser.add_argument("--sample_interval", type=int, default=400, help="interval between image sampling")
opt = parser.parse_args()
print(opt)

cuda = True if torch.cuda.is_available() else False


def weights_init_normal(m):
    classname = m.__class__.__name__
    if classname.find("Conv") != -1:
        torch.nn.init.normal_(m.weight.data, 0.0, 0.02)
    elif classname.find("BatchNorm2d") != -1:
        torch.nn.init.normal_(m.weight.data, 1.0, 0.02)
        torch.nn.init.constant_(m.bias.data, 0.0)


class Generator(nn.Module):
    def __init__(self):
        super(Generator, self).__init__()

        self.init_size = opt.img_size // 4
        self.l1 = nn.Sequential(nn.Linear(opt.latent_dim, 128 * self.init_size ** 2))

        self.conv_blocks = nn.Sequential(
            nn.BatchNorm2d(128),
            nn.Upsample(scale_factor=2),
            nn.Conv2d(128, 128, 3, stride=1, padding=1),
            nn.BatchNorm2d(128, 0.8),
            nn.LeakyReLU(0.2, inplace=True),
            nn.Upsample(scale_factor=2),
            nn.Conv2d(128, 64, 3, stride=1, padding=1),
            nn.BatchNorm2d(64, 0.8),
            nn.LeakyReLU(0.2, inplace=True),
            nn.Conv2d(64, opt.channels, 3, stride=1, padding=1),
            nn.Tanh(),
        )

    def forward(self, z):
        out = self.l1(z)
        out = out.view(out.shape[0], 128, self.init_size, self.init_size)
        img = self.conv_blocks(out)
        return img


class Discriminator(nn.Module):
    def __init__(self):
        super(Discriminator, self).__init__()

        def discriminator_block(in_filters, out_filters, bn=True):
            block = [nn.Conv2d(in_filters, out_filters, 3, 2, 1), nn.LeakyReLU(0.2, inplace=True), nn.Dropout2d(0.25)]
            if bn:
                block.append(nn.BatchNorm2d(out_filters, 0.8))
            return block

        self.model = nn.Sequential(
            *discriminator_block(opt.channels, 16, bn=False),
            *discriminator_block(16, 32),
            *discriminator_block(32, 64),
            *discriminator_block(64, 128),
        )

        # The height and width of downsampled image
        ds_size = opt.img_size // 2 ** 4
        self.adv_layer = nn.Sequential(nn.Linear(128 * ds_size ** 2, 1), nn.Sigmoid())

    def forward(self, img):
        out = self.model(img)
        out = out.view(out.shape[0], -1)
        validity = self.adv_layer(out)

        return validity


# Loss function
adversarial_loss = torch.nn.BCELoss()

# Initialize generator and discriminator
generator = Generator()
discriminator = Discriminator()

if cuda:
    generator.cuda()
    discriminator.cuda()
    adversarial_loss.cuda()

# Initialize weights
generator.apply(weights_init_normal)
discriminator.apply(weights_init_normal)

# Configure data loader
os.makedirs("../../data/mnist", exist_ok=True)
dataloader = torch.utils.data.DataLoader(
    datasets.MNIST(
        "../../data/mnist",
        train=True,
        download=True,
        transform=transforms.Compose(
            [transforms.Resize(opt.img_size), transforms.ToTensor(), transforms.Normalize([0.5], [0.5])]
        ),
    ),
    batch_size=opt.batch_size,
    shuffle=True,
)

# Optimizers
optimizer_G = torch.optim.Adam(generator.parameters(), lr=opt.lr, betas=(opt.b1, opt.b2))
optimizer_D = torch.optim.Adam(discriminator.parameters(), lr=opt.lr, betas=(opt.b1, opt.b2))

Tensor = torch.cuda.FloatTensor if cuda else torch.FloatTensor

# ----------
#  Training
# ----------

for epoch in range(opt.n_epochs):
    for i, (imgs, _) in enumerate(dataloader):

        # Adversarial ground truths
        valid = Variable(Tensor(imgs.shape[0], 1).fill_(1.0), requires_grad=False)
        fake = Variable(Tensor(imgs.shape[0], 1).fill_(0.0), requires_grad=False)

        # Configure input
        real_imgs = Variable(imgs.type(Tensor))

        # -----------------
        #  Train Generator
        # -----------------

        optimizer_G.zero_grad()

        # Sample noise as generator input
        z = Variable(Tensor(np.random.normal(0, 1, (imgs.shape[0], opt.latent_dim))))

        # Generate a batch of images
        gen_imgs = generator(z)

        # Loss measures generator's ability to fool the discriminator
        g_loss = adversarial_loss(discriminator(gen_imgs), valid)

        g_loss.backward()
        optimizer_G.step()

        # ---------------------
        #  Train Discriminator
        # ---------------------

        optimizer_D.zero_grad()

        # Measure discriminator's ability to classify real from generated samples
        real_loss = adversarial_loss(discriminator(real_imgs), valid)
        fake_loss = adversarial_loss(discriminator(gen_imgs.detach()), fake)
        d_loss = (real_loss + fake_loss) / 2

        d_loss.backward()
        optimizer_D.step()

        print(
            "[Epoch %d/%d] [Batch %d/%d] [D loss: %f] [G loss: %f]"
            % (epoch, opt.n_epochs, i, len(dataloader), d_loss.item(), g_loss.item())
        )

        batches_done = epoch * len(dataloader) + i
        if batches_done % opt.sample_interval == 0:
            save_image(gen_imgs.data[:25], "images/%d.png" % batches_done, nrow=5, normalize=True)

prueba minist

Se puede observar que después de usar la estructura de convolución, la imagen generada por el generador converge más rápido, y la estructura del contenido de la imagen es más clara, y se usa la función LOSS de la GAN original. Si se introduce la "w divergencia", es una combinación fuerte Si puede producir mejores resultados.

Supongo que te gusta

Origin blog.csdn.net/fan1102958151/article/details/106396855
Recomendado
Clasificación