生成式AI:使用Pytorch通过GAN生成合成数据

2024年01月19日 由 alex 发表 377 0

生成对抗网络(GAN )因其生成精美且逼真的图像能力而变得极为受欢迎,同样,语言模型(例如ChatGPT)在各个领域的使用也日益增加。可以说,正是这些GAN模型让世界对人工智能/机器学习持有现在的热情(或恐惧);因为它向每个人(特别是那些业外人士)展示了机器学习所拥有的巨大潜力。网上已经有很多关于GAN模型的资源,但这些大多数集中在图像生成上。这些图像生成和语言模型需要复杂的空间或时间细节,这增加了额外的复杂性,使读者更难理解GAN的真正本质。


为了解决这个问题,让更广泛的受众能更轻松地接触到GAN,我们在这个简短的讨论和GAN模型示例中,将采取一种不同且更实际的方法,关注于生成数学函数的合成数据。除了作为学习目的的简化之外,合成数据生成本身正变得越来越重要。数据不仅在商业决策中发挥着核心作用,还有越来越多的用例中,数据驱动方法变得比第一原理模型更受欢迎。一个令人兴奋的例子是天气预报,第一原理模型包括解决Navier-Stokes方程的简化版本,需要通过数值方法解决(我得说,计算成本很高)。然而,利用深度学习进行的最近的天气预报尝试(例如,查看Nvidia的FourCastNet)在捕捉天气模式方面非常成功,且一旦训练完成,运行起来更容易且更快。


生成模型与判别模型


在机器学习中,理解生成模型和判别模型之间的区别非常重要,它们是GAN的关键组成部分。让我们(非常简短地)解开这些术语:


判别模型:


判别模型专注于将数据分类到预定义的类别中,例如将狗和猫的图像分到各自的类别。这些模型与其说是捕捉整个分布,不如说是领悟出区分不同类别的边界。它们输出的是P(y|x)(给定输入数据x时类别y的概率),即它们回答的问题是给定数据点属于什么类别?


生成模型:


生成模型旨在理解数据的基本结构。与区分类别的判别模型不同,生成模型学习数据的整个分布。这些模型输出的是p(x|y),即它们回答的问题是给定类别时生成这个特定数据点的可能性有多大?


这两种模型之间的相互作用形成了GAN的基础。


GANs — 结构和组件


12


让我们现在探索这些概念是如何在一个生成对抗网络(GAN)模型中结合在一起的。生成对抗网络的关键组件包括噪声向量、生成器和鉴别器。


生成器:生成真实数据


为了生成合成数据,生成器使用一个随机噪声向量作为输入。在其试图欺骗鉴别器的努力中,生成器的目标是学习真实数据的分布并产生无法与真实数据区分的合成数据。这里有一个问题,就是对于相同的输入,它总是产生相同的输出(想象一个产生逼真图像的图像生成器,但总是相同的图像,这并不十分有用)。随机噪声向量将随机性注入过程中,为生成的输出提供多样性。


鉴别器:辨别真伪


鉴别器就像一个训练有素的艺术评论家,能够区分真实和伪造的数据。它的角色是审查它接收到的数据并对作品是否为真实的赋予一个概率分数。如果合成数据看起来与真实数据相似,鉴别器会赋予高概率值,否则赋予低概率分数。


对抗性训练:动态的决斗


生成器努力学习产生鉴别器无法与真实数据区分开的合成数据。同时,鉴别器也在学习并提高其区分真伪的能力。这种动态培训过程推动两个模型精炼他们的技能。两个模型总是相互竞争(这就是为什么它被称为对抗性的),通过这种竞争,两个模型在他们的角色上都变得非常出色。


用Pytorch实现GAN


让我们通过查看创建一个GAN的例子。在这个例子中,我们实现了一个可以生成合成数据的模型用pytorch。对于训练,我们有一个6参数的数据集,以下是这些形状(所有参数作为参数1的函数绘制)。每个参数都故意选择了一个显著不同的分布和形状,以增加数据集的复杂性并模拟现实世界的数据。


在这个教程中,我假设你已经理解了正常的人工神经网络(ANN)模型架构和Python。我在代码中提供了注释帮助你理解代码。


13


定义GAN模型组件(生成器和鉴别器)


import torch
from torch import nn
from tqdm.auto import tqdm
from torch.utils.data import DataLoader
import matplotlib.pyplot as plt
import torch.nn.init as init
import pandas as pd
import numpy as np
from torch.utils.data import Dataset

# defining a single generation block function
def FC_Layer_blockGen(input_dim, output_dim):
    single_block = nn.Sequential(
        nn.Linear(input_dim, output_dim),
        nn.ReLU()
    )
    return single_block
    
# DEFINING THE GENERATOR
class Generator(nn.Module):
    def __init__(self, latent_dim, output_dim):
        super(Generator, self).__init__()
        self.model = nn.Sequential(
            nn.Linear(latent_dim, 256),
            nn.ReLU(),
            nn.Linear(256, 512),
            nn.ReLU(),
            nn.Linear(512, 512),
            nn.ReLU(),
            nn.Linear(512, output_dim),
            nn.Tanh()  
        )
    def forward(self, x):
        return self.model(x)
        
#defining a single discriminattor block       
def FC_Layer_BlockDisc(input_dim, output_dim):
    return nn.Sequential(
        nn.Linear(input_dim, output_dim),
        nn.ReLU(),
        nn.Dropout(0.4)
    )
    
# Defining the discriminator
class Discriminator(nn.Module):
    def __init__(self, input_dim):
        super(Discriminator, self).__init__()
        self.model = nn.Sequential(
            nn.Linear(input_dim, 512),
            nn.ReLU(),
            nn.Dropout(0.4),
            nn.Linear(512, 512),
            nn.ReLU(),
            nn.Dropout(0.4),
            nn.Linear(512, 256),
            nn.ReLU(),
            nn.Dropout(0.4),
            nn.Linear(256, 1),
            nn.Sigmoid()
        )
    def forward(self, x):
        return self.model(x)
        
        
#Defining training parameters
batch_size = 128
num_epochs = 500
lr = 0.0002
num_features = 6
latent_dim = 20
# MODEL INITIALIZATION
generator = Generator(noise_dim, num_features)
discriminator = Discriminator(num_features)
# LOSS FUNCTION AND OPTIMIZERS
criterion = nn.BCELoss()
gen_optimizer = torch.optim.Adam(generator.parameters(), lr=lr)
disc_optimizer = torch.optim.Adam(discriminator.parameters(), lr=lr)


模型初始化和数据处理


# IMPORTING DATA
file_path = 'SamplingData7.xlsx'
data = pd.read_excel(file_path)
X = data.values
X_normalized = torch.FloatTensor((X - X.min(axis=0)) / (X.max(axis=0) - X.min(axis=0)) * 2 - 1)
real_data = X_normalized
#Creating a dataset
class MyDataset(Dataset):
    def __init__(self, dataframe):
        self.data = dataframe.values.astype(float)
        self.labels = dataframe.values.astype(float)
    def __len__(self):
        return len(self.data)
    def __getitem__(self, idx):
        sample = {
            'input': torch.tensor(self.data[idx]),
            'label': torch.tensor(self.labels[idx])
        }
        return sample
# Create an instance of the dataset
dataset = MyDataset(data)
# Create DataLoader
dataloader = DataLoader(dataset, batch_size=batch_size, shuffle=True, drop_last=True)
def weights_init(m):
    if isinstance(m, nn.Linear):
        init.xavier_uniform_(m.weight)
        if m.bias is not None:
            init.constant_(m.bias, 0)
pretrained = False
if pretrained:
    pre_dict = torch.load('pretrained_model.pth')
    generator.load_state_dict(pre_dict['generator'])
    discriminator.load_state_dict(pre_dict['discriminator'])
else:
    # Apply weight initialization
    generator = generator.apply(weights_init)
    discriminator = discriminator.apply(weights_init)


模型训练


model_save_freq = 100
latent_dim =20
for epoch in range(num_epochs):
    for batch in dataloader:
        real_data_batch = batch['input']
        # Train discriminator on real data
        real_labels = torch.FloatTensor(np.random.uniform(0.9, 1.0, (batch_size, 1)))
        disc_optimizer.zero_grad()
        output_real = discriminator(real_data_batch)
        loss_real = criterion(output_real, real_labels)
        loss_real.backward()
        # Train discriminator on generated data
        fake_labels = torch.FloatTensor(np.random.uniform(0, 0.1, (batch_size, 1)))
        noise = torch.FloatTensor(np.random.normal(0, 1, (batch_size, latent_dim)))
        generated_data = generator(noise)
        output_fake = discriminator(generated_data.detach())
        loss_fake = criterion(output_fake, fake_labels)
        loss_fake.backward()
        disc_optimizer.step()
        # Train generator 
        valid_labels = torch.FloatTensor(np.random.uniform(0.9, 1.0, (batch_size, 1)))
        gen_optimizer.zero_grad()
        output_g = discriminator(generated_data)
        loss_g = criterion(output_g, valid_labels)
        loss_g.backward()
        gen_optimizer.step()
    # Print progress
    print(f"Epoch {epoch}, D Loss Real: {loss_real.item()}, D Loss Fake: {loss_fake.item()}, G Loss: {loss_g.item()}")


评估和可视化结果


import seaborn as sns
# Generate synthetic data 
synthetic_data = generator(torch.FloatTensor(np.random.normal(0, 1, (real_data.shape[0], noise_dim))))
# Plot the results
fig, axs = plt.subplots(2, 3, figsize=(12, 8))
fig.suptitle('Real and Synthetic Data Distributions', fontsize=16)
for i in range(2):
    for j in range(3):
        sns.histplot(synthetic_data[:, i * 3 + j].detach().numpy(), bins=50, alpha=0.5, label='Synthetic Data', ax=axs[i, j], color='blue')
        sns.histplot(real_data[:, i * 3 + j].numpy(), bins=50, alpha=0.5, label='Real Data', ax=axs[i, j], color='orange')
        axs[i, j].set_title(f'Parameter {i * 3 + j + 1}', fontsize=12)
        axs[i, j].set_xlabel('Value')
        axs[i, j].set_ylabel('Frequency')
        axs[i, j].legend()
plt.tight_layout(rect=[0, 0.03, 1, 0.95])
plt.show()

# Create a 2x3 grid of subplots
fig, axs = plt.subplots(2, 3, figsize=(15, 10))
fig.suptitle('Comparison of Real and Synthetic Data', fontsize=16)
# Define parameter names
param_names = ['Parameter 1', 'Parameter 2', 'Parameter 3', 'Parameter 4', 'Parameter 5', 'Parameter 6']
# Scatter plots for each parameter
for i in range(2):
    for j in range(3):
        param_index = i * 3 + j
        sns.scatterplot(real_data[:, 0].numpy(), real_data[:, param_index].numpy(), label='Real Data', alpha=0.5, ax=axs[i, j])
        sns.scatterplot(synthetic_data[:, 0].detach().numpy(), synthetic_data[:, param_index].detach().numpy(), label='Generated Data', alpha=0.5, ax=axs[i, j])
        axs[i, j].set_title(param_names[param_index], fontsize=12)
        axs[i, j].set_xlabel(f'Real Data - {param_names[param_index]}')
        axs[i, j].set_ylabel(f'Real Data - {param_names[param_index]}')
        axs[i, j].legend()
plt.tight_layout(rect=[0, 0.03, 1, 0.95])
plt.show()


14


15


尽管我们的模型很简单,但合成数据和真实数据的分布以及数学形态看起来非常相似!训练过程和模型架构可以修改以提高准确度,这是我们在这里没有专注的部分。这个模型可以非常容易地调整,用于生成其他应用的合成数据,这些应用具有更多的参数和更复杂的真实物理系统。



文章来源:https://medium.com/towards-data-science/generative-ai-synthetic-data-generation-with-gans-using-pytorch-2e4dde8a17dd
欢迎关注ATYUN官方公众号
商务合作及内容投稿请联系邮箱:bd@atyun.com
评论 登录
热门职位
Maluuba
20000~40000/月
Cisco
25000~30000/月 深圳市
PilotAILabs
30000~60000/年 深圳市
写评论取消
回复取消