【源码解读】cycleGAN(三):数据读取

源码地址:https://github.com/aitorzip/PyTorch-CycleGAN

数据的读取是比较简单的,cycleGAN对数据没有pair的需求,不同域的两个数据集分别存放于A,B两个文件夹,写好dataset接口即可

fake_A_buffer = ReplayBuffer()
fake_B_buffer = ReplayBuffer()

# Dataset loader
transforms_ = [ transforms.Resize(int(opt.size*1.12), Image.BICUBIC), 
                transforms.RandomCrop(opt.size), 
                transforms.RandomHorizontalFlip(),
                transforms.ToTensor(),
                transforms.Normalize((0.5,0.5,0.5), (0.5,0.5,0.5)) ]
dataloader = DataLoader(ImageDataset(opt.dataroot, transforms_=transforms_, unaligned=True), 
                        batch_size=opt.batchSize, shuffle=True, num_workers=opt.n_cpu)

上面的代码中,首先定义好buffer(后面细说),然后定义好图像变换,调用定义好的ImageDataset(继承自dataset) 对象,即可从dataloader中读取数据。下面是ImageDataset的代码

class ImageDataset(Dataset):
    def __init__(self, root, transforms_=None, unaligned=False, mode='train'):
        self.transform = transforms.Compose(transforms_)
        self.unaligned = unaligned

        self.files_A = sorted(glob.glob(os.path.join(root, '%s/A' % mode) + '/*.*'))
        self.files_B = sorted(glob.glob(os.path.join(root, '%s/B' % mode) + '/*.*'))

    def __getitem__(self, index):
        item_A = self.transform(Image.open(self.files_A[index % len(self.files_A)]))

        if self.unaligned:
            item_B = self.transform(Image.open(self.files_B[random.randint(0, len(self.files_B) - 1)]))
        else:
            item_B = self.transform(Image.open(self.files_B[index % len(self.files_B)]))

        return {'A': item_A, 'B': item_B}

    def __len__(self):
        return max(len(self.files_A), len(self.files_B))

标准的实现了__init__, __getitem__, __len__三个接口,不过我还不太清楚这里对数据进行排序和对齐的目的,对齐可以按序读取,不对齐则随机读取最后,关于buffer,参考cycleGAN的论文,原话是这么说的“Second, to reduce model oscillation [15], we follow Shrivastava et al.’s strategy [46] and update the discriminators using a history of generated images rather than the ones produced by the latest generators. We keep an image buffer that stores the 50 previously created images

也就是说,是为了训练的稳定,采用历史生成的虚假样本来更新判别器,而不是当前生成的虚假样本,至于原理,参考的是另一篇论文。我们来看一下代码

class ReplayBuffer():
    def __init__(self, max_size=50):
        assert (max_size > 0), 'Empty buffer or trying to create a black hole. Be careful.'
        self.max_size = max_size
        self.data = []

    def push_and_pop(self, data):
        to_return = []
        for element in data.data:
            element = torch.unsqueeze(element, 0)
            if len(self.data) < self.max_size:
                self.data.append(element)
                to_return.append(element)
            else:
                if random.uniform(0,1) > 0.5:
                    i = random.randint(0, self.max_size-1)
                    to_return.append(self.data[i].clone())
                    self.data[i] = element
                else:
                    to_return.append(element)
        return Variable(torch.cat(to_return))

定义了一个buffer对象,有一个数据存储表data,大小预设为50,我认为它的运转流程是这样的:数据表未填满时,每次读取的都是当前生成的虚假图像,当数据表填满时,随机决定 1. 在数据表中随机抽取一批数据,返回,并且用当前数据补充进来 2. 采用当前数据

至于为什么这样有道理,要看参考论文了

 

 

posted @ 2019-11-20 18:57  wuzeyuan  阅读(2297)  评论(0编辑  收藏  举报