通过在B站上观看一些关于Pytorch的初级教学视频,自己对此强大工具大致有了一个入门级的了解,将一些重要,或者说之后编程经常需要用到的一些知识进行了归纳整理,就当是对所学知识的一个回顾整理啦。
pytorch是一个开源的机器学习框架,包含了大量针对深度学习的张量库,其中主要的“子库”/程序包有:
torch 包含了针对多维张量的数据结构以及和与张量相关的数学运算;此外,它还提供了较多实用工具(torch.utils)
是一个专门针对**计算机视觉(图像、视频相关)
**的库,其包含了计算机视觉领域中的:
(1)常用的数据集datasets,eg:cifar10
(2)常见的模型架构,eg:vgg16
(3)常用的图像处理变化操作(transforms)
是一个专门针对**音频方面
**的库,其包含了音频领域中的:
(1)音频输入输出控制
(2)常用的音频数据集
(3)常用的音频转换操作(transforms)
是一个专门针对**自然语言处理(NLP)领域
**的库,其包含了NLP领域中的:
(1)常见的文本数据处理工具
(2)常用的文本数据集
如上文介绍pytorch主要package中的那样,pytorch中针对不同的应用领域,提供了不同类型的常见的数据集。
以cv领域为例,可使用如下语句导入CIFAR10数据集:
import torchvision
# 数据集的下载
data_train = torchvision.datasets.CIFAR10("./dataset", train=True, transform=torchvision.transforms.ToTensor(), download=True)
data_test = torchvision.datasets.CIFAR10("./dataset", train=False, transform=torchvision.transforms.ToTensor(), download=True)
注:(1)参数train=True/False
表示要得到训练数据集/测试数据集
(2)参数transform
表示需要对数据集进行那些统一的变换,这里是将原始PIL类型图片数据
转换成张量
类型
(3)参数download
可以一直设置为True
,因为如果已经下载好时,程序会检测到,进而不会再重复下载
2.1提到的dataset只是得到数据集,而若要将这些数据集送入神经网络进行训练和验证,还需经过dataload这一步,其主要用来定义如下参数:
(1)batch_size:
规定一次送入神经网的数据规模,此参数直接决定了神经网路输入层
的规模(通道数);
另外,值得一提的是,对于一个图片数据,其维度一般是3维
(h,w,c),但是,引入这个batch_size参数之后,其维度会变成四维,因而,如果一个网络的batch_size
为1,并且想把一张图片输入到这个网络中时,需要先把其reshape成4维地张量:
import torch
# 假定原始图片数据的维度为(32,32,3),用张量img来保存
img = torch.reshape(img,(1,32,32,3))
(2)shuffle:
表示是否进行乱序处理
结合数据导入的代码,对数据进行load的代码如下:
import torchvision
import torch
# 数据集的下载
data_train = torchvision.datasets.CIFAR10("./dataset", train=True, transform=torchvision.transforms.ToTensor(), download=True)
data_test = torchvision.datasets.CIFAR10("./dataset", train=False, transform=torchvision.transforms.ToTensor(), download=True)
# 数据集的导入
train_load = DataLoader(data_train, batch_size=64)
test_load = DataLoader(data_test, batch_size=64)
在后续进行相关操作时,需要对load后的数据进行循环遍历
操作:
# 法1
for data in train_load:
imgs, targets = data
pass
# 法2
for imgs, targets in train_load:
pass
# 以上两种方法本质上是一样的,法1遍历的对象data是一个元组(包括了imgs,targets这两项)
由于不同领域存在不同的数据处理
方式,因而不同领域的变换处理操作包含在不同的package下,以cv领域为例,其变化工具为torchvision.transforms,下面对此transforms进行介绍
(1) transforms包含的工具
使用如下程序,输出transforms包含的工具
import torchvision
function_list = dir(torchvision.transforms)
print(function_list)
得到如下结果
['AutoAugment', 'AutoAugmentPolicy', 'CenterCrop', 'ColorJitter', 'Compose', 'ConvertImageDtype', 'FiveCrop', 'GaussianBlur', 'Grayscale', 'InterpolationMode', 'Lambda', 'LinearTransformation', 'Normalize', 'PILToTensor', 'Pad', 'RandomAdjustSharpness', 'RandomAffine', 'RandomApply', 'RandomAutocontrast', 'RandomChoice', 'RandomCrop', 'RandomEqualize', 'RandomErasing', 'RandomGrayscale', 'RandomHorizontalFlip', 'RandomInvert', 'RandomOrder', 'RandomPerspective', 'RandomPosterize', 'RandomResizedCrop', 'RandomRotation', 'RandomSizedCrop', 'RandomSolarize', 'RandomVerticalFlip', 'Resize', 'Scale', 'TenCrop', 'ToPILImage', 'ToTensor', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', 'autoaugment', 'functional', 'functional_pil', 'functional_tensor', 'transforms']
可以看到,transforms包含的函数较多,但是好在函数的命名比较规范,大部分函数通过函数名称便可大概了解函数的功能,常用到的函数有
a. Totensor()
常用于将PIL类型的图片转换成tensor型
from torchvision import transforms
from PIL import Image
# 原始图片
img = Image.open("./dog.png")
print(img)
# Totensor
trans_totensor = transforms.ToTensor()
img_tensor = trans_totensor(img)
print(img_tensor.type)
print(img_tensor.shape)
运行结果如下:
<PIL.PngImagePlugin.PngImageFile image mode=RGBA size=105x124 at 0x23AEBB10A58>
<built-in method type of Tensor object at 0x0000023AF3A33E58>
torch.Size([4, 124, 105])
注:对于png图片,其包含4个通道。除了(r,g,b)
三个通道之外,还有一个透明通道
b.Normalize
具体参数如下:
Normalize(mean, std, inplace=False)
对于给定的张量型图片数据(仅支持tensor型数据,由此可见Totensor()函数的重要性)。指定每个通道的均值 (mean[1],...,mean[n])
和标准差(std[1],..,std[n])
(假定有n
个通道),此函数进行如下操作并输出:, output[channel] = (input[channel] - mean[channel]) / std[channel]
下面演示一个使得输出图片的均值为0的操作
from torchvision import transforms
from PIL import Image
# 原始图片
img = Image.open("./dog.png")
print(img)
# Totensor
trans_totensor = transforms.ToTensor()
img_tensor = trans_totensor(img)
# print(img_tensor.type)
# print(img_tensor.shape)
# Normalize
mean_list = [img_tensor[0].mean().item(), img_tensor[1].mean().item(), img_tensor[2].mean().item(), img_tensor[3].mean().item(),]
trans_nor = transforms.Normalize(mean_list,[0.1,0.1,0.1,0.1])
img_nor = trans_nor(img_tensor)
print(img_tensor[0].mean())
print(img_nor[0].mean())
print(img_nor[1].mean())
输出结果如下:
tensor(0.6909)
tensor(-6.7717e-08)
tensor(-6.8958e-07)
torch.Size([124, 105])
可以看到,变化后通道的均值可以认定为0.
c.Resize
具体参数如下:
Resize(size, interpolation=<InterpolationMode.BILINEAR: 'bilinear'>, max_size=None, antialias=None)
对于给定的张量型图片数据(支持tensor型数据和PIL类型数据),生成指定大小形状的图片(由参数size
指定),当指定size小于原始图片的size时,函数会进行自动的缩放处理
下面演示一个将原始图片(124*105)分别 缩小成(100*100)
和放大到(200*200)
两种情况
from torchvision import transforms
from PIL import Image
# 原始图片
img = Image.open("./dog.png")
print(img)
# Totensor
trans_totensor = transforms.ToTensor()
img_tensor = trans_totensor(img)
# print(img_tensor.type)
# print(img_tensor.shape)
# Resize
trans_resize0 = transforms.Resize((100,100))
trans_resize1 = transforms.Resize((200,200))
img_resize0 = trans_resize0(img_tensor)
img_resize1 = trans_resize1(img_tensor)
# ToPILimage,为了后续使用PIL输出图片,这里使用ToPILImage()函数
trans_toPIL = transforms.ToPILImage()
img0 = trans_toPIL(img_resize0)
img1 = trans_toPIL(img_resize1)
Image._show(img)
Image._show(img0)
Image._show(img1)
d.Compose
可以将图片要实现的多个变化用此Compose
函数包在一起,这样便可通过这一个函数来实现多个变化
下面演示用此函数实现Totensor(),和Normalize()两种变化
from torchvision import transforms
from PIL import Image
# 原始图片
img = Image.open("./dog.png")
print(img)
# Compose
trans = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize([1,1,1,1],[1,1,1,1]),
transforms.ToPILImage()
])
img_out = trans(img)
Image._show(img_out)
(2)transforms的使用流程:
根据上述几个函数的应用实例可知,transforms像是一个工具箱,其并不能直接拿来用,而是从其中抽出一个或多个工具之后,再用来对数据处理。
这样做的原因是,transforms包含的工具都是类
对象,因而在使用之前需要进行创建对象
操作
(1)Containers
容器相关的模块,比较重要的两个模块介绍如下:
a. nn.Module
非常重要的类,是所有神经网络的基类,也即在定义个人的类时,都需要继承此类,基本形式如下:
class Mylayer(nn.Module):
def __init__(self):
super().__init__()
def forward(self, x):
pass
b. nn.Sequential
线性神经网络容器,将要定义的网络依次放入此函数中,便可实现网络的搭建,举例如下:
model = nn.Sequential(
nn.Conv2d(1,20,5),
nn.ReLU(),
nn.Conv2d(20,64,5),
nn.ReLU()
)
(2) Convolution Layers
神经网络中的卷积层,常见的类别如下:
卷积网络类型 | 功能描述 |
---|---|
nn.Conv1d | 进行1维卷积操作 |
nn.Conv2d | 进行2维卷积操作 |
nn.Conv3d | 进行3维卷积操作 |
(3) Pooling Layers
神经网络中的池化层,常见的类别如下:
卷积网络类型 | 功能描述 |
---|---|
nn.MaxPool1d/AvgPool1d | 进行1维最大/平均池化 |
nn.MaxPool2d/AvgPool2d | 进行2维最大/平均池化 |
nn.MaxPool3d/AvgPool3d | 进行3维最大/平均池化 |
(4) Non-linear Activations
神经网络中的非线性激活层,含有的种类非常多,仅列举最常见的类别如下:
卷积网络类型 | 功能描述 |
---|---|
nn.Softmax | 经Sof |
nn.Relu | 经Relu函数输出 |
nn.Sigmoid | 经Sigmoid函数输出 |
(5) others
除了上述各个层之外,pytorch还包含了很多别的种类的网络层:
卷积网络类型 | 举例 |
---|---|
Normalization Layers | BatchNorm1d/2d/3d |
Recurrent Layers | RNN,LSTM,GRU |
Transformer Layers | Transformer,transformerEncoder,transformerDecoder |
Linear Layers | Linear、Bilinear |
Dropout Layers | Dropout、Dropout2d、Dropout3d |
根据模型输出结果
和真实值
计算出损失值,常见的损失函数有:nn.L1Loss
,nn.MSELoss
,nn.CrossEntropyLoss
以交叉熵损失函数为例,其使用方法举例如下:
# 先定义损失函数
loss = nn.CrossEntropyLoss()
input = torch.randn(3, 5, requires_grad=True)
target = torch.empty(3, dtype=torch.long).random_(5)
# 将输出结果和真实值传入 此定义好的损失函数
output = loss(input, target)
output.backward()
此package可用来实现 多种类型的优化算法
**(1) 使用方法 **
a.先定义一个优化器对象,
b.然后进行优化处理
当在训练过程中需要进行优化处理时,其步骤如下:
for input, target in dataset:
optimizer.zero_grad()
output = model(input)
loss = loss_fn(output, target)
loss.backward()
optimizer.step()
在测试过程中,需要进行如下处理:
# 保证在测试过程中,已经训练好的模型的参数不会改变
with torch.no_grad():
for data in test_load:
pass
在得到训练好的模型后,对其进行测试时,需要进行如下处理操作:
# 保证在测试过程中,已经训练好的模型的参数不会改变
with torch.no_grad():
output = module(input)
pass
对于网络模型的保存和读取,其一共包含两种方法
**(1) 方法1 **
import torch
import torchvision
vgg16 = torchvision.models.vgg16(pretrained=False)
# 保存方式1 .pth文件
torch.save(vgg16,"./module_save/module1.pth")
# 加载方式1
module1 = torch.load("./module_save/module1.pth")
print(module1)
**(2) 方法2 **
import torch
import torchvision
vgg16 = torchvision.models.vgg16(pretrained=False)
# 保存方式2
torch.save(vgg16.state_dict(),"./module_save/module2.pth")
module2 = torch.load("./module_save/module2.pth")
# 以方式2导入的只是模型的参数,还需模型的结构
module3 = torchvision.models.vgg16(pretrained=False)
module3.load_state_dict("./module_save/module2.pth")
(3) 两种方法的比较
a.方法1保存的是网络结构
和模型参数
;而方法2保存的仅是模型参数
b.方法1直接load便可以得到一个完整的神经网路模型;而方法2load的只是模型参数,因而需要重新定义网络(相当于重写一遍未训练之前网络定义的代码)
c.两种方法,官方推荐方法2
以cifar10数据预测为例,完整的模型训练套路如下:
**(0) 导入相关库 **
import torch
import torchvision
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
from torch.utils.data import dataset, DataLoader
from torchvision.datasets import CIFAR10
**(1) 数据准备 **
# 数据准备
data_train = CIFAR10("./dataset/train", train= True, transform=torchvision.transforms.ToTensor(),download=True)
data_test = CIFAR10("./dataset/test", train= False, transform=torchvision.transforms.ToTensor(),download=True)
train_load = DataLoader(data_train,batch_size=64)
test_load = DataLoader(data_test, batch_size=64)
(2) 搭建神经网路
# 神经网路的搭建
class Mylayer(nn.Module):
def __init__(self):
super().__init__()
self.module = Sequential(
Conv2d(3, 32, kernel_size=5, padding=2),
MaxPool2d(2),
Conv2d(32, 32, kernel_size=5, padding=2),
MaxPool2d(2),
Conv2d(32, 64, kernel_size=5, padding=2),
MaxPool2d(2),
Flatten(),
Linear(1024, 64),
Linear(64, 10)
)
def forward(self,x):
x = self.module(x)
return x
# 网路模型的例化
mynet1 = Mylayer()
(3) 定义损失函数和优化器
# 损失函数
loss_fn = nn.CrossEntropyLoss()
# 学习率与优化器
learning_rate = 0.01
optimizer = torch.optim.SGD(mynet1.parameters(),lr=learning_rate)
(4) 模型训练、测试和保存
# 先定义一些参数
# 训练次数
total_train_step = 0
# 测试次数
total_test_step = 0
# 训练轮数
epoch = 1
# 开始训练 双重for循环
for i in range(epoch):
print("-------第 {} 轮训练开始--------".format(i+1))
# 一轮的训练过程
mynet1.train()
for data in train_load:
imgs,targets = data
outputs = mynet1(imgs)
loss = loss_fn(outputs,targets)
optimizer.zero_grad()
loss.backward()
optimizer.step()
total_train_step = total_train_step + 1
if total_train_step % 100 == 0:
# 注:loss.item(),只输出数值大小
print("训练次数:{},Loss:{}".format(total_train_step,loss.item()))
# 一轮的测试过程
total_test_loss = 0
# 注:模型训练开始前,需加入如下这句语句:
mynet1.eval()
with torch.no_grad():
for data in test_load:
imgs,targets = data
outputs = mynet1(imgs)
loss = loss_fn(outputs, targets)
total_test_loss = total_test_loss + loss.item()
print("第{}轮测试集结束,Loss:{}".format(i+1,total_test_loss))
total_test_step = total_test_step + 1
# 模型保存
torch.save(mynet1, "./module_save/mynet1_{}.pth".format(i+1))
(5) GPU加速
以上4个步骤便可实现神经网路模型的训练,在此基础上,进行GPU加速处理。
pytorch提供了比较方便的GPU加速方法,具体步骤如下:
a. 找到程序中网络模型
,数据(输入、标注)
以及损失函数
这三个部分
b.在上述三个部分中加入.cuda()
函数
便可实现GPU加速
a.对于网络模型
部分,其原始代码为:
mynet1 = Mylayer()
将其改成:
mynet1 = Mylayer()
mynet1.cuda()
b.对于数据(输入、标注)
部分,其原始代码如下:
# 在模型训练和测试部分,都出现了如下代码段:
imgs,targets = data
将其修改成:
imgs,targets = data
imgs = imgs.cuda()
targets = targets.cuda()
c.对于损失函数
部分,其原始代码如下:
loss_fn = nn.CrossEntropyLoss()
将其修改成:
loss_fn = nn.CrossEntropyLoss()
loss_fn = loss_fn.cuda()
经过上述修改之后,程序训练和测试的速度得到了明显加快!