pytorch教程第四章分类器代码详细注释

pytorch教程第四章分类器代码详细注释

import torch
import torchvision          # 导入torchvision的包,包含了处理一些基本图像数据集的方法
import torchvision.transforms as transforms       # transforms主要用于数据预处理

# Compose里面的参数实际上是个列表,列表里的元素是你要执行的transform的操作
# ToTensor是指把PIL.Image(RGB) 或者numpy.ndarray(H x W x C) 从0到255的值映射到0到1的范围内,并转化成Tensor格式。
# Normalize(mean,std)是通过此公式实现数据归一化:channel=(channel-mean)/std
transform = transforms.Compose(
    [transforms.ToTensor(),
     transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]) # ToTensor()能够把灰度范围从0-255变换到0-1之间,而后面的transform.Normalize()则把0-1变换到(-1,1).


# datasets.CIFAR10函数加载数据库,CIFAR10有60000张图片,50000张为训练集,10000张是测试集
# root接相对目录./data
# train表示是否加载数据库的训练集,false的时候加载测试集
# download为true时自动在网上下载数据并解压
# transform表示是否需要对数据进行预处理,none为不进行预处理
trainset = torchvision.datasets.CIFAR10(root='./data', train=True,
                                        download=True, transform=transform)

# 训练神经网络时,需要mini-batach(一次输入多张图片),所以使用DataLoader工具包,将50000张图片没四张一分
# shufflt=True表示不同批次的数据遍历时打乱顺序
# num_workers=0表示不使用多进程
trainloader = torch.utils.data.DataLoader(trainset, batch_size=4,
                                          shuffle=True, num_workers=0)

testset = torchvision.datasets.CIFAR10(root='./data', train=False,
                                       download=False, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=4,
                                         shuffle=False, num_workers=0)

classes = ('plane', 'car', 'bird', 'cat',
           'deer', 'dog', 'frog', 'horse', 'ship', 'truck')

import matplotlib.pyplot as plt     # matplotlib.pyplot是pthon的2D绘图库
import numpy as np

# 展示图像的函数


def imshow(img):
    img = img / 2 + 0.5     # unnormalize,反归一化,因为归一话的时候是先减去平均值0.5 ,然后再除以标准偏差0.5。那么反归一化就是先乘以0.5,再加0.5
    npimg = img.numpy()
    plt.imshow(np.transpose(npimg, (1, 2, 0)))

# plt.imshow在现实的时候输入的是(imagesize,imagesize,channels)
# imshow(img)里的img格式为(channels,imagesize,imagesize),调用一次np.transpose函数,将npimg的数据格式转转
# transpose()函数的参数就是改变索引值的地方,对应正常索引索引为(0,1,2)时的调换顺序,此处(1,2,0)即将第一个索引放到最后

# 获取随机数据
dataiter = iter(trainloader)       # iter()生成迭代器
images, labels = dataiter.next()   # 取值时返回了图片的值和索引值,加逗号可以分别赋给两个变量
                                   # next()但会迭代器的下一个项目,要配合iter()函数一起使用
'''
print(class(images)) 
print(class(images))
print(images.size())    %注意size后没有()会报错
print(labels.size())
'''
# 展示图像
imshow(torchvision.utils.make_grid(images))   # make_grid可以将若干图像拼成一幅图像
plt.show()                                    # 接在imshow后用于显示图片,注意该命令会阻止程序继续运行直到关闭图片

# 显示图像标签
# .join() 方法用于将序列中的元素以指定的字符连接生成一个新的字符串,' '.join即用空格连接
# %5s :表示输出字符串至少5个字符,不够5个的话,左侧用空格补;注意格式化输出与右侧变量之间要加%
print(' '.join('%5s' % classes[labels[j]] for j in range(4)))


import torch.nn as nn                # 常用神经网络工具箱,nn包依赖autograd包来定义模型并求导。 一个nn.Module包含各个层和一个forward(input)方法,该方法返回output
import torch.nn.functional as F

# 定义卷积网络
class Net(nn.Module):
    # 定义Net的初始化函数,定义神经网络的基本结构
    def __init__(self):
        # super()是用于调用父类(超类)的一个方法
        super(Net, self).__init__()     # 复制并使用Net的父类的初始化方法,即先运行nn.Module的初始化函数
        # 设置卷积层
        # torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True)
        # kernel_size可以设置为1个int型作者一个(int,int)型的元组
        # 定义conv1函数的是图像卷积函数:输入为图像(3个频道,即彩色图),输出为6张特征图,卷积核为5x5正方形
        self.conv1 = nn.Conv2d(3, 6, 5)  # 定义conv1函数的是图像卷积函数:输入为图像(3个频道,即彩色图),输出为6张特征图,卷积核为5x5正方形

        # Pooling操作
        # torch.nn.MaxPool2d(kernel_size, stride=None, padding=0, dilation=1, return_indices=False, ceil_mode=False)
        # kernel_size(int or tuple) - max pooling的窗口大小
        # stride(int or tuple, optional) - max pooling的窗口移动的步长
        self.pool = nn.MaxPool2d(2, 2)

        self.conv2 = nn.Conv2d(6, 16, 5)

        # 设置全连接层,全连接层的输入与输出都是二维张量
        # linear表示线性函数,即y = w * x + b
        # torch.nn.Linear(in_features,out_features,bias=True)
        # in_features指的是输入的二维张量的大小
        # out_features指的是输出的二维张量的大小,也代表该全连接层的神经元个数
        self.fc1 = nn.Linear(16 * 5 * 5, 120)

        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)

    # 定义该神经网络的向前传播函数,必须定义且定义后自动生成向后传播函数(atuograd
    def forward(self, x):

        # relu为激活函数,得到的SGD的收敛速度会比sigmoid/tanh快很多。有人说这是因为它是linear,而且non-saturating
        # 缺点是 learning rate太大时神经元会dead
        # ”dead“问题无法解决时,可以尝试Leaky ReLU、PReLU 、RReLU等Relu变体来替代ReLU
        # 不建议使用 sigmoid,如果一定要使用,也可以用 tanh来替代
        # 激活函数是用来加入非线性因素的,提高神经网络对模型的表达能力,解决线性模型所不能解决的问题
        x = self.pool(F.relu(self.conv1(x)))   # 输入x经过卷积conv1之后,经过激活函数relU(原来这个词是激活函数的意思),使用2x2的窗口进行最大池化Max pooling,然后更新到x。

        x = self.pool(F.relu(self.conv2(x)))

        # torch里的view函数相当于numpy的reshape
        # -1表示不确定的数,此处即表示不确定reshape成几行
        x = x.view(-1, 16 * 5 * 5)

        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x


net = Net()         # 类的实例化,net成为Net类的对象,或者说是Net类的实例

# 定义损失函数和优化器
import torch.optim as optim     # 实现各种优化算法的库

criterion = nn.CrossEntropyLoss()   #nn.CrossEntropyLoss()该损失函数用于多分类问题,计算交叉熵
                                    #交叉熵主要是用来判定实际的输出与期望的输出的接近程度

# 为了使用torch.optim,你需要构建一个optimizer对象。这个对象能够保持当前参数状态并基于计算得到的梯度进行参数更新
# 可以设置optimizer的参数选项,比如学习率,权重衰减,下面举例SGD优化算法
# torch.optim.SGD(params, lr=<required parameter>, momentum=0, dampening=0, weight_decay=0, nesterov=False)
# params (iterable) – 待优化参数的iterable或者是定义了参数组的dict
# lr (float) – 学习率
# momentum (float, 可选) – 动量因子(默认:0)
# weight_decay (float, 可选) – 权重衰减(L2惩罚)(默认:0)
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)

# 训练网络,在数据迭代器上循环
for epoch in range(2):  # 多批次循环,epoch是循环标志,循环中不会用到,range()指代循环次数
    running_loss = 0.0

    # enumerate(sequence, [start=0])用于将一个可遍历的数据对象组合为一个索引序列,用时列出数据和数据下标,一般用在for循环当中
    # sequence -- 一个序列、迭代器或其他支持迭代对象
    # start -- 下标起始位置
    for i, data in enumerate(trainloader, 0):           # 这里的顺序使得i默认是trainloader的索引号,data是数据
        # 获取输入
        inputs, labels = data

        # 梯度置0
        optimizer.zero_grad()

        # 正向传播,反向传播,优化
        outputs = net(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()

        # 打印状态信息
        running_loss += loss.item()
        if i % 2000 == 1999:    # 每2000批次打印一次
            print('[%d, %5d] loss: %.3f' %
                  (epoch + 1, i + 1, running_loss / 2000))   # 给epoch+1是因为想显示从1开始输出
            running_loss = 0.0

print('Finished Training')
dataiter = iter(testloader)
images, labels = dataiter.next()

# 显示图片
imshow(torchvision.utils.make_grid(images))
print('GroundTruth: ', ' '.join('%5s' % classes[labels[j]] for j in range(4)))
plt.show()

outputs = net(images)                   # 输出为十个标签的能量

#print(outputs.size())
_, predicted = torch.max(outputs, 1)    # 能量越大表示认为是这个类别的概率越高,故取最大能量的标签
                                        # torch.max(outputs, 1)返回两个张量结果,第一个张量为outputs中每行最大值,第二个张量为最大值在每行的位置

print('Predicted: ', ' '.join('%5s' % classes[predicted[j]]
                              for j in range(4)))

correct = 0
total = 0
with torch.no_grad():
    for data in testloader:
        images, labels = data
        outputs = net(images)
        _, predicted = torch.max(outputs.data, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()         #(predicted == labels)返回一个张量,其元素为布尔型
                                                              #.sum()把布尔值取和,即true为1相加,得到只有一个值的张量
                                                              #.item()取该张量的值

print('Accuracy of the network on the 10000 test images: %d %%' % (
    100 * correct / total))

class_correct = list(0. for i in range(10))
class_total = list(0. for i in range(10))
with torch.no_grad():
    for data in testloader:
        images, labels = data
        outputs = net(images)
        _, predicted = torch.max(outputs, 1)
        c = (predicted == labels).squeeze()      # squeeze(a,axis = None),axis用于指定需要删除的单维度(shape中为1的维度)。axis为空,则删除所有单纬度条目
                                                 # 测试时不加squeeze()也可以
                                                 # c类似tensor([False, False, False, False])
        for i in range(4):
            label = labels[i]
            class_correct[label] += c[i].item()  # 如果c中此处元素为true则加1
            class_total[label] += 1


for i in range(10):
    print('Accuracy of %5s : %2d %%' % (
        classes[i], 100 * class_correct[i] / class_total[i]))
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值