Issues with Dataloader for imagenet (should I use datasets.ImageFolder or datasets.ImageNet)?

seyeeet: <p>I want to use the pretrained models from pytorch and evaluate them on imagenet val data. That should be fairly straightforward, but I am getting stuck on the dataloader.<br>
I downloaded the imagenet and the folder structure that I have is like this:</p>
<pre><code class="lang-auto">/media/SSD2/ILSVRC/
                |----Annotation
                |----ImageSets
                |----Data
                      |----CLS-LOC
                               |----test
                               |----train
                               |----val
                                      |----ILSVRC2012_val_00000009.JPEG
                                      |----ILSVRC2012_val_00000010.JPEG
                                      |----...
</code></pre>
<p>First I tried <code>datasets.ImageNet</code>, based on <a href="https://pytorch.org/vision/stable/datasets.html#imagenet" rel="noopener nofollow ugc">pytorch</a>  where it says to use the following</p>
<pre><code class="lang-auto">imagenet_data = torchvision.datasets.ImageNet('path/to/imagenet_root/')
data_loader = torch.utils.data.DataLoader(imagenet_data,
                                          batch_size=4,
                                          shuffle=True,
                                          num_workers=args.nThreads)
</code></pre>
<p>I changed the path_to_imagenet_to <code>/media/SSD2/ILSVRC/</code> like this</p>
<p><code>torchvision.datasets.ImageNet('/media/SSD2/ILSVRC/',split='val',download=False)</code><br>
but I get this error:</p>
<pre><code class="lang-auto">RuntimeError: The archive ILSVRC2012_devkit_t12.tar.gz is not present in the root directory or is corrupted. You need to download it externally and place it in /media/SSD2/ILSVRC/.
</code></pre>
<p>Then I tried using <code>datasets.ImageFolder</code> in the following way:</p>
<pre><code class="lang-auto">imagenet_data = torchvision.datasets.ImageFolder('/media/SSD2/ILSVRC/Data/CLS-LOC/val' , transform=transform)
data_loader = torch.utils.data.DataLoader(imagenet_data,
                                          batch_size=4,
                                          shuffle=True,
                                          num_workers=1)
</code></pre>
<p>it kinda works but the labels that generate are garbage.</p>
<p>I was wondering if anyone knows which one of the aforementioned methods can work and how should I do it?</p>

ptrblck: <p>I don’t know how and where you’ve downloaded the ImageNet dataset, but on my system each class uses a separate subfolder, which is needed in order to find all classes.<br>
E.g. the <code>val</code> folder contains:</p>
<pre><code class="lang-auto">n01440764  n01824575  n02093754  n02115641  n02457408  n02877765  n03272010  n03710193  n03958227  n04296562  n04597913
n01443537  n01828970  n02093859  n02115913  n02480495  n02879718  n03272562  n03710637  n03961711  n04310018  n04599235
n01484850 ...
</code></pre>
<p>where each folder contains the images for this particular class.</p>

MatthiasJReisinger: <p>I think <a class="mention" href="/u/seyeeet">@seyeeet</a> probably downloaded the Imagenet dataset from kaggle: <a href="https://www.kaggle.com/c/imagenet-object-localization-challenge/data" class="inline-onebox" rel="noopener nofollow ugc">ImageNet Object Localization Challenge | Kaggle</a>. It’s no longer possible to download it from the original place on the imagenet website, instead the website also only refers to the dataset that is hosted on kaggle. I also downloaded it there and I get the same error when loading it via the <code>torchvision.datasets.ImageNet</code> class.</p>
<p>Are we just using it wrong or has the layout of the ImageNet dataset changed? The error message mentions that the dataloder cannot find <code>ILSVRC2012_devkit_t12.tar.gz</code> - kaggle’s verison of the dataset actually does not contain such an archive.</p>

ptrblck: <p>I guess Kaggle might have changed the data layout and if so I would assume there would be PyTorch scripts to load this new dataset type.<br>
Based on the previous output it seems as if the images are just stored in the <code>test/train/val</code> folders without any subfolders. In that case <code>ImageFolder</code> wouldn’t be compatible, since the class indices won’t be created. If so, I would also assume that a target file is provided additionally.</p>

MatthiasJReisinger: <p>Thanks, you are right, there’s actually an example script in an official pytorch repo that shows how ImageNet data can be accessed by using <code>ImageFolder</code>, which works for the version of the dataset that’s available on kaggle: <a href="https://github.com/pytorch/examples/tree/master/imagenet" class="inline-onebox" rel="noopener nofollow ugc">examples/imagenet at master · pytorch/examples · GitHub</a></p>

seyeeet: <p>can you please verify what scrips are you referring to? <img src="https://discuss.pytorch.org/images/emoji/apple/slight_smile.png?v=9" title=":slight_smile:" class="emoji" alt=":slight_smile:"></p>

MatthiasJReisinger: <p><a class="mention" href="/u/seyeeet">@seyeeet</a> The script that I’m referring to is linked in my reply above: <a href="https://github.com/pytorch/examples/tree/master/imagenet" class="inline-onebox" rel="noopener nofollow ugc">examples/imagenet at master · pytorch/examples · GitHub</a></p>
<p>That said, I haven’t used the script directly, I simply tried to reuse the code part that sets up the dataloaders which starts here <a href="https://github.com/pytorch/examples/blob/master/imagenet/main.py#L201" class="inline-onebox" rel="noopener nofollow ugc">examples/main.py at master · pytorch/examples · GitHub</a> and that works for me.<br>
Just make sure that you follow the setup instructions that are listed here <a href="https://github.com/pytorch/examples/tree/master/imagenet#requirements" class="inline-onebox" rel="noopener nofollow ugc">examples/imagenet at master · pytorch/examples · GitHub</a>. In particular, you have to execute the shell script, that’s mentioned in the last step, inside the <code>val</code> folder (<code>ILSVRC/Data/CLS-LOC/val/</code>). This makes sure that the <code>val</code> folder is structured in a way that can be understood by the <code>ImageFolder</code> class.</p>

verazuo: <p>Meet the same problem and solve it with <a href="https://github.com/fh295/semanticCNN" rel="noopener nofollow ugc">https://github.com/fh295/semanticCNN</a><br>
Specifically, to make the val dataset have the same structure as train set, you should run the following commands. It will move all validation pictures to corresponding <code>class</code> subfolders.</p>
<pre><code class="lang-auto">cd val/
wget -qO- https://raw.githubusercontent.com/soumith/imagenetloader.torch/master/valprep.sh | bash
</code></pre>

poult: <p>You are genies!!!</p>

thinhnguyen0703: <p>I figured out that you can download the ILSVRC2012 dataset by logging and requesting for permission in ImageNet website. By that you will have the .tar file for further inner processing of torchvision ImageNet dataset.</p>

supprimemortgage: <p>Guys, I dont get it. I also got the data from Kaggle and loading it via torchvision.dataset.ImageNet.</p>
<p>Even if we structure val according to train with subdir as class label. The file “ILSVRC2012_devkit_t12.tar.gz” still does not exist. This was the reason for the OP (and mine) error</p>

supprimemortgage: <p>Ok I guess the folks were just using ImageFolder dataset class</p>
<p>Here is a small wrapper around ImageFolder to have some utility functions to mimic the original ImageNet class:</p>
<pre><code class="lang-auto">class ImageNet(ImageFolder):
    def __init__(self, root, transform = None, target_transform = None,  split = "train"):
        root = str(Path(root / split))
        super().__init__(root, transform, target_transform)

        self.wnid_to_idx = self.build_wnid_to_idx()
        self.idx_to_wnid = {index: wnid for wnid, index in self.wnid_to_idx.items()}

        self.synset_to_class = self.build_synset_to_classes()
        self.class_to_synset = {cl: syn for syn, cl in self.synset_to_class.items()}

        self.classes = [(cl.strip(), syn) for syn, cl in self.synset_to_class.items()]
        self.targets = self.build_targets()

    def build_targets(self):
        # Builds the targets for each sample, assigning each image a class index
        sorted_subdirs = sorted([d.name for d in os.scandir(self.root) if d.is_dir()])  # Ensures only directories are considered
        
        targets = [
            self.wnid_to_idx[wnid] 
            for wnid in sorted_subdirs 
            for f in os.listdir(os.path.join(self.root, wnid)) 
            if os.path.isfile(os.path.join(self.root, wnid, f))  
        ]
        return targets

    def build_wnid_to_idx(self):
        # Builds a WNID to Index mapping based on the assumption that class indices in imagenet are from alphabetical sorting of wn synsets
        sorted_subdirs = sorted([d.name for d in os.scandir(self.root) if d.is_dir()])  # Ensures only directories are considered
        return {wnid: index for index, wnid in enumerate(sorted_subdirs)}
    
    def build_synset_to_classes(self):
        mapping_path = Path(self.root).parent.parent.parent.parent / "LOC_synset_mapping.txt"
        synset_mapping = {}
        with open(mapping_path, "r") as f:
            for l in f.readlines():
                synset, word = l.split(" ", 1)
                synset_mapping[synset] = word
        return synset_mapping
    
    def idx_to_class(self, i):
        wnid = self.idx_to_wnid[i]
        return self.synset_to_class[wnid]
</code></pre>