PyTorch的主要组成模块和实战

举报
livingbody 发表于 2022/11/22 00:53:04 2022/11/22
【摘要】 PyTorch的主要组成模块和实战 1.PyTorch主要模块pytorch和paddlepaddle很相似,所以上手较快。 1.1 环境设置和超参设置超参看一看先:batch size初始学习率(初始)训练次数(max_epochs)GPU配置import osimport numpy as npimport torchimport torch.nn as nnfrom torch.ut...

PyTorch的主要组成模块和实战

1.PyTorch主要模块

pytorch和paddlepaddle很相似,所以上手较快。

1.1 环境设置和超参设置

超参看一看先:

  • batch size
  • 初始学习率(初始)
  • 训练次数(max_epochs)
  • GPU配置
import os
import numpy as np
import torch
import torch.nn as nn
from torch.utils.data import Dataset, DataLoader
import torch.optim as optimizer

batch_size = 16
# 批次的大小
lr = 1e-4
# 优化器的学习率
max_epochs = 100

# 方案一:使用os.environ,这种情况如果使用GPU不需要设置
os.environ['CUDA_VISIBLE_DEVICES'] = '0'

# 方案二:使用“device”,后续对要使用GPU的变量用.to(device)即可
device = torch.device("cuda:1" if torch.cuda.is_available() else "cpu")

print(device)
cuda:1

1.2 数据读取

涉及到两个重要类:

  • Dataset
  • DataLoad

常见自定义Dataset长下面样子

import torch
from torchvision import datasets

class MyDataset(Dataset):
    def __init__(self, data_dir, info_csv, image_list, transform=None):
        """
        Args:
            data_dir: path to image directory.
            info_csv: path to the csv file containing image indexes
                with corresponding labels.
            image_list: path to the txt file contains image names to training/validation set
            transform: optional transform to be applied on a sample.
        """
        label_info = pd.read_csv(info_csv)
        image_file = open(image_list).readlines()
        self.data_dir = data_dir
        self.image_file = image_file
        self.label_info = label_info
        self.transform = transform

    def __getitem__(self, index):
        """
        Args:
            index: the index of item
        Returns:
            image and its labels
        """
        image_name = self.image_file[index].strip('\n')
        raw_label = self.label_info.loc[self.label_info['Image_index'] == image_name]
        label = raw_label.iloc[:,0]
        image_name = os.path.join(self.data_dir, image_name)
        image = Image.open(image_name).convert('RGB')
        if self.transform is not None:
            image = self.transform(image)
        return image, label

    def __len__(self):
        return len(self.image_file)

Dataset定义好后,究竟可以定义DataLoader了。

from torch.utils.data import DataLoader

train_loader = torch.utils.data.DataLoader(train_data, batch_size=batch_size, num_workers=4, shuffle=True, drop_last=True)
val_loader = torch.utils.data.DataLoader(val_data, batch_size=batch_size, num_workers=4, shuffle=False)
  • batch_size:样本是按“批”读入的,batch_size就是每次读入的样本数
  • num_workers:有多少个进程用于读取数据
  • shuffle:是否将读入的数据打乱
  • drop_last:对于样本最后一部分没有达到批次数的样本,使其不再参与训练

1.3 模型构建

模型构建就是基于Module搭积木,该类包含init和forward函数,分别用于创建模型和自定义前向计算。

import torch
from torch import nn


class MLP(nn.Module):
    # 声明带有模型参数的层,这里声明了两个全连接层
    def __init__(self, **kwargs):
        # 调用MLP父类Block的构造函数来进行必要的初始化。这样在构造实例时还可以指定其他函数
        super(MLP, self).__init__(**kwargs)
        self.hidden = nn.Linear(784, 256)
        self.act = nn.ReLU()
        self.output = nn.Linear(256, 10)

    # 定义模型的前向计算,即如何根据输入x计算返回所需要的模型输出
    def forward(self, x):
        o = self.act(self.hidden(x))
        return self.output(o)


X = torch.rand(2, 784)
net = MLP()
print(net)
net(X)

反向传播函数无需定义,系统将通过⾃动求梯度⽽自动⽣成反向传播所需的 backward 函数。

MLP(
  (hidden): Linear(in_features=784, out_features=256, bias=True)
  (act): ReLU()
  (output): Linear(in_features=256, out_features=10, bias=True)
)

此外有一些常用算子,例如:

  • Conv2D
  • pool2d

1.3 模型初始化

1 . torch.nn.init.uniform_(tensor, a=0.0, b=1.0)
2 . torch.nn.init.normal_(tensor, mean=0.0, std=1.0)
3 . torch.nn.init.constant_(tensor, val)
4 . torch.nn.init.ones_(tensor)
5 . torch.nn.init.zeros_(tensor)
6 . torch.nn.init.eye_(tensor)
7 . torch.nn.init.dirac_(tensor, groups=1)
8 . torch.nn.init.xavier_uniform_(tensor, gain=1.0)
9 . torch.nn.init.xavier_normal_(tensor, gain=1.0)
10 . torch.nn.init.kaiming_uniform_(tensor, a=0, mode=‘fan__in’, nonlinearity=‘leaky_relu’)
11 . torch.nn.init.kaiming_normal_(tensor, a=0, mode=‘fan_in’, nonlinearity=‘leaky_relu’)
12 . torch.nn.init.orthogonal_(tensor, gain=1)
13 . torch.nn.init.sparse_(tensor, sparsity, std=0.01)
14 . torch.nn.init.calculate_gain(nonlinearity, param=None)

常见初始化:

import torch
import torch.nn as nn

conv = nn.Conv2d(1,3,3)
linear = nn.Linear(10,1)

isinstance(conv,nn.Conv2d)
isinstance(linear,nn.Conv2d)

1.4 损失函数

数据输入到模型当中,产生的结果与真实标签的评价指标,我们的模型可以按照损失函数的目标来做出改进。

损失函数较多,根据任务需要进行选择。

二分类交叉熵损失函数
交叉熵损失函数
L1损失函数
MSE损失函数
平滑L1 (Smooth L1)损失函数
目标泊松分布的负对数似然损失
KL散度
MarginRankingLoss
多标签边界损失函数
二分类损失函数
多分类的折页损失
三元组损失
HingEmbeddingLoss
余弦相似度
CTC损失函数
损失函数

1.5 训练与评估

model.train()   # 训练状态
model.eval()   # 验证/测试状态

我们前面在DataLoader构建完成后介绍了如何从中读取数据,在训练过程中使用类似的操作即可,区别在于此时要用for循环读取DataLoader中的全部数据。

for data, label in train_loader:

之后将数据放到GPU上用于后续计算,此处以.cuda()为例

data, label = data.cuda(), label.cuda()

开始用当前批次数据做训练时,应当先将优化器的梯度置零:

optimizer.zero_grad()

之后将data送入模型中训练:

output = model(data)

根据预先定义的criterion计算损失函数:

loss = criterion(output, label)

将loss反向传播回网络:

loss.backward()

使用优化器更新模型参数:

optimizer.step()

验证/测试的流程基本与训练过程一致,不同点在于:

  • 需要预先设置torch.no_grad,以及将model调至eval模式
  • 不需要将优化器的梯度置零
  • 不需要将loss反向回传到网络
  • 不需要更新optimizer
  1. 6 可视化
  • VisualDL
  • TensorBoardX
  • Visdom

三种工具可选,就不一一介绍了,建议使用国产VisualDL,因为文档齐全,使用便捷,主要是我经常用,熟悉!!!

1.7 优化器

  • torch.optim.ASGD
  • torch.optim.Adadelta
  • torch.optim.Adagrad
  • torch.optim.Adam
  • torch.optim.AdamW
  • torch.optim.Adamax
  • torch.optim.LBFGS
  • torch.optim.RMSprop
  • torch.optim.Rprop
  • torch.optim.SGD
  • torch.optim.SparseAdam

十种总有一款适合你!!!

2.FashionMNIST时装分类实战

该项目主要耗时在于数据包下载,由于网络原因下载耗时较长。

我们这里的任务是对10个类别的“时装”图像进行分类,使用FashionMNIST数据集(https://github.com/zalandoresearch/fashion-mnist/tree/master/data/fashion )。上图给出了FashionMNIST中数据的若干样例图,其中每个小图对应一个样本。
FashionMNIST数据集中包含已经预先划分好的训练集和测试集,其中训练集共60,000张图像,测试集共10,000张图像。每张图像均为单通道黑白图像,大小为32*32pixel,分属10个类别。

首先导入必要的包

import os
import numpy as np
import pandas as pd
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import Dataset, DataLoader

配置训练环境和超参数

# 配置GPU,这里有两种方式
## 方案一:使用os.environ
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
# 方案二:使用“device”,后续对要使用GPU的变量用.to(device)即可
device = torch.device("cuda:1" if torch.cuda.is_available() else "cpu")

## 配置其他超参数,如batch_size, num_workers, learning rate, 以及总的epochs
batch_size = 256
num_workers = 4   # 对于Windows用户,这里应设置为0,否则会出现多线程错误
lr = 1e-4
epochs = 20

数据读入和加载
这里同时展示两种方式:

  • 下载并使用PyTorch提供的内置数据集
  • 从网站下载以csv格式存储的数据,读入并转成预期的格式
    第一种数据读入方式只适用于常见的数据集,如MNIST,CIFAR10等,PyTorch官方提供了数据下载。这种方式往往适用于快速测试方法(比如测试下某个idea在MNIST数据集上是否有效)
    第二种数据读入方式需要自己构建Dataset,这对于PyTorch应用于自己的工作中十分重要

同时,还需要对数据进行必要的变换,比如说需要将图片统一为一致的大小,以便后续能够输入网络训练;需要将数据格式转为Tensor类,等等。

这些变换可以很方便地借助torchvision包来完成,这是PyTorch官方用于图像处理的工具库,上面提到的使用内置数据集的方式也要用到。PyTorch的一大方便之处就在于它是一整套“生态”,有着官方和第三方各个领域的支持。这些内容我们会在后续课程中详细介绍。

# 首先设置数据变换
from torchvision import transforms

image_size = 28
data_transform = transforms.Compose([
    # transforms.ToPILImage(),   # 这一步取决于后续的数据读取方式,如果使用内置数据集则不需要
    transforms.Resize(image_size),
    transforms.ToTensor()
])
## 读取方式一:使用torchvision自带数据集,下载可能需要一段时间
from torchvision import datasets

train_data = datasets.FashionMNIST(root='./', train=True, download=True, transform=data_transform)
test_data = datasets.FashionMNIST(root='./', train=False, download=True, transform=data_transform)
print(len(train_data))
60000
feature, label = train_data[0]
print(feature.shape, feature.dtype)  # Channel x Height X Width
print(label)

torch.Size([1, 28, 28]) torch.float32
9

在构建训练和测试数据集完成后,需要定义DataLoader类,以便在训练和测试时加载数据

train_loader = DataLoader(train_data, batch_size=batch_size, shuffle=True, num_workers=num_workers, drop_last=True)
test_loader = DataLoader(test_data, batch_size=batch_size, shuffle=False, num_workers=num_workers)

读入后,我们可以做一些数据可视化操作,主要是验证我们读入的数据是否正确

import matplotlib.pyplot as plt
%matplotlib inline
image, label = next(iter(train_loader))
print(image.shape, label.shape)
plt.imshow(image[0][0], cmap="gray")
torch.Size([256, 1, 28, 28]) torch.Size([256])





<matplotlib.image.AxesImage at 0x15f102b5a00>

模型设计
由于任务较为简单,这里我们手搭一个CNN,而不考虑当下各种模型的复杂结构
模型构建完成后,将模型放到GPU上用于训练

class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv = nn.Sequential(
            nn.Conv2d(1, 32, 5),
            nn.ReLU(),
            nn.MaxPool2d(2, stride=2),
            nn.Dropout(0.3),
            nn.Conv2d(32, 64, 5),
            nn.ReLU(),
            nn.MaxPool2d(2, stride=2),
            nn.Dropout(0.3)
        )
        self.fc = nn.Sequential(
            nn.Linear(64*4*4, 512),
            nn.ReLU(),
            nn.Linear(512, 10)
        )
        
    def forward(self, x):
        x = self.conv(x)
        x = x.view(-1, 64*4*4)
        x = self.fc(x)
        # x = nn.functional.normalize(x)
        return x

model = Net()
model = model.cuda()
# model = nn.DataParallel(model).cuda()   # 多卡训练时的写法,之后的课程中会进一步讲解

设定损失函数
使用torch.nn模块自带的CrossEntropy损失
PyTorch会自动把整数型的label转为one-hot型,用于计算CE loss
这里需要确保label是从0开始的,同时模型不加softmax层(使用logits计算),这也说明了PyTorch训练中各个部分不是独立的,需要通盘考虑

criterion = nn.CrossEntropyLoss()
# criterion = nn.CrossEntropyLoss(weight=[1,1,1,1,3,1,1,1,1,1])
?nn.CrossEntropyLoss # 这里方便看一下weighting等策略
Object `nn.CrossEntropyLoss # 这里方便看一下weighting等策略` not found.

设定优化器
这里我们使用Adam优化器

optimizer = optim.Adam(model.parameters(), lr=0.001)

训练和测试(验证)
各自封装成函数,方便后续调用
关注两者的主要区别:

  • 模型状态设置
  • 是否需要初始化优化器
  • 是否需要将loss传回到网络
  • 是否需要每步更新optimizer

此外,对于测试或验证过程,可以计算分类准确率

def train(epoch):
    model.train()
    train_loss = 0
    for data, label in train_loader:
        data, label = data.cuda(), label.cuda()
        optimizer.zero_grad()
        output = model(data)
        loss = criterion(output, label)
        loss.backward()
        optimizer.step()
        train_loss += loss.item()*data.size(0)
    train_loss = train_loss/len(train_loader.dataset)
    print('Epoch: {} \tTraining Loss: {:.6f}'.format(epoch, train_loss))
def val(epoch):       
    model.eval()
    val_loss = 0
    gt_labels = []
    pred_labels = []
    with torch.no_grad():
        for data, label in test_loader:
            data, label = data.cuda(), label.cuda()
            output = model(data)
            preds = torch.argmax(output, 1)
            gt_labels.append(label.cpu().data.numpy())
            pred_labels.append(preds.cpu().data.numpy())
            loss = criterion(output, label)
            val_loss += loss.item()*data.size(0)
    val_loss = val_loss/len(test_loader.dataset)
    gt_labels, pred_labels = np.concatenate(gt_labels), np.concatenate(pred_labels)
    acc = np.sum(gt_labels==pred_labels)/len(pred_labels)
    print('Epoch: {} \tValidation Loss: {:.6f}, Accuracy: {:6f}'.format(epoch, val_loss, acc))
for epoch in range(1, epochs+1):
    train(epoch)
    val(epoch)
Epoch: 1 	Training Loss: 0.684462
Epoch: 1 	Validation Loss: 0.460476, Accuracy: 0.837600
Epoch: 2 	Training Loss: 0.428233
Epoch: 2 	Validation Loss: 0.361145, Accuracy: 0.872700
Epoch: 3 	Training Loss: 0.364548
Epoch: 3 	Validation Loss: 0.346266, Accuracy: 0.875700
Epoch: 4 	Training Loss: 0.328705
Epoch: 4 	Validation Loss: 0.308907, Accuracy: 0.889900
Epoch: 5 	Training Loss: 0.304380
Epoch: 5 	Validation Loss: 0.292033, Accuracy: 0.892800
Epoch: 6 	Training Loss: 0.283067
Epoch: 6 	Validation Loss: 0.289701, Accuracy: 0.896000

模型保存
训练完成后,可以使用torch.save保存模型参数或者整个模型,也可以在训练过程中保存模型
这部分会在后面的课程中详细介绍

save_path = "./FahionModel.pkl"
torch.save(model, save_path)
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。