Principio GAN y mnist simple para generar imágenes.

Aprendamos brevemente GAN, principalmente para expandir el conjunto de datos. Actualmente, hay muy pocos datos disponibles y la cantidad de datos después de la mejora de datos (rotación, inversión, etc.) de 30 imágenes en una categoría está lejos de ser suficiente, por lo que Intenté usar GAN, generar datos, agregar los datos generados y luego detectarlos y clasificarlos, no sé si tendrá buenos resultados. Como se muestra a continuación en mi conjunto de datos, quiero generar grietas en lotes y el fondo de la placa de circuito es complejo, por lo que no sé si se puede hacer.
Insertar descripción de la imagen aquí

Examinando algunos artículos y blogs: el simple aumento de datos utilizando GAN a veces puede mejorar el rendimiento del clasificador, especialmente en el caso de conjuntos de datos muy pequeños o limitados, pero los casos más prometedores para el aumento utilizando GAN parecen incluir el aprendizaje por transferencia o el aprendizaje de pocas oportunidades.

principio GAN

Concepto GAN : A través de juegos continuos entre la red de generación G (Generador) y la red discriminadora D (Discriminador), G puede aprender la distribución de datos.

Insertar descripción de la imagen aquí
El entrenamiento iterativo alternativo optimiza continuamente los ajustes de parámetros para hacer que las falsedades se conviertan en realidad.

Fórmula matemática :
Insertar descripción de la imagen aquí
dividida en optimización D y optimización G:

Optimizar D:
Insertar descripción de la imagen aquí
Optimizar G:
Insertar descripción de la imagen aquí
Al optimizar D, es decir, la red discriminante, en realidad no hay generación de red, la siguiente G (z) equivale a la muestra falsa que se ha obtenido. Optimice el primer término de la fórmula de D para que cuando se ingrese la muestra verdadera x, cuanto mayor sea el resultado, mejor. Esto es comprensible porque el resultado de la predicción de la muestra verdadera debe ser lo más cercano a 1 posible. Para muestras falsas, es necesario optimizar el resultado lo más pequeño posible, es decir, cuanto más pequeño sea D (G (z)), mejor, porque su etiqueta es 0. Pero el primer término es más grande y el segundo término es más pequeño. Esto no es una contradicción, así que cambie el segundo término a 1-D(G(z)), de modo que cuanto más grande, mejor. La combinación de los dos es Cuanto más grande el mejor. Luego, al optimizar G, no hay una muestra real en este momento, por lo que el primer elemento se descarta directamente. En este momento, solo hay muestras falsas, pero decimos que en este momento esperamos que la etiqueta de la muestra falsa sea 1, por lo que cuanto mayor sea D (G (z)), mejor, pero para unificarlo. en la forma de 1-D(G(z)), entonces solo puede minimizar 1-D(G(z)).No hay diferencia en esencia, solo por la unificación de la forma. Posteriormente, estos dos modelos de optimización se pueden combinar y escribir, y se convierten en la función objetivo máxima y mínima original.

consejos de entrenamiento

  1. La función de activación de la última capa usa tanh (excepto BEGAN)

  2. Usando la función de pérdida de wassertein GAN,

  3. Si hay datos de etiquetas, intente usar etiquetas. Algunas personas han sugerido que usar etiquetas invertidas es muy efectivo. Además, use suavizado de etiquetas, suavizado de etiquetas unilateral o suavizado de etiquetas bilateral.

  4. Utilice la norma de mini lote. Si no utiliza la norma de lote, puede utilizar la norma de instancia o la norma de peso.

  5. Para evitar el uso de RELU y capas de agrupación y reducir la posibilidad de gradientes dispersos, puede usar la función de activación de Learelu.

  6. El optimizador debe intentar elegir ADAM. No establezca la tasa de aprendizaje demasiado alta. El 1e-4 inicial se puede usar como referencia. Además, la tasa de aprendizaje se puede reducir continuamente a medida que avanza el entrenamiento.

  7. Agregar ruido gaussiano a la capa de red de D es equivalente a un ruido regular

GAN convencional

Realicé una serie de lecturas y análisis sobre los principales modelos GAN:

  1. StyleGAN : Adecuado para síntesis de rostros, el efecto es muy bueno, pero no lo he visto usado para otra generación de datos, por lo que no planeo usarlo;
  2. CycleGAN y pix2pix GAN : Principalmente adecuado para la transferencia de estilo. Solo planeo generar imágenes de alta calidad y soy demasiado vago para separar el fondo y el objeto para emparejar. No tiene nada que ver con la transformación de estilo, así que no planeo Úselo. Si el efecto posterior no es bueno, consideraré usar este método;
  3. SMOTE : Algunas personas usan el algoritmo smote para amplificar datos en muestras de clases minoritarias, pero solo usan el algoritmo vecino más cercano K para observar la distribución de datos. Finalmente, usan GAN para expandir los datos. En aplicaciones prácticas, el conjunto de datos puede También enfrento no solo problemas El problema es que los datos están desequilibrados y el tamaño de muestra de cada tipo es pequeño, lo que dificulta entrenar el modelo de manera efectiva, por lo que no planeo probarlo.
  4. SGAN (GAN semisupervisada) : en GAN semisupervisada, el Discriminador tiene N+1 salidas, donde N es el número de clasificaciones de la muestra. Este método puede entrenar un clasificador más efectivo y también generar muestras de mayor calidad. En el experimento MNIST, podemos ver si SGAN puede generar mejores muestras que el GAN ​​general, como se muestra en la Figura 3, SGAN (izquierda) La salida de es más clara que el de GAN (derecha), que parece ser correcto para diferentes inicializaciones y arquitecturas de red, pero es difícil realizar una evaluación sistemática de la calidad de la muestra para diferentes hiperparámetros. Voy a intentarlo
    Insertar descripción de la imagen aquí
  5. DCGAN: El efecto no es muy bueno. . . También es posible que mi conjunto de datos provenga de una placa de circuito y la imagen generada esté muy borrosa. Tal vez se pueda usar una imagen de fondo simple. La imagen a continuación es la imagen que generé. . .
    Insertar descripción de la imagen aquí

Implementación de código parcial GAN ​​(mnist como ejemplo 28x28)

Red discriminadora

#定义判别器  #####Discriminator######使用多层网络来作为判别器
 
#将图片28x28展开成784,然后通过多层感知器,中间经过斜率设置为0.2的LeakyReLU激活函数,
# 最后接sigmoid激活函数得到一个0到1之间的概率进行二分类。
class discriminator(nn.Module):
    def __init__(self):
        super(discriminator,self).__init__()
        self.dis=nn.Sequential(
            nn.Linear(784,256),#输入特征数为784,输出为256
            nn.LeakyReLU(0.2),#进行非线性映射
            nn.Linear(256,256),#进行一个线性映射
            nn.LeakyReLU(0.2),
            nn.Linear(256,1),
            nn.Sigmoid()
            # sigmoid可以班实数映射到【0,1】,作为概率值,
            # 多分类用softmax函数
        )
    def forward(self, x):
        x=self.dis(x)
        return x

Red Generativa

####### 定义生成器 Generator #####
#输入一个100维的0~1之间的高斯分布,然后通过第一层线性变换将其映射到256维,
# 然后通过LeakyReLU激活函数,接着进行一个线性变换,再经过一个LeakyReLU激活函数,
# 然后经过线性变换将其变成784维,最后经过Tanh激活函数是希望生成的假的图片数据分布
# 能够在-1~1之间。
class generator(nn.Module):
    def __init__(self):
        super(generator,self).__init__()
        self.gen=nn.Sequential(
            nn.Linear(100,256),#用线性变换将输入映射到256维
            nn.ReLU(True),#relu激活
            nn.Linear(256,256),#线性变换
            nn.ReLU(True),#relu激活
            nn.Linear(256,784),#线性变换
            nn.Tanh()#Tanh激活使得生成数据分布在【-1,1】之间
        )
 
    def forward(self, x):
        x=self.gen(x)
        return x


#创建对象
D=discriminator()
G=generator()
if torch.cuda.is_available():
    D=D.cuda()
    G=G.cuda()

Tren discriminador

El entrenamiento del discriminador consta de dos partes: la primera parte es juzgar las imágenes reales como verdaderas y la segunda parte es juzgar las imágenes falsas como falsas. En estos dos procesos, los parámetros del generador no participan en la actualización.

criterion = nn.BCELoss()  #定义loss的度量方式是单目标二分类交叉熵函数
d_optimizer = torch.optim.Adam(D.parameters(), lr=0.0003)
g_optimizer = torch.optim.Adam(G.parameters(), lr=0.0003)

Ingreso al entrenamiento
Dado que el entrenamiento del generador requiere la salida del discriminador, el discriminador se entrena primero.

img = img.view(num_img, -1)  # 将图片展开乘28x28=784
real_img = Variable(img).cuda()  # 将tensor变成Variable放入计算图中
real_label = Variable(torch.ones(num_img)).cuda()  # 定义真实label为1
fake_label = Variable(torch.zeros(num_img)).cuda()  # 定义假的label为0

# 计算真实图片的损失
real_out = D(real_img)  # 将真实图片放入判别器中
d_loss_real = criterion(real_out, real_label)  # 得到真实图片的loss
real_scores = real_out  # 得到真实图片的判别值,输出的值越接近1越好

# 计算假图片的损失
z = Variable(torch.randn(num_img, z_dimension)).cuda()  # 随机生成一些噪声
fake_img = G(z)  # 放入生成网络生成一张假的图片
fake_out = D(fake_img)  # 判别器判断假的图片
d_loss_fake = criterion(fake_out, fake_label)  # 得到假的图片的loss
fake_scores = fake_out  # 假的图片放入判别器越接近0越好

# 损失函数和优化
d_loss = d_loss_real + d_loss_fake  # 将真假图片的loss加起来
d_optimizer.zero_grad()  # 归0梯度
d_loss.backward()  # 反向传播
d_optimizer.step()  # 更新参数

Tren generativo

Principio : El propósito es esperar que el discriminador juzgue la imagen falsa generada como una imagen real y se
corrija D. El resultado de pasar la imagen falsa al discriminador corresponde a la etiqueta real. Los parámetros actualizados por retropropagación son los parámetros en la red de generación, de esta manera podemos hacer que el discriminador juzgue las imágenes falsas generadas como reales siguiendo los parámetros en la red de nueva generación, logrando así el papel de confrontación generativa.

# compute loss of fake_img
z = Variable(torch.randn(num_img, z_dimension)).cuda()  # 得到随机噪声
fake_img = G(z)  # 生成假的图片
output = D(fake_img)  # 经过判别器得到结果
g_loss = criterion(output, real_label)  # 得到假的图片与真实图片label的loss
 
# bp and optimize
g_optimizer.zero_grad()  # 归0梯度
g_loss.backward()  # 反向传播
g_optimizer.step()  # 更新生成网络的参数

Código completo (simple)

import torch
import torchvision
import torch.nn as nn
import torch.nn.functional as F
from torchvision import datasets
from torchvision import transforms
from torchvision.utils import save_image
from torch.autograd import Variable
import os

if not os.path.exists('./img'):
    os.mkdir('./img')


def to_img(x):
    out = 0.5 * (x + 1)
    out = out.clamp(0, 1)
    out = out.view(-1, 1, 28, 28)
    return out


batch_size = 128
num_epoch = 100
z_dimension = 100

# Image processing
img_transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.5], std=[0.5])
])
# MNIST dataset
mnist = datasets.MNIST(
    root='./data/', train=True, transform=img_transform, download=True)
# Data loader
dataloader = torch.utils.data.DataLoader(
    dataset=mnist, batch_size=batch_size, shuffle=True)


# Discriminator
class discriminator(nn.Module):
    def __init__(self):
        super(discriminator, self).__init__()
        self.dis = nn.Sequential(
            nn.Linear(784, 256),
            nn.LeakyReLU(0.2),
            nn.Linear(256, 256),
            nn.LeakyReLU(0.2),
            nn.Linear(256, 1),
            nn.Sigmoid())

    def forward(self, x):
        x = self.dis(x)
        return x


# Generator
class generator(nn.Module):
    def __init__(self):
        super(generator, self).__init__()
        self.gen = nn.Sequential(
            nn.Linear(100, 256),
            nn.ReLU(True),
            nn.Linear(256, 256),
            nn.ReLU(True),
            nn.Linear(256, 784),
            nn.Tanh())

    def forward(self, x):
        x = self.gen(x)
        return x


D = discriminator()
G = generator()
if torch.cuda.is_available():
    D = D.cuda()
    G = G.cuda()
# Binary cross entropy loss and optimizer
criterion = nn.BCELoss()
d_optimizer = torch.optim.Adam(D.parameters(), lr=0.0003)
g_optimizer = torch.optim.Adam(G.parameters(), lr=0.0003)

# Start training
for epoch in range(num_epoch):
    for i, (img, _) in enumerate(dataloader):
        num_img = img.size(0)
        # =================train discriminator
        img = img.view(num_img, -1)
        real_img = Variable(img).cuda()
        real_label = Variable(torch.ones(num_img)).cuda()
        fake_label = Variable(torch.zeros(num_img)).cuda()

        # compute loss of real_img
        real_out = D(real_img)
        d_loss_real = criterion(real_out, real_label)
        real_scores = real_out  # closer to 1 means better

        # compute loss of fake_img
        z = Variable(torch.randn(num_img, z_dimension)).cuda()
        fake_img = G(z)
        fake_out = D(fake_img)
        d_loss_fake = criterion(fake_out, fake_label)
        fake_scores = fake_out  # closer to 0 means better

        # bp and optimize
        d_loss = d_loss_real + d_loss_fake
        d_optimizer.zero_grad()
        d_loss.backward()
        d_optimizer.step()

        # ===============train generator
        # compute loss of fake_img
        z = Variable(torch.randn(num_img, z_dimension)).cuda()
        fake_img = G(z)
        output = D(fake_img)
        g_loss = criterion(output, real_label)

        # bp and optimize
        g_optimizer.zero_grad()
        g_loss.backward()
        g_optimizer.step()

        if (i + 1) % 100 == 0:
            print('Epoch [{}/{}], d_loss: {:.6f}, g_loss: {:.6f} '
                  'D real: {:.6f}, D fake: {:.6f}'.format(
                epoch, num_epoch, d_loss.item(), g_loss.item(),
                real_scores.data.mean(), fake_scores.data.mean()))
    if epoch == 0:
        real_images = to_img(real_img.cpu().data)
        save_image(real_images, './img/real_images.png')

    fake_images = to_img(fake_img.cpu().data)
    save_image(fake_images, './img/fake_images-{}.png'.format(epoch + 1))

torch.save(G.state_dict(), './generator.pth')
torch.save(D.state_dict(), './discriminator.pth')

La versión de pytorch utilizada esta vez es 0.5 o superior, por lo que está escrita como train_loss+=loss.item(). Si es la versión 0.3, cámbiela a train_loss+=loss.data[0]; de lo contrario, se informará un error.

Resultados experimentales:

La imagen de la izquierda es la imagen generada número 100 y la imagen de la derecha es la imagen real.
Insertar descripción de la imagen aquí
resultados

Código completo (con convolución)

import torch
import torchvision
import torch.nn as nn
import torch.nn.functional as F
from torchvision import datasets
from torchvision import transforms
from torchvision.utils import save_image
from torch.autograd import Variable
from matplotlib import pyplot as plt
import os

if not os.path.exists('./cnn_img'):
    os.mkdir('./cnn_img')


def to_img(x):
    out = 0.5 * (x + 1)
    out = out.clamp(0, 1)
    out = out.view(-1, 1, 28, 28)
    return out


batch_size = 128
num_epoch = 100
z_dimension = 100    #噪声维度

# Image processing
img_transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.5], std=[0.5])
])
# MNIST dataset
mnist = datasets.MNIST(
    root='./data/', train=True, transform=img_transform, download=True)
# Data loader
dataloader = torch.utils.data.DataLoader(
    dataset=mnist, batch_size=batch_size, shuffle=True)


# Discriminator
class discriminator(nn.Module):
    def __init__(self):
        super(discriminator, self).__init__()
        self.conv1 = nn.Sequential(
            nn.Conv2d(1, 32, 5, padding=2),    # batch,32,28,28
            nn.LeakyReLU(0.2),
            nn.MaxPool2d(2, stride=2)     # batch,32,14,14
        )
        self.conv2 = nn.Sequential(
            nn.Conv2d(32, 64, 5, padding=2),  # batch, 64, 14, 14
            nn.LeakyReLU(0.2),
            nn.MaxPool2d(2, stride=2)  # batch, 64, 7, 7
        )
        self.fc = nn.Sequential(
            nn.Linear(64 * 7 * 7, 1024),
            nn.LeakyReLU(0.2),
            nn.Linear(1024, 1),
            nn.Sigmoid()
        )

    def forward(self, x):
        x = self.conv1(x)
        x = self.conv2(x)
        x = x.view(x.size(0), -1)
        x = self.fc(x)
        return x


# Generator
class generator(nn.Module):
    def __init__(self, input_size, num_feature):
        super(generator, self).__init__()
        # 1.第一层线性变换
        self.fc = nn.Linear(input_size, num_feature)  # batch, 3136=1x56x56
        self.br = nn.Sequential(
            nn.BatchNorm2d(1),
            nn.ReLU(True)
        )
        self.conv1_g = nn.Sequential(
            nn.Conv2d(1, 50, 3, stride=1, padding=1),  # batch, 50, 56, 56
            nn.BatchNorm2d(50),
            nn.ReLU(True)
        )
        self.conv2_g = nn.Sequential(
            nn.Conv2d(50, 25, 3, stride=1, padding=1),  # batch, 25, 56, 56
            nn.BatchNorm2d(25),
            nn.ReLU(True)
        )
        self.conv3_g = nn.Sequential(
            nn.Conv2d(25, 1, 2, stride=2),  # batch, 1, 28, 28
            nn.Tanh()
        )

    def forward(self, x):
        x = self.fc(x)
        x = x.view(x.size(0), 1, 56, 56)
        x = self.br(x)
        x = self.conv1_g(x)
        x = self.conv2_g(x)
        x = self.conv3_g(x)
        return x


D = discriminator()
G = generator(z_dimension, 3136)
if torch.cuda.is_available():
    D = D.cuda()
    G = G.cuda()
# Binary cross entropy loss and optimizer
criterion = nn.BCELoss()
d_optimizer = torch.optim.Adam(D.parameters(), lr=0.0001)
g_optimizer = torch.optim.Adam(G.parameters(), lr=0.0001)

# Start training
for epoch in range(num_epoch):
    for i, (img, _) in enumerate(dataloader):
        num_img = img.size(0)
        #print(img.shape)             # inputs:img=[128,1,28,28]
        # =================train discriminator
        #img = img.view(num_img, -1)        # img.shape: [128, 784]
        real_img = Variable(img).cuda()
        #print(real_img.shape)
        real_label = Variable(torch.ones(num_img)).cuda()
        fake_label = Variable(torch.zeros(num_img)).cuda()

        # compute loss of real_img
        real_out = D(real_img)
        d_loss_real = criterion(real_out, real_label)
        real_scores = real_out  # closer to 1 means better

        # compute loss of fake_img
        z = Variable(torch.randn(num_img, z_dimension)).cuda()
        fake_img = G(z)
        fake_out = D(fake_img)
        d_loss_fake = criterion(fake_out, fake_label)
        fake_scores = fake_out  # closer to 0 means better

        # bp and optimize
        d_loss = d_loss_real + d_loss_fake
        d_optimizer.zero_grad()
        d_loss.backward()
        d_optimizer.step()

        # ===============train generator
        # compute loss of fake_img
        z = Variable(torch.randn(num_img, z_dimension)).cuda()
        fake_img = G(z)
        output = D(fake_img)
        g_loss = criterion(output, real_label)

        # bp and optimize
        g_optimizer.zero_grad()
        g_loss.backward()
        g_optimizer.step()

        if (i + 1) % 100 == 0:
            print('Epoch [{}/{}], d_loss: {:.6f}, g_loss: {:.6f} '
                  'D real: {:.6f}, D fake: {:.6f}'.format(
                epoch, num_epoch, d_loss.item(), g_loss.item(),
                real_scores.data.mean(), fake_scores.data.mean()))
    if epoch == 0:
        real_images = to_img(real_img.cpu().data)
        save_image(real_images, './cnn_img/real_images.png')

    fake_images = to_img(fake_img.cpu().data)
    save_image(fake_images, './cnn_img/fake_images-{}.png'.format(epoch + 1))

torch.save(G.state_dict(), './cnn_img/generator.pth')
torch.save(D.state_dict(), './cnn_img/discriminator.pth')

Resultados experimentales

Insertar descripción de la imagen aquí
El lado izquierdo es una imagen real y el lado derecho es una imagen generada en la época 100. Lo ejecuté en mi computadora NVIDIA-MX450 sin ningún problema y el efecto sigue siendo bueno.

Referencia

1. Comprensión simple y experimento de la red generativa adversarial GAN
​​2. Notas en papel sobre GAN y amplificación de pequeñas muestras
3. Análisis del principio de GAN (red neuronal generativa adversarial)
4. Implementación en Pytorch del uso de GAN para generar un conjunto de datos MNIST

Supongo que te gusta

Origin blog.csdn.net/qq_42740834/article/details/123692837
Recomendado
Clasificación