上海徐汇网站建设公司,站长之家html,杭州seo服务公司,哪个网站可以做翻译兼职Pytorch从零开始实战——生成手势图像
本系列来源于365天深度学习训练营
原作者K同学 文章目录 Pytorch从零开始实战——生成手势图像环境准备模型选择模型训练模型分析总结 环境准备
本文基于Jupyter notebook#xff0c;使用Python3.8#xff0c;Pytorch2.0.1cu118使用Python3.8Pytorch2.0.1cu118torchvision0.15.2需读者自行配置好环境且有一些深度学习理论基础。本次实验的目的是了解并使用CGAN模型完成手势图像生成。 第一步导入常用包
import torch
import numpy as np
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms
from torch.autograd import Variable
from torchvision.utils import save_image
from torchvision.utils import make_grid
from torch.utils.tensorboard import SummaryWriter
from torchsummary import summary
import matplotlib.pyplot as plt查看设备对象
device torch.device(cuda if torch.cuda.is_available() else cpu)
batch_size 128
device # device(typecuda)使用transform调整数据集图像本次数据集使用手势图像用于生成对抗网络的训练。其中数据集源于K同学
train_transform transforms.Compose([transforms.Resize(128),transforms.ToTensor(),transforms.Normalize([0.5,0.5,0.5], [0.5,0.5,0.5])])train_dataset datasets.ImageFolder(root./data/Gan3/rps/, transformtrain_transform)
train_loader torch.utils.data.DataLoader(datasettrain_dataset, batch_sizebatch_size, shuffleTrue,num_workers6)查看数据集图像
def show_images(images):fig, ax plt.subplots(figsize(20, 20))ax.set_xticks([]); ax.set_yticks([])ax.imshow(make_grid(images.detach(), nrow22).permute(1, 2, 0))def show_batch(dl):for images, _ in dl:show_images(images)breakshow_batch(train_loader)设置超参数
image_shape (3, 128, 128) # 生成器和判别器操作的图像的形状
image_dim int(np.prod(image_shape)) # 图像的总维度
latent_dim 100 # 生成器的输入随机噪声向量的维度n_classes 3 # 条件生成对抗网络中的类别数量
embedding_dim 100 # 类别嵌入向量的维度模型选择
条件生成对抗网络CGAN是一种生成对抗网络GAN的变体它引入了条件信息以指导生成器生成特定类型的输出。在标准的生成对抗网络中生成器的输入是一个随机噪声向量而在条件生成对抗网络中生成器的输入不仅包括随机噪声向量还包括一个额外的条件向量用于指定所需输出的特征。 例如我们需要生成器G生成一张没有阴影的图像此时判别器D就需要判断生成器所生成的图像是否是一张没有阴影的图像。条件生成对抗网络的本质是将额外添加的信息融入到生成器和判别器中其中添加的信息可以是图像的类别、人脸表情和其他辅助信息等旨在把无监督学习的GAN转化为有监督学习的CGAN便于网络能够在我们的掌控下更好地进行训练。
使用weights_init(m)对神经网络的权重进行初始化
# 自定义权重初始化函数用于初始化生成器和判别器的权重
def weights_init(m):# 获取当前层的类名classname m.__class__.__name__# 如果当前层是卷积层类名中包含 Conv if classname.find(Conv) ! -1:# 使用正态分布随机初始化权重均值为0标准差为0.02torch.nn.init.normal_(m.weight, 0.0, 0.02)# 如果当前层是批归一化层类名中包含 BatchNorm elif classname.find(BatchNorm) ! -1:# 使用正态分布随机初始化权重均值为1标准差为0.02torch.nn.init.normal_(m.weight, 1.0, 0.02)# 将偏置项初始化为全零torch.nn.init.zeros_(m.bias)构建生成器下面代码实现了一个生成器模型它接受一个随机噪声向量和一个条件标签作为输入并生成一个与条件标签相关的合成图像。首先条件标签被嵌入到一个稠密向量中将离散的标签映射到连续的嵌入空间以便与噪声向量进行合并。接下来从随机噪声向量中生成潜在向量。将噪声向量映射到一个更高维度的表示空间中最后将生成的条件标签嵌入向量和潜在向量在通道维度上合并。然后将合并后的特征图通过一系列反卷积层进行处理逐渐将其转换为与所需输出图像相同尺寸的特征图。
class Generator(nn.Module):def __init__(self):super(Generator, self).__init__()# 定义条件标签的生成器部分用于将标签映射到嵌入空间中# n_classes条件标签的总数# embedding_dim嵌入空间的维度self.label_conditioned_generator nn.Sequential(nn.Embedding(n_classes, embedding_dim), # 使用Embedding层将条件标签映射为稠密向量nn.Linear(embedding_dim, 16) # 使用线性层将稠密向量转换为更高维度)# 定义潜在向量的生成器部分用于将噪声向量映射到图像空间中# latent_dim潜在向量的维度self.latent nn.Sequential(nn.Linear(latent_dim, 4*4*512), # 使用线性层将潜在向量转换为更高维度nn.LeakyReLU(0.2, inplaceTrue) # 使用LeakyReLU激活函数进行非线性映射)# 定义生成器的主要结构将条件标签和潜在向量合并成生成的图像self.model nn.Sequential(# 反卷积层1将合并后的向量映射为64x8x8的特征图nn.ConvTranspose2d(513, 64*8, 4, 2, 1, biasFalse),nn.BatchNorm2d(64*8, momentum0.1, eps0.8), # 批标准化nn.ReLU(True), # ReLU激活函数# 反卷积层2将64x8x8的特征图映射为64x4x4的特征图nn.ConvTranspose2d(64*8, 64*4, 4, 2, 1, biasFalse),nn.BatchNorm2d(64*4, momentum0.1, eps0.8),nn.ReLU(True),# 反卷积层3将64x4x4的特征图映射为64x2x2的特征图nn.ConvTranspose2d(64*4, 64*2, 4, 2, 1, biasFalse),nn.BatchNorm2d(64*2, momentum0.1, eps0.8),nn.ReLU(True),# 反卷积层4将64x2x2的特征图映射为64x1x1的特征图nn.ConvTranspose2d(64*2, 64*1, 4, 2, 1, biasFalse),nn.BatchNorm2d(64*1, momentum0.1, eps0.8),nn.ReLU(True),# 反卷积层5将64x1x1的特征图映射为3x64x64的RGB图像nn.ConvTranspose2d(64*1, 3, 4, 2, 1, biasFalse),nn.Tanh() # 使用Tanh激活函数将生成的图像像素值映射到[-1, 1]范围内)def forward(self, inputs):noise_vector, label inputs# 通过条件标签生成器将标签映射为嵌入向量label_output self.label_conditioned_generator(label)# 将嵌入向量的形状变为batch_size, 1, 4, 4以便与潜在向量进行合并label_output label_output.view(-1, 1, 4, 4)# 通过潜在向量生成器将噪声向量映射为潜在向量latent_output self.latent(noise_vector)# 将潜在向量的形状变为batch_size, 512, 4, 4以便与条件标签进行合并latent_output latent_output.view(-1, 512, 4, 4)# 将条件标签和潜在向量在通道维度上进行合并得到合并后的特征图concat torch.cat((latent_output, label_output), dim1)# 通过生成器的主要结构将合并后的特征图生成为RGB图像image self.model(concat)return image将模型导入GPU并初始化权重查看模型
generator Generator().to(device)
generator.apply(weights_init)
print(generator)下面实现判别器模型首先条件标签被嵌入到一个特征向量中。这一步类似于生成器中的过程目的是将离散的标签映射到一个连续的嵌入空间以便与图像特征进行合并。接下来输入图像和嵌入的标签特征被拼接在一起作为鉴别器的输入。然后这个输入通过一系列卷积层和批量归一化层进行处理逐渐将其转换为一个用于区分真实和合成图像的特征表示。在每个卷积层之后都跟随着 激活函数以及批量归一化层最后通过全连接层将特征向量映射到一个单一的输出。
class Discriminator(nn.Module):def __init__(self):super(Discriminator, self).__init__()# 定义一个条件标签的嵌入层用于将类别标签转换为特征向量self.label_condition_disc nn.Sequential(nn.Embedding(n_classes, embedding_dim), # 嵌入层将类别标签编码为固定长度的向量nn.Linear(embedding_dim, 3*128*128) # 线性层将嵌入的向量转换为与图像尺寸相匹配的特征张量)# 定义主要的鉴别器模型self.model nn.Sequential(nn.Conv2d(6, 64, 4, 2, 1, biasFalse), # 输入通道为6包含图像和标签的通道数输出通道为644x4的卷积核步长为2padding为1nn.LeakyReLU(0.2, inplaceTrue), # LeakyReLU激活函数带有负斜率增加模型对输入中的负值的感知能力nn.Conv2d(64, 64*2, 4, 3, 2, biasFalse), # 输入通道为64输出通道为64*24x4的卷积核步长为3padding为2nn.BatchNorm2d(64*2, momentum0.1, eps0.8), # 批量归一化层有利于训练稳定性和收敛速度nn.LeakyReLU(0.2, inplaceTrue),nn.Conv2d(64*2, 64*4, 4, 3, 2, biasFalse), # 输入通道为64*2输出通道为64*44x4的卷积核步长为3padding为2nn.BatchNorm2d(64*4, momentum0.1, eps0.8),nn.LeakyReLU(0.2, inplaceTrue),nn.Conv2d(64*4, 64*8, 4, 3, 2, biasFalse), # 输入通道为64*4输出通道为64*84x4的卷积核步长为3padding为2nn.BatchNorm2d(64*8, momentum0.1, eps0.8),nn.LeakyReLU(0.2, inplaceTrue),nn.Flatten(), # 将特征图展平为一维向量用于后续全连接层处理nn.Dropout(0.4), # 随机失活层用于减少过拟合风险nn.Linear(4608, 1), # 全连接层将特征向量映射到输出维度为1的向量nn.Sigmoid() # Sigmoid激活函数用于输出范围限制在0到1之间的概率值)def forward(self, inputs):img, label inputs# 将类别标签转换为特征向量label_output self.label_condition_disc(label)# 重塑特征向量为与图像尺寸相匹配的特征张量label_output label_output.view(-1, 3, 128, 128)# 将图像特征和标签特征拼接在一起作为鉴别器的输入concat torch.cat((img, label_output), dim1)# 将拼接后的输入通过鉴别器模型进行前向传播得到输出结果output self.model(concat)return output将模型导入GPU并初始化权重查看模型
discriminator Discriminator().to(device)
discriminator.apply(weights_init)
print(discriminator)模型训练
设置损失函数、优化算法和学习率
adversarial_loss nn.BCELoss() def generator_loss(fake_output, label):gen_loss adversarial_loss(fake_output, label)return gen_lossdef discriminator_loss(output, label):disc_loss adversarial_loss(output, label)return disc_losslearning_rate 0.0002G_optimizer optim.Adam(generator.parameters(), lr learning_rate, betas(0.5, 0.999))
D_optimizer optim.Adam(discriminator.parameters(), lr learning_rate, betas(0.5, 0.999))开始训练
# 设置训练的总轮数
num_epochs 100
# 初始化用于存储每轮训练中判别器和生成器损失的列表
D_loss_plot, G_loss_plot [], []# 循环进行训练
for epoch in range(1, num_epochs 1):# 初始化每轮训练中判别器和生成器损失的临时列表D_loss_list, G_loss_list [], []# 遍历训练数据加载器中的数据for index, (real_images, labels) in enumerate(train_loader):# 清空判别器的梯度缓存D_optimizer.zero_grad()# 将真实图像数据和标签转移到GPU如果可用real_images real_images.to(device)labels labels.to(device)# 将标签的形状从一维向量转换为二维张量用于后续计算labels labels.unsqueeze(1).long()# 创建真实目标和虚假目标的张量用于判别器损失函数real_target Variable(torch.ones(real_images.size(0), 1).to(device))fake_target Variable(torch.zeros(real_images.size(0), 1).to(device))# 计算判别器对真实图像的损失D_real_loss discriminator_loss(discriminator((real_images, labels)), real_target)# 从噪声向量中生成假图像生成器的输入noise_vector torch.randn(real_images.size(0), latent_dim, devicedevice)noise_vector noise_vector.to(device)generated_image generator((noise_vector, labels))# 计算判别器对假图像的损失注意detach()函数用于分离生成器梯度计算图output discriminator((generated_image.detach(), labels))D_fake_loss discriminator_loss(output, fake_target)# 计算判别器总体损失真实图像损失和假图像损失的平均值D_total_loss (D_real_loss D_fake_loss) / 2D_loss_list.append(D_total_loss)# 反向传播更新判别器的参数D_total_loss.backward()D_optimizer.step()# 清空生成器的梯度缓存G_optimizer.zero_grad()# 计算生成器的损失G_loss generator_loss(discriminator((generated_image, labels)), real_target)G_loss_list.append(G_loss)# 反向传播更新生成器的参数G_loss.backward()G_optimizer.step()# 打印当前轮次的判别器和生成器的平均损失print(Epoch: [%d/%d]: D_loss: %.3f, G_loss: %.3f % ((epoch), num_epochs, torch.mean(torch.FloatTensor(D_loss_list)), torch.mean(torch.FloatTensor(G_loss_list))))# 将当前轮次的判别器和生成器的平均损失保存到列表中D_loss_plot.append(torch.mean(torch.FloatTensor(D_loss_list)))G_loss_plot.append(torch.mean(torch.FloatTensor(G_loss_list)))if epoch%10 0:# 将生成的假图像保存为图片文件save_image(generated_image.data[:50], ./images/sample_%d % epoch .png, nrow5, normalizeTrue)# 将当前轮次的生成器和判别器的权重保存到文件torch.save(generator.state_dict(), ./training_weights/generator_epoch_%d.pth % (epoch))torch.save(discriminator.state_dict(), ./training_weights/discriminator_epoch_%d.pth % (epoch))模型分析
加载模型
generator.load_state_dict(torch.load(./training_weights/generator_epoch_100.pth), strictFalse)
generator.eval()下段代码实现了在两个潜在空间点之间进行插值并使用生成器生成相应的图像。 使用generate_latent_points函数生成两个潜在空间的点。这些点是从标准正态分布中随机生成的作为生成器的输入。 使用interpolate_points函数对两个潜在空间点进行线性插值生成插值比率在0到1之间。这些比率用于在两个点之间生成一系列插值点数量为n_steps。 对于三个类别的循环分别进行插值和生成图片。对于每个类别创建包含相同类别标签的张量然后将其与插值点一起传递给生成器。生成器将生成对应类别的插值图像。生成的图像通过permute函数调整维度顺序以匹配预期的通道顺序。最后将生成的插值图像连接起来形成一个输出。
# 导入所需的库
from numpy import asarray
from numpy.random import randn
from numpy.random import randint
from numpy import linspace
from matplotlib import pyplot
from matplotlib import gridspec# 生成潜在空间的点作为生成器的输入
def generate_latent_points(latent_dim, n_samples, n_classes3):# 从标准正态分布中生成潜在空间的点x_input randn(latent_dim * n_samples)# 将生成的点整形成用于神经网络的输入的批量z_input x_input.reshape(n_samples, latent_dim)return z_input# 在两个潜在空间点之间进行均匀插值
def interpolate_points(p1, p2, n_steps10):# 在两个点之间进行插值生成插值比率ratios linspace(0, 1, numn_steps)# 线性插值向量vectors list()for ratio in ratios:v (1.0 - ratio) * p1 ratio * p2vectors.append(v)return asarray(vectors)# 生成两个潜在空间的点
pts generate_latent_points(100, 2)
# 在两个潜在空间点之间进行插值
interpolated interpolate_points(pts[0], pts[1])# 将数据转换为torch张量并将其移至GPU假设device已正确声明为GPU
interpolated torch.tensor(interpolated).to(device).type(torch.float32)output None
# 对于三个类别的循环分别进行插值和生成图片
for label in range(3):# 创建包含相同类别标签的张量labels torch.ones(10) * labellabels labels.to(device)labels labels.unsqueeze(1).long()print(labels.size())# 使用生成器生成插值结果predictions generator((interpolated, labels))predictions predictions.permute(0,2,3,1)pred predictions.detach().cpu()if output is None:output predelse:output np.concatenate((output,pred))查看图像
nrow 3
ncol 10fig plt.figure(figsize(15,4))
gs gridspec.GridSpec(nrow, ncol) k 0
for i in range(nrow):for j in range(ncol):pred (output[k, :, :, :] 1 ) * 127.5pred np.array(pred) ax plt.subplot(gs[i,j])ax.imshow(pred.astype(np.uint8))ax.set_xticklabels([])ax.set_yticklabels([])ax.axis(off)k 1 plt.show()总结
与标准GAN相比CGAN额外接收一个条件向量用于指导生成器生成特定类型的输出。从中了解如何生成样本时使用额外的条件信息未来掌握利用条件信息生成特定类别的图像或生成带有特定属性的图像。