Pytorch搭建常见分类网络模型------VGG、Googlenet、 MobileNetV3、ResNet50(1)

常见的分类网络都可以分为两部分,一部分是特征提取部分,另一部分是分类部分。

1、特征提取部分的功能是对输入进来的图片进行特征提取 , 优秀的特征可以帮助更容易区分目标,所以特征提取部分一般由各类卷积组成,卷积拥有强大的特征提取能力 ;

2、分类部分会利用特征提取部分获取到的特征进行分类 ,分类部分一般由全连接组成, 特征提取部分获取到的特征一般是一维向量,可以直接进行全连接分类。

通常情况下,特征提取部分就是我们平常了解到的各种神经网络,比如VGG、Mobilenet、Resnet等等;而分类部分就是一次或者几次的全连接,最终我们会获得一个长度为num_classes的一维向量。

接下来让我们从头开始定义我们的神经网络:

1、加载并预处理数据集


# 导入库:
import torch
import torchvision
import torchvision.models

from matplotlib import pyplot as plt
from tqdm import tqdm
from torch import nn
from torch.utils.data import DataLoader
from torchvision.transforms import transforms

# 图像预处理方法:
data_transform = {
    "train": transforms.Compose([transforms.RandomResizedCrop(120),
                              #先随机采集裁剪,然后对得到的图像缩放为同一大小
                                 transforms.RandomHorizontalFlip(),
                              #以给定的概率随机水平旋转给定的PIL的图像,默认为0.5;
                                 transforms.ToTensor(),#将给定图像转为Tensor
                                 transforms.Normalize((0.5, 0.5, 0.5), 
                                       (0.5, 0.5, 0.5))]),      # 归一化处理
    "val": transforms.Compose([transforms.Resize((120, 120)),  
# 这种预处理的地方尽量别修改,修改意味着需要修改网络结构的参数
                               transforms.ToTensor(),
                               transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])}

# 数据导入方法: 导入自己的数据,自己的数据放在跟代码相同的文件夹下新建一个data文件夹,
#data文件夹里的新建一个train文件夹用于放置训练集的图片。也可以导入自带的CIFAR10数据集。
#同理新建一个val文件夹用于放置测试集的图片。
train_data = torchvision.datasets.CIFAR10(root = "../data" , train = True ,
                       download = False,transform=data_transform["train"])
#train_data = torchvision.datasets.ImageFolder(root="./data", transform=data_transform["train"])
traindata = DataLoader(dataset=train_data, batch_size=32, shuffle=True, num_workers=0)

test_data = torchvision.datasets.CIFAR10(root = "../data" , train = False ,
                         download = False,transform=data_transform["val"])
#test_data = torchvision.datasets.ImageFolder(root="../data", transform=data_transform["val"])
testdata = DataLoader(dataset=test_data, batch_size=32, shuffle=True,num_workers=0)  # windows系统下,num_workers设置为0,linux系统下可以设置多进程

train_size = len(train_data)  # 求出训练集的长度
test_size = len(test_data)  # 求出测试集的长度
print(train_size)  # 输出训练集的长度
print(test_size)  # 输出测试集的长度

# 设置调用GPU,如果有GPU就调用GPU,如果没有GPU则调用CPU训练模型
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print("using {} device.".format(device))

 输出结果:

10000
50000
using cuda:0 device.

(1)在图像预处理方法中,对图像操作过程详解 :

transforms.RandomResizedCrop(224) 将给定图像随机裁剪为不同的大小和宽高比,然后缩放所裁剪得到的图像为制定的大小;(即先随机采集,然后对裁剪得到的图像缩放为同一大小)。该操作的含义在于:即使只是该物体的一部分,也认为这是该类物体;

from matplotlib import pyplot as plt
from torchvision.transforms import transforms
from PIL import Image

img = Image.open("./11.PNG")
print("原图大小:",img.size)
data1 = transforms.RandomResizedCrop(224)(img)
print("随机裁剪后的大小:",data1.size)
data2 = transforms.RandomResizedCrop(120)(img)
data3 = transforms.RandomResizedCrop(64)(img)

plt.subplot(2,2,1),plt.imshow(img),plt.title("原图")
plt.subplot(2,2,2),plt.imshow(data1),plt.title("图1")
plt.subplot(2,2,3),plt.imshow(data2),plt.title("图2")
plt.subplot(2,2,4),plt.imshow(data3),plt.title("图3")
plt.show()

 输出结果:

 Pytorch搭建常见分类网络模型------VGG、Googlenet、 MobileNetV3、ResNet50(1)_第1张图片

 transforms.RandomHorizontalFlip() 以给定的概率随机水平旋转给定的PIL的图像,默认为0.5;

from matplotlib import pyplot as plt
from torchvision.transforms import transforms
from PIL import Image
img = Image.open("./11.PNG")
img1 = transforms.RandomHorizontalFlip()(img)
img2 = transforms.RandomHorizontalFlip()(img)
img3 = transforms.RandomHorizontalFlip()(img)
plt.subplot(2,2,1),plt.imshow(img),plt.title("原图")
plt.subplot(2,2,2),plt.imshow(img1),plt.title("图1")
plt.subplot(2,2,3),plt.imshow(img2),plt.title("图2")
plt.subplot(2,2,4),plt.imshow(img3),plt.title("图3")
plt.show()

Pytorch搭建常见分类网络模型------VGG、Googlenet、 MobileNetV3、ResNet50(1)_第2张图片

(2)数据导入方法:

PyTorch基础-自定义数据集和数据加载器(2)_一只小小的土拨鼠的博客-CSDN博客

(3)导入指定设备

CPU和GPU的训练差别太大了,推荐使用GPU训练,例如cpu2分钟10%,gpu 1:30训练一轮。

 torch.device用法总结:

使用GPU进行深度学习环境的搭建_一只小小的土拨鼠的博客-CSDN博客

torch.device代表将 torch.Tensor分配到的设备的对象。 torch.device包含一个设备类型( 'cpu''cuda'设备类型)和可选的设备的序号。如果设备序号不存在,则为当前设备;

#通过字符串构造设备 
torch.device('cpu')
torch.device('cuda')  # current cuda device
#通过字符串+序号构造设备 
torch.device('cuda', 0)
torch.device('cpu', 0)
#可直接使用字符串构建 
cuda1 = torch.device('cuda:0')
torch.randn((2,3), device=cuda1)
#有cuda的也可直接输入序号 
torch.device(1)

判断使用的设备是哪一种:

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print("using {} device.".format(device))

2、各种模型的搭建

(1) VGG16 网络介绍

该模型参加2014年的 ImageNet图像分类与定位挑战赛,取得了优异成绩:在分类任务上排名第二,在定位任务上排名第一。VGG的Classification模型从原理上并没有与传统的CNN模型有太大不同:

训练:各种数据Augmentation(剪裁,不同大小,调亮度,饱和度,对比度,偏色),剪裁送入CNN模型,Softmax,Backprop。

测试:尽量把测试数据又各种Augmenting(剪裁,不同大小),把测试数据各种Augmenting后在训练的不同模型上的结果再继续Averaging出最后的结果。

本例程使用pytorch框架复现了一下VGG代码。同时画出训练集的loss 、accuracy 和测试集的loss、accuracy的折线图

它的结构如下图所示:

Pytorch搭建常见分类网络模型------VGG、Googlenet、 MobileNetV3、ResNet50(1)_第3张图片

这是一个VGG16的结构图,很好的反应了VGG16的结构,整个VGG16由三种不同的层组成,分别是卷积层、最大池化层、全连接层。
VGG16的具体执行方式如下:

  • 一张原始图片被resize到(224,224,3)。
  • conv1:进行两次[3,3]卷积网络,输出的特征层为64,输出为(224,224,64),再进行2X2最大池化,输出net为(112,112,64)。
  • conv2:进行两次[3,3]卷积网络,输出的特征层为128,输出net为(112,112,128),再进行2X2最大池化,输出net为(56,56,128)。
  • conv3:进行三次[3,3]卷积网络,输出的特征层为256,输出net为(56,56,256),再进行2X2最大池化,输出net为(28,28,256)。
  • conv4:进行三次[3,3]卷积网络,输出的特征层为512,输出net为(28,28,512),再进行2X2最大池化,输出net为(14,14,512)。
  • conv5:进行三次[3,3]卷积网络,输出的特征层为512,输出net为(14,14,512),再进行2X2最大池化,输出net为(7,7,512)。
  • 对结果进行展平。
  • 进行两次神经元为4096的全连接层。
  • 全连接到1000维上,用于进行分类。最后输出的就是每个类的预测。

(2) VGG16 网络模型搭建

这里我的电脑gpu显存不够,所以图片大小只有(120,120),最终特征层net输出为(3,3,512)

class VGG(nn.Module):
    def __init__(self, features, num_classes=10, init_weights=False):  # 自己是几种就把这个7改成几
        super(VGG, self).__init__()
        self.features = features
        #self.avgpool = nn.AdaptiveAvgPool2d((3, 3))
        self.classifier = nn.Sequential(
            nn.Linear(512*3*3, 4096),
            nn.ReLU(True),
            nn.Dropout(p=0.5),
            nn.Linear(4096, 4096),
            nn.ReLU(True),
            nn.Dropout(p=0.5),
            nn.Linear(4096, num_classes)
        )
        if init_weights:
            self._initialize_weights()  # 参数初始化

    def forward(self, x):
        # N x 3 x 224 x 224
        x = self.features(x)
        #x = self.avgpool(x)
        # N x 512 x 7 x 7
        x = torch.flatten(x, start_dim=1) #     x = torch.flatten(x, 1)
        # N x 512*7*7
        x = self.classifier(x)
        return x

    def _initialize_weights(self):
        for m in self.modules():  # 遍历各个层进行参数初始化
            if isinstance(m, nn.Conv2d):  # 如果是卷积层的话 进行下方初始化
                # nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')# 正态分布初始化
                nn.init.xavier_uniform_(m.weight)  # 均匀分布初始化
                if m.bias is not None:
                    nn.init.constant_(m.bias, 0)  # 如果偏置不是0 将偏置置成0  相当于对偏置进行初始化
            elif isinstance(m, nn.Linear):  # 如果是全连接层
                nn.init.xavier_uniform_(m.weight)  # 也进行正态分布初始化
                # nn.init.normal_(m.weight, 0, 0.01)
                nn.init.constant_(m.bias, 0)  # 将所有偏执置为0


def make_features(cfg: list):
    layers = []
    in_channels = 3
    for v in cfg:
        if v == "M":
            layers += [nn.MaxPool2d(kernel_size=2, stride=2)]
        else:
            conv2d = nn.Conv2d(in_channels, v, kernel_size=3, padding=1)
            layers += [conv2d, nn.ReLU(True)]
            in_channels = v
    return nn.Sequential(*layers)


cfgs = {
    'vgg11': [64, 'M', 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'],
    'vgg13': [64, 64, 'M', 128, 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'],
    'vgg16': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 'M', 512, 512, 512, 'M', 512, 512, 512, 'M'],
    'vgg19': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 256, 'M', 512, 512, 512, 512, 'M', 512, 512, 512, 512, 'M'],
}


def vgg(model_name="vgg19", **kwargs):
    assert model_name in cfgs, "Warning: model number {} not in cfgs dict!".format(model_name)
    cfg = cfgs[model_name]

    model = VGG(make_features(cfg), **kwargs)
    return model


VGGnet = vgg(num_classes=10, init_weights=True)  # 将模型命名为#自己是几种就把这个7改成几
VGGnet.to(device)
print(VGGnet.to(device))  # 输出模型结构


# 设置训练需要的参数,epoch,学习率learning 优化器。损失函数。
epoch = 1  # 这里是训练的轮数
learning = 0.0001  # 学习率
optimizer = torch.optim.Adam(VGGnet.parameters(), lr=learning)  # 优化器
loss = nn.CrossEntropyLoss()  # 损失函数

#设置四个空数组,用来存放训练集的loss和accuracy    测试集的loss和 accuracy
train_loss_all = []
train_accur_all = []
test_loss_all = []
test_accur_all = []


#开始训练:
for i in range(epoch):  # 开始迭代
    train_loss = 0  # 训练集的损失初始设为0
    train_num = 0.0  #
    train_accuracy = 0.0  # 训练集的准确率初始设为0
    VGGnet.train()  # 将模型设置成 训练模式
    train_bar = tqdm(traindata)  # 用于进度条显示,没啥实际用处
    for step, data in enumerate(train_bar):  # 开始迭代跑, enumerate这个函数不懂可以查查,将训练集分为 data是序号,data是数据
        img, target = data  # 将data 分位 img图片,target标签
        optimizer.zero_grad()  # 清空历史梯度
        outputs = VGGnet(img.to(device))  # 将图片打入网络进行训练,outputs是输出的结果

        loss1 = loss(outputs, target.to(device))  # 计算神经网络输出的结果outputs与图片真实标签target的差别-这就是我们通常情况下称为的损失
        outputs = torch.argmax(outputs, 1)  # 会输出10个值,最大的值就是我们预测的结果 求最大值
        loss1.backward()  # 神经网络反向传播
        optimizer.step()  # 梯度优化 用上面的abam优化
        train_loss += abs(loss1.item()) * img.size(0)  # 将所有损失的绝对值加起来
        accuracy = torch.sum(outputs == target.to(device))  # outputs == target的 即使预测正确的,统计预测正确的个数,从而计算准确率
        train_accuracy = train_accuracy + accuracy  # 求训练集的准确率
        train_num += img.size(0)  #

    print("epoch:{} , train-Loss:{} , train-accuracy:{}".format(i + 1, train_loss / train_num,  # 输出训练情况
                                                                train_accuracy / train_num))
    train_loss_all.append(train_loss / train_num)  # 将训练的损失放到一个列表里 方便后续画图
    train_accur_all.append(train_accuracy.double().item() / train_num)  # 训练集的准确率
    test_loss = 0  # 同上 测试损失
    test_accuracy = 0.0  # 测试准确率
    test_num = 0



    VGGnet.eval()  # 将模型调整为测试模型
    with torch.no_grad():  # 清空历史梯度,进行测试  与训练最大的区别是测试过程中取消了反向传播
        test_bar = tqdm(testdata)
        for data in test_bar:
            img, target = data

            outputs = VGGnet(img.to(device))
            loss2 = loss(outputs, target.to(device))
            outputs = torch.argmax(outputs, 1)
            test_loss = test_loss + abs(loss2.item()) * img.size(0)
            accuracy = torch.sum(outputs == target.to(device))
            test_accuracy = test_accuracy + accuracy
            test_num += img.size(0)

    print("test-Loss:{} , test-accuracy:{}".format(test_loss / test_num, test_accuracy / test_num))
    test_loss_all.append(test_loss / test_num)
    test_accur_all.append(test_accuracy.double().item() / test_num)

# 下面的是画图过程,将上述存放的列表  画出来即可
plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.plot(range(epoch), train_loss_all,
         "ro-", label="Train loss")
plt.plot(range(epoch), test_loss_all,
         "bs-", label="test loss")
plt.legend()
plt.xlabel("epoch")
plt.ylabel("Loss")
plt.subplot(1, 2, 2)
plt.plot(range(epoch), train_accur_all,
         "ro-", label="Train accur")
plt.plot(range(epoch), test_accur_all,
         "bs-", label="test accur")
plt.xlabel("epoch")
plt.ylabel("acc")
plt.legend()
plt.show()

torch.save(VGGnet, "VGG.pth")
print("模型已保存")

 这里是gitub源码:GitHub - Brave-you/VGG16

池化层主要分为两类:最大值(Max)池化层,均值(Avg)池化层。前者用取最大值的方式抽取样本,后者用平均值的方式抽取样本。 其中nn.AdaptiveAvgPool2d是二维自适应平均池化运算 ,自适应池化层。函数通过输入原始尺寸和目标尺寸,自适应地计算核的大小和每次移动的步长。对于任何输入大小,输出大小均为指定的H×W大小。

#torch.nn.AdaptiveAvgPool2d(output_size)
import torch
from torch import nn
img=torch.arange(24,dtype=torch.float).reshape(1,1,4,6)
pool_1=nn.AdaptiveAvgPool2d((2,3))
pool_2=nn.AdaptiveAvgPool2d(2)
img_1=pool_1(img)
img_2=pool_2(img)
print(img)
print(img_1)
print(img_2)

输出结果:

tensor([[[[ 0.,  1.,  2.,  3.,  4.,  5.],
          [ 6.,  7.,  8.,  9., 10., 11.],
          [12., 13., 14., 15., 16., 17.],
          [18., 19., 20., 21., 22., 23.]]]])
tensor([[[[ 3.5000,  5.5000,  7.5000],
          [15.5000, 17.5000, 19.5000]]]])
tensor([[[[ 4.,  7.],
          [16., 19.]]]])

output_size指定的输出大小,可以是元组(H,W),或者是单个的数,如果是单个的数,则表示输出的高和宽尺寸一样, output_size大小可以大于输入的图片尺寸大小。 当 output_size指定为1时,相当于全局平均池化

  • AdaptivePooling的核的大小和步长是函数自己计算的, 不需要人为设定 ;而General Pooling需要指定核的大小和步长。
  • AdaptivePooling的 核是可变大小的,且步长也是动态的 ;而General Pooling是固定核的大小和步长的。
  • AdaptivePooling的 相邻池化窗口之间是可以出现重叠的 ;General Pooling作用于图像中不重叠的区域。(也存在OverlappingPooling(重叠池化层))

第一种情况-输入尺寸是输出尺寸的整数倍
自适应层的核的大小相同且不重叠,AdaptivePooling可以改写为General Pooling,核的大小和步长的计算规则如下:

#input_size: 输入尺寸 output_size: 输出尺寸
stride = intput_size // output_size #步长
kernel_size = input_size - ( output_size -1 ) * stride #核的尺寸
padding = 0

第二种情况-输入尺寸不是输出尺寸的整数倍
在这种情况下,自适应层的核是可变大小的,且可能互相重叠,此时固定步长和核的尺寸的General Pooling不能改写AdaptivePooling。详细解释如下:

AdaptiveAvgPool2d理解(中网、外网整合)_xiyou__的博客-CSDN博客_adaptiveavgpool2d

权重初始化代码 init.kaiming_uniform_和kaiming_normal_ 可以使得输入值x的方差和经过网络层后的输出值y的方差一致。 神经网络要优化一个非常复杂的非线性模型,而且基本没有全局最优解,初始化在其中扮演着非常重要的作用,尤其在没有BN等技术的早期,它直接影响模型能否收敛。

好的初始化应该满足以下两个条件: (1) 让神经元各层激活值不会出现饱和现象; (2) 各层激活值也不能为0。 权重初始化的目的是防止在深度神经网络的正向(前向)传播过程中层激活函数的输出损失梯度出现爆炸或消失。随机初始化就是搞一些很小的值进行初始化,实验表明大了就容易饱和,小的就激活不动。 如果使用了BatchNorm的话,不同的初始化方法结果差不多 ,说明使用BN可以使得初始化不那么敏感了。

pytorch默认使用 kaiming正态分布 初始化卷积层参数。,所以不用手动初始化,权重是用的0均值高斯分布, 偏置是0均值0方差的均匀分布

torch.nn.init.kaiming_normal_   # kaiming正态分布 :
	(tensor, a=0, mode='fan_in', nonlinearity='leaky_relu')
torch.nn.init.xavier_normal_(w, #xavier正态分布 
                        gain=torch.nn.init.calculate_gain('relu'))
torch.nn.init.kaiming_uniform_   # kaiming均匀分布:
	(tensor, a=0, mode='fan_in', nonlinearity='leaky_relu')
torch.nn.init.xavier_uniform_(w,  #xavier均匀分布
                        gain=torch.nn.init.calculate_gain('relu'))

Pytorch搭建常见分类网络模型------VGG、Googlenet、 MobileNetV3、ResNet50(1)_第4张图片

选择"fan_in"可以保留前向计算中权重方差的大小。选择"fan_out"将保留后向传播的方差大小。 对于Linear模型,fan_in就是输入size,fan_out就是输出size。 详细解释如下:pytorch学习:xavier分布和kaiming分布_UQI-LIUWJ的博客-CSDN博客_xavier分布

 #torch.nn.init.constant_(tensor,val),初始化参数使其为常值,即每个参数值都相同。
 #一般是给网络中bias进行初始化。val:常量数值
nn.init.constant_(m.bias, 0)
#torch.init.normal_(tensor,mean=,std=) ,给tensor初始化,一般是给网络中参数weight初始化,
#初始化参数值符合正态分布。mean:均值,std:正态分布的标准差
nn.init.normal_(m.weight, 0, 0.01)

import torch
import torch.nn as nn
l=nn.Conv2d(2,2,kernel_size=1)
a=l.weight
b=l.bias
print("a:",a)
print("b:",b)
c=nn.init.normal_(l.weight,mean=0,std=0.01)
d=nn.init.constant_(l.bias,val=1)
print("c:",c)
print("d:",d)

输出结果:

a: Parameter containing:
tensor([[[[ 0.4417]],
         [[-0.6113]]],
        [[[-0.2329]],
         [[ 0.3184]]]], requires_grad=True)

b: Parameter containing:
tensor([ 0.3144, -0.1925], requires_grad=True)

c: Parameter containing:
tensor([[[[ 0.0011]],
         [[-0.0047]]],
        [[[-0.0147]],
         [[ 0.0061]]]], requires_grad=True)

d: Parameter containing:
tensor([1., 1.], requires_grad=True)

nn.BatchNorm2D() 进行的操作是对channel维度就行批归一化,卷积层之后总会添加BatchNorm2d进行数据的归一化处理,这使得数据在进行Relu之前不会因为数据过大而导致网络性能的不稳定。

torch.nn.BatchNorm2d(features, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
  • features:对一个输入参数为batch×features×height×width张量,features即为其中特征的数量
  • eps:分母中添加的一个值,目的是为了计算的稳定性,默认为:1e-5
  • momentum:一个用于运行过程中均值和方差的一个估计参数
  • affine:当设为true时,给定可以学习的系数矩阵gamma和beta,表示weight和bias将被使用

具体的计算过程是:

Pytorch搭建常见分类网络模型------VGG、Googlenet、 MobileNetV3、ResNet50(1)_第5张图片

 其中,x_{i}是之前的元素, \bar{x}是channel维度上的均值, σ ^{2}是channel 维度上的标准差, ϵ  是一个系数因子, 防止分母为0,默认为 10^{-5}。gamma和beta为一维数组。

import torch
import torch.nn as nn
m=nn.BatchNorm2d(2,affine=True) #affine参数设为True表示weight和bias将被使用
input=torch.randn(1,2,2,3)
output=m(input)

print(input)#输出当前张量
print(m.weight)#未经过反向传播,权重全为1  gamma
print(m.bias)  #偏置全为0               beta
print(output)  #经过BatchNorm2d后的输出

print("输入的第一个维度:")
print(input[0][0]) #这个数据是第一个2*3的二维矩阵,即input的
#求第一个维度的均值和方差
firstDimenMean=torch.Tensor.mean(input[0][0])#第一个维度的均值
firstDimenVar=torch.Tensor.var(input[0][0],False)   #false表示贝塞尔校正不会被使用,第一个维度的方差
print(m)  # 输出BatchNorm2d的结构参数
print(firstDimenMean)
print(firstDimenVar)
batchnormone=((input[0][0][0][0]-firstDimenMean)/(torch.pow(firstDimenVar,0.5)+m.eps))\
    *m.weight[0]+m.bias[0]                   #根据公式计算第一个维度的输出
print(batchnormone)

输出结果:[(-1.3369+0.4498)/( \sqrt{}0.9422 + 10^{-5})]*1+0=0.9139

tensor([[[[-1.3369,  1.0957, -0.8165],
          [-0.5185,  0.5142, -1.6370]],

         [[ 1.0270,  0.1169, -0.1541],
          [-0.7898, -1.7316,  1.3420]]]])
Parameter containing:
tensor([1., 1.], requires_grad=True)
Parameter containing:
tensor([0., 0.], requires_grad=True)
tensor([[[[-0.9139,  1.5922, -0.3777],
          [-0.0707,  0.9931, -1.2230]],

         [[ 1.0163,  0.1426, -0.1176],
          [-0.7280, -1.6321,  1.3188]]]], grad_fn=)
输入的第一个维度:
tensor([[-1.3369,  1.0957, -0.8165],
        [-0.5185,  0.5142, -1.6370]])
BatchNorm2d(2, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
tensor(-0.4498)
tensor(0.9422)
tensor(-0.9139, grad_fn=)

 net.train()、net.eval():两者将网络中每一层的training分别设置为True和False

#一般在训练模型的代码段加入:
model.train()
#在测试模型时候加入:
model.eval()
  • model.eval(),不启用 BatchNormalization 和 Dropout。此时pytorch会自动把BN和DropOut固定住,不会取平均,而是用训练好的值。不然的话,一旦test的batch_size过小,很容易就会因BN层导致模型performance损失较大;
  • model.train() :启用 BatchNormalization 和 Dropout。 在模型测试阶段使用model.train() 让model变成训练模式,此时 dropout和batch normalization的操作在训练q起到防止网络过拟合的问题。

因此,在使用PyTorch进行训练和测试时一定要记得把实例化的model指定train/eval。model.eval() 负责改变batchnorm、dropout的工作方式,在eval()模式下,dropout是不工作的。

Tqdm 是一个快速,可扩展的Python进度条,可以在 Python 长循环中添加一个进度提示信息,用户只需要封装任意的迭代器 tqdm(iterator)。

from tqdm import tqdm
for i in tqdm(range(1000)):  
     #do something
     pass  

plt.figure()参数使用详解

  • figure(num=None, figsize=None, dpi=None, facecolor=None, edgecolor=None, frameon=True)figsize:指定figure的宽和高,单位为英寸; num:图像编号或名称,数字为编号 ,字符串为名称。dpi参数指定绘图对象分辨率。facecolor:背景颜色。edgecolor:边框颜色。frameon:是否显示边框
  •  subplot创建单个子图,subplot(nrows,ncols,sharex,sharey,subplot_kw,**fig_kw)。numRows 是行数和 numCols 是列数。plotNum 参数指定创建的对象所在的区域 。
  • plt. plot()函数,plt.plot(x, y, format_string, **kwargs)。x:X轴数据,列表或数组。y:Y轴数据,列表或数组 。format_string:控制曲线的格式字符串。

  • xticks()函数设置X轴方法--刻度、标签 ,一个是刻标(locs),一个是刻度标签(tick labels)。xticks(ticks, [labels], **kwargs)。参数说明 : ticks:数组类型,用于设置X轴刻度间隔 。[labels]:数组类型,用于设置每个间隔的显示标签 。**kwargs:用于设置标签字体倾斜度和颜色等外观属性。

  • plt.legend()的几种用法 ,设置图列位置 ,设置图例边框及背景,设置图例标题 ,设置图例名字及对应关系 。详细用法如下:

  • plt.legend()的几种用法_没有梦想的研究生的博客-CSDN博客_plt.legend

    from matplotlib import pyplot as plt
    import random
    #3 创建另一个曲线的数据
    x = range(60)
    y = [random.uniform(15,20)for i in x]
    y_another = [random.uniform(5,10)for i in x]
    #输入两个曲线的信息
    plt.figure( figsize=(12,8), dpi=80 )
    plt.plot(x, y, color='r', linestyle='--', label = 'ShangHai')
    plt.plot(x, y_another, color='g', linestyle='-.', label = 'BeiJing')
    #显示图例
    plt.legend() #默认loc=Best
    #设置刻度及步长
    z = range(40)
    x_label = ['11:{}'.format(i) for i in x]
    plt.xticks( x[::5], x_label[::5])#刻度0-60,间隔为5,刻度标签0-60,间隔为5
    plt.yticks(z[::5])  #5是步长,#刻度0-40,间隔为5
    #添加网格信息
    plt.grid(True, linestyle='--', alpha=0.5) #默认是True,风格设置为虚线,alpha为透明度
    #添加标题
    plt.xlabel('Time')
    plt.ylabel('Temperature')
    plt.title('Curve of Temperature Change with Time')
    plt.show()

    输出结果:Pytorch搭建常见分类网络模型------VGG、Googlenet、 MobileNetV3、ResNet50(1)_第6张图片

你可能感兴趣的:(pytorch,分类,深度学习)