Custom Dataset labeling from CSV

Kevin_Diaz_Guarneros: <p>Hi, I’m trying to start my first pytorch project from a Kaggle Dataset, the goal is to simply classify some images.</p>
<p>So far I’ve managed to use ImageFolder to use my own Dataset but it lacks the labels of all images.</p>
<p>The issue lies here: The dataset by itself contains 2 folders <strong>Train</strong> and <strong>Test</strong>.</p>
<ul>
<li>Inside Train there are 26684 images.</li>
<li>Inside Test there are 3000 images.</li>
<li>And there’s a csv file with two columns <strong>File name</strong> and <strong>Class</strong>, this csv matches the image with it’s corresponding label. (There are only 3 different classes for all the images)</li>
</ul>
<p>I’ve tried to find some tutorials on internet on how to handle this, but so far all responses available are related to organize my images into a folder structure like this:</p>
<blockquote>
<p>root/dog/xxx.png<br>
root/dog/xxy.png<br>
root/dog/xxz.png</p>
<p>root/cat/123.png<br>
root/cat/nsdf3.png<br>
root/cat/asd932_.png</p>
</blockquote>
<p>But thinking in a more real life example, I thought that there must be a way of avoiding me classifying my own images into subfolders if there’s already a reference file for the computer to do this by it’s own.</p>
<p>So I’m sure I’m missing something, could any please help me on how would you do this?<br>
I’ve published my progress on github on the following link <a href="https://github.com/kevoniano/AI-stuff/blob/master/Pneumonia_Challenge/Pneumonia%20Challenge%20v2.ipynb" rel="noopener nofollow ugc">Pneumonia_Kevin</a></p>

Nikronic: <p>Hi,</p>
<p>You can define your own custom dataset class easily to handle this kind of situations.<br>
Here is the top-level structure of the class your can implement:</p>
<pre><code class="lang-python">
class PlacesDataset(Dataset):
  def __init__():
     # initialize variables such is path to csv file and images and transforms
  def __len__():
    # here you just need to return a single integer number as the length of your dataset, in your 
    #  case, number of images in your train folder or lines in csv file
  def __getitem__(): 
    # this is the most important part, you need to define a code to read images from folder and
    # labels from csv files and return only a pair of (image, class). Note that here, you just 
    # need to consider 1 sample no more. Let say, you have only 1 image in your whole 
    # dataset, the method will work on batches parallely when you pass it to DataLoader class.
</code></pre>
<p>Now, you can do whatever you wanted to do with ImageFolder with this class too.<br>
I know the explanation is too abstract, but this is the whole idea and if you need a real code which works, the link below is mine which uses a csv file to read images and generate labels on the go.<br>
<aside class="onebox githubblob">
  <header class="source">
      <a href="https://github.com/Nikronic/CoarseNet/blob/master/utils/preprocess.py" target="_blank" rel="nofollow noopener">github.com</a>
  </header>
  <article class="onebox-body">
    <h4><a href="https://github.com/Nikronic/CoarseNet/blob/master/utils/preprocess.py" target="_blank" rel="nofollow noopener">Nikronic/CoarseNet/blob/master/utils/preprocess.py</a></h4>
<pre><code class="lang-py">from __future__ import print_function, division
from PIL import Image
from torchvision.transforms import ToTensor, ToPILImage, Normalize, Compose
from torch.utils.data import DataLoader
import numpy as np
import random

import tarfile
import io
import os
import pandas as pd

from torch.utils.data import Dataset
import torch

from utils.Halftone.halftone import generate_halftone


class PlacesDataset(Dataset):
    def __init__(self, txt_path='filelist.txt', img_dir='data', transform=None, test=False):
</code></pre>

  This file has been truncated. <a href="https://github.com/Nikronic/CoarseNet/blob/master/utils/preprocess.py" target="_blank" rel="nofollow noopener">show original</a>

  </article>
  <div class="onebox-metadata">
    
    
  </div>
  <div style="clear: both"></div>
</aside>
</p>
<p>If you had any questions, feel free to ask.</p>
<p>Bests<br>
Nik</p>

Kevin_Diaz_Guarneros: <p>Hi <a class="mention" href="/u/nikronic">@Nikronic</a> Nik,<br>
I think I get it. I’ll try it and if I get stuck I’ll let you know. Thanks a lot for the guidance. <img src="https://discuss.pytorch.org/images/emoji/apple/grinning.png?v=9" title=":grinning:" class="emoji" alt=":grinning:"></p>

Kevin_Diaz_Guarneros: <p><a class="mention" href="/u/nikronic">@Nikronic</a> Hi Nik,<br>
I managed to create my DataSet following your indications along other resources on internet. It seems that it worked! Thanks! I’m able to see my images along with their classes.<br>
But I’m left with a question during this process.<br>
Before doing this I created the dataset using ImageFolder, and when visualizing the images they kind of were seen as blue, the tensor shape was [3 224 224]<br>
But after creating my own Dataset, an error was displaying indicating that he shape [1 224 224] was not ~“appropiate” for the [3 224 224] specified, something like that.<br>
I had to alter my transforms so it would match the actual shape.<br>
I’m not sure if I’m completely clear, but what I noticed is that at first with ImageFolder dataset the dimension of the images was 3 224 224 [RGB] but with my own dataset I have 1 224 224. Do you happen to know the explanation of this? I’m pretty sure I barely changed something, nor did anything beyond extraordinary.<br>
I’m attaching my github project for you to see, both behaviors are seen on the notebook.<br>
Thanks in advance. <a href="https://github.com/kevoniano/AI-stuff/blob/master/Pneumonia_Challenge/Pneumonia%20Challenge%20v3.ipynb" rel="nofollow noopener">Kevin Projct</a></p>

Nikronic: <p>About the first problem the “blue thing”. When you use <code>transforms.ToTensor()</code> PyTorch standardize your input from [0, 255] to [0, 1]. So if you want to visualize these images using matplotlib, you need to first convert back to [0, 255] and you can use <code>torch.ToPILImage()</code> to extract a batch of your images then convert them to numpy and plot using matplotlib. <code>torch.vutils</code> is a good approach to visualize your images during training or testing process.</p>
<pre><code class="lang-python"># obtain one batch of training images
dataiter = iter(train_loader)
images, labels = dataiter.next()
images = images.numpy() # convert images to numpy for display

# plot the images in the batch, along with the corresponding labels
fig = plt.figure(figsize=(25, 4))
for idx in np.arange(20):
    ax = fig.add_subplot(2, 20/2, idx+1, xticks=[], yticks=[])
    plt.imshow(np.transpose(images[idx], (1, 2, 0)))
    #ax.set_title(classes[labels[idx]])
</code></pre>
<hr>
<p>About the size problem. I saw your code and I cannot really figure out where you have such a problem, I just noticed you used <code>transforms.Normalize</code> for single channel image.</p>
<pre><code class="lang-auto">transforms.Normalize([0.5,], std=[0.5,])
</code></pre>
<p>First thing came to my mind is that all of your images are grayscale. Right? So when you use <code>PIL</code> library, it figures out the type of your image between possible type such as <code>RGB, CMYK, aRGB, I (binary), etc</code>. So I really when saw images at the start of notebook, I thought why opencv is saying it has 3 channels. Actually, I think <code>[1, 224, 224]</code> is the right shape for your images but if you insist to use <code>RGB</code>, as you have <code>vgg</code> as a feature extractor, you can just set <code>mode</code> argument in <code>Image.open(path, mode='string')</code> method to force <code>PIL</code> to use <code>RGB</code> encodings.</p>
<pre><code class="lang-auto">image = Image.open(img_name+'.jpg')
</code></pre>
<p>Final note that you already took care of it is that PyTorch uses <code>[batch_size, channel_size, height, width]</code> convention, in contrary, numpy or others (I don’t know about the others!), use <code>batch_size, height, width, channel_size]</code> approach. So be aware of it when converting to each other.</p>
<p>PS: It is really hard to comment on jupyter notebook in github, if it was .py file, I would comment these things on the aforementioned lines.</p>

Kevin_Diaz_Guarneros: <p>Thanks a lot <a class="mention" href="/u/nikronic">@Nikronic</a>, I understand. I appreciate the explanation. Have a nice day! <img src="https://discuss.pytorch.org/images/emoji/apple/slight_smile.png?v=9" title=":slight_smile:" class="emoji" alt=":slight_smile:"></p>

Nikronic: <p>You are welcome mate. Thanks you too. Good luck. <img src="https://discuss.pytorch.org/images/emoji/apple/smiley.png?v=9" title=":smiley:" class="emoji" alt=":smiley:"></p>

Arnaud_Mal: <p>Hello,</p>
<p>I hope you were able to submit this Kaggle.</p>
<p>I am in a similar situation as you - trying to submit my first Kaggle competition. I have a similar problem (<a href="https://discuss.pytorch.org/t/valueerror-expected-input-batch-size-4-to-match-target-batch-size-16/77516">link</a>) and I created a discussion for it.</p>
<p>I was able to create a CustomDataset that return an image and a label (both tensor). Then I pass them to the Dataloader, but then, we I get the Image and Target from the Dataloader in the BackPropagation, the size is not right.</p>
<p>This is the link to the <a href="https://github.com/ArnaudMallet/Plant_Patho.git" rel="nofollow noopener">github</a> so I can track my progress</p>
<p>Any ideas, suggestions?</p>