欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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()





相关标签: GAN