Representación interpretable Aprendizaje por información Maximización de la red de adversarios generativos (小白 学 GAN 八)

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

Introducción

Antecedentes: Desde GAN condicional agregando etiquetas a la entrada para controlar el tipo de datos generados, hasta GAN convencional profundo encontrando que el ruido aleatorio de la entrada puede controlar la forma de los datos generados. Ya podemos estar bastante seguros de que el ruido aleatorio de la entrada puede determinar los datos generados. Pero, ¿cómo se determina? ¿Cómo podemos utilizar este principio?

La idea central: divide el vector del generador de entrada en dos partes: una es ruido aleatorio z, y la otra es codificación de restricción latente c; luego c se usa para controlar los datos generados.

                                                            

Después de introducir el código de restricción c, estamos obligados a considerar una pregunta: ¿cómo hacer que el código de restricción c se corresponda con los datos generados? Aquí , la estructura del generador de este artículo es muy similar a Conditional GAN , e incluso si la c en la figura anterior se reemplaza con una etiqueta, entonces el generador de Info GAN degenerará en Conditional GAN . Son diferentes en el diseño del discriminador La GAN condicional envía la etiqueta restringida junto con la imagen al discriminador, porque la etiqueta analógica controlamos claramente la categoría y no necesita ser entrenado en base a ella. El vector de restricción de InfoGAN es una condición de restricción desconocida, que requiere entrenamiento. Por lo tanto, después de que el discriminador extrae las características, se usa una red Q (capa FC) para hacer una regresión del vector de restricción, y el vector de restricción introducido por el generador se calcula para que LOSS se optimice gradualmente.

estructura basica

Concepto basico

Entropía de información

Shannon propuso el concepto de "entropía de la información" y resolvió el problema de la medición cuantitativa de la información.

En la fuente de información lo que se considera no es la incertidumbre de un solo símbolo, sino la incertidumbre promedio de todas las situaciones posibles de esta fuente de información. Si el símbolo fuente tiene n valores: U1 ... Ui ... Un, la probabilidad correspondiente es: P1 ... Pi ... Pn, y la apariencia de varios símbolos es independiente entre sí. En este momento, la incertidumbre promedio de la fuente de información debe ser una incertidumbre de un solo símbolo: el promedio estadístico (E) de logPi, que se puede llamar entropía de información:

\ dpi {150} H (X) = - \ sum_X P (x) logP (x) = - \ int_ {x} P (x) logP (x) dx = - \ mathbb {E} _ {x \ sim X } [registro (p_i)]

El logaritmo en la fórmula es generalmente en base 2 y la unidad es un bit. Sin embargo, también se pueden usar otras bases logarítmicas y se pueden usar otras unidades correspondientes para la conversión entre ellas.

Información mutua

Puede verse como la cantidad de información sobre otra variable aleatoria contenida en una variable aleatoria , o la incertidumbre de una variable aleatoria debido al hecho de que se conoce otra variable aleatoria.

H (X) -H (X | Y) = H (Y) -H (Y | X)H (X, Y) = H (X) + H (Y | X) = H (Y) + H (X | Y)

Yo (X; Y) = H (X) - H (X | Y)

                    = H (X) + H (Y) -H (X, Y)         

                   = \ sum_xp (x) log \ frac {1} {p (x)} + \ sum_yp (y) log \ frac {1} {p (y)} - \ sum- {x, y} p (x, y ) log \ frac {1} {log (x, y)}

                  = \ sum_ {x, y} p (x, y) log \ frac {p (xy)} {p (x) p (y)}

Optimiza el proceso

Se puede ver en la información mutua que cuando p (x) yq (x) son independientes, I (X; Y) es 0, lo que significa irrelevante; si I (X; Y) es alto, puede indicar que los dos grupos tienen una gran relación. . Obtener cualquier x del generador. El objetivo es hacer la Pg posterior (c∣x) en el generador, es decir, el código potencial c tiene una entropía de información menor; de esta manera, se puede describir el proceso de retroceso de xa c sin perder el código potencial Información:

\ min_G \ max_D \ mathbb {E} _ {x \ sim P_ {data}} [logD (x)] + \ mathbb {E} _ {z \ sim P_z} [log (1-D (G (z)) )] - \ lambda I (c; G (z, c))

En esta fórmula, la primera mitad es la función objetivo de la GAN convencional, y la segunda mitad usa la información mutua de la salida del generador y la codificación de restricción como término de penalización, y el coeficiente de penalización es \ lambda.

Distribución posterior de codificación restringida

Dado que no podemos conocer la distribución posterior del código de restricción, usamos la estructura de red Q agregada para hacer una regresión del código de restricción c, y ajustar la distribución del código de restricción c mediante el entrenamiento de la red neuronal.

Aquí hay una prueba de expectativa sobre la probabilidad condicional, es decir, la expectativa de probabilidad condicional es la misma que su propia expectativa:

\ mathbb {E} _Y [\ ​​mathbb {E} _X {X | Y}] = \ int_Y \ mathbb {E} _x [X | Y] f (y) dy

                             = \ int_ {Y} [\ int_ {X} x \ frac {f (x, y)} {f (y)} dx] f (y) dy

                             = \ int_ {Y} \ int_ {X} x \ frac {f (x, y)} {f (y)} dxf (y) dy \ \ f (y) \ \ is \ \ constante \ \ wrt \ \ Y

                            = \ int _ {Y} \ int_ {X} xf (x, y) dxdy

                            = \ int_Xx \ int_Yf (x, y) dydx \ \ \ \ X \ \ es \ \ constante \ \ wrt \ \ Y

                            = \ int_Xx [\ int_Yf (x, y) dy] dx

                            = \ int_Xxf (x) dx

                            = \ mathbb {E} _ {X} [X]

La fórmula anterior es la recurrencia equivalente, entonces podemos saber a partir de esto:

\ mathbb {E} _ {x \ sim X, y \ sim Y | x} [f (x, y)] = \ int_xP (x) \ int_yP (y | x) f (x, y) dydx

                                             = \ int _x \ int_yP (x, y) f (x, y) dydx

                                             = \ int_yP (y) \ int_xP (x | y) f (x, y) dxdy

                                             = \ int_yP (y) [\ int_ {x '} P (x' | y) f (x ', y) dx'] dy

                                            = \ int_xP (x) [\ int_yP (y | x) [\ int_ {x '} P (x' | y) f (x ', y) dx'] dy] dx

                                            = \ mathbb {E} _ {x \ sim X, y \ sim Y | x, x '\ sim X | y} [f (x', y)]

Con base en la conclusión anterior, finalmente podemos escalar la información mutua como:

Yo (c; G (z, c)) \ geq H (c) + \ mathbb {E} _ {x \ sim G (z, c)} [\ mathbb {E} _ {c '\ sim P (c | x)} [logQ (c '| x)]]

                             = H (c) + \ mathbb {E} _ {c \ sim P (c), x \ sim P_ {G} (x | c)} [\ mathbb {E} _ {c '\ sim P (c | x)} [logQ (c '| x)]]

                             = H (c) + \ mathbb {E} _ {c \ sim P (c), x \ sim G (z, c)} [logQ (c | x)]

                            = L_I (G, Q)

Simplifique la función objetivo para:

\ min_G \ max_D \ mathbb {E} _ {x \ sim P_ {data}} [logD (x)] + \ mathbb {E} _ {z \ sim P_z} [log (1-D (G (z)) )] - \ lambda L_I (G, Q)

Resultados de código y práctica

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

import argparse
import os
import numpy as np
import math
import itertools

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/static/", exist_ok=True)
os.makedirs("images/varying_c1/", exist_ok=True)
os.makedirs("images/varying_c2/", 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=62, help="dimensionality of the latent space")
parser.add_argument("--code_dim", type=int, default=2, help="latent code")
parser.add_argument("--n_classes", type=int, default=10, help="number of classes for dataset")
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("BatchNorm") != -1:
        torch.nn.init.normal_(m.weight.data, 1.0, 0.02)
        torch.nn.init.constant_(m.bias.data, 0.0)


def to_categorical(y, num_columns):
    """Returns one-hot encoded Variable"""
    y_cat = np.zeros((y.shape[0], num_columns))
    y_cat[range(y.shape[0]), y] = 1.0

    return Variable(FloatTensor(y_cat))


class Generator(nn.Module):
    def __init__(self):
        super(Generator, self).__init__()
        input_dim = opt.latent_dim + opt.n_classes + opt.code_dim

        self.init_size = opt.img_size // 4  # Initial size before upsampling
        self.l1 = nn.Sequential(nn.Linear(input_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, noise, labels, code):
        gen_input = torch.cat((noise, labels, code), -1)
        out = self.l1(gen_input)
        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):
            """Returns layers of each discriminator block"""
            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.conv_blocks = 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

        # Output layers
        self.adv_layer = nn.Sequential(nn.Linear(128 * ds_size ** 2, 1))
        self.aux_layer = nn.Sequential(nn.Linear(128 * ds_size ** 2, opt.n_classes), nn.Softmax())
        self.latent_layer = nn.Sequential(nn.Linear(128 * ds_size ** 2, opt.code_dim))

    def forward(self, img):
        out = self.conv_blocks(img)
        out = out.view(out.shape[0], -1)
        validity = self.adv_layer(out)
        label = self.aux_layer(out)
        latent_code = self.latent_layer(out)

        return validity, label, latent_code


# Loss functions
adversarial_loss = torch.nn.MSELoss()
categorical_loss = torch.nn.CrossEntropyLoss()
continuous_loss = torch.nn.MSELoss()

# Loss weights
lambda_cat = 1
lambda_con = 0.1

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

if cuda:
    generator.cuda()
    discriminator.cuda()
    adversarial_loss.cuda()
    categorical_loss.cuda()
    continuous_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))
optimizer_info = torch.optim.Adam(
    itertools.chain(generator.parameters(), discriminator.parameters()), lr=opt.lr, betas=(opt.b1, opt.b2)
)

FloatTensor = torch.cuda.FloatTensor if cuda else torch.FloatTensor
LongTensor = torch.cuda.LongTensor if cuda else torch.LongTensor

# Static generator inputs for sampling
static_z = Variable(FloatTensor(np.zeros((opt.n_classes ** 2, opt.latent_dim))))
static_label = to_categorical(
    np.array([num for _ in range(opt.n_classes) for num in range(opt.n_classes)]), num_columns=opt.n_classes
)
static_code = Variable(FloatTensor(np.zeros((opt.n_classes ** 2, opt.code_dim))))


def sample_image(n_row, batches_done):
    """Saves a grid of generated digits ranging from 0 to n_classes"""
    # Static sample
    z = Variable(FloatTensor(np.random.normal(0, 1, (n_row ** 2, opt.latent_dim))))
    static_sample = generator(z, static_label, static_code)
    save_image(static_sample.data, "images/static/%d.png" % batches_done, nrow=n_row, normalize=True)

    # Get varied c1 and c2
    zeros = np.zeros((n_row ** 2, 1))
    #约束编码分为两部分:一部是零向量zeros,一部分是c_varied
    #交换前后两部分,分别得到不同的生数据
    c_varied = np.repeat(np.linspace(-1, 1, n_row)[:, np.newaxis], n_row, 0)
    c1 = Variable(FloatTensor(np.concatenate((c_varied, zeros), -1)))
    c2 = Variable(FloatTensor(np.concatenate((zeros, c_varied), -1)))
    #采样
    sample1 = generator(static_z, static_label, c1)
    sample2 = generator(static_z, static_label, c2)
    save_image(sample1.data, "images/varying_c1/%d.png" % batches_done, nrow=n_row, normalize=True)
    save_image(sample2.data, "images/varying_c2/%d.png" % batches_done, nrow=n_row, normalize=True)


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

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

        batch_size = imgs.shape[0]

        # Adversarial ground truths
        valid = Variable(FloatTensor(batch_size, 1).fill_(1.0), requires_grad=False)
        fake = Variable(FloatTensor(batch_size, 1).fill_(0.0), requires_grad=False)

        # Configure input
        real_imgs = Variable(imgs.type(FloatTensor))
        labels = to_categorical(labels.numpy(), num_columns=opt.n_classes)

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

        optimizer_G.zero_grad()

        # Sample noise and labels as generator input
        z = Variable(FloatTensor(np.random.normal(0, 1, (batch_size, opt.latent_dim))))
        label_input = to_categorical(np.random.randint(0, opt.n_classes, batch_size), num_columns=opt.n_classes)
        code_input = Variable(FloatTensor(np.random.uniform(-1, 1, (batch_size, opt.code_dim))))

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

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

        g_loss.backward()
        optimizer_G.step()

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

        optimizer_D.zero_grad()

        # Loss for real images
        real_pred, _, _ = discriminator(real_imgs)
        d_real_loss = adversarial_loss(real_pred, valid)

        # Loss for fake images
        fake_pred, _, _ = discriminator(gen_imgs.detach())
        d_fake_loss = adversarial_loss(fake_pred, fake)

        # Total discriminator loss
        d_loss = (d_real_loss + d_fake_loss) / 2

        d_loss.backward()
        optimizer_D.step()

        # ------------------
        # Information Loss
        # ------------------

        optimizer_info.zero_grad()

        # Sample labels
        sampled_labels = np.random.randint(0, opt.n_classes, batch_size)

        # Ground truth labels
        gt_labels = Variable(LongTensor(sampled_labels), requires_grad=False)

        # Sample noise, labels and code as generator input
        z = Variable(FloatTensor(np.random.normal(0, 1, (batch_size, opt.latent_dim))))
        label_input = to_categorical(sampled_labels, num_columns=opt.n_classes)
        code_input = Variable(FloatTensor(np.random.uniform(-1, 1, (batch_size, opt.code_dim))))

        gen_imgs = generator(z, label_input, code_input)
        _, pred_label, pred_code = discriminator(gen_imgs)

        info_loss = lambda_cat * categorical_loss(pred_label, gt_labels) + lambda_con * continuous_loss(
            pred_code, code_input
        )

        info_loss.backward()
        optimizer_info.step()

        # --------------
        # Log Progress
        # --------------

        print(
            "[Epoch %d/%d] [Batch %d/%d] [D loss: %f] [G loss: %f] [info loss: %f]"
            % (epoch, opt.n_epochs, i, len(dataloader), d_loss.item(), g_loss.item(), info_loss.item())
        )
        batches_done = epoch * len(dataloader) + i
        if batches_done % opt.sample_interval == 0:
            sample_image(n_row=10, batches_done=batches_done)

datos de prueba minist

   

                      Registrar durante el entrenamiento. Controlar el grosor mediante la variable de restricción c. Controlar el tamaño mediante la variable de restricción c

 


                                       

 

 

 

 

 

 

 

 

 

Supongo que te gusta

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