Pytorch学习(三)—— 常用工具包

数据集读取

在Pytorch中,数据加载可以通过自定义的数据集对象实现。数据集对象被抽象为Dataset类,实现自定义的数据集需要继承Dataset,并实现两个Python的魔方方法:

  • _ _ getitem _ _: 返回一条数据或一个样本。
  • _ _ len _ _: 返回样本的数量。

以Kaggle Dog vs. Cat 数据集为例:

import os
import torch
from torch.utils import data
from PIL import Image
import numpy as np

'''
    DataSet
'''
class DogCat(data.Dataset):
    def __init__(self, root):
        imgs = os.listdir(root)
        self.imgs = [os.path.join(root, img) for img in imgs]

    def __getitem__(self, index):
        img_path = self.imgs[index]
        lable = 1 if 'dog' in img_path.split('/')[-1] else 0
        pil_img = Image.open(img_path)
        array = np.asarray(pil_img)
        data = torch.from_numpy(array)
        return data, lable
    def __len__(self):
        return len(self.imgs)

dataset = DogCat('../../../DogCat/')
img, label = dataset[0]

数据预处理和ImageFolder

数据预处理

Pytorch提供的torchvision视觉工具包提供了很多视觉图像处理的工具,其中transforms模块提供了对PIL Image对象和Tensor对象的常用操作。

对PIL Image的常见操作如下:

  • Resize: 调整图片尺寸。
  • Crop: 裁剪图片。
  • Pad: 填充。
  • ToTensor: 将PIL Image对象转成Tensor,会自动将[0, 255]归一化为[0,1]。

对Tensor的常见操作如下:

  • Normalize: 标准化,即减均值,除以标准差。
  • ToPILImage: 将Tensor转换为PIL Image对象。

可以使用Compose操作将多个预处理操作组合起来:

from torchvision import transforms as T

'''
    torchvision
'''

transforms = T.Compose([
    T.Resize(224),          # 缩放,保持长宽比不变,最短边为224
    T.CenterCrop(224),      # 从图片中间切出224 * 224的图片
    T.ToTensor(),           # 将图片(Image)转成Tensor,归一化至[0, 1]
    T.Normalize(mean=[.5, .5, .5], std=[.5, .5, .5])        # 标准化至[-1, 1]
])

class PreDogCat(data.Dataset):
    def __init__(self, root):
        imgs = os.listdir(root)
        self.imgs = [os.path.join(root, img) for img in imgs]
        self.transforms = transforms

    def __getitem__(self, item):
        img_path = self.imgs[item]
        lable = 1 if 'dog' in img_path.split('/')[-1] else 0
        data = Image.open(img_path)
        if self.transforms:
            data = self.transforms(data)
        return data, lable

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

dataset = PreDogCat('../../../DogCat/')

for img, label in dataset:
	print(img.size(), label)

除此之外,transforms还可以通过Lambda封装自定义的转换策略:

trans = T.Lambda(lambda img: img.rotate(random() * 360))

ImageFolder

torchvision中预先实现了常用的Dataset,包括CIFAR-10,ImageNet,COCO,MNIST、LSUN等数据集,可以通过调用torchvision.datasets下相应对象来调用相关数据集。

此外,torchvision中还定义了一个常用的Dataset——ImageFolder,它假设所有文件按照文件夹保存,每个文件夹下存储同一个类别的图片,文件夹名为类名,其构造函数如下:

    def __init__(self, root, transform=None, target_transform=None,
                 loader=default_loader, is_valid_file=None):

主要参数如下:

  • root: 在指定root路径下寻找图片。
  • transform: 对PIL Image进行的转换操作,transform的输入是使用loader函数读取的图片对象。
  • target_transform: 对label的转换。
  • loader: 指定加载图片的函数,默认操作是读取为PIL Image对象。
from torchvision.datasets import ImageFolder

# 不加transform的ImageFolder
dataset = ImageFolder('../../../DogCat/')
print(dataset.class_to_idx)
print(dataset.imgs)
dataset[0][0].show()        # image
print(dataset[0][1])        # label

# 添加transform的ImageFolder
normalize = T.Normalize(mean=[0.4, 0.4, 0.4], std=[0.2, 0.2, 0.2])
transforms = T.Compose([
    T.RandomResizedCrop(224),
    T.RandomHorizontalFlip(),
    T.ToTensor(),
    normalize
])

dataset = ImageFolder('../../../DogCat/', transform=transforms)

toimg = T.ToPILImage()
toimg(dataset[0][0] * 0.2 + 0.4)

数据加载

在训练神经网络时,通常都是对一个batch的数据进行操作,同时还需要对数据进行shuffle和并行加速。对此,Pytorch提供了DataLoader进行数据加载。

DataLoader的函数定义如下:

    def __init__(self, dataset, batch_size=1, shuffle=False, sampler=None,
                 batch_sampler=None, num_workers=0, collate_fn=None,
                 pin_memory=False, drop_last=False, timeout=0,
                 worker_init_fn=None, multiprocessing_context=None):
  • dataset: 加载的数据集,Dataset对象。
  • batch_size: 批大小。
  • shuffle: 是否将数据打乱。
  • sampler: 样本抽取方式。
  • num_worker: 使用多线程加载的进程数。
  • collate_fn: 如何将多个样本拼接成一个batch。
  • pin_memory: 是否将数据保存在pin memory区,pin memory中的数据转移到GPU中会快一些。
  • drop_last: dataset中的数据个数可能不是batch_size的整数倍,drop_last为True时会将多出来不足一个batch的数据丢弃。

另外,Pytorch中单独提供了一个sampler模块,用来对数据进行采样。当DataLoader的shuffle参数为Ture时,系统会自动调用随机采样器RandomSampler,否则调用默认采样器SequentialSampler,此外还提供了另一个很有用的采样器:WeightRandomSampler,它会根据每个样本的权重选取数据。

from torch.utils.data import DataLoader
from torchvision.datasets import ImageFolder
from torchvision import transforms as T
from torch.utils.data.sampler import WeightedRandomSampler

transforms = T.Compose([
    T.RandomResizedCrop(224),
    T.ToTensor()
])
dataset = ImageFolder("../../../DogCat", transform=transforms)
dataloader = DataLoader(dataset, batch_size=20, shuffle=True, num_workers=0, drop_last=False)


for batch_datas, batch_labels in dataloader:
    print(batch_datas.size(), batch_labels.size())

weights = [2 if lable == 1 else 1 for data, lable in dataset]

sampler = WeightedRandomSampler(weights, num_samples=10, replacement=True)

dataloader = DataLoader(dataset, batch_size=5, sampler=sampler)

当DataLoader使用多进程加载时,程序出现异常终止,比如Ctrl+C强制退出时,相应的数据加载进程可能无法正常退出,出现程序已经退出但是GPU显存和内存依旧被占用的情况,这时候就需要使用如下命令手动强行终止进程:
ps x | grep <cmdline> | awk '{print $1}' | xargs kill

  • ps x: 获取当前用户的所有进程。
  • grep < cmdline >: 找到已经停止的Pytorch程序进程。
  • awk ‘{print $1}’: 获取进程pid。
  • xargs kill: kill进程。

在kill之前,建议先打印确认进程:
ps x | grep <cmdline>

torchvision

除了上述的datasets数据集和transforms数据预处理操作,torchvision中还提供了models。

models中提供了深度学习中各种经典网络的网络结构及预训练模型:

from .alexnet import *
from .resnet import *
from .vgg import *
from .squeezenet import *
from .inception import *
from .densenet import *
from .googlenet import *
from .mobilenet import *
from .mnasnet import *
from .shufflenetv2 import *

加载模型:

from torchvision import models
from torch import nn

'''
    torchvision models
'''

resnet34 = models.resnet34(pretrained=True, num_classes=1000)
# resnet34.fc = nn.Linear(512, 10)        #   修改为10分类问题

# 模型保存
torch.save(resnet34.state_dict(), 'resnet34.pth')

# 模型加载
resnet34.load_state_dict(torch.load('resnet34.pth'))

# 优化器参数保存
optimizer = torch.optim.Adam(resnet34.parameters(), lr=0.01)
torch.save(optimizer.state_dict(), 'optimizer.pth')

# 优化器参数加载
optimizer.load_state_dict(torch.load('optimizer.pth'))

all_data = dict(
    optimizer=optimizer.state_dict(),
    model=resnet34.state_dict(),
    info='模型和优化器的所有参数'
)

torch.save(all_data, 'all.pth')

all_data = torch.load('all.pth')
all_data.key()

torchvision还提供了两个常用函数:一个make_grid,它能将多张图片拼接在一个网格中;另一个是save_img,它能将Tensor保存为图片。

GPU加速:CUDA

Pytorch中的Tensor和nn.Module都带有一个.cuda方法,调用此方法可以将其转换为对应的GPU对象。需要注意的是,tensor.cuda会返回一个新对象,这个新对象的数据已转移到GPU,而之前的tensor数据还在原设备上。module.cuda会将所有的数据都迁移到GPU,并返回自己,所以 module = module.cudamodule.cuda效果相同。

为什么将数据转移至GPU的方法叫做.cuda而不叫.gpu?
这是因为GPU的编程接口采用CUDA,而目前并不是所有的GPU都支持CUDA,只有部分NVIDIA的GPU才支持。AMD GPU的编程接口采用OpenCL,因此Pytorch中预留了.cl方法,用于支持AMD GPU。

关于使用GPU加速的建议:

  • GPU运算很快,但是当计算量小时,并不能体现出它的优势,因此一些简单的操作可以直接利用CPU完成。
  • 数据在CPU和GPU之间传递比较耗时,应当尽量避免。
  • 在低精度计算时,可以考虑HalfTensor,相比FloatTensor能够节省一半的显存,但是需要注意数值溢出的问题。
  • Pytorch封装了分布式训练接口。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值