【轉載】PyTorch系列 (二):pytorch數據讀取

原文:https://likewind.top/2019/02/01/Pytorch-dataprocess/javascript

Pytorch系列:html

參考:java

  1. PyTorch documentation
  2. PyTorch 碼源

本文首先介紹了有關預處理包的源碼,接着介紹了在數據處理中的具體應用; 其主要目錄以下:git

1 PyTorch數據預處理以及源碼分析 (torch.utils.data)

torch.utils.data腳本碼源github

1.1 Dataset

Dataset

1
class torch.utils.data.Dataset

表示Dataset的抽象類。全部其餘數據集都應該進行子類化。 全部子類應該override__len____getitem__,前者提供了數據集的大小,後者支持整數索引,範圍從0到len(self)。網絡

class Dataset(object):
    # 強制全部的子類override getitem和len兩個函數,不然就拋出錯誤;
    # 輸入數據索引,輸出爲索引指向的數據以及標籤;
    def __getitem__(self, index):
        raise NotImplementedError
    
    # 輸出數據的長度
    def __len__(self):
        raise NotImplementedError
        
    def __add__(self, other):
        return ConcatDataset([self, other])

TensorDataset

class torch.utils.data.TensorDataset(*tensors)

Dataset的子類。包裝tensors數據集;輸入輸出都是元組; 經過沿着第一個維度索引一個張量來回復每一個樣本。 我的感受比較適用於數字類型的數據集,好比線性迴歸等。dom

class TensorDataset(Dataset):
    def __init__(self, *tensor):
        assert all(tensors[0].size(0) == tensor.size(0) for tensor in tensors)
        self.tensors = tensors
        
    def __getitem__(self, index):
        return tuple(tensor[index] for tensor in tensors
        
    def __len__(self):
        return self.tensors[0].size(0)

ConcatDateset

class torch.utils.data.ConcatDateset(datasets)

鏈接多個數據集。 目的:組合不一樣的數據集,多是大規模數據集,由於連續操做是隨意鏈接的。 datasets的參數:要鏈接的數據集列表 datasets的樣式:iterableasync

class ConcatDataset(Dataset):
    @staticmethod
    def cumsum(sequence):
        # sequence是一個列表,e.g. [[1,2,3], [a,b], [4,h]]
        # return 一個數據大小列表,[3, 5, 7], 明顯看的出來包含數據多少,第一個表明第一個數據的大小,第二個表明第一個+第二數據的大小,最後表明全部的數據大學;
    ...
    def __getitem__(self, idx):
        # 主要是這個函數,經過bisect的類實現了任意索引數據的輸出;
        dataset_idx = bisect.bisect_right(self.cumulative_size, idx)
        if dataset_idx == 0:
            sample_idx == idx
        else:
            sample_idx = idx - self.cumulative_sizes[dataset_idx -1]
        return self.datasets[dataset_idx][sample_idx]
    ...

Subset

class torch.utils.data.Subset(dataset, indices)

選取特殊索引下的數據子集; dataset:數據集; indices:想要選取的數據的索引;ide

random_split

class torch.utils.data.random_split(dataset, lengths):

隨機不重複分割數據集; dataset:要被分割的數據集 lengths:長度列表,e.g. [7, 3], 保證7+3=len(dataset)函數

1.2 DataLoader

DataLoader

class torch.utils.data.DataLoader(dataset, batch_size=1, shuffle=False, sampler=None, batch_sampler=None, num_workers=0, collate_fn=<function default_collate>, pin_memory=False, drop_last=False, timeout=0, worker_init_fn=None)

數據加載器。 組合數據集和採樣器,並在數據集上提供單進程或多進程迭代器。 參數:

  • dataset (Dataset) - 從中加載數據的數據集。
  • batch_size (int, optional) - 批訓練的數據個數。
  • shuffle (bool, optional) - 是否打亂數據集(通常打亂較好)。
  • sampler (Sampler, optional) - 定義從數據集中提取樣本的策略。若是指定,則忽略shuffle參數。
  • batch_sampler (Sample, optional) - 和sampler相似,返回批中的索引。
  • num_workers (int, optional) - 用於數據加載的子進程數。
  • collate_fn (callable, optional) - 合併樣本列表以造成小批量。
  • pin_memory (bool, optional) - 若是爲True,數據加載器在返回去將張量複製到CUDA固定內存中。
  • drop_last (bool, optional) - 若是數據集大小不能被batch_size整除, 設置爲True能夠刪除最後一個不完整的批處理。
  • timeout (numeric, optional) - 正數,收集數據的超時值。
  • worker_init_fn (callabel, optional) - If not None, this will be called on each worker subprocess with the worker id (an int in [0, num_workers - 1]) as input, after seeding and before data loading. (default: None)

特別重要:DataLoader中是不斷調用DataLoaderIter

DataLoaderIter

class _DataLoaderIter(loader)

從DataLoader’s數據中迭代一次。其上面DataLoader功能都在這裏; 插個眼,有空在分析這個

1.3 sampler

Sampler

class torch.utils.data.sampler.Sampler(data_source)

全部採樣器的基礎類; 每一個採樣器子類必須提供一個__iter__方法,提供一種迭代數據集元素的索引的方法,以及返回迭代器長度__len__方法。

class Sampler(object):
    def __init__(self, data_source):
        pass
        
    def __iter__(self):
        raise NotImplementedError
        
    def __len__(self):
        raise NotImplementedError

SequentialSampler

class torch.utils.data.SequentialSampler(data_source)

樣本元素順序排列,始終以相同的順序。 參數:-data_source (Dataset) - 採樣的數據

RandomSampler

class torch.utils.data.RandomSampler(data_source, replacement=False, num_samples=None)

樣本隨機排列,若是沒有Replacement,將會從打亂的數據採樣,不然,。。 參數:

  • data_source (Dataset) - 採樣數據
  • num_samples (int) - 採樣數據大小,默認是所有。
  • replacement (bool) - 是否放回

SubsetRandomSampler

class torch.utils.data.SubsetRandomSampler(indices)

從給出的索引中隨機採樣,without replacement。 參數:

  • indices (sequence) - 索引序列。

BatchSampler

class torch.utils.data.BatchSampler(sampler, batch_size, drop_last)

將採樣封裝到批處理索引。 參數:

  • sampler (sampler) - 基本採樣
  • batch_size (int) - 批大小
  • drop_last (bool) - 是否刪掉最後的批次

weightedRandomSampler

class torch.utils.data.WeightedRandomSampler(weights, num_samples, replacement=True)

樣本元素來自[0,…,len(weights)-1], 給定機率(權重)。 參數:

  • weights (list) - 權重列表。不須要加起來爲1
  • num_samplers (int) - 要採樣數目
  • replacement (bool) -

1.4 Distributed

DistributedSampler

class torch.utils.data.distributed.DistributedSampler(dataset, num_replicas=None, rank=None)

????沒讀呢

1.5 其它連接

  1. PyTorch源碼解讀之torch.utils.data.DataLoader

2 torchvision

計算機視覺用到的庫,文檔以及碼源以下:

  1. torchvision documentation
  2. torchvision 其庫主要包含一下內容:
  • torchvision.datasets
    • MNIST
    • Fashion-MNIST
    • EMNIST
    • COCO
    • LSUN
    • ImageFolder
    • DatasetFolder
    • Imagenet-12
    • CIFAR
    • STL10
    • SVHN
    • Photo Tour
    • SBU
    • Flickr
    • VOC
  • torchvision.models
    • Alexnet
    • VGG
    • ResNet
    • SqueezeNet
    • DenseNet
    • Inception v3
  • torchvision.transforms
    • Transforms on PIL Image
    • Transfroms on torch.* Tensor
    • Conversion Transforms
    • Generic Transforms
    • Functional Transforms
  • torchvision.utils

3 應用

3.1 init

具備一下圖像數據以下表示:

  • train
    • normal
      • 1.png
      • 2.png
      • 8000.png
    • tumor
      • 1.png
      • 2.png
      • 8000.png
  • validation
    • normal
      • 1.png
    • tumor
      • 1.png

但願可以訓練模型,使得可以識別tumor, normal兩類,將tumor–>1, normal–>0。

3.2 數據讀取

在PyTorch中數據的讀取藉口須要通過,Dataset和DatasetLoader (DatasetloaderIter)。下面就此分別介紹。

Dataset

首先導入必要的包。

import os

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

np.random.seed(0)

其次定義MyDataset類,爲了代碼整潔精簡,將沒必要要的操做全刪,e.g. 圖像剪切等。

class MyDataset(Dataset):
    
    def __init__(self, root, size=229, ):
        """
        Initialize the data producer
        """
        self._root = root
        self._size = size
        self._num_image = len(os.listdir(root))
        self._img_name = os.listdir(root)
    
    def __len__(self):
        return self._num_image
        
    def __getitem__(self, index):
        img = Image.open(os.path.join(self._root, self._img_name[index]))
        
        # PIF image: H × W × C
        # torch image: C × H × W
        img = np.array(img, dtype-np.float32).transpose((2, 0, 1))
        
        return img

DataLoader

將MyDataset封裝到loader器中。

from torch.utils.data import DataLoader

# 實例化MyData
dataset_tumor_train = MyDataset(root=/img/train/tumor/)
dataset_normal_train = MyDataset(root=/img/train/normal/)
dataset_tumor_validation = MyDataset(root=/img/validation/tumor/)
dataset_normal_validation = MyDataset(root=/img/validation/normal/)

# 封裝到loader
dataloader_tumor_train = DataLoader(dataset_tumor_train, batch_size=10)
dataloader_normal_train = DataLoader(dataset_normal_train, batch_size=10)
dataloader_tumor_validation = DataLoader(dataset_tumor_validation, batch_size=10)
dataloader_normal_validation = DataLoader(dataset_normal_validation, batch_size=10)

3.3 train_epoch

簡單將數據流接口與訓練鏈接起來

def train_epoch(model, loss_fn, optimizer, dataloader_tumor, dataloader_normal):
    model.train()
    
    # 因爲tumor圖像和normal圖像同樣多,因此將tumor,normal鏈接起來,steps=len(tumor_loader)=len(normal_loader)
    steps = len(dataloader_tumor)
    batch_size = dataloader_tumor.batch_size
    dataiter_tumor = iter(dataloader_tumor)
    dataiter_normal = iter(dataloader_normal)
    
    for step in range(steps):
        data_tumor = next(dataiter_tumor)
        target_tumor = [1, 1,..,1] # 和data_tumor長度相同的tensor
        data_tumor = Variable(data_tumor.cuda(async=True))
        target_tumor = Variable(target_tumor.cuda(async=True))
         
        data_normal = next(dataiter_normal)
        target_normal = [0, 0,..,0] # 
        data_normal = Variable(data_normal.cuda(async=True))
        target_normal = Variable(target_normal.cuda(async=True))
        
        idx_rand = Variable(torch.randperm(batch_size*2).cuda(async=True))
        
        data = torch.cat([data_tumor, data_normal])[idx_rand]
        target = torch.cat([target_tumor, target_normal])[idx_rand]
        output = model(data)
        loss = loss_fn(output, target)
        
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        
        probs = output.sigmoid()



MARSGGBO原創




2019-3-8

相關文章
相關標籤/搜索