使用pytorch读取数据集

目录
  • pytorch读取数据集
    • 第一种
    • 第二种
    • 第三种
  • pytorch学习记录
    • 注意事项

pytorch读取数据集

使用pytorch读取数据集一般有三种情况

第一种

读取官方给的数据集,例如Imagenet,CIFAR10,MNIST等

这些库调用torchvision.datasets.XXXX()即可,例如想要读取MNIST数据集

import torch
import torch.nn as nn
import torch.utils.data as Data
import torchvision
train_data = torchvision.datasets.MNIST(
    root='./mnist/',
    train=True,                                     # this is training data
    transform=torchvision.transforms.ToTensor(),    # Converts a PIL.Image or numpy.ndarray to
                                                    # torch.FloatTensor of shape (C x H x W) and normalize in the range [0.0, 1.0]
    download=True,
)

这样就会自动从网上下载MNIST数据集,并且以保存好的数据格式来读取

然后直接定义DataLoader的一个对象,就可以进行训练了

train_loader = Data.DataLoader(dataset=train_data, batch_size=BATCH_SIZE, shuffle=True)
for epoch in range(EPOCH):
    for step, (b_x, b_y) in enumerate(train_loader):   # gives batch data, normalize x when iterate train_loader
    	XXXX
    	XXXX

第二种

这种就比较常用了,针对图像的分类问题

适用情况是,对于图片的多分类问题,图片按照指定的格式来存放:

  • 根路径/类别(标签label)/图片

按照上面的格式来存放图片,根路径下面保存了许多文件夹,每个文件夹中存放了某一类的图片,并且文件夹名就是类的映射,例如这样,根目录就是learn_pytorch,下面的每个文件夹代表一个类,类的名字随便命名,在训练过程中会自动被映射成0,1,2,3…

保存成这样的格式之后,就可以直接利用pytorch定义好的派生类ImageFolder来读取了,ImageFolder其实就是Dataset的派生类,专门被定义来读取特定格式的图片的,它也是 torchvision库帮我们方便使用的,比如这样

然后就可以作为DataLoader的数据集输入用了

from torchvision.datasets import ImageFolder
data_transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.5,0.5,0.5], std=[0.5, 0.5, 0.5])
])
dataset = ImageFolder("/home/xxx/learn_pytorch/",transform = data_transform)
train_loader = Data.DataLoader(dataset=dataset, batch_size=BATCH_SIZE, shuffle=True)

它的构造函数要求输入两个参数,一个根目录,一个对数据的操作,因为图片被自动读取成PILimage数据格式,因此Totensor()必不可少,而且可以用transforms.Compose把许多操作合成一个参数输入,就能实现数据增强,非常方便。上面的例子是先转成tensor,然后归一化,没做数据增强的各种操作。如果要数据增强,可以再加一些裁剪、反转之类的,都可以。比如下面的

transforms.RandomSizedCrop
transforms.RandomHorizontalFlip()

还有一个问题是,如何知道文件夹名被映射成了什么标签,这个可以直接查看定义的对象的class_to_idx属性

这个ImageFolder产生的dataset对象,第一维就是第几张图片,第二维元素0是图片矩阵 元素1是label

接下来就是建立模型+训练了

训练的过程和第一种一样

第三种

这种情况是最通用的,适用于不是分类问题,或者标签不是简单的文件名的映射

思路就是自己定义一个Dataset的派生类,并且对数据的处理、数据增强之类的都需要自己定义,这些定义的时候利用__call_()就可以了

实现过程是:

首先

定义一个Dataset的派生类,这个派生类目标是重载两个魔法方法 __ len __ (),__ getitem__()

  • __ len __ () 函数是在调用 len(对象)的时候会被调用并返回,重载的目的是,在调用的时候返回数据集的大小
  • __getitem __() 函数可让对象编程可迭代的,定义了它之后就可以使得对像被for语句迭代,重载它的目的是能够使得它每次都迭代返回数据集的一个样本

现在定义一个派生类

class FaceLandmarksDataset(Dataset):
    """Face Landmarks dataset."""
    def __init__(self, csv_file, root_dir, transform=None):
        """
        Args:
            csv_file (string): Path to the csv file with annotations.
            root_dir (string): Directory with all the images.
            transform (callable, optional): Optional transform to be applied
                on a sample.
        """
        self.landmarks_frame = pd.read_csv(csv_file)
        self.root_dir = root_dir
        self.transform = transform
    def __len__(self):
        return len(self.landmarks_frame)
    def __getitem__(self, idx):
        img_name = os.path.join(self.root_dir,
                                self.landmarks_frame.iloc[idx, 0])
        image = io.imread(img_name)
        landmarks = self.landmarks_frame.iloc[idx, 1:].as_matrix()
        landmarks = landmarks.astype('float').reshape(-1, 2)
        sample = {'image': image, 'landmarks': landmarks}
        if self.transform:
            sample = self.transform(sample)
        return sample

构造函数就是定义了一些属性,例如读取出保存整个数据集的表格,然后len就是返回了数据集的数目,getitem则是定义了迭代返回一个数据集样本,返回值可以是包含训练样本和标签的list,也可以是字典,根据这个不同后面的用法也回不太一样(无非就是索引是数字还是key的区别)

除此之外,Dataset一般还会要求输入对数据集的操作,要是不想数据增强,就加个ToTensor就可以(因为要转换成tensor才能训练),要是想数据增强就自己加一些新的类(没错,ToTensor、各种数据增强的函数其实都是一个类,然后定义的一个对象),接着用transforms.Compose把他们连在一起就可以了。上面的transform写的是None,就是不进行数据处理,直接输出

然后实例化这个类,就可以作为DataLoader的参数输入了

face_dataset = FaceLandmarksDataset(csv_file='faces/face_landmarks.csv',
                                    root_dir='faces/')

这时候分析一下这个对象,定义它的参数就是init构造函数需要的,然后对他进行迭代的时候会自动调用getitem 例如下面的操作结果是

for i in range(len(face_dataset)):
    sample = face_dataset[i]
    print(sample['image'])
    print(i,sample['image'].shape, sample['landmarks'].shape)

可以看到每次迭代的时候都会输入一个字典

接下来定义一下DataLoader,就可以去迭代输入了,当然这里还不行,因为需要将数据集转换成tensor才能输入到模型进行训练

那么接下来就是考虑刚才那个DataSet类里的transform怎么改,最初给的是None,不做处理,因此出来的还是ImageArray,至少要实现ToTensor才行。

实现ToTensor这个类就主要用到了 __call __()魔法函数

__ call__()函数比较特殊,可以让对象本身变成可调用的,可以后面加括号并输入参数,然后就会自动调用call这个魔法函数

Totensor类的实现如下,注意numpy和tensor数组区别在 一个通道数在后,一个通道数在前,因此还需要交换不同维度的位置

class ToTensor(object):
    """Convert ndarrays in sample to Tensors."""
    def __call__(self, sample):
        image, landmarks = sample['image'], sample['landmarks']
        # swap color axis because
        # numpy image: H x W x C
        # torch image: C X H X W
        image = image.transpose((2, 0, 1))
        return {'image': torch.from_numpy(image),
                'landmarks': torch.from_numpy(landmarks)}

使用的时候先定义一个对象,然后 对象(参数)就会自动调用call函数了

再看几个数据增强的类的实现,它们所有的相似点都是,call函数的参数都是sample,也就是输入的数据集

class Rescale(object):
    """Rescale the image in a sample to a given size.
    Args:
        output_size (tuple or int): Desired output size. If tuple, output is
            matched to output_size. If int, smaller of image edges is matched
            to output_size keeping aspect ratio the same.
    """
    def __init__(self, output_size):
        assert isinstance(output_size, (int, tuple))
        self.output_size = output_size
    def __call__(self, sample):
        image, landmarks = sample['image'], sample['landmarks']
        h, w = image.shape[:2]
        if isinstance(self.output_size, int):
            if h > w:
                new_h, new_w = self.output_size * h / w, self.output_size
            else:
                new_h, new_w = self.output_size, self.output_size * w / h
        else:
            new_h, new_w = self.output_size
        new_h, new_w = int(new_h), int(new_w)
        img = transform.resize(image, (new_h, new_w))
        # h and w are swapped for landmarks because for images,
        # x and y axes are axis 1 and 0 respectively
        landmarks = landmarks * [new_w / w, new_h / h]
        return {'image': img, 'landmarks': landmarks}
class RandomCrop(object):
    """Crop randomly the image in a sample.
    Args:
        output_size (tuple or int): Desired output size. If int, square crop
            is made.
    """
    def __init__(self, output_size):
        assert isinstance(output_size, (int, tuple))
        if isinstance(output_size, int):
            self.output_size = (output_size, output_size)
        else:
            assert len(output_size) == 2
            self.output_size = output_size
    def __call__(self, sample):
        image, landmarks = sample['image'], sample['landmarks']
        h, w = image.shape[:2]
        new_h, new_w = self.output_size
        top = np.random.randint(0, h - new_h)
        left = np.random.randint(0, w - new_w)
        image = image[top: top + new_h,
                      left: left + new_w]
        landmarks = landmarks - [left, top]
        return {'image': image, 'landmarks': landmarks}

这两个就很清晰了,首先是构造函数要求在定义对象的时候输入参数,接着再用call实现直接调用对象。

用的时候就可以

transformed_dataset = FaceLandmarksDataset(csv_file='faces/face_landmarks.csv',
                                           root_dir='faces/',
                                           transform=transforms.Compose([
                                               Rescale(256),
                                               RandomCrop(224),
                                               ToTensor()
                                           ]))
for i in range(len(transformed_dataset)):
    sample = transformed_dataset[i]
    print(i, sample['image'].size(), sample['landmarks'].size())
    if i == 3:
        break

分析一下,首先定义重载DataSet类的对象,transform参数写成上面定义的三个操作类的组合,回头去看这个类的定义

        self.transform = transform

上面就定义了一个三个类联合起来的对象

        if self.transform:
            sample = self.transform(sample)

然后直接调用该对象,调用了三个类的call函数,就返回了处理后的数据集了

最后终于可以迭代训练了

dataloader = DataLoader(transformed_dataset, batch_size=4, shuffle=True, num_workers=4)

定义一个DataLoader的对象,剩下的用法就和第二种的一样,两重循环进行训练了,这个DataLoader也有点技巧,就是每次对它迭代的时候,返回的还是DataSet类对象返回值的形式,但是里面的内容又在前面加了一个维度,大小就是batch_size,也就是说,DataLoader对象调用的时候每次从迭代器里取出来batch_size个样本,并把它们堆叠起来(这个堆叠是在列表/字典内堆叠的),每次迭代出来的内容还都是一个字典/数组

pytorch学习记录

这是我随便搭的一个简单模型,测试一下

import os
import torch
import torch.nn as nn
import torch.utils.data as Data
import torchvision
import matplotlib.pyplot as plt
from torchvision import transforms
from torchvision.datasets import ImageFolder
import matplotlib.pyplot as plt
%matplotlib inline
#定义几个参数
EPOCH = 20
BATCH_SIZE = 4
LR = 0.001
#读取数据
data_transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.5,0.5,0.5], std=[0.5, 0.5, 0.5])
])
dataset = ImageFolder("/home/xxx/learn_pytorch/",transform = data_transform)
print(dataset[0][0].size())
print(dataset.class_to_idx)
#定义
train_loader = Data.DataLoader(dataset=dataset, batch_size=BATCH_SIZE, shuffle=True)
#定义模型类,是 nn.Module的继承类,思路是先把每个层都定义出来,每个都是模型类的属性,然后再定义一个成员函数forward()作为前向传播过程,就可以把每个层连起来了,通过这个就搭好了整个模型
class CNN(nn.Module):
    def __init__(self):
        super(CNN,self).__init__()
        self.conv1 = nn.Sequential(
            nn.Conv2d(3,16,5,1,2),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2),
        )
        self.conv2 = nn.Sequential(
            nn.Conv2d(16, 32, 5, 1, 2),
            nn.ReLU(),
            nn.MaxPool2d(2),
        )
        self.conv3 = nn.Sequential(
            nn.Conv2d(32, 64, 5, 1, 2),
            nn.ReLU(),
            nn.MaxPool2d(2),
        )
        self.conv4 = nn.Sequential(
            nn.Conv2d(64, 128, 5, 1, 2),
            nn.ReLU(),
            nn.MaxPool2d(2),
        )
        self.out1 = nn.Sequential(
            nn.Linear(128*16*30, 1000),
            nn.ReLU(),
        )
        self.out2 = nn.Sequential(
            nn.Linear(1000, 100),
            nn.ReLU(),
        )
        self.out3 = nn.Sequential(
            nn.Linear(100, 4),
        )
    def forward(self, x):
        x = self.conv1(x)
        x = self.conv2(x)
        x = self.conv3(x)
        x = self.conv4(x)
        x = x.view(x.size(0), -1)           # flatten the output of conv2 to (batch_size, 32 * 7 * 7)
        x = self.out1(x)
        x = self.out2(x)
        output = self.out3(x)
        return output, x    # return x for visualization
#如果使用GPU训练要把模型和tensor放到GPU上,通过.cuda来实现
cnn = CNN().cuda()
print(cnn)
#定义优化器对象、损失函数
optimizer = torch.optim.Adam(cnn.parameters(), lr=LR)   # optimize all cnn parameters
loss_func = nn.CrossEntropyLoss()                       # the target label is not one-hotted
#二重循环开始训练,外层循环是迭代次数,第二重循环就是每次对batch_size的数据读取并训练
for epoch in range(EPOCH):
    accy_count = 0
    for step,(b_x,b_y) in enumerate(train_loader):
        output = cnn(b_x.cuda())[0]
        loss = loss_func(output,b_y.cuda())     #carcute loss
        optimizer.zero_grad()           #clear gradient
        loss.backward()                 #sovel gradient
        optimizer.step()                #gradient sovel
        output_index = torch.max(output,1)[1].cpu().data.numpy()
        accy_count += float((output_index==b_y.data.numpy()).astype(int).sum())
    accuracy = accy_count/(BATCH_SIZE * train_loader.__len__())
    print("Epoch:",epoch," accuracy is: ",accuracy)

注意事项

使用GPU训练的时候,要把模型、tensor都放在GPU上,就是后面加个.cuda(),例如定义模型对象的时候,cnn.cuda()

还有输入进模型、计算loss的时候,b_x.cuda() b_y.cuda()

tensor a 转numpy a.data.numpy()

如果是在GPU上,要先a.cpu().data.numpy()

nn.CrossEntropyLoss()这个损失函数是个大坑,它是softmax + 归一化,所以使用这个损失函数的时候模型最后就不要再加softmax了,不然会发现自己的损失就那几个值,也降不下去

输入模型的 input图像,格式为(batch_size,Nc,H,W)的四维矩阵

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • PyTorch加载数据集梯度下降优化

    目录 一.实现过程 1.准备数据 2.设计模型 3.构造损失函数和优化器 4.训练过程 5.结果展示 二.参考文献 一.实现过程 1.准备数据 与PyTorch实现多维度特征输入的逻辑回归的方法不同的是:本文使用DataLoader方法,并继承DataSet抽象类,可实现对数据集进行mini_batch梯度下降优化. 代码如下: import torch import numpy as np from torch.utils.data import Dataset,DataLoader clas

  • Pytorch中的数据集划分&正则化方法

    1.训练集&验证集&测试集 训练集:训练数据 验证集:验证不同算法(比如利用网格搜索对超参数进行调整等),检验哪种更有效 测试集:正确评估分类器的性能 正常流程:验证集会记录每个时间戳的参数,在加载test数据前会加载那个最好的参数,再来评估.比方说训练完6000个epoch后,发现在第3520个epoch的validation表现最好,测试时会加载第3520个epoch的参数. import torch import torch.nn as nn import torch.nn.func

  • PyTorch手写数字数据集进行多分类

    目录 一.实现过程 0.导包 1.准备数据 2.设计模型 3.构造损失函数和优化器 4.训练和测试 二.参考文献 一.实现过程 本文对经典手写数字数据集进行多分类,损失函数采用交叉熵,激活函数采用ReLU,优化器采用带有动量的mini-batchSGD算法. 所有代码如下: 0.导包 import torch from torchvision import transforms,datasets from torch.utils.data import DataLoader import tor

  • Python深度学习pytorch实现图像分类数据集

    目录 读取数据集 读取小批量 整合所有组件 目前广泛使用的图像分类数据集之一是MNIST数据集.如今,MNIST数据集更像是一个健全的检查,而不是一个基准. 为了提高难度,我们将在接下来的章节中讨论在2017年发布的性质相似但相对复杂的Fashion-MNIST数据集. import torch import torchvision from torch.utils import data from torchvision import transforms from d2l import to

  • 使用pytorch读取数据集

    目录 pytorch读取数据集 第一种 第二种 第三种 pytorch学习记录 注意事项 pytorch读取数据集 使用pytorch读取数据集一般有三种情况 第一种 读取官方给的数据集,例如Imagenet,CIFAR10,MNIST等 这些库调用torchvision.datasets.XXXX()即可,例如想要读取MNIST数据集 import torch import torch.nn as nn import torch.utils.data as Data import torchv

  • pytorch dataset实战案例之读取数据集的代码

    目录 概述 项目结构与代码 总结 参考资料 概述 最近在跑一篇图像修复论文的代码,配置好环境之后开始运行,发现数据一直加载不进去.害,还是得看人家代码咋写的,一句一句看逻辑,准能找出问题.通读dataset后,发现了问题所在,终于成功加载了数据集. 项目结构与代码 项目结构 主要的目的就是从数据集中读取到彩色图像和掩码图像.代码代码中涉及到torch.transforms.合并路径等知识点,我在代码中都进行了详细的注释,路径要对照着项目结构,如果自己用的话要根据项目结构去将相对路径改过来.dat

  • Pytorch 实现数据集自定义读取

    以读取VOC2012语义分割数据集为例,具体见代码注释: VocDataset.py from PIL import Image import torch import torch.utils.data as data import numpy as np import os import torchvision import torchvision.transforms as transforms import time #VOC数据集分类对应颜色标签 VOC_COLORMAP = [[0,

  • 解决pytorch读取自制数据集出现过的问题

    问题1 问题描述: TypeError: default_collate: batch must contain tensors, numpy arrays, numbers, dicts or lists; found <class 'PIL.Image.Image'> 解决方式 数据格式不对, 把image转成tensor,参数transform进行如下设置就可以了:transform=transform.ToTensor().注意检测一下transform 问题2 问题描述: TypeE

  • Pytorch中使用ImageFolder读取数据集时忽略特定文件

    目录 一.使用ImageFolder读取数据集时忽略特定文件 二.ImageFolder只读取部分类别文件夹 一.使用ImageFolder读取数据集时忽略特定文件 如果事先知道需要忽略哪些文件,当然直接从数据集里删除就行了.但如果需要在程序运行时动态确认,或者筛选规则比较复杂,人工不好做,就需要让ImageFolder在读取时使用自定义的筛选规则. ImageFolder有一个可选参数为is_valid_file,参数类型为可调用的函数,该函数传入一个str参数,返回一个bool值.当返回值为

  • PyTorch读取Cifar数据集并显示图片的实例讲解

    首先了解一下需要的几个类所在的package from torchvision import transforms, datasets as ds from torch.utils.data import DataLoader import matplotlib.pyplot as plt import numpy as np #transform = transforms.Compose是把一系列图片操作组合起来,比如减去像素均值等. #DataLoader读入的数据类型是PIL.Image

  • pytorch 自定义数据集加载方法

    pytorch 官网给出的例子中都是使用了已经定义好的特殊数据集接口来加载数据,而且其使用的数据都是官方给出的数据.如果我们有自己收集的数据集,如何用来训练网络呢?此时需要我们自己定义好数据处理接口.幸运的是pytroch给出了一个数据集接口类(torch.utils.data.Dataset),可以方便我们继承并实现自己的数据集接口. torch.utils.data torch的这个文件包含了一些关于数据集处理的类. class torch.utils.data.Dataset: 一个抽象类

  • pytorch读取图像数据转成opencv格式实例

    pytorch读取图像数据转成opencv格式方法:先转成numpy通用的格式,再将其转换成opencv格式. pytorch读取的数据使用loaddata这类函数实现.pytorch网络输入图像的格式为(C, H, W),就是(通道数,高,宽)而numpy中图像的格式为(H,W,C). 那就将其通道调换一下.用到函数transpose. 转换方法如下 例如A 的格式为(c,h,w) 那么经过 A = A.transpose(1,2,0) 后就变成了(h,w,c)了 然后用语句 B= cv2.c

  • 使用pytorch加载并读取COCO数据集的详细操作

    目录 环境配置 基础知识:元祖.字典.数组 利用PyTorch读取COCO数据集 利用PyTorch读取自己制作的数据集 如何使用pytorch加载并读取COCO数据集 环境配置基础知识:元祖.字典.数组利用PyTorch读取COCO数据集利用PyTorch读取自己制作的数据集 环境配置 看pytorch入门教程 基础知识:元祖.字典.数组 # 元祖 a = (1, 2) # 字典 b = {'username': 'peipeiwang', 'code': '111'} # 数组 c = [1

  • 关于Pytorch的MNIST数据集的预处理详解

    关于Pytorch的MNIST数据集的预处理详解 MNIST的准确率达到99.7% 用于MNIST的卷积神经网络(CNN)的实现,具有各种技术,例如数据增强,丢失,伪随机化等. 操作系统:ubuntu18.04 显卡:GTX1080ti python版本:2.7(3.7) 网络架构 具有4层的CNN具有以下架构. 输入层:784个节点(MNIST图像大小) 第一卷积层:5x5x32 第一个最大池层 第二卷积层:5x5x64 第二个最大池层 第三个完全连接层:1024个节点 输出层:10个节点(M

随机推荐