Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor to numpy #7097

Merged
merged 41 commits into from
Jan 10, 2022
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
421dc8c
tensor numpy method
liufengwei0103 Dec 20, 2021
f138b68
to numpy
liufengwei0103 Dec 23, 2021
876bdd3
delete useless file
liufengwei0103 Dec 23, 2021
4ae708f
replace CHECK_JUST with JUST
liufengwei0103 Dec 23, 2021
4ad98e3
tensor cpu method return self if it is in cpu
liufengwei0103 Dec 23, 2021
add1cf4
delete tensor buffer
liufengwei0103 Dec 23, 2021
916c319
delete useless code
liufengwei0103 Dec 27, 2021
392449d
refine
liufengwei0103 Dec 28, 2021
17406d3
Update python/oneflow/nn/modules/tensor_ops.py
liufengwei0103 Dec 28, 2021
5fc016d
refine
liufengwei0103 Dec 28, 2021
5b78661
add docstr of cpu method
liufengwei0103 Dec 28, 2021
388f4e4
delete useless code
liufengwei0103 Dec 29, 2021
34960ae
refine
liufengwei0103 Dec 29, 2021
04e171b
add comment
liufengwei0103 Dec 29, 2021
d2a26aa
refine
liufengwei0103 Dec 29, 2021
0954b4c
add 'assert' info
liufengwei0103 Dec 29, 2021
c6aaf7a
refine
liufengwei0103 Dec 29, 2021
925a8d9
Merge branch 'master' into refactor_to_numpy
liufengwei0103 Dec 30, 2021
c7c36f6
Merge branch 'master' into refactor_to_numpy
oneflow-ci-bot Jan 1, 2022
dd2ba70
do .cpu if tensor is not in cpu memory
liufengwei0103 Jan 6, 2022
0ab668d
revert format change
liufengwei0103 Jan 6, 2022
ced5f02
Merge branch 'master' into refactor_to_numpy
oneflow-ci-bot Jan 6, 2022
3df1da6
Merge branch 'master' into refactor_to_numpy
oneflow-ci-bot Jan 6, 2022
ce0e225
Merge branch 'master' into refactor_to_numpy
oneflow-ci-bot Jan 6, 2022
9f9988e
Merge branch 'master' into refactor_to_numpy
oneflow-ci-bot Jan 6, 2022
00c19e8
Merge branch 'master' into refactor_to_numpy
oneflow-ci-bot Jan 6, 2022
f9ed32c
fix tensor buffer numpy
liufengwei0103 Jan 7, 2022
3b12b1a
Merge branch 'refactor_to_numpy' of github.com:Oneflow-Inc/oneflow in…
liufengwei0103 Jan 7, 2022
5a7cfa4
Merge branch 'master' into refactor_to_numpy
oneflow-ci-bot Jan 7, 2022
c26dd9b
support tensor buffer to invoke numpy
liufengwei0103 Jan 8, 2022
518e9fe
Merge branch 'refactor_to_numpy' of github.com:Oneflow-Inc/oneflow in…
liufengwei0103 Jan 8, 2022
a647d79
Merge branch 'master' into refactor_to_numpy
oneflow-ci-bot Jan 8, 2022
97a3d62
Merge branch 'master' into refactor_to_numpy
oneflow-ci-bot Jan 8, 2022
53dae94
fix bug
liufengwei0103 Jan 8, 2022
fd81291
Merge branch 'refactor_to_numpy' of github.com:Oneflow-Inc/oneflow in…
liufengwei0103 Jan 8, 2022
9ab997d
fix nd sbp numpy bug
liufengwei0103 Jan 9, 2022
59108a6
Merge branch 'master' into refactor_to_numpy
liufengwei0103 Jan 9, 2022
5f6b7d5
fix bug about test case because of numpy sharing memory with tensor
liufengwei0103 Jan 10, 2022
e9aba74
Merge branch 'refactor_to_numpy' of github.com:Oneflow-Inc/oneflow in…
liufengwei0103 Jan 10, 2022
582b3a4
auto format by CI
oneflow-ci-bot Jan 10, 2022
21218da
Merge branch 'master' into refactor_to_numpy
liufengwei0103 Jan 10, 2022
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
13 changes: 13 additions & 0 deletions oneflow/api/python/framework/tensor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,18 @@ const Symbol<DType>* GetTensorDType(const Tensor& tensor) {
return &CHECK_JUST(DType::Get(tensor.dtype()->data_type()));
}

py::array ApiSwitchEagerTensorToNumpy(const py::handle& py_tensor) {
const std::shared_ptr<Tensor> tensor = py::cast<const std::shared_ptr<Tensor>>(py_tensor);
DataType data_type = tensor->dtype()->data_type();
switch (data_type) {
#define EAGER_TENSOR_TO_NUMPY(cpp_type, of_type) \
liufengwei0103 marked this conversation as resolved.
Show resolved Hide resolved
case of_type: return EagerTensorToNumpy<cpp_type>(py_tensor).GetOrThrow();
OF_PP_FOR_EACH_TUPLE(EAGER_TENSOR_TO_NUMPY, POD_DATA_TYPE_SEQ BOOL_DATA_TYPE_SEQ)
default:
return Maybe<py::array>(Error::UnimplementedError() << "not support datatype").GetOrThrow();
}
}

void ApiEagerMirroredTensorZeros(const std::shared_ptr<Tensor>& tensor) {
return EagerMirroredTensorZeros(tensor).GetOrThrow();
}
Expand Down Expand Up @@ -198,6 +210,7 @@ ONEFLOW_API_PYBIND11_MODULE("", m) {
[](const std::shared_ptr<one::Tensor>& tensor) {
return CheckMetaConsistency(tensor).GetOrThrow();
})
.def("to_numpy", &ApiSwitchEagerTensorToNumpy, py::return_value_policy::move)
wyg1997 marked this conversation as resolved.
Show resolved Hide resolved
#define DEFINE_TENSOR_METHOD(T, type_proto) \
.def("_copy_to_numpy_" #T, &ApiCopyMirroredTensorToNumpy<T>) \
.def("_copy_from_numpy_" #T, &ApiCopyMirroredTensorFromNumpy<T>)
Expand Down
59 changes: 58 additions & 1 deletion oneflow/api/python/utils/tensor_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,71 @@ limitations under the License.
#include "oneflow/core/framework/tensor.h"
#include "oneflow/core/framework/nd_sbp.h"
#include "oneflow/core/functional/functional_api.yaml.h"

#include "oneflow/core/framework/stride.h"
#include "oneflow/core/register/ofblob.h"
#include "oneflow/extension/python/numpy.h"
namespace py = pybind11;

namespace oneflow {
namespace one {

Maybe<void> EagerMirroredTensorZeros(const std::shared_ptr<Tensor>& t);

template<typename T>
inline static Maybe<py::array> EagerTensorToNumpy(const py::handle& py_tensor) {
const std::shared_ptr<Tensor> t = py::cast<const std::shared_ptr<Tensor>>(py_tensor);
py::handle handle;
std::shared_ptr<MirroredTensor> tensor;
CHECK_OR_RETURN(JUST(t->device()) == JUST(Device::New("cpu")));
CHECK_OR_RETURN(t->is_eager()) << "eager tensors supported only";
if (t->is_local()) {
tensor = JUST(t->AsMirroredTensor());
// set base object attr
handle = py::handle(py_tensor.ptr());
} else {
const Symbol<ConsistentTensorMeta>& tensor_meta = JUST(t->consistent_tensor_meta());
const Symbol<cfg::NdSbp>& nd_sbp = tensor_meta->nd_sbp();
CHECK_OR_RETURN(!nd_sbp->sbp_parallel().empty());
cfg::SbpParallel broadcast_sbp;
broadcast_sbp.mutable_broadcast_parallel();
std::vector<Symbol<cfg::SbpParallel>> sbp_tuple(nd_sbp->sbp_parallel_size(),
SymbolOf(broadcast_sbp));
std::vector<Symbol<cfg::SbpParallel>> none;
const auto& consistent_tensor =
JUST(functional::ToConsistent(t, tensor_meta->parallel_desc(), sbp_tuple, none));
tensor = JUST(consistent_tensor->cur_rank_phy_tensor());
}

const size_t ndim = tensor->ndim();
const auto shape = numpy::OFShapeToNumpyShape(tensor->shape()->dim_vec());
// NumPy strides use bytes. OneFlow strides use element counts.
const auto stride = numpy::OFStrideToNumpyStride(JUST(tensor->stride())->StrideVec(),
tensor->dtype()->data_type());

T* data_ptr = nullptr;
const auto& Callback = std::make_shared<std::function<void(uint64_t)>>([&](uint64_t ofblob_ptr) {
data_ptr = reinterpret_cast<OfBlob*>(ofblob_ptr)->mut_blob()->mut_dptr<T>();
});
bool is_printed = false;
SpinCounter::SpinWait(
1,
[&](const std::shared_ptr<SpinCounter>& sc) -> Maybe<void> {
return PhysicalRun([&](InstructionsBuilder* builder) -> Maybe<void> {
return builder->SyncAccessBlobByCallback(tensor, sc, Callback, "mut");
});
},
[&is_printed]() {
if (!is_printed) {
blocking::StackInfoCallback();
is_printed = true;
}
});

return py::array(
py::buffer_info(data_ptr, sizeof(T), py::format_descriptor<T>::format(), ndim, shape, stride),
handle);
}

template<typename T>
inline Maybe<void> CopyBetweenMirroredTensorAndNumpy(
const std::shared_ptr<Tensor>& t, PyObject* array,
Expand Down
16 changes: 16 additions & 0 deletions oneflow/extension/python/numpy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,22 @@ Maybe<DataType> GetOFDataTypeFromNpArray(PyArrayObject* array) {
return NumpyTypeToOFDataType(np_array_type);
}

std::vector<size_t> OFShapeToNumpyShape(const DimVector& fixed_vec) {
size_t ndim = fixed_vec.size();
auto result = std::vector<size_t>(ndim);
for (int i = 0; i < ndim; i++) { result[i] = fixed_vec.at(i); }
return result;
}

// NumPy strides use bytes. OneFlow strides use element counts.
std::vector<size_t> OFStrideToNumpyStride(const StrideVector& fixed_vec, const DataType data_type) {
size_t ndim = fixed_vec.size();
auto result = std::vector<size_t>(ndim);
int byte_per_elem = GetSizeOfDataType(data_type);
for (int i = 0; i < ndim; i++) { result[i] = fixed_vec.at(i) * byte_per_elem; }
return result;
}

// Executing any numpy c api before _import_array() results in segfault
// NOTE: this InitNumpyCAPI() works because of `PY_ARRAY_UNIQUE_SYMBOL`
// defined in numpy_internal.h
Expand Down
6 changes: 6 additions & 0 deletions oneflow/extension/python/numpy_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ limitations under the License.
// ************************

#include "oneflow/core/common/data_type.h"
#include "oneflow/core/common/fixed_vector.h"
#include "oneflow/core/common/shape_vec.h"

// PyArrayObject cannot be forward declared, or a compile error will occur

Expand Down Expand Up @@ -56,6 +58,10 @@ Maybe<DataType> NumpyTypeToOFDataType(int np_array_type);

Maybe<DataType> GetOFDataTypeFromNpArray(PyArrayObject* array);

std::vector<size_t> OFShapeToNumpyShape(const DimVector& fixed_vec);

std::vector<size_t> OFStrideToNumpyStride(const StrideVector& fixed_vec, const DataType data_type);

Maybe<void> InitNumpyCAPI();

} // namespace numpy
Expand Down
9 changes: 9 additions & 0 deletions python/oneflow/framework/docstr/tensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,15 @@
""",
)

add_docstr(
oneflow.Tensor.cpu,
"""
Tensor.cpu() → Tensor

Returns a copy of this object in CPU memory. if this object is already in CPU memory and on the correct device, then no copy is performed and the original object is returned.
liufengwei0103 marked this conversation as resolved.
Show resolved Hide resolved
""",
)

add_docstr(
oneflow.Tensor.pow,
"""
Expand Down
42 changes: 17 additions & 25 deletions python/oneflow/framework/tensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,29 +29,6 @@
TensorTuple = flow._oneflow_internal.TensorTuple


def _tensor_numpy(eager_local_tensor):
assert (
not eager_local_tensor.is_lazy
), "tensor.numpy() is not allowed to called in nn.Graph.build(*args) or called by lazy tensor."
if eager_local_tensor.dtype == flow.tensor_buffer:
shapes, dtypes = eager_local_tensor._tensor_buffer_shapes_and_dtypes
tensors = flow.tensor_buffer_to_list_of_tensors(
eager_local_tensor, shapes, dtypes
)
return [t.numpy() for t in tensors]
method_name = eager_local_tensor._get_copy_mirrored_tensor_to_numpy_func_name()
copy_to_numpy = getattr(eager_local_tensor, method_name)

ndarray = np.empty(
shape=tuple(eager_local_tensor.shape),
dtype=flow.convert_oneflow_dtype_to_numpy_dtype(eager_local_tensor.dtype),
)

if ndarray.size != 0:
copy_to_numpy(ndarray)
return ndarray


def _size(self, idx=None):
if idx is None:
return self.shape
Expand Down Expand Up @@ -733,7 +710,7 @@ def _get_device(self):

def _format(self, format_spec):
if self.dim() == 0:
return self.numpy().tolist().__format__(format_spec)
return self.tolist().__format__(format_spec)
return object.__format__(self, format_spec)


Expand All @@ -745,13 +722,27 @@ def _gather(self, dim, index):
return flow._C.dim_gather(self, dim, index, False)


def _cpu(self):
if self.device == flow.device("cpu"):
return self
return self.to(device="cpu")


def _numpy(self):
assert self.device == flow.device("cpu")
liufengwei0103 marked this conversation as resolved.
Show resolved Hide resolved
assert (
not self.is_lazy
), "tensor.numpy() is not allowed to called in nn.Graph.build(*args) or called by lazy tensor."
return self.to_numpy()


def RegisterMethods():
Tensor.__mul__ = lambda self, other: self.mul(other)
Tensor.__rmul__ = lambda self, other: self.mul(other)
Tensor.__add__ = lambda self, other: self.add(other)
Tensor.__iadd__ = lambda self, other: self.add_(other)
Tensor.ndim = property(_ndim)
Tensor.numpy = _tensor_numpy
Tensor.numpy = _numpy
Tensor.size = _size
Tensor.dim = _ndim
Tensor.ndimension = _ndim
Expand Down Expand Up @@ -893,6 +884,7 @@ def RegisterMethods():
Tensor.gather = _gather
Tensor.all = _all
Tensor.any = _any
Tensor.cpu = _cpu


def register_tensor_op(op_name):
Expand Down
4 changes: 4 additions & 0 deletions python/oneflow/nn/modules/tensor_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,8 @@ def item_op(input):
1.0
"""
assert input.numel() == 1, "Only a Tensor with 1 element can be converted to Scalar"
if input.device != flow.device("cpu"):
input = input.cpu()
return input.numpy().item()


Expand All @@ -245,6 +247,8 @@ def tolist_op(input):
"""
if input.numel() == 1 and input.ndim == 0:
return input.item()
if input.device != flow.device("cpu"):
input = input.to(device="cpu")
wyg1997 marked this conversation as resolved.
Show resolved Hide resolved
return input.numpy().tolist()


Expand Down