Pytorch基础

第一章 Pytorch加载数据

1.1 Pytorch加载数据初识

(一) 常见的数据组织形式
  • 每一类事物分别放在单独的文件夹内,文件夹的名称即为该类的名字;
  • 将数据集划分为训练集和测试集两个文件夹(即每类事物混合放),并会提供一个训练集的数据对应的label文件;
  • 直接在数据文件的命名中体现出label名。
(二) 使用的类

pytorch中加载数据主要涉及两个类——Dataset、Dataloader

直白的说:

  • Dataset作用:提供一种方式获取数据以及数据对应的label,获取总共有多少数据;
  • Dataloader作用:为后面的网络提供不同的数据形式,如:mini_batch大小、是否打乱等。

1.2 Dataset类

Dataset是一个抽象类,所有的数据集都要继承这个类,所有的子类都应该重写__getitem__魔法方法(获取每一个数据及其对应的label),可以选择重写__len__魔法方法(获取数据的总长度)

在Python中我们可以使用__getitem____len__等方法去创建类似于序列和映射的类。这种方法的好处是可以像列表一样使用索引功能访问元素。

(一) __getitem__()魔法方法

如果在类中定义了__getitem__()方法,那么他的实例对象(假设为P)就可以这样P[idx]取值。当实例对象做P[idx]运算时,就会调用类中的__getitem__()方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class DataTest:
def __init__(self, id, address):
self.id = [1, 2, 3, 4]
self.address = address

def __getitem__(self, idx):
print('调用了__getitem__方法')
return self.id[idx]


data = DataTest(1, "192.168.2.11")
print(data[2])
# 调用了__getitem__方法
# 3
(二) __len__()魔法方法

如果一个类表现得像一个list,要获取有多少个元素,就得用len()函数。要让len()函数工作正常,类必须提供一个特殊方法__len__(),它返回元素的个数。

  • 类的对象能否使用len()函数,仅仅取决于其是否实现了__len__()函数而已。正如基本的str,tuple,list,dict,set等,它们可以使用len()函数,也仅仅是因为它们的类实现了__len__()函数而已;
  • 自己创建的类和python中的基础类型(int,float,str,tuple,forzenset,list,set,dict)等均是平等的地位, 差别仅在于您自己创建的类中是否实现了与这些基础类中相同的功能(或者魔术函数)。
1
2
3
4
5
6
7
8
9
10
11
12
# 例如,写一个Students类,把名字传进去:
class Students(object):
def __init__(self, *args):
self.names = args
def __len__(self):
return len(self.names)


# 只要正确实现了__len__()方法,就可以用len()函数返回Students实例的“长度”:
ss = Students('Bob', 'Alice', 'Tim')
print(len(ss))
# 3

1.3 Tensorboard类

(一) Tensorboard介绍

Tensorboard原本是Google TensorFlow的可视化工具,可以用于记录训练数据、评估数据、网络结构、图像等,并且可以在web上展示,对于观察神经网络的过程非常有帮助。PyTorch也推出了自己的可视化工具,一个是tensorboardX包,一个是torch.utils.tensorboard,二者的使用相差不大。

(二) Tensorboard的使用

首先展示该包的使用的大致流程

  • Step1:导入tensorboard,实例化SummaryWriter类,指明记录日记路径等信息
1
2
3
4
5
6
7
8
9
from torch.utils.tensorboard import SummaryWriter
# 实例化SummaryWriter,并指明日志存放路径。在当前目录如果每月logs目录将自动创建
# 如果不写log_dir,系统将会创建runs目录
writer = SummaryWriter(log_dir = ‘logs’)
# 调用实例
writer.add_xxx()
# 关闭writer
writer.close()

  • Step2:调用相应的API,接口一般格式为:
1
add_xxx(tag_name, object, iteration-number)
  • Step3:启动tensorboard,在命令行中输入
1
tensorboard --logdir=r’加logs所在路径’
  • Step4:复制网址在浏览器中打开
(三) 使用各种add方法记录数据

(1) 单条曲线(scalar)

1
add_scalar(tag, scalar_value, global_step=None, walltime=None)
  • 参数:
    • tag ( string ) – 数据标识符
    • scalar_value ( float或string/blobname ) – 要保存的值(对应Y轴)
    • global_step ( int ) – 要记录的全局步长值(对应X轴)
    • walltime ( float ) – 记录训练的时间,默认 walltime (time.time()) 秒
    • new_style ( boolean ) – 是使用新样式(张量字段)还是旧样式(simple_value 字段)。新样式可能会导致更快的数据加载。

示例

1
2
3
4
5
6
7
from torch.utils.tensorboard import SummaryWriter
import numpy as np

writer = SummaryWriter()
for x in range(1, 101) :
writer.add_scalar('y = 2x', x, 2 * x)
writer.close()

(2) 多条曲线(scalars)

1
add_scalars( main_tag , tag_scalar_dict , global_step = None , walltime = None)
  • 参数:
    • main_tag ( string ) – 标签的父名称
    • tag_scalar_dict ( dict ) – 存储标签和对应值的键值对
    • global_step ( int ) – 要记录的全局步长值
    • walltime ( float ) – 记录训练的时间,默认 walltime (time.time()) 秒

示例

1
2
3
4
5
6
7
8
9
10
from torch.utils.tensorboard import SummaryWriter
import numpy as np

writer = SummaryWriter()
r = 5
for x in range(1, 101) :
writer.add_scalars('run_14h', {'xsinx' : x * np.sin(x / r),
'xcosx' : x * np.cos(x / r),
'xtanx' : x * np.tan(x / r)}, x)
writer.close()

(3) 图片(image)

1
add_image(tag, img_tensor, global_step=None, walltime=None, dataformats = ‘CHW’)
  • 参数:
    • tag ( string ) – 数据标识符
    • img_tensor ( torch.Tensor , numpy.array , or string/blobname ) – 图像数据
    • global_step ( int ) – 要记录的全局步长值
    • walltime ( float ) – 记录训练的时间,默认 walltime (time.time()) 秒

示例

1
2
3
4
5
6
7
8
9
10
11
from torch.utils.tensorboard import SummaryWriter
import numpy as np
import cv2 as cv
import torch

img = cv.imread('zhou.jpg', cv.IMREAD_COLOR)#输入图像要是3通道的,所以读取彩色图像
img = cv.cvtColor(img, cv.COLOR_BGR2RGB)
img = torch.tensor(img.transpose(2, 0, 1))#cv读取为numpy图像为(H * W * C),所以要进行轴转换
writer.add_image('zhou_ge', img, 0)

writer.close()

(4) 其他

直方图(histogram)、渲染(figure)、网络(graph)等。

请参考博文:详解Tensorboard及使用教程 - 八岁爱玩耍 - CSDN

1.4 Transforms类

(一) Transforms结构

torchvision 中的 transforms 主要是对图片进行一些变换。

tranforms对应 tranforms.py 文件,里面定义了很多类,输入一个图片对象,返回经过处理的图片对象。

transforms 更多指的是python文件,transforms.py 就像一个工具箱,里面定义的各种类就像各种工具,特定格式的图片就是输入对象,经过工具处理,输出期望的图片结果。

(二) Python中的用法

使用 transforms 的方法就是先实例化选中的类,然后用实例化的对象去处理图片就行。

(三) 补充:ToTensor

将第一节中的代码复制到 python 控制台,回车,可在右侧看到各种变量和对象的具体信息:

tensor 数据类型可以理解为包装了反向神经网络一些理论基础参数。在神经网络中,要将数据先转换为Tensor类型,再进行训练。

(四) 图像的读取与显示

(1) 图像读取

之前PIL image已经学会了读取,numpy.ndarray 最常用的读取方法就是 opencv。

  • 区别1

    • img = cv2.imread(path),这是opencv中的处理图片的函数,使用时需 import cv2
    • img = Image.open(path),这是PIL中的一个处理图片的函数,使用时需 from PIL import Image
  • 区别2

    • cv2.imread()读取的是图像的真实数据

    • Image.open()函数只是保持了图像被读取的状态,但是图像的真实数据并未被读取,因此如果对需要操作图像每个元素,如输出某个像素的RGB值等,需要执行对象的load()方法读取数据。具体如下:

1
2
3
4
img = Image.open("lena.jpg")
img = img.load()
print(img[0,0])
# result:(255, 201, 166)
  • 区别3

    • cv2.imread()得到的img数据类型是np.array()类型

    • Image.open()得到的img数据类型呢是Image对象,不是普通的数组。

  • 区别4

    • cv2.imread()读取通道的顺序为BGR

    • Image.open()函数默认彩色图像读取通道的顺序为RGB

(2) 图像显示

图像显示时常见方法有两种

  • 一种是matplotlib的plt.imshow()方法

  • 一种是opencv的cv2.imshow()。

两个函数的输入都要求是数组。因此Image读取的图片要先转化为数组,再进行图片的显示。plt函数读入的顺序为RGB,cv2.imshow()读入的顺序是BGR。

  • 因此image与plt.imshow()配合使用

  • opencv的方法配套使用

(五) 常用的Transforms类/方法

(1) ToTensor():将图片对象类型转为 tensor

(2) Normalize():对图像像素进行归一化计算

(3) Resize():重新设置 PIL Image的大小,返回也是PIL Image格式,效果是缩放,不是裁剪

(4) Compose():输入为 transforms 类型参数的列表

Compose()中的参数需要是一个列表,python中列表的表示形式为 [数据1,数据2,…],在 Compose 中,数据需要是 transforms 类型,所以 Cmpose( [transforms 参数1, transforms 参数2], …)

目的是将几个 transforms 操作打包成一个,比如要先进行大小调整,然后进行归一化计算,返回 tensor 类型,则可以将 ToTensor、Normalize、Resize,按操作顺序输入到 Compose 中。

(5) RandomCrop:随机裁剪

1.5 torchvision类

(一) 简介

在Pytorch官网,torchvision 文档列出了很多科研或者毕设常用的一些数据集,如入门数据集 MNIST,用于手写文字。这些数据集位于 torchvision.datasets 模块,可以通过该模块对数据集进行下载,转换等操作。

torchvision 还有 io模块,但不常用;torchvision.models 会提供一些训练好的神经网络模型,在之后会用到;torchvision.transforms 之前已经学习过了,主要提供一些数据处理的工具。

接下来主要讲解如何联合使用 torchvision.datasets 和 torchvision.transforms

(二) CIFAR数据集

用到的数据集是 CIFIAR,点击官网文档进行查看。

(1) 数据集下载

数据集官网,该数据集包含了 60000 张 32*32 像素的 10 个类别的彩色图片,每个种类 6000 张图片,其中 60000 张中 50000 张是训练图片,10000 张是测试图片。

1
2
3
4
5
6
7
8
import torchvision
'''
若有 ssl 报错,添加如下代码
import ssl
ssl._create_default_https_context = ssl._create_unverified_context
'''
train_set = torchvision.datasets.CIFAR10(root="./dataset", train=True, download=True)
test_set = torchvision.datasets.CIFAR10(root="./dataset", train=False, download=True)

然后运行,torchvision 就会自动进行 CIFAR10 数据集的下载。

这里分别下载训练集和测试集,下载好后会放到所设置的路径下,这里下载的数据集会被放带当前目录的 dataset目录下。

(2) 数据集的使用

查看下测试数据集中每个数据包含什么:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import torchvision
import ssl

ssl._create_default_https_context = ssl._create_unverified_context

train_set = torchvision.datasets.CIFAR10(root="./dataset", train=True, download=True)
test_set = torchvision.datasets.CIFAR10(root="./dataset", train=False, download=True)
print(test_set[0])
print(test_set.classes)

img, target = test_set[0]
print(img)
print(target)
print(test_set.classes[target])
img.show()

即一个数据单元里包含输入图片和对应的 tag,这里用数字进行映射,数字 3 也就是表示 cat ,可用 img.show() 查看下图片。因为这个数据集比较小,只有百 MB,图片像素只有 32*32,所以模糊,这里是将其分类为猫。

1.6 DataLoader类

(一) 简介

dataset类:在程序中起到的作用是告诉程序数据在哪,以及每个索引所对应的数据是什么。相当于一系列的存储单元,每个单元都存储了数据。这里可以类比成一幅扑克牌,一张扑克牌就是一个数据,一幅扑克牌就是一个完整的数据集。

dataloader类: 是一个加载器,将数据加载到神经网络中。类比成手(神经网络),dataloader 每次从dataset 中去取数据,每次取多少,怎么取,通过 dataloader 参数进行设置。用手去抓扑克牌,每次抓几张,用一只手去抓取,还是用两只手,这就是 dataloader 要做的事,可以通过参数进行一个设置。

Pytoch 官网对 dataloader 的介绍,各个参数都有详细的描述,这里就不再赘述。

(二) dataloader 的使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import torchvision
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

# 准备的测试数据集
test_data = torchvision.datasets.CIFAR10("./dataset", train=False, transform=torchvision.transforms.ToTensor())
test_loader = DataLoader(dataset=test_data, batch_size=4, shuffle=True, num_workers=0, drop_last=False)

# 测试数据集中第一张图片及 target
img, target = test_data[0]
print(img.shape) # 查看图片大小
print(target)

writer = SummaryWriter("dataloader")
step = 0
for data in test_loader:
imgs, targets = data
# print(imgs.shape)
# print(targets)
writer.add_images("test_data", imgs, step) # 注意加 s
step = step + 1

writer.close()

注意:

1、shuffle打乱是指在epoch阶段打乱,不是mini_batch级打乱

第二章 神经网络的基本骨架nn.Module的使用

(一) torch.nn简介

搭建神经网络常用的工具在 torch.nn 模块,官网

Containers 中文翻译为容器,但这里可以理解为骨架,往这个骨架中添加一些内容就可以构成一个神经网络。

Convolution Layers、Pooling Layers、Paading Layers 都是要添加进网络的各层。

Containers 中 Module 是最常用的,它是所有神经网络的基本类,给所有神经网络提供基本的骨架。

(二) 简单示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import torch
from torch import nn

class Jiaolong(nn.Module):
# Alt+insert 可重写方法或实现方法(Windows)
def __init__(self) -> None:
super().__init__()

def forward(self, input):
output = input + 1
return output


jiaolong = Jiaolong()
x = torch.tensor(1.0) # 将 1.0 这个数转换成 tensor 数据类型
output = jiaolong(x)
print(output)
(三) 卷积操作

第三章 神经网络

(一) 卷积层

官网 Pytorch 的 nn 模块有 Convolution Layers,有3种卷积操作,nn.Conv1d、nn.Conv2d、nn.Conv3d 分别对应一维二维以及三维。

注:在Pytorch 官网文档左侧,有 torch.nn 和 torch.nn.fuctional,torch.nn 是对 torch.nn.fuctional 进行了一个封装,方便用户使用。想细致的了解一些 nn 模块中的函数可以从 torch.nn.fuctional 入手。这里主要介绍 nn.Conv2d,打开 torch.nn.fuctional 对应页面,可以看到对 conv2d 函数的介绍。

conv2d 需要的参数有 输入 input、权重 weight(更专业的名称是卷积核)、偏置 bias、步长 stride、填充Padding等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import torch
import torchvision
from torch import nn
from torch.nn import Conv2d
from torch.utils.data import DataLoader
import ssl

from torch.utils.tensorboard import SummaryWriter

ssl._create_default_https_context = ssl._create_unverified_context

dataset = torchvision.datasets.CIFAR10("./data", train=False, transform=torchvision.transforms.ToTensor(), download=True)
dataloader = DataLoader(dataset, batch_size=16)

class Jiaolong(nn.Module):
def __init__(self) -> None:
super().__init__()
self.conv1 = Conv2d(in_channels=3, out_channels=6, kernel_size=3, stride=1, padding=0)

def forward(self, x):
x = self.conv1(x)
return x

jiaolong = Jiaolong()
print(jiaolong)

writer = SummaryWriter("logs")
step = 0
for data in dataloader:
imgs, targets = data
output = jiaolong(imgs)
print(output.shape)
print(imgs.shape)
# torch.Size([64, 3, 32, 32])
writer.add_images("input", imgs, step)
# torch.Size([64, 6, 30, 30]) -> torch.Size([xx, 3, 30, 30])
output = torch.reshape(output, (-1,3, 30, 30)) # 第一个数不知道是多少用 -1,它会自动根据值计算
writer.add_images("output", output,step)
step += 1

(二) 池化层

最大池化(又称“下采样”)的作用是在保存数据特征的前提下去减小数据量。 池化函数使用某一位置的相邻输出的总体统计特征来代替网络在该位置的输出。本质是将采样,可以大幅减少网络参数量。

ceil_mode:当ceil_mode为 True时,将用ceil(向上取整)模式代替floor(向下取整) 模式去计算输出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import torch
import torchvision
from torch import nn
from torch.nn import MaxPool2d
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

dataset = torchvision.datasets.CIFAR10("./data", train=False, download=True,
transform=torchvision.transforms.ToTensor())
dataloader = DataLoader(dataset, batch_size=36)

'''
# 最大池化无法对 Long 数据类型进行实现
input = torch.tensor([[1, 2, 0, 3, 1],
[0, 1, 2, 3, 1],
[1, 2, 1, 0, 0],
[5, 2, 3, 1, 1],
[2, 1, 0, 1, 1]], dtype=torch.float32)
input = torch.reshape(input, (-1, 1, 5, 5))
print(input.shape)
'''

class Jiaolong(nn.Module):
def __init__(self) -> None:
super().__init__()
self.maxpool1 = MaxPool2d(kernel_size=3, ceil_mode=True)

def forward(self, input):
output = self.maxpool1(input)
return output

jiaolong = Jiaolong()
# output = jiaolong(input)
# print(output)

writer = SummaryWriter("logs_maxpool")
step = 0
for data in dataloader:
imgs, targets = data
writer.add_images("input", imgs, step)
output = jiaolong(imgs)
writer.add_images("output", output, step)
step += 1

writer.close()
(三) 非线性激活

非线性激活主要目的就是给网络增加非线性特征,以便训练出符合要求的泛化模型。常用的有

ReLU函数、Sigmoid函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import torch
from torch import nn
from torch.nn import ReLU

input = torch.tensor([[1, -0.5],
[-1, 3]])
input = torch.reshape(input, (-1, 1, 2, 2))
print(input.shape)

class Jiaolong(nn.Module):
def __init__(self) -> None:
super().__init__()
'''
inplace 为替换的意思
如 input = -1
ReLU(input, inplace = True)
input = 0
'''
self.relu = ReLU()

def forward(self, input):
output = self.relu(input)
return output

jiaolong = Jiaolong()
output = jiaolong(input)
print(output)
(四) 正则化层Normalization

将数据进行正则化可以加快神经网络的训练速度

(五) 线性层

线性层又叫全连接层,其中每个神经元与上一层所有神经元相连,多看官方文档

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import torch
import torchvision
from torch import nn
from torch.nn import Linear
from torch.utils.data import DataLoader

dataset = torchvision.datasets.CIFAR10("./data", train=False,
transform=torchvision.transforms.ToTensor(),download=True)
dataloader = DataLoader(dataset, batch_size=64, drop_last=True)

class Jiaolong(nn.Module):
def __init__(self) -> None:
super().__init__()
self.linear = Linear(196608, 10)

def forward(self, input):
output = self.linear(input)
return output

jiaolong = Jiaolong()

for data in dataloader:
imgs, targets = data
print(imgs.shape)
output = torch.flatten(imgs) # 展平
# output = torch.reshape(imgs, (1, 1, 1, -1))
print(output.shape)
output = jiaolong(output)
print((output.shape))
(六) 神经网络-搭建小实战和Sequential的使用

(1) Sequential的使用

Sequential 是一个时序容器。Modules 会以他们传入的顺序被添加到容器中。包含在 PyTorch 官网中 torch.nn 模块中的 Containers 中,在神经网络搭建的过程中如果使用 Sequential,代码更简洁。

(2) 搭建神经网络小示例

搭建上述神经网络的具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import torch
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear

class Jiaolong(nn.Module):
def __init__(self):
super(Jiaolong, self).__init__()
self.conv1 = Conv2d(in_channels=3, out_channels=32, kernel_size=5, padding=2)
self.maxpool1 = MaxPool2d(kernel_size=2)
self.conv2 = Conv2d(in_channels=32, out_channels=32, kernel_size=5, padding=2)
self.maxpool2 = MaxPool2d(kernel_size=2)
self.conv3 = Conv2d(in_channels=32, out_channels=64, kernel_size=5, padding=2)
self.maxpool3 = MaxPool2d(kernel_size=2)
self.flatten = Flatten()
self.linear1 = Linear(1024, 64)
self.linear2 = Linear(64, 10)

def forward(self, x):
x = self.conv1(x)
x = self.maxpool1(x)
x = self.conv2(x)
x = self.maxpool2(x)
x = self.conv3(x)
x = self.maxpool3(x)
x = self.flatten(x)
x = self.linear1(x)
x = self.linear2(x)
return x

jiaolong = Jiaolong()
print(jiaolong)
input = torch.ones((64, 3, 32, 32)) # 指定数据创建的形状,都是1
output = jiaolong(input)
print(output.shape)

现以Sequential搭建上述一模一样的神经网络,并借助tensorboard显示计算图的具体信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import torch
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
from torch.utils.tensorboard import SummaryWriter

class Jiaolong(nn.Module):
def __init__(self):
super(Jiaolong, self).__init__()
self.model1 = Sequential(
Conv2d(in_channels=3, out_channels=32, kernel_size=5, padding=2),
MaxPool2d(kernel_size=2),
Conv2d(in_channels=32, out_channels=32, kernel_size=5, padding=2),
MaxPool2d(kernel_size=2),
Conv2d(in_channels=32, out_channels=64, kernel_size=5, padding=2),
MaxPool2d(kernel_size=2),
Flatten(),
Linear(1024, 64),
Linear(64, 10)
)

def forward(self, x):
x = self.model1(x)
return x

jiaolong = Jiaolong()
# print(jiaolong)
input = torch.ones((64, 3, 32, 32)) # 指定数据创建的形状,都是1
output = jiaolong(input)
# print(output.shape)

writer = SummaryWriter("logs")
writer.add_graph(jiaolong, input) # 计算图
writer.close()

第四章 损失函数与反向传播

4.1 交叉熵基本表示

交叉熵常用于分类问题的loss函数,其形式为

4.2 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import torch
from torch import nn
from torch.nn import L1Loss, MSELoss

inputs = torch.tensor([1, 2, 3], dtype=torch.float32)
targets = torch.tensor([1, 2, 5], dtype=torch.float32)

inputs = torch.reshape(inputs, (1, 1, 1, 3))
targets = torch.reshape(targets, (1, 1, 1, 3))

loss = L1Loss(reduction='sum')
result = loss(inputs, targets)

loss_mse = MSELoss()
result_mse = loss_mse(inputs, targets)

print(result)
print(result_mse)

# 交叉熵
x = torch.tensor([0.1, 0.2, 0.3])
y = torch.tensor([1])
x = torch.reshape(x, (1, 3))
loss_cross = nn.CrossEntropyLoss()
result_cross = loss_cross(x, y)
print(result_cross)

现在以 CIFAR10 数据集为例,在上一篇文章中最后搭建的神经网络中使用 CrossEntropyLoss 函数作为损失函数,讲解在神经网络中如何使用损失函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import torchvision
from torch import nn
from torch.nn import Sequential, Conv2d, MaxPool2d, Flatten, Linear
from torch.utils.data import DataLoader

dataset = torchvision.datasets.CIFAR10("D:\Code\Project\learn_pytorch\pytorch_p17-21\data", train=False,download=True, transform=torchvision.transforms.ToTensor())
dataloader = DataLoader(dataset, batch_size=4)

class Jiaolong(nn.Module):
def __init__(self):
super(Jiaolong, self).__init__()
self.model1 = Sequential(
Conv2d(in_channels=3, out_channels=32, kernel_size=5, padding=2),
MaxPool2d(kernel_size=2),
Conv2d(in_channels=32, out_channels=32, kernel_size=5, padding=2),
MaxPool2d(kernel_size=2),
Conv2d(in_channels=32, out_channels=64, kernel_size=5, padding=2),
MaxPool2d(kernel_size=2),
Flatten(),
Linear(1024, 64),
Linear(64, 10)
)

def forward(self, x):
x = self.model1(x)
return x

loss = nn.CrossEntropyLoss()
jiaolong = Jiaolong()
for data in dataloader:
imgs, targets = data
outputs = jiaolong(imgs)
result_loss = loss(outputs, targets)
print(result_loss)

第五章 优化器

优化器:神经网络的学习的目的就是寻找合适的参数,使得损失函数的值尽可能小。解决这个问题的过程为称为最优化。解决这个问题使用的算法叫做优化器。在 PyTorch 官网中,将优化器放置在 torch.optim 中,并详细介绍了各种优化器的使用方法。

现以 CIFAR10 数据集为例,损失函数选取交叉熵函数,优化器选择 SGD 优化器,搭建神经网络,并计算其损失值,用优化器优化各个参数,使其朝梯度下降的方向调整。设置 epoch,让其执行 20 次,并将每一次完整的训练的损失函数值求和输出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import torch
import torchvision
from torch import nn
from torch.nn import Sequential, Conv2d, MaxPool2d, Flatten, Linear
from torch.utils.data import DataLoader

dataset = torchvision.datasets.CIFAR10("D:\Code\Project\learn_pytorch\pytorch_p17-21\data", train=False,
download=True, transform=torchvision.transforms.ToTensor())
dataloader = DataLoader(dataset, batch_size=4)

class Jiaolong(nn.Module):
def __init__(self):
super(Jiaolong, self).__init__()
self.model1 = Sequential(
Conv2d(in_channels=3, out_channels=32, kernel_size=5, padding=2),
MaxPool2d(kernel_size=2),
Conv2d(in_channels=32, out_channels=32, kernel_size=5, padding=2),
MaxPool2d(kernel_size=2),
Conv2d(in_channels=32, out_channels=64, kernel_size=5, padding=2),
MaxPool2d(kernel_size=2),
Flatten(),
Linear(1024, 64),
Linear(64, 10)
)

def forward(self, x):
x = self.model1(x)
return x

loss = nn.CrossEntropyLoss()
jiaolong = Jiaolong()
# 构建 SGD 优化器,其中 jiaolong.parameters() 表示:待优化参数的 iterable 或者是定义了参数组的 dict,lr=0.01 表示学习率
optim = torch.optim.SGD(jiaolong.parameters(), lr=0.01)
for epoch in range(20):
running_loss = 0.0
for data in dataloader:
imgs, targets = data
outputs = jiaolong(imgs)
result_loss = loss(outputs, targets)
# 将上一轮计算的梯度清零,避免上一轮的梯度值会影响下一轮的梯度值计算
optim.zero_grad()
# 反向传播过程,在反向传播过程中会计算每个参数的梯度值
result_loss.backward()
# 所有的 optimizer 都实现了 step() 方法,该方法会更新所有的参数。
optim.step()
running_loss = running_loss + result_loss
print(running_loss)

第六章 现有网络模型的使用与修改

PyTorch是一个开源的Python机器学习库,基于Torch,用于自然语言处理等应用程序。他提供了大量的模型供我们所使用,如下图所示:

下面,我们选择其中一个网络进行使用,介绍如何使用、并修改 pytorch 本身为我们提供的现有网络。最后介绍一下模型的保存和修改。

6.1 pytorch 现有网络的使用与修改

下面以 VGG(Very Deep Convolutional Networks for Large-Scale Image Recognition)的使用为例,进行介绍该网络。

(一) VGG 16 简介

VGG16网络是14年牛津大学计算机视觉组和Google DeepMind公司研究员一起研发的深度网络模型。该网络一共有16个训练参数的网络,该网络的具体网络结构如下所示:

不难看出,该网络主要用于对 224 x 224 的图像进行1000分类。下面我们查看 VGG 在 pytorch 上的官方文档。

(二) VGG 16 doc

从帮助文档中,我们可以清楚的看到 pytorch 为我们提供了各种版本的 VGG,我们选择 VGG 16 进行查看。

(三) VGG16 的简单使用

从VGG 16的帮助文档可以得知,该模型训练的数据是 ImageNet,我们进入 torchvision.datasets 查看 ImageNet

但是该数据集实在是太大了,根本下不了,还是不搞了。建立一个该网络的模型查看参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
import torch 
import torchvision
import torch.nn as nn
# import torchvision.models

vgg_model_pretrained = torchvision.models.vgg16(pretrained=True, progress=True)

vgg_model_original = torchvision.models.vgg16(pretrained=False, progress=True)

print(vgg_model_original)

print(vgg_model_pretrained)

6.2 修改网络

使用以下语句可以实现对网络的修改

(一) 添加网络结构

vgg_model_pretrained.add_module()

(二) 修改网络结构

vgg_model_original.classifier[num]

num表示要修改网络的哪一层

(三) 伪代码示例
1
2
3
4
5
6
# 添加:vgg_model_pretrained.add_module()
vgg_model_original.classifier.add_module('15', nn.Linear(in_features=1000, out_features=10, bias=True))
print(vgg_model_original)
# 修改:vgg_model_original.classifier[num]
vgg_model_original.classifier[7] = nn.Linear(in_features=1000, out_features=15, bias=True)
print(vgg_model_original)

第七章 网络模型的保存与读取

在搭建自己的神经网络模型之后,需要将模型进行保存,同时也需要读取或加载现有的神经网络模型。

7.1 加载模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# File : model_load.py
import torch

# 方式1 -> 保存方式1,加载模型
import torchvision
from torch import nn

model = torch.load("vgg16_method1.pth")
print(model)

# 方式2 ,加载模型
vgg16 = torchvision.models.vgg16(pretrained=False)
vgg16.load_state_dict(torch.load("vgg16_method2.pth"))
# model = torch.load("vgg16_method2.pth")
print(vgg16)

# 方式 1 陷阱
class Jiaolong(nn.Module):
def __init__(self) -> None:
super().__init__()
self.conv1 = nn.Conv2d(3, 64, 3)

def forward(self, x):
x = self.conv1(x)
return x

jiaolong = Jiaolong()
torch.save(jiaolong, 'jiaolong_method1.pth')

7.2 保存模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# File : model_save.py
import torch
import torchvision

vgg16 = torchvision.models.vgg16(pretrained=False)
# 保存方式 1,模型结构+模型参数
torch.save(vgg16, "vgg16_method1.pth")

# 保存方式2,模型参数(官方推荐)
torch.save(vgg16.state_dict(), 'vgg16_method2.pth') # 将模型的状态(参数)保存成字典形式

# 方式 1 陷阱,需要将模型写过来,但无需创建实例
# 可以引入,from model_save import *
model = torch.load('jiaolong_method1.pth')
print(model) # 报错

第八章 完整地模型训练/验证套路

8.1 训练套路

8.2 验证套路

验证不是指的时在测试集上测试,而是找一张没有在数据集中的图片进行训练。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import torch
import torchvision
from PIL import Image
from torch import nn

image_path = "imgs/dog.png"
image = Image.open(image_path)
print(image)
image = image.convert('RGB')
# 因为png格式为四个通道,除了RGB三个通道外,还有一个透明度通道。所以调用此语句保留其颜色通道。

transform = torchvision.transforms.Compose([torchvision.transforms.Resize((32, 32)),
torchvision.transforms.ToTensor()])
# 这个照片size=258x208,因为模型为32×32,所以改一下
image = transform(image)
print(image.shape)

# 加载网络模型
class Tudui(nn.Module):
def __init__(self):
super(Tudui, self).__init__()
# 为了避免上下两个def都写一整串,将整个网络放到序列当中,前边有讲过,注释了很多代码有老乡的那一篇
self.model = nn.Sequential(
nn.Conv2d(3, 32, 5, 1, 2), # 卷积
nn.MaxPool2d(2), # 池化
nn.Conv2d(32, 32, 5, 1, 2),
nn.MaxPool2d(2),
nn.Conv2d(32, 64, 5, 1, 2),
nn.MaxPool2d(2),
nn.Flatten(),
nn.Linear(64*4*4, 64), # 最后两步的展平
nn.Linear(64, 10)

)


def forward(self, x):
x = self.model(x)
return x

model = torch.load("tudui_0.pth")
print(model)

# img输入到这个模型中
# 这里一开始用的gpu的模型,会报错,换成cpu的"tudui_0.pth"模型就行了
image = torch.reshape(image, (1, 3, 32, 32))
# 这几步比较重要且容易忘记======================
model.eval()
with torch.no_grad():
output = model(image)
# 这几步比较重要且容易忘记======================

print(output)
# 显示是哪一类的概率最大
print(output.argmax(1))

第九章 利用GPU进行训练

9.1 方法 1

采用方法 1 实现GPU训练网络模型只需要将原来的 CPU 版本的代码进行小量修改即可,具体修改的位置包括下面3个地方:

  • 网络模型
  • 数据(输入、标注)
  • 损失函数

只需找到上述 3 个位置的代码加上.cuda()操作即可实现将CPU版本的代码修改为GPU版本的代码,现以上一篇博文中完整的模型训练代码为例。

1
2
3
4
5
6
7
# 网络模型
junheng = junheng.cuda()
# 损失函数
loss_function = loss_function.cuda()
# 数据(输入、标注)——包括训练和测试部分
imgs = imgs.cuda()
labels = labels.cuda()

注意:最好可以在使用cuda()前加一个判断if torch.cuda.is_available()

9.2 方法2

打开 google colab(科学上网)

9.3 方法3

用的.to操作来指定模型训练的设备,具体是CPU还是GPU,如果是GPU,还能具体到时哪一块GPU。

具体方法总结如下:

  • 定义具体的 device,例如:device=torch.device(“cpu”) 或者 device=torch.device(“cuda:0”) 或者 device=torch.device(“cuda:1”);
  • 利用 .to() 操作训练使用的具体设备,例如 .to(device)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 1. 定义训练设备
mydevice = torch.device("cpu") # 设备在cpu运行
mydevice = torch.device("cuda") # 设备在gpu运行
'''
另一种写法
mydevice = torch.device("cuda" if torch.cuda.is_available() else "cpu")
'''

# 2. 网络模型使用训练设备
junheng = junheng.to(mydevice)

# 3. 损失函数使用训练设备
loss_function = loss_function.to(mydevice)

# 4. 数据(输入、标注)使用训练设备——包括训练和测试部分
imgs = imgs.to(mydevice)
labels = labels.to(mydevice)

参考笔记

PyTorch深度学习笔记 - 小于同学饿了 - CSDN

  • Copyrights © 2015-2024 wjh
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信