【PyTorch】PyTorch的各个组件和实战

目录

  • 1 PyTorch的各个组件
    • 1.1 基本配置
      • 1.1.1 导入包
      • 1.1.2 超参数
      • 1.1.3 GPU配置
    • 1.2 数据读入
      • 1.2.1 Dataset
        • 1.2.1.1 使用pytorch自带的dataset类
        • 1.2.1.2 自己定义dataset
      • 1.2.2 Dataloader
    • 1.3 模型构建
    • 1.4 模型初始化
    • 1.5 损失函数:
    • 1.6 训练和评估
    • 1.7 可视化
    • 1.8 优化器
  • 2 FashionMNIST时装分类实战
    • 2.1 基本配置
    • 2.2 数据读入
    • 2.3 模型构建
    • 2.4 损失函数
    • 2.5 优化器
    • 1.6训练与评估

1 PyTorch的各个组件

1.1 基本配置

1.1.1 导入包

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

1.1.2 超参数

  • batch size:批次的大小,决定一次读入多少数据
  • learning rate:初始学习率(初始)
  • max_epoches:最大迭代次数
  • num_workers:决定使用多少线程读入数据

1.1.3 GPU配置

有以下几种方式:

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

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

#方案三:对于想用gpu的macOS用户,需要搭配预览版的pytorch(Nightly)
device = torch.device("mps")

1.2 数据读入

Dataset + Dataloader

1.2.1 Dataset

常见有两种方式。

1.2.1.1 使用pytorch自带的dataset类

import torch
from torchvision import datasets
train_data = datasets.ImageFolder(train_path, transform=data_transform)
val_data = datasets.ImageFolder(val_path, transform=data_transform)

1.2.1.2 自己定义dataset

我们可以定义自己的Dataset类来实现灵活的数据读取,定义的类需要继承PyTorch自身的Dataset类。主要包含三个函数:

  • __ init __: 用于向类中传入外部参数,同时定义样本集
  • __ getitem __: 用于逐个读取样本集合中的元素,可以进行一定的变换,并将返回训练/验证所需的数据
  • __ len __: 用于返回数据集的样本数

1.2.2 Dataloader

from torch.utils.data import DataLoader

它的参数:

  • batch_size:样本是按“批”读入的,batch_size就是每次读入的样本数
  • num_workers:有多少个进程用于读取数据
  • shuffle:是否将读入的数据打乱
  • drop_last:对于样本最后一部分没有达到批次数的样本,使其不再参与训练

1.3 模型构建

Module 类是 nn 模块里提供的一个模型构造类,是所有神经⽹网络模块的基类,我们可以继承它来定义我们想要的模型。下面继承 Module 类构造多层感知机。这里定义的 MLP 类重载了 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)   

1.4 模型初始化

torch.nn.init提供了常用的初始化方法,如:

  • torch.nn.init.uniform_(tensor, a=0.0, b=1.0)
  • torch.nn.init.normal_(tensor, mean=0.0, std=1.0)
  • torch.nn.init.constant_(tensor, val)
  • torch.nn.init.ones_(tensor)
  • torch.nn.init.zeros_(tensor)
  • torch.nn.init.eye_(tensor)

通常使用isinstance()来进行判断模块属于什么类型,对于不同的类型层,我们就可以设置不同的权值初始化的方法。

常常将各种初始化方法定义为一个initialize_weights()的函数并在模型初始后进行使用。

1.5 损失函数:

  • 二分类交叉熵损失函数
    • torch.nn.BCELoss(weight=None, size_average=None, reduce=None, reduction=‘mean’)
    • 功能:计算二分类任务时的交叉熵(Cross Entropy)函数。
  • 交叉熵损失函数
    • torch.nn.CrossEntropyLoss(weight=None, size_average=None, ignore_index=-100, reduce=None, reduction=‘mean’)
    • 功能:计算交叉熵(Cross Entropy)函数。
  • L1损失函数
    • torch.nn.L1Loss(size_average=None, reduce=None, reduction=‘mean’)
    • 功能: 计算输出 y 和真实标签 target 之间的差值的绝对值。
  • MSE损失函数
    • torch.nn.MSELoss(size_average=None, reduce=None, reduction=‘mean’)
    • 功能:计算输出 y 和真实标签 target 之差的平方。
  • 平滑L1 (Smooth L1)损失函数
    • torch.nn.SmoothL1Loss(size_average=None, reduce=None, reduction=‘mean’, beta=1.0)
    • 功能:L1的平滑输出,其功能是减轻离群点带来的影响。
  • 目标泊松分布的负对数似然损失
    • torch.nn.PoissonNLLLoss(log_input=True, full=False, size_average=None, eps=1e-08, reduce=None, reduction=‘mean’)
    • 功能:泊松分布的负对数似然损失函数。
  • KL散度
    • torch.nn.KLDivLoss(size_average=None, reduce=None, reduction=‘mean’, log_target=False)
    • 功能:计算KL散度,也就是计算相对熵。用于连续分布的距离度量,并且对离散采用的连续输出空间分布进行回归通常很有用。
  • MarginRankingLoss
    • torch.nn.MarginRankingLoss(margin=0.0, size_average=None, reduce=None, reduction=‘mean’)
    • 功能:计算两个向量之间的相似度,用于排序任务。该方法用于计算两组数据之间的差异。
  • 多标签边界损失函数
    • torch.nn.MultiLabelMarginLoss(size_average=None, reduce=None, reduction=‘mean’)
    • 功能:对于多标签分类问题计算损失函数。
  • 二分类损失函数
    • torch.nn.SoftMarginLoss(size_average=None, reduce=None, reduction=‘mean’)torch.nn.(size_average=None, reduce=None, reduction=‘mean’)
    • 功能: 计算二分类的 logistic 损失。
  • 多分类的折页损失
    • torch.nn.MultiMarginLoss(p=1, margin=1.0, weight=None, size_average=None, reduce=None, reduction=‘mean’)
    • 功能:计算多分类的折页损失。
  • 三元组损失
    • torch.nn.TripletMarginLoss(margin=1.0, p=2.0, eps=1e-06, swap=False, size_average=None, reduce=None, reduction=‘mean’)
    • 功能: 计算三元组损失。
  • HingEmbeddingLoss
    • torch.nn.HingeEmbeddingLoss(margin=1.0, size_average=None, reduce=None, reduction=‘mean’)
    • 功能:对输出的embedding结果做Hing损失计算。
  • 余弦相似度
    • torch.nn.CosineEmbeddingLoss(margin=0.0, size_average=None, reduce=None, reduction=‘mean’)
    • 功能:对两个向量做余弦相似度。
  • CTC损失函数
    • torch.nn.CTCLoss(blank=0, reduction=‘mean’, zero_infinity=False)
    • 功能:用于解决时序类数据的分类。

1.6 训练和评估

首先设置模型的状态:

  • 如果是训练状态,那么模型的参数应支持反向传播的修改;
  • 如果是验证/测试状态,则不应该修改模型参数。

在PyTorch中,模型的状态设置非常简便,如下的两个操作二选一即可:

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

训练过程:

用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.7 可视化

  • 分类的ROC曲线
  • 卷积网络中的卷积核
  • 训练/验证过程的损失函数曲线

1.8 优化器

优化器是根据网络反向传播的梯度信息来更新网络的参数,以起到降低loss函数计算值,使得模型输出更加接近真实标签。

Pytorch优化器库torch.optim,提供了十种优化器:

  • 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
    以上这些优化算法均继承于Optimizer

2 FashionMNIST时装分类实战

通过FashionMNIST时装分类来学习PyTorch进行深度学习的流程。FashionMNIST数据集中包含已经预先划分好的训练集和测试集,其中训练集共60,000张图像,测试集共10,000张图像。每张图像均为单通道黑白图像,大小为32*32pixel,分属10个类别。

2.1 基本配置

  • 导入必要的packages
    • os, numpy, pandas, torch, torch.nn, torch.optim, torch.utils.data
  • 配置训练的超参数
    • batch size, learning rate, max_epoches, num_workers
  • 配置训练使用的硬件设备
    • CPU/GPU, which GPUs
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")
device = torch.device('mps') #这里使用的是M1 Pro的GPU

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

2.2 数据读入

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

  • 下载并使用PyTorch提供的内置数据集:只适用于常见的数据集,如MNIST,CIFAR10等,PyTorch官方提供了数据下载。这种方式往往适用于快速测试方法(比如测试下某个idea在MNIST数据集上是否有效)
  • 从网站下载以csv格式存储的数据,读入并转成预期的格式:需要自己构建Dataset,这对于PyTorch应用于自己的工作中十分重要。
    • 如何读取格式不定的本地数据:定义一个Dataset
      • __ init __, __ getitem __, __ len __
    • 将数据加载以供模型输入;定义Dataloader
      • batch_size, num_workers, shuffle, drop_last, pin_memory
        同时,还需要对数据进行必要的变换,比如说需要将图片统一为一致的大小,以便后续能够输入网络训练;需要将数据格式转为Tensor类,等等。

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

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

image_size = 28 #可以随便设置,但是要与后面的模型一致
data_transform = transforms.Compose([
    transforms.ToPILImage(),   # 这一步取决于后续的数据读取方式,如果使用内置数据集则不需要
    transforms.Resize(image_size), #改变大小
    transforms.ToTensor() #转成tensor
])
## 读取方式一:使用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)
## 读取方式二:读入csv格式的数据,自行构建Dataset类
# csv数据下载链接:https://www.kaggle.com/zalando-research/fashionmnist
class FMDataset(Dataset):
    def __init__(self, df, transform=None):
        self.df = df
        self.transform = transform
        self.images = df.iloc[:,1:].values.astype(np.uint8) #提取图像
        self.labels = df.iloc[:, 0].values #提取标签
        
    def __len__(self):
        return len(self.images) #图片数量
    
    def __getitem__(self, idx): #决定dataset是如何构建的,通过idx索引图片
        image = self.images[idx].reshape(28,28,1) #变成28*28的图像,1用于加一个通道
        label = int(self.labels[idx])
        if self.transform is not None:
            image = self.transform(image)
        else:
            image = torch.tensor(image/255., dtype=torch.float)#变成0-1的tensor
        label = torch.tensor(label, dtype=torch.long)
        return image, label

train_df = pd.read_csv("./fashion-mnist_train.csv")
test_df = pd.read_csv("./fashion-mnist_test.csv")
train_data = FMDataset(train_df, data_transform)
test_data = FMDataset(test_df, data_transform)
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)
  • batch_size:一次读入模型的图片数量
  • shuffle:打乱后采样
  • num_workers:用多少个线程读,数值越大,读得越快
  • drop_last:最后一个batch的图片数量可能小于batch_size,如果drop就会将其丢弃
  • pin_memory:空间换时间,将数据存起来,增加内存但运行更快,默认False

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

import matplotlib.pyplot as plt
image, label = next(iter(train_loader))
print(image.shape, label.shape)
plt.imshow(image[0][0], cmap="gray")

运行到这里,我的jupyter notebook就开始内核崩溃挂掉,上网查了很多资料,其实主要还是依赖的冲突问题,解决方案查看这里。
【PyTorch】PyTorch的各个组件和实战_第1张图片

next(iterable[, default])

  • 函数要和生成迭代器的 iter() 函数一起使用。
  • iterable – 可迭代对象
  • default – 可选,用于设置在没有下一个元素时返回该默认值,如果不设置,又没有下一个元素则会触发 StopIteration 异常。

2.3 模型构建

  • 神经网络的构造:基于nn.Module
    • __ init __, forward
  • 神经网络通过“层定义+层顺序”的方式构建
  • 常见层:nn.Conv2, nn.MaxPool2d, nn.Linear, nn.ReLU…
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)    # 10的设定比较关键,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.to(device)
# model = nn.DataParallel(model).cuda()   # 多卡训练时的写法,之后的课程中会进一步讲解

2.4 损失函数

  • torch.nn提供了多种预定义的损失函数
  • 可以自己定义损失函数
  • 根据实际需求选用对应的损失函数
  • 损失函数常用操作
    • backward()
cirterion = nn.CrossEntropyLoss()

2.5 优化器

  • torch.optim提供了多种预定义的优化器
  • 可以自己定义优化器
  • 根据实际需求选用对应的损失函数
  • 优化器常用操作:
    • step(), zero_grad(), load_state_dict()…
optimizer = optim.Adam(model.parameters(),lr=0.001)

1.6训练与评估

  • 模型状态设置
    • model.train(). model.eval()
  • 训练流程:读取、转换、梯度清零、输入、计算损失、反向传播、参数更新
  • 验证流程、读取、转换、输入、计算损失、计算指标
def train(epoch):
    model.train()
    train_loss = 0
    for data, label in train_loader:
        data, label = data.to(device), label.to(device) # 把data和label放在gpu上
        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))

如果需要对每一个batch进行一些操作,可以使用 for i,(data,label) in enumerate(train_loader)

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.to(device), label.to(device)
            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)

你可能感兴趣的:(PyTorch,Python,pytorch,深度学习,python)