summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authoraktersnurra <gustaf.rydholm@gmail.com>2020-08-05 01:03:37 +0200
committeraktersnurra <gustaf.rydholm@gmail.com>2020-08-05 01:03:37 +0200
commit125d5da5fb845d03bda91426e172bca7f537584a (patch)
tree6daf305555b76338ae482e81da58aa444a8255df /src
parent0d0540952f79437026fc5a146b81e4b45190ff6a (diff)
Emnist lines data loader implemented.
Diffstat (limited to 'src')
-rw-r--r--src/notebooks/02b-emnist-lines-dataset.ipynb171
-rw-r--r--src/text_recognizer/datasets/__init__.py7
-rw-r--r--src/text_recognizer/datasets/emnist_dataset.py31
-rw-r--r--src/text_recognizer/datasets/emnist_lines_dataset.py120
4 files changed, 261 insertions, 68 deletions
diff --git a/src/notebooks/02b-emnist-lines-dataset.ipynb b/src/notebooks/02b-emnist-lines-dataset.ipynb
index 3a3b88e..edd3956 100644
--- a/src/notebooks/02b-emnist-lines-dataset.ipynb
+++ b/src/notebooks/02b-emnist-lines-dataset.ipynb
@@ -2,9 +2,18 @@
"cells": [
{
"cell_type": "code",
- "execution_count": 1,
+ "execution_count": 3,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "The autoreload extension is already loaded. To reload it, use:\n",
+ " %reload_ext autoreload\n"
+ ]
+ }
+ ],
"source": [
"%load_ext autoreload\n",
"%autoreload 2\n",
@@ -22,16 +31,16 @@
},
{
"cell_type": "code",
- "execution_count": 53,
+ "execution_count": 11,
"metadata": {},
"outputs": [],
"source": [
- "from text_recognizer.datasets import EmnistDataset, EmnistLinesDataset, Transpose, construct_image_from_string, get_samples_by_character"
+ "from text_recognizer.datasets import EmnistDataset, EmnistLinesDataLoaders, EmnistLinesDataset, Transpose, construct_image_from_string, get_samples_by_character"
]
},
{
"cell_type": "code",
- "execution_count": 27,
+ "execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
@@ -41,7 +50,7 @@
},
{
"cell_type": "code",
- "execution_count": 54,
+ "execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
@@ -50,14 +59,14 @@
},
{
"cell_type": "code",
- "execution_count": 55,
+ "execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
- "2020-08-03 21:23:17.973 | DEBUG | text_recognizer.datasets.emnist_lines_dataset:_load_data:113 - EmnistLinesDataset loading data from HDF5...\n"
+ "2020-08-04 23:29:22.523 | DEBUG | text_recognizer.datasets.emnist_lines_dataset:_load_data:153 - EmnistLinesDataset loading data from HDF5...\n"
]
}
],
@@ -67,7 +76,7 @@
},
{
"cell_type": "code",
- "execution_count": 56,
+ "execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
@@ -77,36 +86,7 @@
},
{
"cell_type": "code",
- "execution_count": 57,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "(tensor([[[0., 0., 0., ..., 0., 0., 0.],\n",
- " [0., 0., 0., ..., 0., 0., 0.],\n",
- " [0., 0., 0., ..., 0., 0., 0.],\n",
- " ...,\n",
- " [0., 0., 0., ..., 0., 0., 0.],\n",
- " [0., 0., 0., ..., 0., 0., 0.],\n",
- " [0., 0., 0., ..., 0., 0., 0.]]]),\n",
- " tensor([ 4, 1, 2, 62, 32, 40, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79,\n",
- " 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79],\n",
- " dtype=torch.uint8))"
- ]
- },
- "execution_count": 57,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "emnist_lines[0]"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 59,
+ "execution_count": 9,
"metadata": {
"scrolled": false
},
@@ -251,6 +231,119 @@
},
{
"cell_type": "code",
+ "execution_count": 10,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "EMNIST Lines Dataset\n",
+ "Max length: 34\n",
+ "Min overlap: 0\n",
+ "Max overlap: 0.33\n",
+ "Num classes: 80\n",
+ "Input shape: (28, 952)\n",
+ "Data: (10000, 28, 952)\n",
+ "Tagets: (10000, 34)\n",
+ "\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(emnist_lines)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "2020-08-05 00:40:26.070 | DEBUG | text_recognizer.datasets.emnist_lines_dataset:_load_data:153 - EmnistLinesDataset loading data from HDF5...\n"
+ ]
+ }
+ ],
+ "source": [
+ "dl = EmnistLinesDataLoaders(\"train\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 16,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "ddl = dl(\"train\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 17,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "batch = next(iter(ddl))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 21,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "torch.Size([1, 28, 952])"
+ ]
+ },
+ "execution_count": 21,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "batch[0][0].shape"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 24,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "<matplotlib.image.AxesImage at 0x7f139b1cf1c0>"
+ ]
+ },
+ "execution_count": 24,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "image/png": "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\n",
+ "text/plain": [
+ "<Figure size 1440x1440 with 1 Axes>"
+ ]
+ },
+ "metadata": {
+ "needs_background": "light"
+ },
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "plt.figure(figsize=(20, 20))\n",
+ "plt.imshow(batch[0][-1].squeeze(0), cmap='gray')"
+ ]
+ },
+ {
+ "cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
diff --git a/src/text_recognizer/datasets/__init__.py b/src/text_recognizer/datasets/__init__.py
index a8c46c4..1b4cc59 100644
--- a/src/text_recognizer/datasets/__init__.py
+++ b/src/text_recognizer/datasets/__init__.py
@@ -1,21 +1,28 @@
"""Dataset modules."""
from .emnist_dataset import (
+ _augment_emnist_mapping,
+ _load_emnist_essentials,
DATA_DIRNAME,
EmnistDataLoaders,
EmnistDataset,
+ ESSENTIALS_FILENAME,
)
from .emnist_lines_dataset import (
construct_image_from_string,
+ EmnistLinesDataLoaders,
EmnistLinesDataset,
get_samples_by_character,
)
from .util import Transpose
__all__ = [
+ "_augment_emnist_mapping",
+ "_load_emnist_essentials",
"construct_image_from_string",
"DATA_DIRNAME",
"EmnistDataset",
"EmnistDataLoaders",
+ "EmnistLinesDataLoaders",
"EmnistLinesDataset",
"get_samples_by_character",
"Transpose",
diff --git a/src/text_recognizer/datasets/emnist_dataset.py b/src/text_recognizer/datasets/emnist_dataset.py
index 525df95..f3d65ee 100644
--- a/src/text_recognizer/datasets/emnist_dataset.py
+++ b/src/text_recognizer/datasets/emnist_dataset.py
@@ -260,21 +260,23 @@ class EmnistDataLoaders:
"""
self.splits = splits
- self.sample_to_balance = sample_to_balance
if subsample_fraction is not None:
if not 0.0 < subsample_fraction < 1.0:
raise ValueError("The subsample fraction must be in (0, 1).")
- self.subsample_fraction = subsample_fraction
- self.transform = transform
- self.target_transform = target_transform
+ self.dataset_args = {
+ "sample_to_balance": sample_to_balance,
+ "subsample_fraction": subsample_fraction,
+ "transform": transform,
+ "target_transform": target_transform,
+ "seed": seed,
+ }
self.batch_size = batch_size
self.shuffle = shuffle
self.num_workers = num_workers
self.cuda = cuda
- self.seed = seed
- self._data_loaders = self._fetch_emnist_data_loaders()
+ self._data_loaders = self._load_data_loaders()
def __repr__(self) -> str:
"""Returns information about the dataset."""
@@ -303,7 +305,7 @@ class EmnistDataLoaders:
except KeyError:
raise ValueError(f"Split {split} does not exist.")
- def _fetch_emnist_data_loaders(self) -> Dict[str, DataLoader]:
+ def _load_data_loaders(self) -> Dict[str, DataLoader]:
"""Fetches the EMNIST dataset and return a Dict of PyTorch DataLoaders."""
data_loaders = {}
@@ -311,18 +313,11 @@ class EmnistDataLoaders:
if split in self.splits:
if split == "train":
- train = True
+ self.dataset_args["train"] = True
else:
- train = False
-
- emnist_dataset = EmnistDataset(
- train=train,
- sample_to_balance=self.sample_to_balance,
- subsample_fraction=self.subsample_fraction,
- transform=self.transform,
- target_transform=self.target_transform,
- seed=self.seed,
- )
+ self.dataset_args["train"] = False
+
+ emnist_dataset = EmnistDataset(**self.dataset_args)
emnist_dataset.load_emnist_dataset()
diff --git a/src/text_recognizer/datasets/emnist_lines_dataset.py b/src/text_recognizer/datasets/emnist_lines_dataset.py
index 4d8b646..1c6e959 100644
--- a/src/text_recognizer/datasets/emnist_lines_dataset.py
+++ b/src/text_recognizer/datasets/emnist_lines_dataset.py
@@ -8,17 +8,20 @@ import h5py
from loguru import logger
import numpy as np
import torch
-from torch.utils.data import Dataset
+from torch.utils.data import DataLoader, Dataset
from torchvision.transforms import Compose, Normalize, ToTensor
-from text_recognizer.datasets import DATA_DIRNAME, EmnistDataset
+from text_recognizer.datasets import (
+ _augment_emnist_mapping,
+ _load_emnist_essentials,
+ DATA_DIRNAME,
+ EmnistDataset,
+ ESSENTIALS_FILENAME,
+)
from text_recognizer.datasets.sentence_generator import SentenceGenerator
from text_recognizer.datasets.util import Transpose
DATA_DIRNAME = DATA_DIRNAME / "processed" / "emnist_lines"
-ESSENTIALS_FILENAME = (
- Path(__file__).resolve().parents[0] / "emnist_lines_essentials.json"
-)
class EmnistLinesDataset(Dataset):
@@ -26,8 +29,8 @@ class EmnistLinesDataset(Dataset):
def __init__(
self,
- emnist: EmnistDataset,
train: bool = False,
+ emnist: Optional[EmnistDataset] = None,
transform: Optional[Callable] = None,
target_transform: Optional[Callable] = None,
max_length: int = 34,
@@ -40,7 +43,7 @@ class EmnistLinesDataset(Dataset):
Args:
emnist (EmnistDataset): A EmnistDataset object.
- train (bool): Flag for the filename. Defaults to False.
+ train (bool): Flag for the filename. Defaults to False. Defaults to None.
transform (Optional[Callable]): The transform of the data. Defaults to None.
target_transform (Optional[Callable]): The transform of the target. Defaults to None.
max_length (int): The maximum number of characters. Defaults to 34.
@@ -61,15 +64,19 @@ class EmnistLinesDataset(Dataset):
if self.target_transform is None:
self.target_transform = torch.tensor
- self.mapping = self.emnist.mapping
- self.num_classes = self.emnist.num_classes
+ # Load emnist dataset infromation.
+ essentials = _load_emnist_essentials()
+ self.mapping = _augment_emnist_mapping(dict(essentials["mapping"]))
+ self.num_classes = len(self.mapping)
+ self.input_shape = essentials["input_shape"]
+
self.max_length = max_length
self.min_overlap = min_overlap
self.max_overlap = max_overlap
self.num_samples = num_samples
self.input_shape = (
- self.emnist.input_shape[0],
- self.emnist.input_shape[1] * self.max_length,
+ self.input_shape[0],
+ self.input_shape[1] * self.max_length,
)
self.output_shape = (self.max_length, self.num_classes)
self.seed = seed
@@ -325,3 +332,94 @@ def create_datasets(
num_samples=num,
)
emnist_lines._load_or_generate_data()
+
+
+class EmnistLinesDataLoaders:
+ """Wrapper for a PyTorch Data loaders for the EMNIST lines dataset."""
+
+ def __init__(
+ self,
+ splits: List[str],
+ max_length: int = 34,
+ min_overlap: float = 0,
+ max_overlap: float = 0.33,
+ num_samples: int = 10000,
+ transform: Optional[Callable] = None,
+ target_transform: Optional[Callable] = None,
+ batch_size: int = 128,
+ shuffle: bool = False,
+ num_workers: int = 0,
+ cuda: bool = True,
+ seed: int = 4711,
+ ) -> None:
+ """Sets the data loader arguments."""
+ self.splits = splits
+ self.dataset_args = {
+ "max_length": max_length,
+ "min_overlap": min_overlap,
+ "max_overlap": max_overlap,
+ "num_samples": num_samples,
+ "transform": transform,
+ "target_transform": target_transform,
+ "seed": seed,
+ }
+ self.batch_size = batch_size
+ self.shuffle = shuffle
+ self.num_workers = num_workers
+ self.cuda = cuda
+ self._data_loaders = self._load_data_loaders()
+
+ def __repr__(self) -> str:
+ """Returns information about the dataset."""
+ return self._data_loaders[self.splits[0]].dataset.__repr__()
+
+ @property
+ def __name__(self) -> str:
+ """Returns the name of the dataset."""
+ return "EmnistLines"
+
+ def __call__(self, split: str) -> DataLoader:
+ """Returns the `split` DataLoader.
+
+ Args:
+ split (str): The dataset split, i.e. train or val.
+
+ Returns:
+ DataLoader: A PyTorch DataLoader.
+
+ Raises:
+ ValueError: If the split does not exist.
+
+ """
+ try:
+ return self._data_loaders[split]
+ except KeyError:
+ raise ValueError(f"Split {split} does not exist.")
+
+ def _load_data_loaders(self) -> Dict[str, DataLoader]:
+ """Fetches the EMNIST Lines dataset and return a Dict of PyTorch DataLoaders."""
+ data_loaders = {}
+
+ for split in ["train", "val"]:
+ if split in self.splits:
+
+ if split == "train":
+ self.dataset_args["train"] = True
+ else:
+ self.dataset_args["train"] = False
+
+ emnist_lines_dataset = EmnistLinesDataset(**self.dataset_args)
+
+ emnist_lines_dataset._load_or_generate_data()
+
+ data_loader = DataLoader(
+ dataset=emnist_lines_dataset,
+ batch_size=self.batch_size,
+ shuffle=self.shuffle,
+ num_workers=self.num_workers,
+ pin_memory=self.cuda,
+ )
+
+ data_loaders[split] = data_loader
+
+ return data_loaders