PyTorch學(xué)習(xí)之路(level2)——自定義數(shù)據(jù)讀取

采用torchvision.datasets.ImageFolder這個(gè)接口來(lái)讀取圖像數(shù)據(jù)负饲,該接口默認(rèn)你的訓(xùn)練數(shù)據(jù)是按照一個(gè)類(lèi)別存放在一個(gè)文件夾下配乱。但是有些情況下你的圖像數(shù)據(jù)不是這樣維護(hù)的栈拖,比如一個(gè)文件夾下面各個(gè)類(lèi)別的圖像數(shù)據(jù)都有司草,同時(shí)用一個(gè)對(duì)應(yīng)的標(biāo)簽文件败京,比如txt文件來(lái)維護(hù)圖像和標(biāo)簽的對(duì)應(yīng)關(guān)系兜喻,在這種情況下就不能用torchvision.datasets.ImageFolder來(lái)讀取數(shù)據(jù)了,需要自定義一個(gè)數(shù)據(jù)讀取接口赡麦。

繼承的類(lèi)是torch.utils.data.Dataset朴皆,主要包含三個(gè)方法:初始化init,獲取圖像getitem泛粹,數(shù)據(jù)集數(shù)量 len遂铡。init方法中先通過(guò)find_classes函數(shù)得到分類(lèi)的類(lèi)別名(classes)和類(lèi)別名與數(shù)字類(lèi)別的映射關(guān)系字典(class_to_idx)。然后通過(guò)make_dataset函數(shù)得到imags晶姊,這個(gè)imags是一個(gè)列表扒接,其中每個(gè)值是一個(gè)tuple,每個(gè)tuple包含兩個(gè)元素:圖像路徑和標(biāo)簽。剩下的就是一些賦值操作了钾怔。在getitem方法中最重要的就是 img = self.loader(path)這行碱呼,表示數(shù)據(jù)讀取,可以從init方法中看出self.loader采用的是default_loader宗侦,這個(gè)default_loader的核心就是用python的PIL庫(kù)的Image模塊來(lái)讀取圖像數(shù)據(jù)愚臀。

class ImageFolder(data.Dataset):
    """A generic data loader where the images are arranged in this way: ::

        root/dog/xxx.png
        root/dog/xxy.png
        root/dog/xxz.png

        root/cat/123.png
        root/cat/nsdf3.png
        root/cat/asd932_.png

    Args:
        root (string): Root directory path.
        transform (callable, optional): A function/transform that  takes in an PIL image
            and returns a transformed version. E.g, ``transforms.RandomCrop``
        target_transform (callable, optional): A function/transform that takes in the
            target and transforms it.
        loader (callable, optional): A function to load an image given its path.

     Attributes:
        classes (list): List of the class names.
        class_to_idx (dict): Dict with items (class_name, class_index).
        imgs (list): List of (image path, class_index) tuples
    """


    def __init__(self, root, transform=None, target_transform=None,
                 loader=default_loader):
        classes, class_to_idx = find_classes(root)
        imgs = make_dataset(root, class_to_idx)
        if len(imgs) == 0:
            raise(RuntimeError("Found 0 images in subfolders of: " + root + "\n"
                               "Supported image extensions are: " + ",".join(IMG_EXTENSIONS)))

        self.root = root
        self.imgs = imgs
        self.classes = classes
        self.class_to_idx = class_to_idx
        self.transform = transform
        self.target_transform = target_transform
        self.loader = loader

    def __getitem__(self, index):
        """
        Args:
            index (int): Index

        Returns:
            tuple: (image, target) where target is class_index of the target class.
        """
        path, target = self.imgs[index]
        img = self.loader(path)
        if self.transform is not None:
            img = self.transform(img)
        if self.target_transform is not None:
            target = self.target_transform(target)

        return img, target

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

稍微看下default_loader函數(shù),該函數(shù)主要分兩種情況調(diào)用兩個(gè)函數(shù)矾利,一般采用pil_loader函數(shù)姑裂。

def pil_loader(path):
    with open(path, 'rb') as f:
        with Image.open(f) as img:
            return img.convert('RGB')

def accimage_loader(path):
    import accimage
    try:
        return accimage.Image(path)
    except IOError:
        # Potentially a decoding problem, fall back to PIL.Image
        return pil_loader(path)

def default_loader(path):
    from torchvision import get_image_backend
    if get_image_backend() == 'accimage':
        return accimage_loader(path)
    else:
        return pil_loader(path)

看懂了ImageFolder這個(gè)類(lèi),就可以自定義一個(gè)你自己的數(shù)據(jù)讀取接口了梦皮。

首先在PyTorch中和數(shù)據(jù)讀取相關(guān)的類(lèi)基本都要繼承一個(gè)基類(lèi):torch.utils.data.Dataset炭分。然后再改寫(xiě)其中的initlen剑肯、getitem等方法即可捧毛。

下面假設(shè)img_path是你的圖像文件夾,該文件夾下面放了所有圖像數(shù)據(jù)(包括訓(xùn)練和測(cè)試)让网,然后txt_path下面放了train.txt和val.txt兩個(gè)文件呀忧,txt文件中每行都是圖像路徑,tab鍵溃睹,標(biāo)簽而账。所以下面代碼的init方法中self.img_name和self.img_label的讀取方式就跟你數(shù)據(jù)的存放方式有關(guān),你可以根據(jù)你實(shí)際數(shù)據(jù)的維護(hù)方式做調(diào)整因篇。getitem方法沒(méi)有做太大改動(dòng)泞辐,依然采用default_loader方法來(lái)讀取圖像。最后在Transform中將每張圖像都封裝成Tensor竞滓。

class customData(Dataset):
    def __init__(self, img_path, txt_path, dataset = '', data_transforms=None, loader = default_loader):
        with open(txt_path) as input_file:
            lines = input_file.readlines()
            self.img_name = [os.path.join(img_path, line.strip().split('\t')[0]) for line in lines]
            self.img_label = [int(line.strip().split('\t')[-1]) for line in lines]
        self.data_transforms = data_transforms
        self.dataset = dataset
        self.loader = loader

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

    def __getitem__(self, item):
        img_name = self.img_name[item]
        label = self.img_label[item]
        img = self.loader(img_name)

        if self.data_transforms is not None:
            try:
                img = self.data_transforms[self.dataset](img)
            except:
                print("Cannot transform image: {}".format(img_name))
        return img, label

定義好了數(shù)據(jù)讀取接口后咐吼,怎么用呢?

在代碼中可以這樣調(diào)用商佑。

image_datasets = {x: customData(img_path='/ImagePath',
                                    txt_path=('/TxtFile/' + x + '.txt'),
                                    data_transforms=data_transforms,
                                    dataset=x) for x in ['train', 'val']}

這樣返回的image_datasets就和用torchvision.datasets.ImageFolder類(lèi)返回的數(shù)據(jù)類(lèi)型一樣.

有了image_datasets锯茄,然后依然用torch.utils.data.DataLoader類(lèi)來(lái)做進(jìn)一步封裝,將這個(gè)batch的圖像數(shù)據(jù)和標(biāo)簽都分別封裝成Tensor茶没。

dataloders = {x: torch.utils.data.DataLoader(image_datasets[x],
                                                 batch_size=batch_size,
                                                 shuffle=True) for x in ['train', 'val']}

另外肌幽,每次迭代生成的模型要怎么保存呢?非常簡(jiǎn)單抓半,那就是用torch.save喂急。輸入就是你的模型和要保存的路徑及模型名稱,如果這個(gè)output文件夾沒(méi)有笛求,可以手動(dòng)新建一個(gè)或者在代碼里面新建煮岁。

torch.save(model, 'output/resnet_epoch{}.pkl'.format(epoch))

最后讥蔽,關(guān)于多GPU的使用,PyTorch支持多GPU訓(xùn)練模型画机,假設(shè)你的網(wǎng)絡(luò)是model,那么只需要下面一行代碼(調(diào)用 torch.nn.DataParallel接口)就可以讓后續(xù)的模型訓(xùn)練在0和1兩塊GPU上訓(xùn)練新症,加快訓(xùn)練速度步氏。

model = torch.nn.DataParallel(model, device_ids=[0,1])

參考

PyTorch使用及源碼解讀
Finetuning Torchvision Models
Welcome to PyTorch Tutorials
PyTorch學(xué)習(xí)之路(level2)——自定義數(shù)據(jù)讀取
https://github.com/miraclewkf/ImageClassification-PyTorch/blob/master/level2/train_customData.py

pytorch/tutorials

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市徒爹,隨后出現(xiàn)的幾起案子荚醒,更是在濱河造成了極大的恐慌,老刑警劉巖隆嗅,帶你破解...
    沈念sama閱讀 222,183評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件界阁,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡胖喳,警方通過(guò)查閱死者的電腦和手機(jī)泡躯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)丽焊,“玉大人较剃,你說(shuō)我怎么就攤上這事〖冀。” “怎么了写穴?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,766評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)雌贱。 經(jīng)常有香客問(wèn)我啊送,道長(zhǎng),這世上最難降的妖魔是什么欣孤? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,854評(píng)論 1 299
  • 正文 為了忘掉前任馋没,我火速辦了婚禮,結(jié)果婚禮上导街,老公的妹妹穿的比我還像新娘披泪。我一直安慰自己,他們只是感情好搬瑰,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布款票。 她就那樣靜靜地躺著,像睡著了一般泽论。 火紅的嫁衣襯著肌膚如雪艾少。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,457評(píng)論 1 311
  • 那天翼悴,我揣著相機(jī)與錄音缚够,去河邊找鬼幔妨。 笑死,一個(gè)胖子當(dāng)著我的面吹牛谍椅,可吹牛的內(nèi)容都是我干的误堡。 我是一名探鬼主播,決...
    沈念sama閱讀 40,999評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼雏吭,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼锁施!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起杖们,我...
    開(kāi)封第一講書(shū)人閱讀 39,914評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤悉抵,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后摘完,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體姥饰,經(jīng)...
    沈念sama閱讀 46,465評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評(píng)論 3 342
  • 正文 我和宋清朗相戀三年孝治,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了列粪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,675評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡荆秦,死狀恐怖篱竭,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情步绸,我是刑警寧澤掺逼,帶...
    沈念sama閱讀 36,354評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站瓤介,受9級(jí)特大地震影響吕喘,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜刑桑,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評(píng)論 3 335
  • 文/蒙蒙 一氯质、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧祠斧,春花似錦闻察、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,514評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至吴超,卻和暖如春钉嘹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背鲸阻。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,616評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工跋涣, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留缨睡,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,091評(píng)論 3 378
  • 正文 我出身青樓陈辱,卻偏偏與公主長(zhǎng)得像奖年,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子沛贪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評(píng)論 2 360

推薦閱讀更多精彩內(nèi)容