Skip to content

Commit

Permalink
Numpy-compatible Infra (apache#15581)
Browse files Browse the repository at this point in the history
* [Do not review] [Do not merge] New numpy-compatible sum (apache#14739)

* Add numpy namespace and initial impl of np.sum (not complete)

* Clean up

* Fix import error

* numpy sum

* add test and backward data type support

* add license to test_numpy_op.py

* improve test to reduce flakiness

* fix sanity build

* extra numeric test and imperative test

* add error message for initial argument

* [numpy] Infra for supporting numpy ops in imperative mode and Gluon APIs (apache#14758)

* Infra of new ndarray and symbol types for numpy operators

* Rename

* Fix import problem

* Refactor

* Remove redundant code

* Add docstring

* More on numpy ndarray and symbol

* Override unimplemented methdos for ndarray and _NumpySymbol

* Fix built-in methods of ndarray and _NumpySymbol

* Fix test and sanity check

* Fix pylint

* Address cr comments

* Add unit tests for ndarray and _NumpySymbol

* Add _true_divide

* Fix gpu build

* Add future import division

* More correct way of checking if an output is from a np compat op

* Fix gpu build

* Fix output ndarray/symbol types with at least one new ndarray/symbol

* Modify true_divide doc

* Fix flaky copying zero-size arrays via gpus

* Fix zero size in gluon hybridize and zeros/ones symbol not creating new symbol type

* Fix doc

* Enable np op compat check with name prefix (apache#14897)

* [numpy] Numpy dot (apache#14831)

* Numpy Dot case 1-4 + case 3.5 forward and 0.5 backward

* Backward computation and test coverage

* numpy-compatible mean (apache#14859)

* [numpy] Some np ops for d2l (apache#14924)

* Add np transpose

More ops and namespaces for submodules

Add relu and sigmoid

Add reshape

Fix symbolic name mismatch

Add maximum and minimum

* Add convenience fluent method

* Add ndarray.item()

* Fix CI

* Fix lint

* Fix lint

* Fix reshape gpu

* Add example

* Remove python notebook outputs

* Remove notebook output

* Add one more example

* [numpy] Refactor np modules (apache#14989)

* Refactor

* Initial refactoring

* Fix notebook

* Move numpy op check from backend to frontend

* Add homogeneous ndarray check

* Fix grouping inhomogeneous types of symbols

* Improve error handling of different types of symbols as outputs

* Fix test

* Fix numpy test

* Fix ci

* Try to fix gpu ci failure

* [numpy] Refactor np module (example runs through) (apache#15055)

* Refactor notebook

* notebook working with hybrid block

* More refactoring

* Remove unnecessary use_np_compat

* Use class decorator to initialize numpy ndarrays in parameter.py

* Clear notebook outputs

* Improve np decorator

* Remove npe op from optimizer

* Fix CI

* Fix functools.wraps issue in Python2

* Fix ci

* Change np_compat to np_shape

* Temporarily disable test_amp

* Numpy-compatible stack (apache#15027)

* numpy stack

* migrate to use_np_shape

* Numpy Unary Ops (apache#15010)

* Unary Ops

* new version of unit tests

* [numpy] Fix np branch after rebase (apache#15086)

* Add np_array semantics for Gluon

Fix notebook

Fix sanity

Fix gluon deferred infer shape

Add np.random.uniform

Add random normal

Add boolean comparison ops

Add np.ndarray indexing

Reformat test ndarray indexing

Fix unit tests

Add one more test of indexing

Fix sanity

Enable amp test

Add np.arange

Revert cython unit test to ctypes

Delete unnecessary use_np_shape decorator from test

Rebase with numpy branch

support range as index

Fix python2 range type check

Add argmax

Disable clojure test

* Fix ci

* Add np.linalg.norm for ord='fro'

* Fix pylint

* numpy concatenate (apache#15104)

* [WIP][numpy] Fix for D2L Chapters 2/3/4 (apache#15139)

* Fix

* Fix linear regression gluon

* More fix

* Fix pylint

* Fix for chapter 4

* Add np.add mul div mod pow sub and shuffle

* Fix model selection, underfitting, overfitting

* Fix weight decay

* Fix dropout

* Fix

* Fix chapter 4

* [numpy] Fix d2l performance regression (apache#15173)

* Add np array adapter decorator for layers

* Fix performance regression caused by too many conversions between nd.NDArray and np.ndarray

* Fix pylint

* Fix test backward compatibility issue

* Fix test_lambda

* Fix (apache#15188)

* fix for chapter6 conv nn (apache#15224)

* [numpy] Fix d2l chapter8 (apache#15237)

* Add np op doc

* Fix several issues

* Add a N-D dot b 2D support

* Simplify array creation api

* Add swapaxes

* Fix rnn gluon

* More fix

* Fix pylint

* Delete

* Fix mp windows

* fix for ch11 (apache#15244)

* Numpy-compatible split (apache#15049)

* numpy split

* numpy split

* unit test

* unit test

* [numpy] [DO NOT MERGE] Fix d2l chapters 9 and 13 (apache#15246)

* Add npx batch_dot and topk

* Text embedding uses numpy

* Fix SoftmaxCrossEntropyLoss with np

* Fix sentiment cnn

* Fix pylint

* Fix dot attention

* Fix seq2seq attention

* Add np.tile

* Fix transformer

* Fix ci

* Fix ci and rebase

* [numpy] Fix d2l chapter 5 (apache#15264)

* Fix parameter initializer

* Add np.save and np.load

* Fix read-write

* Fix lint

* Numpy compatible max (apache#15161)

* numpy amax

* weird cu file diff

* fix the unit test error

* fix gpu bug

* minor fix

* fix lint

* remove scalar value check

* fix the bug on unit test

* fix the case () that breaks the kernel launch

* add zero dimension unit test

* revert the tuple change

* use mshadow maximum

* remove test zero

* change the macro for now

* change the cuda to use mashadow op

* fix the broadcast_reduce_op_value.cu wrong kernel

* add more logic in shape to detect the invalid situation

* change back to type swtich

* change to as_nd_ndarray

* add missing @npx.use_np_shape

* retrigger CI

* address the comment

* undo algorithm import

* remove the numeric gradient check

* Numpy compatible multinomial (apache#15219)

* draft of multinomial

* rename to more concise name

* finish shape

* complete the forward function

* complete forward without handle 0 dimension & scalar

* handle 0 dimension

* add new line

* fix lint

* fix the build error

* fix lint

* finish unit test

* change the registration

* make multinomial support pvals as mx.ndarray

* delete newline

* fix lint error

* support input as list, mx.ndarray, np.ndarray & unit test

* fix lint

* fix the include error

* fix lint

* refactor & pass the tensor instead of tuple to kernel

* fix lint

* updata the doc

* address the comment

* Numpy compatible linspace (apache#15256)

* draft

* finish linspace implementation

* finish linspace

* delete newline

* fix pylint

* add more unit test

* address comment

* add more test case

* disable too-many-arguments

* resolve confliction

* add ctx

* numpy-compatible cumsum (apache#15309)

* [numpy] Misc fix for other chapters (apache#15332)

* Add np.prod

* Fix ndarray.reshape accepting positional integers as arguments

* Rebase

* Fix rebase error

* Add np.ndarray.flatten

* Fix

* Add broadcast_to

* Add meshgrid and broadcast_arrays

* Fix sin, cos, sinh, cosh not supporting scalars

* Add more unary ops supporting python scalars

* Fix

* Fix

* Fix ci

* Fix sanity

* [numpy] Change d2l chapters cv and gan to use numpy (apache#15368)

* Change op name style to lower case underscore

* Add ops under image to npx

* Add image submodule to npx

* Fix split_and_load use np

* Fix fine tuning

* Fix bbox and anchor

* Fix odd

* Fix ssd and rcnn

* Remove restriction on binary element-wise scalar

* Fix gan

* Fix sanity

* Try to fix website build failure

* Add npx.random.seed

* Fix doc

* add doc for multinomial, dot, cumsum, clip, abs, exp, arctan (apache#15386)

* [numpy] Fix several places in numpy (apache#15398)

* Fix

* More fix

* [numpy] fix cython (apache#15418)

* add cython support for numpy

* stay with original API for backward compatibility

* fix after rebase

* get rid of coverage in clang60 mkldnn

* fix lint issues

* fix flaky test and get rid of extra print

* remove numpy examples

* revert apache#15309 apache#15256 apache#15219 apache#15161

* remove numpy docs

* remove changes to contrib/text/embedding.py

* remove numpy changes to gluon peripherals

* Revert "remove numpy docs"

This reverts commit c104695.

* get rid of most operators

* Revert "get rid of coverage in clang60 mkldnn"

This reverts commit 77dc905.

* remove np-compatible from mxnet.image mxnet.initializer

* address comments
  • Loading branch information
haojin2 authored and Rohit Kumar Srivastava committed Sep 25, 2019
1 parent fab9688 commit fa3bc9f
Show file tree
Hide file tree
Showing 115 changed files with 6,394 additions and 231 deletions.
4 changes: 3 additions & 1 deletion include/mxnet/base.h
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,9 @@ inline int32_t Context::GetGPUCount() {
#if MXNET_USE_CUDA
int32_t count;
cudaError_t e = cudaGetDeviceCount(&count);
if (e == cudaErrorNoDevice) {
// TODO(junwu): Remove e == cudaErrorInsufficientDriver
// This is skipped for working around wheel build system with older CUDA driver.
if (e == cudaErrorNoDevice || e == cudaErrorInsufficientDriver) {
return 0;
}
CHECK_EQ(e, cudaSuccess) << " CUDA: " << cudaGetErrorString(e);
Expand Down
12 changes: 12 additions & 0 deletions include/mxnet/c_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -2860,6 +2860,18 @@ MXNET_DLL int MXEnginePushSync(EngineSyncFunc sync_func, void* func_param,
EngineVarHandle mutable_vars_handle, int num_mutable_vars,
EngineFnPropertyHandle prop_handle DEFAULT(NULL),
int priority DEFAULT(0), const char* opr_name DEFAULT(NULL));
/*!
* \brief Create an NDArray from source sharing the same data chunk.
* \param src source NDArray
* \param out new NDArray sharing the same data chunck with src
*/
MXNET_DLL int MXShallowCopyNDArray(NDArrayHandle src, NDArrayHandle* out);
/*!
* \brief Create an Symbol from source sharing the same graph structure.
* \param src source Symbol
* \param out new Symbol sharing the same graph structure with src
*/
MXNET_DLL int MXShallowCopySymbol(SymbolHandle src, SymbolHandle * out);

/*!
* \brief Push an asynchronous operation to the engine.
Expand Down
15 changes: 15 additions & 0 deletions include/mxnet/tuple.h
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,14 @@ class Tuple {
is.get();
if (ch == '(' || ch == '[') break;
if (!isspace(ch)) {
if (ch == 'N') {
std::string tmp_val;
is >> tmp_val;
if (tmp_val == "one") { // is stores "None"
t.SetDim(-1);
return is;
}
}
is.setstate(std::ios::failbit);
return is;
}
Expand Down Expand Up @@ -653,6 +661,13 @@ inline bool shape_is_known(const TShape& x) {
return true;
}

inline bool shape_is_known(const std::vector<TShape>& shapes) {
for (const TShape& shape : shapes) {
if (!shape_is_known(shape)) return false;
}
return true;
}

/*! \brief helper function to cast type of container elements */
template<typename SrcIter, typename DstIter>
inline DstIter ShapeTypeCast(const SrcIter begin,
Expand Down
5 changes: 5 additions & 0 deletions python/mxnet/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,16 @@
from . import engine
from .base import MXNetError
from .util import is_np_shape, set_np_shape, np_shape, use_np_shape
from .util import is_np_array, np_array, use_np_array, use_np
from . import base
from . import library
from . import contrib
from . import ndarray
from . import ndarray as nd
from . import numpy
from . import numpy_extension
from . import numpy as np
from . import numpy_extension as npx
from . import name
# use mx.sym as short for symbol
from . import symbol as sym
Expand Down
36 changes: 24 additions & 12 deletions python/mxnet/_ctypes/ndarray.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,22 @@ def __reduce__(self):


_ndarray_cls = None
_np_ndarray_cls = None


def _set_ndarray_class(cls):
"""Set the symbolic class to be cls"""
global _ndarray_cls
_ndarray_cls = cls


def _imperative_invoke(handle, ndargs, keys, vals, out):
def _set_np_ndarray_class(cls):
"""Set the symbolic class to be cls"""
global _np_ndarray_cls
_np_ndarray_cls = cls


def _imperative_invoke(handle, ndargs, keys, vals, out, is_np_op):
"""ctypes implementation of imperative invoke wrapper"""
if out is not None:
original_output = out
Expand Down Expand Up @@ -91,23 +99,27 @@ def _imperative_invoke(handle, ndargs, keys, vals, out):
c_str_array([str(s) for s in vals]),
ctypes.byref(out_stypes)))

create_ndarray_fn = _np_ndarray_cls if is_np_op else _ndarray_cls
if original_output is not None:
return original_output
if num_output.value == 1:
return _ndarray_cls(ctypes.cast(output_vars[0], NDArrayHandle),
stype=out_stypes[0])
return create_ndarray_fn(ctypes.cast(output_vars[0], NDArrayHandle),
stype=out_stypes[0])
else:
return [_ndarray_cls(ctypes.cast(output_vars[i], NDArrayHandle),
stype=out_stypes[i])
for i in range(num_output.value)]
return [create_ndarray_fn(ctypes.cast(output_vars[i], NDArrayHandle),
stype=out_stypes[i]) for i in range(num_output.value)]


class CachedOp(object):
"""Cached operator handle."""
__slots__ = ["handle"]
__slots__ = ["handle", "is_np_sym"]

def __init__(self, sym, flags=()):
self.handle = CachedOpHandle()

from ..symbol.numpy._symbol import _Symbol
self.is_np_sym = bool(isinstance(sym, _Symbol))

check_call(_LIB.MXCreateCachedOpEx(
sym.handle,
len(flags),
Expand Down Expand Up @@ -151,10 +163,10 @@ def __call__(self, *args, **kwargs):

if original_output is not None:
return original_output
create_ndarray_fn = _np_ndarray_cls if self.is_np_sym else _ndarray_cls
if num_output.value == 1:
return _ndarray_cls(ctypes.cast(output_vars[0], NDArrayHandle),
stype=out_stypes[0])
return create_ndarray_fn(ctypes.cast(output_vars[0], NDArrayHandle),
stype=out_stypes[0])
else:
return [_ndarray_cls(ctypes.cast(output_vars[i], NDArrayHandle),
stype=out_stypes[i])
for i in range(num_output.value)]
return [create_ndarray_fn(ctypes.cast(output_vars[i], NDArrayHandle),
stype=out_stypes[i]) for i in range(num_output.value)]
13 changes: 11 additions & 2 deletions python/mxnet/_ctypes/symbol.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@
from ..base import SymbolHandle
from ..base import check_call

# The symbol class to be used (Cython or Ctypes)
_symbol_cls = None
_np_symbol_cls = None

class SymbolBase(object):
"""Symbol is symbolic graph."""
Expand Down Expand Up @@ -115,7 +117,13 @@ def _set_symbol_class(cls):
_symbol_cls = cls


def _symbol_creator(handle, args, kwargs, keys, vals, name):
def _set_np_symbol_class(cls):
"""Set the numpy-compatible symbolic class to be cls"""
global _np_symbol_cls
_np_symbol_cls = cls


def _symbol_creator(handle, args, kwargs, keys, vals, name, is_np_op):
sym_handle = SymbolHandle()
check_call(_LIB.MXSymbolCreateAtomicSymbol(
ctypes.c_void_p(handle),
Expand All @@ -128,7 +136,8 @@ def _symbol_creator(handle, args, kwargs, keys, vals, name):
raise TypeError(
'Operators with variable length input can only accept input'
'Symbols either as positional or keyword arguments, not both')
s = _symbol_cls(sym_handle)
create_symbol_fn = _np_symbol_cls if is_np_op else _symbol_cls
s = create_symbol_fn(sym_handle)
if args:
s._compose(*args, name=name)
elif kwargs:
Expand Down
54 changes: 54 additions & 0 deletions python/mxnet/_numpy_op_doc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.

# pylint: skip-file

"""Doc placeholder for numpy ops with prefix _np."""


def _np_ones_like(a):
"""Return an array of ones with the same shape and type as a given array.
Parameters
----------
a : ndarray
The shape and data-type of `a` define these same attributes of
the returned array.
Returns
-------
out : ndarray
Array of ones with the same shape and type as `a`.
"""
pass


def _np_zeros_like(a):
"""Return an array of zeros with the same shape and type as a given array.
Parameters
----------
a : ndarray
The shape and data-type of `a` define these same attributes of
the returned array.
Returns
-------
out : ndarray
Array of zeros with the same shape and type as `a`.
"""
pass
117 changes: 114 additions & 3 deletions python/mxnet/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
# under the License.

# coding: utf-8
# pylint: disable=invalid-name, no-member, trailing-comma-tuple, bad-mcs-classmethod-argument, unnecessary-pass, wrong-import-position
# pylint: disable=invalid-name, no-member, trailing-comma-tuple, bad-mcs-classmethod-argument, unnecessary-pass, too-many-lines, wrong-import-position
"""ctypes library of mxnet and helper functions."""
from __future__ import absolute_import

Expand Down Expand Up @@ -598,7 +598,9 @@ def _init_op_module(root_namespace, module_name, make_op_func):
ctypes.byref(plist)))
op_names = []
for i in range(size.value):
op_names.append(py_str(plist[i]))
op_name = py_str(plist[i])
if not _is_np_op(op_name):
op_names.append(op_name)

module_op = sys.modules["%s.%s.op" % (root_namespace, module_name)]
module_internal = sys.modules["%s.%s._internal" % (root_namespace, module_name)]
Expand Down Expand Up @@ -692,7 +694,9 @@ def write_all_str(module_file, module_all_list):
ctypes.byref(plist)))
op_names = []
for i in range(size.value):
op_names.append(py_str(plist[i]))
op_name = py_str(plist[i])
if not _is_np_op(op_name):
op_names.append(op_name)

module_op_file = get_module_file("%s.%s.op" % (root_namespace, module_name))
module_op_all = []
Expand Down Expand Up @@ -739,3 +743,110 @@ def write_all_str(module_file, module_all_list):
if Features().is_enabled("TVM_OP"):
_LIB_TVM_OP = libinfo.find_lib_path("libtvmop")
check_call(_LIB.MXLoadTVMOp(c_str(_LIB_TVM_OP[0])))


_NP_OP_PREFIX = '_np_'
_NP_OP_SUBMODULE_LIST = ['_random_', '_linalg_']

_NP_EXT_OP_PREFIX = '_npx_'
_NP_EXT_OP_SUBMODULE_LIST = ['_image_']

_NP_INTERNAL_OP_PREFIX = '_npi_'


def _is_np_op(op_name):
return op_name.startswith(_NP_OP_PREFIX) or op_name.startswith(_NP_EXT_OP_PREFIX)\
or op_name.startswith(_NP_INTERNAL_OP_PREFIX)


def _get_op_submodule_name(op_name, op_name_prefix, submodule_name_list):
"""Get the submodule name of a specific op"""
assert op_name.startswith(op_name_prefix)
for submodule_name in submodule_name_list:
if op_name[len(op_name_prefix):].startswith(submodule_name):
return submodule_name
return ""


def _init_np_op_module(root_module_name, np_module_name, mx_module_name, make_op_func):
"""
Register numpy operators in namespaces `mxnet.numpy`, `mxnet.ndarray.numpy`
and `mxnet.symbol.numpy`. They are used in imperative mode, Gluon APIs w/o hybridization,
and Gluon APIs w/ hybridization, respectively. Essentially, operators with the same name
registered in three namespaces, respectively share the same functionality in C++ backend.
Different namespaces are needed for dispatching operator calls in Gluon's `HybridBlock` by `F`.
Parameters
----------
root_module_name : str
Top level module name, `mxnet` in the current cases.
np_module_name : str
Second level module name, `numpy` or `numpy_extension` in the current case.
make_op_func : function
Function for creating op functions.
"""
from . import _numpy_op_doc as _np_op_doc
if np_module_name == 'numpy':
op_name_prefix = _NP_OP_PREFIX
submodule_name_list = _NP_OP_SUBMODULE_LIST
elif np_module_name == 'numpy_extension':
op_name_prefix = _NP_EXT_OP_PREFIX
submodule_name_list = _NP_EXT_OP_SUBMODULE_LIST
elif np_module_name == 'numpy._internal':
op_name_prefix = _NP_INTERNAL_OP_PREFIX
submodule_name_list = []
else:
raise ValueError('unsupported np module name {}'.format(np_module_name))

plist = ctypes.POINTER(ctypes.c_char_p)()
size = ctypes.c_uint()
check_call(_LIB.MXListAllOpNames(ctypes.byref(size), ctypes.byref(plist)))
op_names = []
for i in range(size.value):
name = py_str(plist[i])
if name.startswith(op_name_prefix):
op_names.append(name)

if mx_module_name is None:
# register np/npx ops for imperative programming
op_module_name = "%s.%s._op" % (root_module_name, np_module_name) # e.g. mxnet.numpy._op
op_submodule_name = "%s.%s" % (root_module_name, np_module_name) # e.g. mxnet.numpy.random
elif mx_module_name in ('ndarray', 'symbol'):
# register numpy internal ops and np/npx ops for use in Gluon
# np internal ops are registered in mxnet.ndarray/symbol.numpy._internal
# np ops are registered in mxnet.ndarray/symbol.numpy._op
# npx ops are registered in mxnet.ndarray/symbol.numpy_extension._op
op_module_name = "%s.%s.%s" % (root_module_name, mx_module_name, np_module_name)
if op_name_prefix != _NP_INTERNAL_OP_PREFIX:
op_module_name += '._op'
# e.g. mxnet.symbol.numpy.random
op_submodule_name = "%s.%s.%s" % (root_module_name, mx_module_name, np_module_name)
else:
raise ValueError('unsupported mxnet module {}'.format(mx_module_name))
op_submodule_name += '.%s'

op_module = sys.modules[op_module_name]
submodule_dict = {}
for submodule_name in submodule_name_list:
submodule_dict[submodule_name] = sys.modules[op_submodule_name % submodule_name[1:-1]]
for name in op_names:
hdl = OpHandle()
check_call(_LIB.NNGetOpHandle(c_str(name), ctypes.byref(hdl)))
submodule_name = _get_op_submodule_name(name, op_name_prefix, submodule_name_list)
if len(submodule_name) > 0:
func_name = name[(len(op_name_prefix) + len(submodule_name)):]
cur_module = submodule_dict[submodule_name]
module_name_local = op_submodule_name % submodule_name[1:-1]
else:
func_name = name[len(op_name_prefix):]
cur_module = op_module
module_name_local =\
op_module_name[:-len('._op')] if op_module_name.endswith('._op') else op_module_name

function = make_op_func(hdl, name, func_name)
function.__module__ = module_name_local
setattr(cur_module, function.__name__, function)
cur_module.__all__.append(function.__name__)

if hasattr(_np_op_doc, name):
function.__doc__ = getattr(_np_op_doc, name).__doc__
Loading

0 comments on commit fa3bc9f

Please sign in to comment.