pytorch 生成器-识别器模型代码
程序员文章站
2022-03-09 13:35:25
...
本节代码主要来自张老师,由于时代久远自己做了一些修改符合torch1.4
import torch
import torch.nn as nn
from torch.autograd import Variable
import torch.optim as optim
import torch.nn.functional as F
import torchvision.datasets as dsets
import torchvision.transforms as transforms
import torchvision.utils as vutil
import matplotlib.pyplot as plt
import numpy as np
image_size = 28 #图像尺寸大小
input_dim = 100 #输入给生成器的向量维度,维度越大可以增加生成器输出样本的多样性
num_channels = 1# 图像的通道数
num_features = 64 #生成器中间的卷积核数量
batch_size = 64 #批次大小
#1、在MINST数据集中,选出一个样本,输入数字标签,输出图像,并让输出的图像与样本图像尽可能相似,总误差最小化;
use_cuda = torch.cuda.is_available() #定义一个布尔型变量,标志当前的GPU是否可用
# 如果当前GPU可用,则将优先在GPU上进行张量计算
dtype = torch.cuda.FloatTensor if use_cuda else torch.FloatTensor
itype = torch.cuda.LongTensor if use_cuda else torch.LongTensor
# 加载MINIST数据,如果没有下载过,就会在当前路径下新建/data子目录,并把文件存放其中
# MNIST数据是属于torchvision包自带的数据,所以可以直接调用。
# 在调用自己的数据的时候,我们可以用torchvision.datasets.ImageFolder或者torch.utils.data.TensorDataset来加载
train_dataset = dsets.MNIST(root='./data', #文件存放路径
train=True, #提取训练集
transform=transforms.ToTensor(), #将图像转化为Tensor,在加载数据的时候,就可以对图像做预处理
download=True) #当找不到文件的时候,自动下载
# 加载测试数据集
test_dataset = dsets.MNIST(root='./data',
train=False,
transform=transforms.ToTensor())
# 训练数据集的加载器,自动将数据分割成batch,顺序随机打乱
train_loader = torch.utils.data.DataLoader(dataset=train_dataset,
batch_size=batch_size,
shuffle=True)
# 首先,我们定义下标数组indices,它相当于对所有test_dataset中数据的编码
# 然后定义下标indices_val来表示校验集数据的那些下标,indices_test表示测试集的下标
indices = range(len(test_dataset))
indices_val = indices[:5000]
indices_test = indices[5000:]
# 根据这些下标,构造两个数据集的SubsetRandomSampler采样器,它会对下标进行采样
sampler_val = torch.utils.data.sampler.SubsetRandomSampler(indices_val)
sampler_test = torch.utils.data.sampler.SubsetRandomSampler(indices_test)
# 根据两个采样器来定义加载器,注意将sampler_val和sampler_test分别赋值给了validation_loader和test_loader
validation_loader = torch.utils.data.DataLoader(dataset =test_dataset,
batch_size = batch_size,
sampler = sampler_val
)
test_loader = torch.utils.data.DataLoader(dataset=test_dataset,
batch_size=batch_size,
sampler = sampler_test
)
class ModelG(nn.Module):
def __init__(self):
super(ModelG, self).__init__()
self.model = nn.Sequential()
self.model.add_module('deconv1',nn.ConvTranspose2d(input_dim,num_features*2,5,2,0,bias=False))
# 利用add_module增加一个反卷积层,输入为input_dim维,输出为2*num_features维,窗口大小为5,padding是0
# 输入图像大小为1,输出图像大小为W'=(W-1)S-2P+K+P'=(1-1)*2-2*0+5+0=3, 5*5
self.model.add_module('bnorm1',nn.BatchNorm2d(num_features*2))
self.model.add_module('relu1',nn.ReLU(True))
#这里这个true什么意思就很奇怪
self.model.add_module('deconv2',nn.ConvTranspose2d(num_features*2,num_features,5,2,0,bias=False))
self.model.add_module('bnorm2',nn.BatchNorm2d(num_features))
self.model.add_module('relu2',nn.ReLU(True))
# 计算结果不会有影响。利用in-place计算可以节省内(显)存,同时还可以省去反复申请和释放内存的时间。但是会对原变量覆盖,只要不带来错误就用。
# 图像的通道数是由直接定义来的
# 图像的尺寸是反卷积的计算结果
self.model.add_module('deconv3',nn.ConvTranspose2d(num_features,num_channels,4,2,0,bias=False))
self.model.add_module('sigmoid',nn.Sigmoid())
def forward(self,x):
output = x
for name,module in self.model.named_children():
output = module(output)
return (output)
def weight_init(m):
# initial the parameter
# m是给定的class_name的类名
class_name = m.__class__.__name__
if class_name.find('conv')!=-1:
m.weight.data.normal_(0,0.002)
if class_name.find('norm') != -1:
m.weight.data.normal_(1.0,0.02)
def make_show(img):
img = img.data.expand(batch_size,3,image_size,image_size)
return img
#这个Img_show没怎么看懂
def img_show(inp,title=None,ax=None):
if inp.size()[0]>1:
inp = inp.numpy().transpose((1,2,0))
else:
inp = inp[0].numpy()
mvalue = np.amin(inp)
maxvalue = np.amax(inp)
if maxvalue > mvalue:
inp = (inp - mvalue) / (maxvalue - mvalue)
ax.imshow(inp)
if title is not None:
ax.set_title(title)
depth = [4,8]
class ConvNet(nn.Module):
def __init__(self):
super(ConvNet, self).__init__()
def forward(self,x):
x = F.relu(self.conv1(x))
x = self.pool(x)
x = F.relu(self.conv2(x))
x = self.pool(x)
# 将立体的Tensor全部转换成一维的Tensor。两次pooling操作,所以图像维度减少了1/4
x = x.view(-1, image_size // 4 * image_size // 4 * depth[1])
x = F.relu(self.fc1(x)) # 全链接,**函数
x = F.dropout(x, training=self.training) # 以默认为0.5的概率对这一层进行dropout操作
x = self.fc2(x) # 全链接,**函数
x = F.log_softmax(x, dim=1) # log_softmax可以理解为概率对数值
return x
def retrieve_features(self, x):
# 该函数专门用于提取卷积神经网络的特征图的功能,返回feature_map1, feature_map2为前两层卷积层的特征图
feature_map1 = F.relu(self.conv1(x)) # 完成第一层卷积
x = self.pool(feature_map1) # 完成第一层pooling
feature_map2 = F.relu(self.conv2(x)) # 第二层卷积,两层特征图都存储到了feature_map1, feature_map2中
return (feature_map1, feature_map2)
def rightness(predictions,target):
pred = torch.max(predictions,dim=1)[1]
rights = pred.eq(target.data.view_as(pred)).sum()
return rights,len(target)
netR = torch.load('mnist_conv_checkpoint')
netR = netR.cuda() if use_cuda else netR
for para in netR.parameters():
para.requires_grad = False
print('NOW LETS TRAIN')
netG = ModelG()
netG = netG.cuda() if use_cuda else netG
netG.apply(weight_init)
#这是一个初始化参数的方法
criterion = nn.CrossEntropyLoss() #用交叉熵作为损失函数
optimizer = optim.SGD(netG.parameters(), lr=0.0001, momentum=0.9) #定义优化器
#随机选择batch_size个数字,用他们来生成数字图像
samples1 = np.random.choice(10, batch_size)
samples1 = torch.from_numpy(samples1).type(dtype).requires_grad_(False)
num_epochs = 20 #总训练周期
statistics = [] #数据记载器
step = 0
for epoch in range(num_epochs):
train_loss = []
train_rights = []
# 加载数据
for batch_idx, (data, target) in enumerate(train_loader):
# 注意图像和标签互换了
target, data = data.clone().detach().requires_grad_(True), target.clone().detach() # data为一批标签,target为一批图像
if use_cuda:
target, data = target.cuda(), data.cuda()
# 复制标签变量放到了label中
label = data.clone()
data = data.type(dtype)
# 改变张量形状以适用于生成器网络
data = data.reshape(data.size()[0], 1, 1, 1)
data = data.expand(data.size()[0], input_dim, 1, 1)
netR.train()
netG.train()
output1 = netG(data)
output = netR(output1)
loss = criterion(output,label)
optimizer.zero_grad() # 清空梯度
loss.backward() # 反向传播
optimizer.step() # 一步随机梯度下降算法
step += 1
if use_cuda:
loss = loss.cpu()
train_loss.append(loss.data.numpy())
right = rightness(output, label) # 计算准确率所需数值,返回数值为(正确样例数,总样本数)
train_rights.append(right) # 将计算结果装到列表容器train_rights中
if step % 100 == 0: # 每间隔100个batch执行一次打印等操作
netG.eval() # 给网络模型做标记,标志说模型正在校验集上运行,
netR.eval() # 这种区分主要是为了打开关闭net的training标志,从而决定是否运行dropout
val_loss = [] # 记录校验数据集准确率的容器
val_rights = []
'''开始在校验数据集上做循环,计算校验集上面的准确度'''
for (data, target) in validation_loader:
# 注意target是图像,data是标签
target, data = data.clone().detach().requires_grad_(True), target.clone().detach()
if use_cuda:
target, data = target.cuda(), data.cuda()
label = data.clone()
data = data.type(dtype)
# 改变Tensor大小以适应生成网络
data = data.reshape(data.size()[0], 1, 1, 1)
data = data.expand(data.size()[0], input_dim, 1, 1)
output1 = netG(data) # 神经网络完成一次前馈的计算过程,得到预测输出output
output = netR(output1) # 利用识别器来识别
loss = criterion(output, label) # 将output与标签target比较,计算误差
if use_cuda:
loss = loss.cpu()
val_loss.append(loss.data.numpy())
right = rightness(output, label) # 计算准确率所需数值,返回正确的数值为(正确样例数,总样本数)
val_rights.append(right)
train_r = (sum([tup[0] for tup in train_rights]), sum([tup[1] for tup in train_rights]))
# val_r为一个二元组,分别记录校验集中分类正确的数量和该集合中总的样本数
val_r = (sum([tup[0] for tup in val_rights]), sum([tup[1] for tup in val_rights]))
print(('训练周期: {} [{}/{} ({:.0f}%)]\t训练数据Loss: {:.6f},正确率: {:.2f}%\t校验数据Loss:' +
'{:.6f},正确率:{:.2f}%').format(epoch, batch_idx * batch_size, len(train_loader.dataset),
100. * batch_idx / len(train_loader), np.mean(train_loss),
100. * train_r[0] / train_r[1],
np.mean(val_loss),
100. * val_r[0] / val_r[1]))
# 记录中间的数据
statistics.append({'loss': np.mean(train_loss), 'train': 100. * train_r[0] / train_r[1],
'valid': 100. * val_r[0] / val_r[1]})
samples = samples1.data.reshape(batch_size, 1, 1, 1)
samples = samples.data.expand(batch_size, input_dim, 1, 1)
samples = samples.cuda() if use_cuda else samples
fake_u = netG(samples)
fake_u = fake_u.cpu() if use_cuda else fake_u
img = make_show(fake_u)
vutil.save_image(img, 'temp1/fake%s.png' % (epoch))
result1 = [100 - i['train'] for i in statistics]
result2 = [100 - i['valid'] for i in statistics]
plt.figure(figsize = (10, 7))
plt.plot(result1, label = 'Training')
plt.plot(result2, label = 'Validation')
plt.xlabel('Step')
plt.ylabel('Error Rate')
plt.legend()
plt.show()
上一篇: QML元素介绍 (二)
推荐阅读
-
编辑器漏洞原理(代码生成器原理)
-
使用PyTorch实现MNIST手写体识别代码
-
pytorch 调用 deeplabv3模型,进行识别
-
CV之街景字符编码识别三----pytorch之模型训练
-
街景字符编码识别Task03-CNN发展介绍及Pytorch构建CNN模型
-
超详细PyTorch实现手写数字识别器的示例代码
-
pytorch :: Dataloader中的迭代器和生成器应用
-
网络高并发服务器之epoll接口、epoll反应堆模型详解及代码实现
-
pytorch学习笔记--nn.Module、nn.Sequential的搭建模型、损失函数、反向传播和训练器
-
Python中迭代器和生成器以及列表的介绍(附代码)