Skip to content
This repository was archived by the owner on Nov 17, 2023. It is now read-only.

NumPy-compatible infrastructure on Gluon #16024

Merged
merged 4 commits into from
Aug 30, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 22 additions & 7 deletions python/mxnet/contrib/text/embedding.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@
from ... import ndarray as nd
from ... import registry
from ... import base
from ...util import is_np_array
from ... import numpy as _mx_np
from ... import numpy_extension as _mx_npx


def register(embedding_cls):
Expand Down Expand Up @@ -295,12 +298,15 @@ def _load_embedding(self, pretrained_file_path, elem_delim, init_unknown_vec, en
tokens.add(token)

self._vec_len = vec_len
self._idx_to_vec = nd.array(all_elems).reshape((-1, self.vec_len))
array_fn = _mx_np.array if is_np_array() else nd.array
self._idx_to_vec = array_fn(all_elems).reshape((-1, self.vec_len))

if loaded_unknown_vec is None:
self._idx_to_vec[C.UNKNOWN_IDX] = init_unknown_vec(shape=self.vec_len)
init_val = init_unknown_vec(shape=self.vec_len)
self._idx_to_vec[C.UNKNOWN_IDX] =\
init_val.as_np_ndarray() if is_np_array() else init_val
else:
self._idx_to_vec[C.UNKNOWN_IDX] = nd.array(loaded_unknown_vec)
self._idx_to_vec[C.UNKNOWN_IDX] = array_fn(loaded_unknown_vec)

def _index_tokens_from_vocabulary(self, vocabulary):
self._token_to_idx = vocabulary.token_to_idx.copy() \
Expand Down Expand Up @@ -328,7 +334,8 @@ def _set_idx_to_vec_by_embeddings(self, token_embeddings, vocab_len, vocab_idx_t
"""

new_vec_len = sum(embed.vec_len for embed in token_embeddings)
new_idx_to_vec = nd.zeros(shape=(vocab_len, new_vec_len))
zeros_fn = _mx_np.zeros if is_np_array() else nd.zeros
new_idx_to_vec = zeros_fn(shape=(vocab_len, new_vec_len))

col_start = 0
# Concatenate all the embedding vectors in token_embeddings.
Expand Down Expand Up @@ -397,7 +404,13 @@ def get_vecs_by_tokens(self, tokens, lower_case_backup=False):
else self.token_to_idx.get(token.lower(), C.UNKNOWN_IDX)
for token in tokens]

vecs = nd.Embedding(nd.array(indices), self.idx_to_vec, self.idx_to_vec.shape[0],
if is_np_array():
embedding_fn = _mx_npx.embedding
array_fn = _mx_np.array
else:
embedding_fn = nd.Embedding
array_fn = nd.array
vecs = embedding_fn(array_fn(indices), self.idx_to_vec, self.idx_to_vec.shape[0],
self.idx_to_vec.shape[1])

return vecs[0] if to_reduce else vecs
Expand Down Expand Up @@ -425,7 +438,8 @@ def update_token_vectors(self, tokens, new_vectors):
if not isinstance(tokens, list):
tokens = [tokens]
if len(new_vectors.shape) == 1:
new_vectors = new_vectors.expand_dims(0)
expand_dims_fn = _mx_np.expand_dims if is_np_array() else nd.expand_dims
new_vectors = expand_dims_fn(new_vectors, axis=0)

else:
assert isinstance(new_vectors, nd.NDArray) and len(new_vectors.shape) == 2, \
Expand All @@ -444,7 +458,8 @@ def update_token_vectors(self, tokens, new_vectors):
'`unknown_token` %s in `tokens`. This is to avoid unintended '
'updates.' % (token, self.idx_to_token[C.UNKNOWN_IDX]))

self._idx_to_vec[nd.array(indices)] = new_vectors
array_fn = _mx_np.array if is_np_array() else nd.array
self._idx_to_vec[array_fn(indices)] = new_vectors

@classmethod
def _check_pretrained_file_names(cls, pretrained_file_name):
Expand Down
34 changes: 26 additions & 8 deletions python/mxnet/gluon/data/dataloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
# coding: utf-8
# pylint: disable=ungrouped-imports
"""Dataset generator."""
from __future__ import absolute_import
__all__ = ['DataLoader']

import pickle
Expand All @@ -37,6 +38,8 @@

from . import sampler as _sampler
from ... import nd, context
from ...util import is_np_shape, is_np_array, set_np
from ... import numpy as _mx_np # pylint: disable=reimported

if sys.platform == 'darwin' or sys.platform == 'win32':
def rebuild_ndarray(*args):
Expand Down Expand Up @@ -128,27 +131,33 @@ def __init__(self, *args, **kwargs):
def default_batchify_fn(data):
"""Collate data into batch."""
if isinstance(data[0], nd.NDArray):
return nd.stack(*data)
return _mx_np.stack(data) if is_np_array() else nd.stack(*data)
elif isinstance(data[0], tuple):
data = zip(*data)
return [default_batchify_fn(i) for i in data]
else:
data = np.asarray(data)
return nd.array(data, dtype=data.dtype)
array_fn = _mx_np.array if is_np_array() else nd.array
return array_fn(data, dtype=data.dtype)


def default_mp_batchify_fn(data):
"""Collate data into batch. Use shared memory for stacking."""
if isinstance(data[0], nd.NDArray):
out = nd.empty((len(data),) + data[0].shape, dtype=data[0].dtype,
empty_fn = _mx_np.empty if is_np_array() else nd.empty
out = empty_fn((len(data),) + data[0].shape, dtype=data[0].dtype,
ctx=context.Context('cpu_shared', 0))
return nd.stack(*data, out=out)
if is_np_array():
return _mx_np.stack(data, out=out)
else:
return nd.stack(*data, out=out)
elif isinstance(data[0], tuple):
data = zip(*data)
return [default_mp_batchify_fn(i) for i in data]
else:
data = np.asarray(data)
return nd.array(data, dtype=data.dtype,
array_fn = _mx_np.array if is_np_array() else nd.array
return array_fn(data, dtype=data.dtype,
ctx=context.Context('cpu_shared', 0))


Expand Down Expand Up @@ -384,14 +393,20 @@ def __len__(self):
return len(self._batch_sampler)


def _thread_worker_initializer(active_shape, active_array):
"""Initializer for ThreadPool."""
set_np(shape=active_shape, array=active_array)


_worker_dataset = None
def _worker_initializer(dataset):
def _worker_initializer(dataset, active_shape, active_array):
"""Initialier for processing pool."""
# global dataset is per-process based and only available in worker processes
# this is only necessary to handle MXIndexedRecordIO because otherwise dataset
# can be passed as argument
global _worker_dataset
_worker_dataset = dataset
set_np(shape=active_shape, array=active_array)

def _worker_fn(samples, batchify_fn, dataset=None):
"""Function for processing data in worker process."""
Expand Down Expand Up @@ -558,10 +573,13 @@ def __init__(self, dataset, batch_size=None, shuffle=False, sampler=None,
self._prefetch = max(0, int(prefetch) if prefetch is not None else 2 * self._num_workers)
if self._num_workers > 0:
if self._thread_pool:
self._worker_pool = ThreadPool(self._num_workers)
self._worker_pool = ThreadPool(self._num_workers,
initializer=_thread_worker_initializer,
initargs=(is_np_shape(), is_np_array()))
else:
self._worker_pool = multiprocessing.Pool(
self._num_workers, initializer=_worker_initializer, initargs=[self._dataset])
self._num_workers, initializer=_worker_initializer,
initargs=[self._dataset, is_np_shape(), is_np_array()])
if batchify_fn is None:
if num_workers > 0:
self._batchify_fn = default_mp_batchify_fn
Expand Down
12 changes: 9 additions & 3 deletions python/mxnet/gluon/data/vision/datasets.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
from .. import dataset
from ...utils import download, check_sha1, _get_repo_file_url
from .... import nd, image, recordio, base
from .... import numpy as _mx_np # pylint: disable=reimported
from ....util import is_np_array


class MNIST(dataset._DownloadedDataset):
Expand Down Expand Up @@ -81,13 +83,16 @@ def _get_data(self):
with gzip.open(label_file, 'rb') as fin:
struct.unpack(">II", fin.read(8))
label = np.frombuffer(fin.read(), dtype=np.uint8).astype(np.int32)
if is_np_array():
label = _mx_np.array(label, dtype=label.dtype)

with gzip.open(data_file, 'rb') as fin:
struct.unpack(">IIII", fin.read(16))
data = np.frombuffer(fin.read(), dtype=np.uint8)
data = data.reshape(len(label), 28, 28, 1)

self._data = nd.array(data, dtype=data.dtype)
array_fn = _mx_np.array if is_np_array() else nd.array
self._data = array_fn(data, dtype=data.dtype)
self._label = label


Expand Down Expand Up @@ -183,8 +188,9 @@ def _get_data(self):
data = np.concatenate(data)
label = np.concatenate(label)

self._data = nd.array(data, dtype=data.dtype)
self._label = label
array_fn = _mx_np.array if is_np_array() else nd.array
self._data = array_fn(data, dtype=data.dtype)
self._label = array_fn(label, dtype=label.dtype) if is_np_array() else label


class CIFAR100(CIFAR10):
Expand Down
25 changes: 25 additions & 0 deletions python/mxnet/gluon/data/vision/transforms.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from ...nn import Sequential, HybridSequential
from .... import image
from ....base import numeric_types
from ....util import is_np_array


class Compose(Sequential):
Expand Down Expand Up @@ -92,6 +93,8 @@ def __init__(self, dtype='float32'):
self._dtype = dtype

def hybrid_forward(self, F, x):
if is_np_array():
F = F.npx
return F.cast(x, self._dtype)


Expand Down Expand Up @@ -134,6 +137,8 @@ def __init__(self):
super(ToTensor, self).__init__()

def hybrid_forward(self, F, x):
if is_np_array():
F = F.npx
return F.image.to_tensor(x)


Expand Down Expand Up @@ -187,6 +192,8 @@ def __init__(self, mean=0.0, std=1.0):
self._std = std

def hybrid_forward(self, F, x):
if is_np_array():
F = F.npx
return F.image.normalize(x, self._mean, self._std)


Expand Down Expand Up @@ -369,6 +376,8 @@ def __init__(self, size, keep_ratio=False, interpolation=1):
self._interpolation = interpolation

def hybrid_forward(self, F, x):
if is_np_array():
F = F.npx
return F.image.resize(x, self._size, self._keep, self._interpolation)

class RandomFlipLeftRight(HybridBlock):
Expand All @@ -385,6 +394,8 @@ def __init__(self):
super(RandomFlipLeftRight, self).__init__()

def hybrid_forward(self, F, x):
if is_np_array():
F = F.npx
return F.image.random_flip_left_right(x)


Expand All @@ -402,6 +413,8 @@ def __init__(self):
super(RandomFlipTopBottom, self).__init__()

def hybrid_forward(self, F, x):
if is_np_array():
F = F.npx
return F.image.random_flip_top_bottom(x)


Expand All @@ -427,6 +440,8 @@ def __init__(self, brightness):
self._args = (max(0, 1-brightness), 1+brightness)

def hybrid_forward(self, F, x):
if is_np_array():
F = F.npx
return F.image.random_brightness(x, *self._args)


Expand All @@ -452,6 +467,8 @@ def __init__(self, contrast):
self._args = (max(0, 1-contrast), 1+contrast)

def hybrid_forward(self, F, x):
if is_np_array():
F = F.npx
return F.image.random_contrast(x, *self._args)


Expand All @@ -477,6 +494,8 @@ def __init__(self, saturation):
self._args = (max(0, 1-saturation), 1+saturation)

def hybrid_forward(self, F, x):
if is_np_array():
F = F.npx
return F.image.random_saturation(x, *self._args)


Expand All @@ -502,6 +521,8 @@ def __init__(self, hue):
self._args = (max(0, 1-hue), 1+hue)

def hybrid_forward(self, F, x):
if is_np_array():
F = F.npx
return F.image.random_hue(x, *self._args)


Expand Down Expand Up @@ -536,6 +557,8 @@ def __init__(self, brightness=0, contrast=0, saturation=0, hue=0):
self._args = (brightness, contrast, saturation, hue)

def hybrid_forward(self, F, x):
if is_np_array():
F = F.npx
return F.image.random_color_jitter(x, *self._args)


Expand All @@ -559,4 +582,6 @@ def __init__(self, alpha):
self._alpha = alpha

def hybrid_forward(self, F, x):
if is_np_array():
F = F.npx
return F.image.random_lighting(x, self._alpha)
Loading