pytorch 生成器-识别器模型代码

本节代码主要来自张老师,由于时代久远自己做了一些修改符合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()





发布了73 篇原创文章 · 获赞 26 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/weixin_43914889/article/details/104658501