使用Pytorch变换和自定义数据集时出错

时间:2018-04-08 12:36:03

标签: python python-3.x pytorch

此问题主要涉及__getitem__pytorch Dataset的返回值,我在源代码中将其视为元组和字典。

我一直关注this tutorial在我的代码中创建一个数据集类,这是关于转移学习的this tutorial。它具有以下数据集定义。

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

如您所见,__getitem__返回包含两个条目的字典。 在转移学习教程中,进行以下调用以转换数据集:

    data_transforms = {
    'train': transforms.Compose([
        transforms.RandomResizedCrop(224),
        transforms.RandomHorizontalFlip(),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ]),
    'val': transforms.Compose([
        transforms.Resize(256),
        transforms.CenterCrop(224),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ]),
}

data_dir = 'hymenoptera_data'
image_datasets = {x: datasets.ImageFolder(os.path.join(data_dir, x),
                                          data_transforms[x])
                  for x in ['train', 'val']}
dataloaders = {x: torch.utils.data.DataLoader(image_datasets[x], batch_size=4,
                                             shuffle=True, num_workers=4)
              for x in ['train', 'val']}
dataset_sizes = {x: len(image_datasets[x]) for x in ['train', 'val']}
class_names = image_datasets['train'].classes

use_gpu = torch.cuda.is_available()

inputs, classes = next(iter(dataloaders['train']))

最后一行代码尝试对自定义数据集中的示例运行转换,从而导致代码出错。

  

'字典'对象没有属性' size'

但是如果正确实现了教程数据集,那么它是否应该通过转换正常运行?我自己的混合实现如下:

import torch
import torch.nn as nn
import torch.optim as optim
from torch.optim import lr_scheduler
from torch.autograd import Variable
import numpy as np
import torchvision
from torchvision import datasets, models, transforms
import matplotlib.pyplot as plt
import time
import os
import copy
from torch.utils.data import *
from skimage import io, transform
plt.ion()


class NumsDataset(Dataset):
    """Face Landmarks dataset."""

    def __init__(self, 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.docs = []
        for file in os.listdir(root_dir):
            #print(file)
            if file.endswith(".txt"):
                path = os.path.join(root_dir, file)
                with open(path, 'r') as f:
                    self.docs.append( (  file , list(f.read()) ) ) #tup containing file, image values pairs
        self.root_dir = root_dir
        self.transform = transform

    def __len__(self): #returns number of images
        i = 0
        for j in self.docs:
            i += len(j[1])
        return i

    def len2(self): #returns number of batches
        return len(self.docs)

    def __getitem__(self, idx):
        idx1 = idx // self.len2()
        idx2 = idx % self.len2()
        imglabel = self.docs[idx1][0] #label with filename for batch error calculation later
        imgdir = os.path.join(self.root_dir, self.docs[idx1][0].strip(".txt"))
        img = None
        l = idx2

        for file in os.listdir(imgdir):
            file = os.path.join(imgdir, file)
            if(l == 0):
                img = io.imread(file)
            l -= 1
        sample = (img , imglabel)
        sample ={'image': img, 'label': imglabel}
        if self.transform:
            sample = self.transform(sample)

        return sample




data_transforms = {
    'train': transforms.Compose([
        transforms.RandomResizedCrop(224),
        transforms.RandomHorizontalFlip(),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ]),
    'val': transforms.Compose([
        transforms.Resize(256),
        transforms.CenterCrop(224),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ]),
}
data_dir = "images"
image_datasets = {x: NumsDataset(os.path.join(data_dir, x),
                                          data_transforms[x])
                  for x in ['train', 'val']}
dataloaders = {x: torch.utils.data.DataLoader(image_datasets[x], batch_size=5) 
              for x in ['train', 'val']}

dataset_sizes = {x: len(image_datasets[x]) for x in ['train', 'val']}
class_names = ["one", "two", "four"]

use_gpu = torch.cuda.is_available()
# Get a batch of training data
inputs, classes = next(iter(dataloaders['train']))

目录结构:

images
     /train
        /file1
            *.jpg
        /file2...
            *.jpg
        file1.txt
        file2.txt...
     /val
        /file1
            *.jpg
        /file2...
            *.jpg
        file1.txt
        file2.txt...

我回复的样本格式不正确吗?

2 个答案:

答案 0 :(得分:0)

数据加载教程使用自定义数据集的特定方式是使用自定义转换。必须设计变换以适合数据集。因此,数据集必须输出与库转换函数兼容的样本,或者必须为特定样本案例定义转换。选择后者,除其他外,还产生了完整的功能代码。

答案 1 :(得分:0)

当您将字典而不是图像传递给变换时,将发生以下问题。示例中提到的自定义转换可以处理此问题,但是默认转换无法处理,而是只能将图像传递给该转换。这样可以解决一半的问题。

  

'dict'对象没有属性'size'

其余的问题在于示例中的图像处理代码,因此我不得不深入研究直到torchvision中的transforms.py为止。与示例中提到的skimage不同,它使用PIL图像,因此我用PIL替换了代码,并且工作得很好。

  

site-packages / torchvision / transforms / transforms.py

原始代码:

def __getitem__(self, idx):
        if torch.is_tensor(idx):
        img_name = os.path.join(self.root_dir,self.anb_frame.iloc[idx, 0])
        image = io.imread(img_name)
        labels = self.anb_frame.iloc[idx, 1:]
        labels = np.array([labels])
        sample = {'image': image, 'labels': labels}
        if self.transform:
            image = self.transform(image)
        return sample

已修改:

def __getitem__(self, idx):
        if torch.is_tensor(idx):
        img_name = os.path.join(self.root_dir,self.anb_frame.iloc[idx, 0])
        image = Image.open(img_name)
        if self.transform:
            image = self.transform(image)
        labels = self.anb_frame.iloc[idx, 1:]
        labels = np.array([labels])
        sample = {'image': image, 'labels': labels}
        return sample