{"cells":[{"metadata":{},"cell_type":"markdown","source":"Code from this: https://github.com/ufoym/imbalanced-dataset-sampler\nIn many machine learning applications, we often come across datasets where some types of data may be seen more than other types. Take identification of rare diseases for example, there are probably more normal samples than disease ones. In these cases, we need to make sure that the trained model is not biased towards the class that has more data. As an example, consider a dataset where there are 5 disease images and 20 normal images. If the model predicts all images to be normal, its accuracy is 80%, and F1-score of such a model is 0.88. Therefore, the model has high tendency to be biased toward the ‘normal’ class.\n\nTo solve this problem, a widely adopted technique is called resampling. It consists of removing samples from the majority class (under-sampling) and / or adding more examples from the minority class (over-sampling). Despite the advantage of balancing classes, these techniques also have their weaknesses (there is no free lunch). The simplest implementation of over-sampling is to duplicate random records from the minority class, which can cause overfitting. In under-sampling, the simplest technique involves removing random records from the majority class, which can cause loss of information.\n\n![under-sampling](https://user-images.githubusercontent.com/2270240/40656410-e0baa230-6376-11e8-8904-c092fb38fcdc.png)","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"In this notebook, we implement an easy-to-use PyTorch sampler ImbalancedDatasetSampler that is able to\n\nrebalance the class distributions when sampling from the imbalanced dataset\nestimate the sampling weights automatically\navoid creating a new balanced dataset\nmitigate overfitting when it is used in conjunction with data augmentation techniques\n![](https://user-images.githubusercontent.com/2270240/40677251-b08f504a-63af-11e8-9653-f28e973a5664.png)","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"## Define ImbalancedDatasetSampler","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"import torch\nimport torch.utils.data\nimport torchvision\n\n\nclass ImbalancedDatasetSampler(torch.utils.data.sampler.Sampler):\n    \"\"\"Samples elements randomly from a given list of indices for imbalanced dataset\n    Arguments:\n        indices (list, optional): a list of indices\n        num_samples (int, optional): number of samples to draw\n        callback_get_label func: a callback-like function which takes two arguments - dataset and index\n    \"\"\"\n\n    def __init__(self, dataset, indices=None, num_samples=None, callback_get_label=None):\n                \n        # if indices is not provided, \n        # all elements in the dataset will be considered\n        self.indices = list(range(len(dataset))) \\\n            if indices is None else indices\n\n        # define custom callback\n        self.callback_get_label = callback_get_label\n\n        # if num_samples is not provided, \n        # draw `len(indices)` samples in each iteration\n        self.num_samples = len(self.indices) \\\n            if num_samples is None else num_samples\n            \n        # distribution of classes in the dataset \n        label_to_count = {}\n        for idx in self.indices:\n            label = self._get_label(dataset, idx)\n            if label in label_to_count:\n                label_to_count[label] += 1\n            else:\n                label_to_count[label] = 1\n                \n        # weight for each sample\n        weights = [1.0 / label_to_count[self._get_label(dataset, idx)]\n                   for idx in self.indices]\n        self.weights = torch.DoubleTensor(weights)\n\n    def _get_label(self, dataset, idx):\n        return dataset.train_labels[idx].item()\n                \n    def __iter__(self):\n        return (self.indices[i] for i in torch.multinomial(\n            self.weights, self.num_samples, replacement=True))\n\n    def __len__(self):\n        return self.num_samples","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Dataloader","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"from torchsampler import ImbalancedDatasetSampler\n\ntrain_loader = torch.utils.data.DataLoader(\n    train_dataset, \n    sampler=ImbalancedDatasetSampler(train_dataset),\n    batch_size=args.batch_size, \n    **kwargs\n)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Then in each epoch, the loader will sample the entire dataset and weigh your samples inversely to your class appearing probability.\nHighly recomment you to use this in YOUR notebook to have high LB.\nGood luck! Upvote me if it is helpfully!","execution_count":null}],"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat":4,"nbformat_minor":4}