How to access image names from dataloader?

username: Asya
cooked: <p>I am loading images as following:</p>
<pre><code class="lang-auto">train_data = datasets.ImageFolder(data_dir, transform=transform['train'])
lengths = [len(train_data) * 0.6, len(train_data) * 0.3, len(train_data) * 0.1]
train_data, val_data, test_data = random_split(train_data, lengths)

train_loader = torch.utils.data.DataLoader(train_data, shuffle=True, batch_size=batch_size, num_workers=4)
val_loader = torch.utils.data.DataLoader(val_data, batch_size=batch_size)
test_loader = torch.utils.data.DataLoader(test_data, shuffle=False, batch_size=batch_size)
</code></pre>
<p>how I can extract image names (eg ‘img1.jpg’) from such dataloader structure?</p>

username: ptrblck
cooked: <p>You could write a custom <code>Dataset</code> and return the image names in the <code>__getitem__</code>. <a href="https://pytorch.org/tutorials/beginner/data_loading_tutorial.html">This tutorial</a> might be a good starter.</p>

username: Asya
cooked: <p>I wrote a <em>CustomImageDataset</em> class, it now allows me to access image file names, but I now got another problem - it started to be way slower than when I used <code>datasets.ImageFolder</code> with <code>torch.utils.data.DataLoader</code></p>
<p>Could you please advise what should I change to make it faster? (and one more problem I have is that I can’t use big batch size as it doesn’t fit in GPU memory despite that I do <code>del my_vars</code> and <code>torch.cuda.empty_cache()</code></p>
<pre><code class="lang-auto">class CustomImageDataset(Dataset):
    def __init__(self, annotations_file): 
        self.img_labels = annotations_file

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

    def __getitem__(self, idx, idx2):
        img_path = self.img_labels[self.img_labels.columns[0]][idx:idx2]
        all_imgs = np.empty((0, 3, img_width, img_height), float)
        for imid in img_path:
            image = Image.open(imid)
            image = transforms.Scale((img_width, img_height), interpolation=2)(image)
            image = transforms.ToTensor()(image)
            image = image.unsqueeze(0)
            all_imgs = np.append(all_imgs, image, axis=0)

        label = torch.tensor(self.img_labels['class'][idx:idx2].values)
        all_imgs = torch.tensor(all_imgs)
        return all_imgs, label, img_path
</code></pre>
<pre><code class="lang-auto">df = pd.DataFrame(pics, columns = ['img_paths'])
df['class'] = df['img_paths'].apply(lambda row: row.split('/')[3]).astype('category').cat.codes.astype(int)
classes = np.unique(df['class'])
df = shuffle(df)
train_data = df[0:(len(df)/ /10)*7]
val_data = df[len(train_data):(len(df)/ /10)*9]
test_data =  df[len(train_data)+len(val_data):]
train_data = CustomImageDataset(train_data)
val_data = CustomImageDataset(val_data)
test_data = CustomImageDataset(test_data)
</code></pre>

username: ptrblck
cooked: <p>I don’t know how your custom <code>Dataset</code> works exactly as it seems a custom sampler would be needed, since <code>__getitem__</code> expects two index values? If that’s the case, how many images are you loading? <code>ImageFolder</code> would load a single sample in the <code>__getitem__</code> and I don’t know how you let it load two images so what’s the baseline you are comparing against?<br>
To get some speedup you could use <code>tensor = torch.from_numpy(arr)</code> to share the underlying data instead of creating a copy via <code>tensor = torch.tensor(arr)</code>.</p>
<aside class="quote no-group" data-username="Asya" data-post="3" data-topic="132992">
<div class="title">
<div class="quote-controls"></div>
<img loading="lazy" alt="" width="24" height="24" src="https://discuss.pytorch.org/letter_avatar_proxy/v4/letter/a/22d042/48.png" class="avatar"> Asya:</div>
<blockquote>
<p>and one more problem I have is that I can’t use big batch size as it doesn’t fit in GPU memory despite that I do <code>del my_vars</code> and <code>torch.cuda.empty_cache()</code></p>
</blockquote>
</aside>
<p>I don’t know what <code>my_vars</code> is, but you might need to reduce the batch size to be able to train your model. Deleting unused tensors would reduce the memory requirement, but calling <code>empty_cache()</code> would slow down your code without avoiding the OOM issue.</p>

username: Asya
cooked: <p>I’m using two index values in <code>__getitem__ </code> to create a batch:<br>
<code>data, labels, img_path = train_data.__getitem__(i, i + batch_size)</code><br>
where I will be able to access image file names, so indices are just for batch slicing</p>
<p>the total number of images is around 80k (currently wikiart dataset, but later i will have to use 300k+ set of images) so I’m not able to keep the whole set of images together in memory, and I’m trying to find the most efficient way to load them by batches</p>
<p><code>my_vars</code> it’s just all my variables that I am deleting after use (but it’s still not enough)</p>
<p>and seems like Im not able to use batch &gt; 80 ( but when I load images with <code>datasets.ImageFolder</code> without image file names, I can easily use batch size 300 for example )</p>

username: Asya
cooked: <p>i measured the time and it turnes out that slowness came from <code>loss.backward()</code> - it takes up to 20 seconds per batch now</p>
<p>but when I was loading images via<code> datasets.ImageFolder</code> it was way faster</p>

username: ptrblck
cooked: <p>The <code>loss.backward</code> operation isn’t sped up or slowed down by the usage of another <code>Dataset</code>, but depends on the model architecture as well as input shapes.<br>
If you’ve increased the input shapes in the new workflow, a slow down might be expected. Otherwise, I would guess you are timing your code wrong.</p>
<p>Your current <code>__getitem__</code> approach would not leverage the <code>DataLoader</code>, which could pre-load the batches using multiprocessing. If you want to load an entire batch in each worker, use the <code>BatchSampler</code> instead.</p>