{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "Deep Learning Models -- A collection of various deep learning architectures, models, and tips for TensorFlow and PyTorch in Jupyter Notebooks.\n", "- Author: Sebastian Raschka\n", "- GitHub Repository: https://github.com/rasbt/deeplearning-models" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Sebastian Raschka \n", "\n", "CPython 3.7.1\n", "IPython 7.2.0\n", "\n", "torch 1.0.0\n" ] } ], "source": [ "%load_ext watermark\n", "%watermark -a 'Sebastian Raschka' -v -p torch" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Model Zoo -- Using PyTorch Dataset Loading Utilities for Custom Datasets (CSV files converted to HDF5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This notebook provides an example for how to load a dataset from an HDF5 file created from a CSV file, using PyTorch's data loading utilities. For a more in-depth discussion, please see the official\n", "\n", "- [Data Loading and Processing Tutorial](http://pytorch.org/tutorials/beginner/data_loading_tutorial.html)\n", "- [torch.utils.data](http://pytorch.org/docs/master/data.html) API documentation\n", "\n", "An Hierarchical Data Format (HDF) is a convenient way that allows quick access to data instances during minibatch learning if a dataset is too large to fit into memory. The approach outlined in this notebook uses uses the common [HDF5](https://support.hdfgroup.org/HDF5/) format and should be accessible to any programming language or tool with an HDF5 API.\n", "\n", "**In this example, we are going to use the Iris dataset for illustrative purposes. Let's pretend it's our large training dataset that doesn't fit into memory**.\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Imports" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "import pandas as pd\n", "import numpy as np\n", "import h5py\n", "import torch\n", "from torch.utils.data import Dataset\n", "from torch.utils.data import DataLoader" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Converting a CSV file to HDF5" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In this first step, we are going to process a CSV file (here, Iris) into an HDF5 database:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "# suppose this is a large CSV that does not \n", "# fit into memory:\n", "csv_path = 'https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data'\n", "\n", "# Get number of lines in the CSV file if it's on your hard drive:\n", "#num_lines = subprocess.check_output(['wc', '-l', in_csv])\n", "#num_lines = int(nlines.split()[0]) \n", "num_lines = 150\n", "num_features = 4\n", "\n", "class_dict = {'Iris-setosa': 0,\n", " 'Iris-versicolor': 1,\n", " 'Iris-virginica': 2}\n", "\n", "# use 10,000 or 100,000 or so for large files\n", "chunksize = 10\n", "\n", "# this is your HDF5 database:\n", "with h5py.File('iris.h5', 'w') as h5f:\n", " \n", " # use num_features-1 if the csv file has a column header\n", " dset1 = h5f.create_dataset('features',\n", " shape=(num_lines, num_features),\n", " compression=None,\n", " dtype='float32')\n", " dset2 = h5f.create_dataset('labels',\n", " shape=(num_lines,),\n", " compression=None,\n", " dtype='int32')\n", "\n", " # change range argument from 0 -> 1 if your csv file contains a column header\n", " for i in range(0, num_lines, chunksize): \n", "\n", " df = pd.read_csv(csv_path, \n", " header=None, # no header, define column header manually later\n", " nrows=chunksize, # number of rows to read at each iteration\n", " skiprows=i) # skip rows that were already read\n", " \n", " df[4] = df[4].map(class_dict)\n", "\n", " features = df.values[:, :4]\n", " labels = df.values[:, -1]\n", " \n", " # use i-1 and i-1+10 if csv file has a column header\n", " dset1[i:i+10, :] = features\n", " dset2[i:i+10] = labels[0]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "After creating the database, let's double-check that everything works correctly:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(150, 4)\n", "(150,)\n" ] } ], "source": [ "with h5py.File('iris.h5', 'r') as h5f:\n", " print(h5f['features'].shape)\n", " print(h5f['labels'].shape)" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Features of entry no. 99: [5.7 2.8 4.1 1.3]\n", "Class label of entry no. 99: 1\n" ] } ], "source": [ "with h5py.File('iris.h5', 'r') as h5f:\n", " print('Features of entry no. 99:', h5f['features'][99])\n", " print('Class label of entry no. 99:', h5f['labels'][99])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Implementing a Custom Dataset Class" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, we implement a custom `Dataset` for reading the training examples. The `__getitem__` method will\n", "\n", "1. read a single training example from HDF5 based on an `index` (more on batching later)\n", "2. return a single training example and it's corresponding label\n", "\n", "Note that we will keep an open connection to the database for efficiency via `self.h5f = h5py.File(h5_path, 'r')` -- you may want to close it when you are done (more on this later)." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "class Hdf5Dataset(Dataset):\n", " \"\"\"Custom Dataset for loading entries from HDF5 databases\"\"\"\n", "\n", " def __init__(self, h5_path, transform=None):\n", " \n", " self.h5f = h5py.File(h5_path, 'r')\n", " self.num_entries = self.h5f['labels'].shape[0]\n", " self.transform = transform\n", "\n", " def __getitem__(self, index):\n", " \n", " features = self.h5f['features'][index]\n", " label = self.h5f['labels'][index]\n", " if self.transform is not None:\n", " features = self.transform(features)\n", " return features, label\n", "\n", " def __len__(self):\n", " return self.num_entries" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now that we have created our custom Dataset class, we can initialize a Dataset instance for the training examples using the 'iris.h5' database file. Then, we initialize a `DataLoader` that allows us to read from the dataset." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "train_dataset = Hdf5Dataset(h5_path='iris.h5',\n", " transform=None)\n", "\n", "train_loader = DataLoader(dataset=train_dataset,\n", " batch_size=50,\n", " shuffle=True,\n", " num_workers=4) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "That's it! Now we can iterate over an epoch using the train_loader as an iterator and use the features and labels from the training dataset for model training as shown in the next section" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Iterating Through the Custom Dataset" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Epoch: 1 | Batch index: 0 | Batch size: 50\n", "Epoch: 1 | Batch index: 1 | Batch size: 50\n", "Epoch: 1 | Batch index: 2 | Batch size: 50\n", "Epoch: 2 | Batch index: 0 | Batch size: 50\n", "Epoch: 2 | Batch index: 1 | Batch size: 50\n", "Epoch: 2 | Batch index: 2 | Batch size: 50\n", "Epoch: 3 | Batch index: 0 | Batch size: 50\n", "Epoch: 3 | Batch index: 1 | Batch size: 50\n", "Epoch: 3 | Batch index: 2 | Batch size: 50\n", "Epoch: 4 | Batch index: 0 | Batch size: 50\n", "Epoch: 4 | Batch index: 1 | Batch size: 50\n", "Epoch: 4 | Batch index: 2 | Batch size: 50\n", "Epoch: 5 | Batch index: 0 | Batch size: 50\n", "Epoch: 5 | Batch index: 1 | Batch size: 50\n", "Epoch: 5 | Batch index: 2 | Batch size: 50\n" ] } ], "source": [ "device = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\n", "torch.manual_seed(0)\n", "\n", "num_epochs = 5\n", "for epoch in range(num_epochs):\n", "\n", " for batch_idx, (x, y) in enumerate(train_loader):\n", " \n", " print('Epoch:', epoch+1, end='')\n", " print(' | Batch index:', batch_idx, end='')\n", " print(' | Batch size:', y.size()[0])\n", " \n", " x = x.to(device)\n", " y = y.to(device)\n", "\n", " # do model training on x and y here" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Remember that we kept an open connection to the HDF5 database in the `Hdf5Dataset` (via `self.h5f = h5py.File(h5_path, 'r')`). Once we are done, we may want to close this connection:**" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "train_dataset.h5f.close()" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "torch 1.0.0\n", "pandas 0.23.4\n", "numpy 1.15.4\n", "h5py 2.8.0\n", "\n" ] } ], "source": [ "%watermark -iv" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.1" }, "toc": { "nav_menu": {}, "number_sections": true, "sideBar": true, "skip_h1_title": false, "title_cell": "Table of Contents", "title_sidebar": "Contents", "toc_cell": false, "toc_position": {}, "toc_section_display": true, "toc_window_display": false } }, "nbformat": 4, "nbformat_minor": 2 }