Repository: keras-team/autokeras

Issue:
feature_request: Support multicore/multiprocess for read_images function
### Feature Description
Support multicore for `read_images` function in `image/image_supervised.py`.

### Reason
As [read_images function in image_supervised.py](https://github.com/jhfjhfj1/autokeras/blob/36752fcbce58bd1a26c17144637cc0ecbef83da0/autokeras/image/image_supervised.py#L14) does not support multi-core(just read files one by one), so it's **too slow** when reading image files more than 10k files.

### Solution
Use multiprocessing or equivalent one in `read_images` function as a default, or optional.



Discussion:

Resolving patch:
diff --git a/autokeras/image/image_supervised.py b/autokeras/image/image_supervised.py
--- a/autokeras/image/image_supervised.py
+++ b/autokeras/image/image_supervised.py
@@ -1,6 +1,7 @@
 import os
 from abc import ABC
 import numpy as np
+from multiprocessing import Pool, cpu_count
 
 from autokeras.constant import Constant
 from autokeras.nn.loss_function import classification_loss, regression_loss
@@ -11,31 +12,48 @@
     read_csv_file, read_image, compute_image_resize_params, resize_image_data
 
 
-def read_images(img_file_names, images_dir_path):
+def _image_to_array(img_path):
+    """Read the image from the path and return image object.
+        Return an image object.
+
+    Args:
+        img_path: image file name in images_dir_path.
+    """
+    if os.path.exists(img_path):
+        img = read_image(img_path)
+        if len(img.shape) < 3:
+            img = img[..., np.newaxis]
+        return img
+    else:
+        raise ValueError("%s image does not exist" % img_path)
+
+
+def read_images(img_file_names, images_dir_path, parallel=True):
     """Read the images from the path and return their numpy.ndarray instance.
         Return a numpy.ndarray instance containing the training data.
 
     Args:
         img_file_names: List containing images names.
         images_dir_path: Path to the directory containing images.
+        parallel: (Default: True) Run _image_to_array will use multiprocessing.
     """
-    x_train = []
+    img_paths = [os.path.join(images_dir_path, img_file)
+                 for img_file in img_file_names]
+
     if os.path.isdir(images_dir_path):
-        for img_file in img_file_names:
-            img_path = os.path.join(images_dir_path, img_file)
-            if os.path.exists(img_path):
-                img = read_image(img_path)
-                if len(img.shape) < 3:
-                    img = img[..., np.newaxis]
-                x_train.append(img)
-            else:
-                raise ValueError("%s image does not exist" % img_file)
+        if parallel:
+            pool = Pool(processes=cpu_count())
+            x_train = pool.map(_image_to_array, img_paths)
+            pool.close()
+            pool.join()
+        else:
+            x_train = [_image_to_array(img_path) for img_path in img_paths]
     else:
         raise ValueError("Directory containing images does not exist")
     return np.asanyarray(x_train)
 
 
-def load_image_dataset(csv_file_path, images_path):
+def load_image_dataset(csv_file_path, images_path, parallel=True):
     """Load images from the files and labels from a csv file.
 
     Second, the dataset is a set of images and the labels are in a CSV file.
@@ -48,13 +66,14 @@ def load_image_dataset(csv_file_path, images_path):
     Args:
         csv_file_path: CSV file path.
         images_path: Path where images exist.
+        parallel: (Default: True) Load dataset with multiprocessing.
 
     Returns:
         x: Four dimensional numpy.ndarray. The channel dimension is the last dimension.
         y: The labels.
     """
     img_file_name, y = read_csv_file(csv_file_path)
-    x = read_images(img_file_name, images_path)
+    x = read_images(img_file_name, images_path, parallel)
     return np.array(x), np.array(y)
 
 
@@ -112,7 +131,8 @@ def fit(self, x, y, time_limit=None):
 
     def init_transformer(self, x):
         if self.data_transformer is None:
-            self.data_transformer = ImageDataTransformer(x, augment=self.augment)
+            self.data_transformer = ImageDataTransformer(
+                x, augment=self.augment)
 
     def preprocess(self, x):
         return resize_image_data(x, self.resize_shape)


Test patch: