forked from mindspore-Ecosystem/mindspore
!5899 Fix Some bugs and add test cases on Tensor printing.
Merge pull request !5899 from huangbingjian/master
This commit is contained in:
commit
9c896fd22c
|
@ -103,14 +103,13 @@ template <typename T>
|
|||
void PrintScalarToString(const char *str_data_ptr, const string &tensor_type, std::ostringstream *const buf) {
|
||||
MS_EXCEPTION_IF_NULL(str_data_ptr);
|
||||
MS_EXCEPTION_IF_NULL(buf);
|
||||
*buf << "Tensor shape:[1] " << tensor_type;
|
||||
*buf << "\nval:";
|
||||
*buf << "Tensor(shape=[1], dtype=" << GetParseType(tensor_type) << ", value=";
|
||||
const T *data_ptr = reinterpret_cast<const T *>(str_data_ptr);
|
||||
if constexpr (std::is_same<T, int8_t>::value || std::is_same<T, uint8_t>::value) {
|
||||
const int int_data = static_cast<int>(*data_ptr);
|
||||
*buf << int_data << "\n";
|
||||
*buf << int_data << ")\n";
|
||||
} else {
|
||||
*buf << *data_ptr << "\n";
|
||||
*buf << *data_ptr << ")\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -118,12 +117,11 @@ void PrintScalarToBoolString(const char *str_data_ptr, const string &tensor_type
|
|||
MS_EXCEPTION_IF_NULL(str_data_ptr);
|
||||
MS_EXCEPTION_IF_NULL(buf);
|
||||
const bool *data_ptr = reinterpret_cast<const bool *>(str_data_ptr);
|
||||
*buf << "Tensor shape:[1] " << tensor_type;
|
||||
*buf << "\nval:";
|
||||
*buf << "Tensor(shape=[1], dtype=" << GetParseType(tensor_type) << ", value=";
|
||||
if (*data_ptr) {
|
||||
*buf << "True\n";
|
||||
*buf << "True)\n";
|
||||
} else {
|
||||
*buf << "False\n";
|
||||
*buf << "False)\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -238,17 +238,49 @@ class TensorDataImpl : public TensorData {
|
|||
OutputDataString(ss, 0, 0, 1, false);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
ssize_t cursor = 0;
|
||||
num_width_ = GetMaxNumLength(shape);
|
||||
SummaryStringRecursive(ss, shape, &cursor, 0, use_comma);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
private:
|
||||
int GetNumLength(const T &num) const {
|
||||
T value = num;
|
||||
int count = 0;
|
||||
if (value <= 0) { // Return 1 when value is 0, or add the length of '-' when value < 0
|
||||
count++;
|
||||
}
|
||||
while (value != 0) {
|
||||
value /= 10;
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
int GetMaxNumLength(const ShapeVector &shape) const {
|
||||
if constexpr (std::is_same<T, bool>::value) {
|
||||
constexpr int bool_max_len = sizeof("False") - 1;
|
||||
return bool_max_len;
|
||||
} else if constexpr (std::is_same<T, float16>::value) {
|
||||
return 11; // The placeholder of float16 is set to 11.
|
||||
} else if (std::is_same<T, float>::value || std::is_same<T, double>::value) {
|
||||
return 15; // The placeholder of float/double is set to 15.
|
||||
} else {
|
||||
T max_value = 0;
|
||||
T min_value = 0;
|
||||
ssize_t index = 0;
|
||||
GetMaxMinValueRecursive(shape, &index, 0, &max_value, &min_value);
|
||||
return std::max(GetNumLength(max_value), GetNumLength(min_value));
|
||||
}
|
||||
}
|
||||
|
||||
void OutputDataString(std::ostringstream &ss, ssize_t cursor, ssize_t start, ssize_t end, bool use_comma) const {
|
||||
const bool isScalar = ndim_ == 0 && end - start == 1;
|
||||
constexpr auto isBool = std::is_same<T, bool>::value;
|
||||
constexpr auto isFloat =
|
||||
std::is_same<T, float16>::value || std::is_same<T, float>::value || std::is_same<T, double>::value;
|
||||
constexpr auto isBool = std::is_same<T, bool>::value;
|
||||
constexpr int linefeedThreshold = isFloat ? kThreshold1DFloat : (isBool ? kThreshold1DBool : kThreshold1DInt);
|
||||
for (ssize_t i = start; i < end && (cursor + i) < static_cast<ssize_t>(data_size_); i++) {
|
||||
const auto value = data_[cursor + i];
|
||||
|
@ -256,52 +288,25 @@ class TensorDataImpl : public TensorData {
|
|||
if (isScalar) {
|
||||
ss << value;
|
||||
} else {
|
||||
if constexpr (std::is_same<T, float16>::value) {
|
||||
ss << std::setw(11) << std::setprecision(4) << std::setiosflags(std::ios::scientific | std::ios::right)
|
||||
<< value;
|
||||
} else {
|
||||
ss << std::setw(15) << std::setprecision(8) << std::setiosflags(std::ios::scientific | std::ios::right)
|
||||
<< value;
|
||||
}
|
||||
const int precision = std::is_same<T, float16>::value ? 4 : 8;
|
||||
ss << std::setw(num_width_) << std::setprecision(precision)
|
||||
<< std::setiosflags(std::ios::scientific | std::ios::right) << value;
|
||||
}
|
||||
} else if (std::is_same<T, bool>::value) {
|
||||
} else if (isBool) {
|
||||
if (isScalar) {
|
||||
ss << (value ? "True" : "False");
|
||||
} else {
|
||||
ss << std::setw(5) << std::setiosflags(std::ios::right) << (value ? "True" : "False");
|
||||
ss << std::setw(num_width_) << std::setiosflags(std::ios::right) << (value ? "True" : "False");
|
||||
}
|
||||
} else {
|
||||
constexpr auto isSigned = std::is_same<T, int64_t>::value;
|
||||
if constexpr (isSigned) {
|
||||
if (!isScalar && static_cast<int64_t>(value) >= 0) {
|
||||
ss << ' ';
|
||||
}
|
||||
}
|
||||
|
||||
// Set width and indent for different int type with signed position.
|
||||
//
|
||||
// uint8 width: 3, [0, 255]
|
||||
// int8 width: 4, [-128, 127]
|
||||
// uint16 width: 5, [0, 65535]
|
||||
// int16 width: 6, [-32768, 32767]
|
||||
// uint32 width: 10, [0, 4294967295]
|
||||
// int32 width: 11, [-2147483648, 2147483647]
|
||||
// uint64 width: NOT SET (20, [0, 18446744073709551615])
|
||||
// int64 width: NOT SET (20, [-9223372036854775808, 9223372036854775807])
|
||||
if constexpr (std::is_same<T, uint8_t>::value) {
|
||||
ss << std::setw(3) << std::setiosflags(std::ios::right) << static_cast<uint16_t>(value);
|
||||
} else if constexpr (std::is_same<T, int8_t>::value) {
|
||||
ss << std::setw(4) << std::setiosflags(std::ios::right) << static_cast<int16_t>(value);
|
||||
} else if constexpr (std::is_same<T, uint16_t>::value) {
|
||||
ss << std::setw(5) << std::setiosflags(std::ios::right) << value;
|
||||
} else if constexpr (std::is_same<T, int16_t>::value) {
|
||||
ss << std::setw(6) << std::setiosflags(std::ios::right) << value;
|
||||
} else if constexpr (std::is_same<T, uint32_t>::value) {
|
||||
ss << std::setw(10) << std::setiosflags(std::ios::right) << value;
|
||||
} else if constexpr (std::is_same<T, int32_t>::value) {
|
||||
ss << std::setw(11) << std::setiosflags(std::ios::right) << value;
|
||||
} else {
|
||||
if (isScalar) {
|
||||
ss << value;
|
||||
} else if constexpr (std::is_same<T, uint8_t>::value) {
|
||||
ss << std::setw(num_width_) << std::setiosflags(std::ios::right) << static_cast<uint16_t>(value);
|
||||
} else if constexpr (std::is_same<T, int8_t>::value) {
|
||||
ss << std::setw(num_width_) << std::setiosflags(std::ios::right) << static_cast<int16_t>(value);
|
||||
} else {
|
||||
ss << std::setw(num_width_) << std::setiosflags(std::ios::right) << value;
|
||||
}
|
||||
}
|
||||
if (!isScalar && i != end - 1) {
|
||||
|
@ -366,9 +371,9 @@ class TensorDataImpl : public TensorData {
|
|||
}
|
||||
// Handle the second half.
|
||||
if (num > kThreshold / 2) {
|
||||
auto continue_pos = num - kThreshold / 2;
|
||||
for (ssize_t i = continue_pos; i < num; i++) {
|
||||
if (use_comma && i != continue_pos) {
|
||||
ssize_t iter_times = std::min(static_cast<ssize_t>(num - kThreshold / 2), static_cast<ssize_t>(kThreshold / 2));
|
||||
for (ssize_t i = 0; i < iter_times; i++) {
|
||||
if (use_comma && i != 0) {
|
||||
ss << ',';
|
||||
}
|
||||
ss << '\n';
|
||||
|
@ -380,6 +385,46 @@ class TensorDataImpl : public TensorData {
|
|||
ss << ']';
|
||||
}
|
||||
|
||||
void GetMaxMinValueRecursive(const ShapeVector &shape, ssize_t *index, ssize_t depth, T *max_value,
|
||||
T *min_value) const {
|
||||
if (depth >= static_cast<ssize_t>(ndim_)) {
|
||||
return;
|
||||
}
|
||||
if (depth == static_cast<ssize_t>(ndim_) - 1) { // Bottom dimension
|
||||
ssize_t num = shape[depth];
|
||||
const bool is_multi_dim = num > kThreshold && ndim_ > 1;
|
||||
for (ssize_t i = 0; i < num; i++) {
|
||||
if (is_multi_dim && i >= kThreshold / 2 && i < num - kThreshold / 2) {
|
||||
continue;
|
||||
}
|
||||
const auto value = data_[i];
|
||||
*max_value = std::max(*max_value, value);
|
||||
*min_value = std::min(*min_value, value);
|
||||
}
|
||||
*index += num;
|
||||
} else { // Middle dimension
|
||||
ssize_t num = shape[depth];
|
||||
for (ssize_t i = 0; i < std::min(static_cast<ssize_t>(kThreshold / 2), num); i++) {
|
||||
GetMaxMinValueRecursive(shape, index, depth + 1, max_value, min_value);
|
||||
}
|
||||
if (num > kThreshold) {
|
||||
ssize_t ignored = shape[depth + 1];
|
||||
for (ssize_t i = depth + 2; i < static_cast<ssize_t>(ndim_); i++) {
|
||||
ignored *= shape[i];
|
||||
}
|
||||
ignored *= num - kThreshold;
|
||||
*index += ignored;
|
||||
}
|
||||
if (num > kThreshold / 2) {
|
||||
ssize_t iter_times = std::min(static_cast<ssize_t>(num - kThreshold / 2), static_cast<ssize_t>(kThreshold / 2));
|
||||
for (ssize_t i = 0; i < iter_times; i++) {
|
||||
GetMaxMinValueRecursive(shape, index, depth + 1, max_value, min_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mutable int num_width_{0};
|
||||
size_t ndim_{0};
|
||||
size_t data_size_{0};
|
||||
std::unique_ptr<T[]> data_;
|
||||
|
@ -522,7 +567,7 @@ std::string Tensor::ToStringInternal(int limit_size) const {
|
|||
auto dtype = Dtype();
|
||||
MS_EXCEPTION_IF_NULL(dtype);
|
||||
data_sync();
|
||||
buf << "Tensor(shape=" << ShapeToString(shape_) << ", dtype=" << dtype->ToString() << ',';
|
||||
buf << "Tensor(shape=" << ShapeToString(shape_) << ", dtype=" << dtype->ToString() << ", value=";
|
||||
if (limit_size <= 0 || DataSize() < limit_size) {
|
||||
// Only print data for small tensor.
|
||||
buf << ((data().ndim() > 1) ? '\n' : ' ') << data().ToString(data_type_, shape_, false);
|
||||
|
@ -548,8 +593,8 @@ std::string Tensor::ToStringRepr() const {
|
|||
auto dtype = Dtype();
|
||||
MS_EXCEPTION_IF_NULL(dtype);
|
||||
data_sync();
|
||||
buf << "Tensor(shape=" << ShapeToString(shape_) << ", dtype=" << dtype->ToString() << ','
|
||||
<< ((data().ndim() > 1) ? '\n' : ' ') << data().ToString(data_type_, shape_, true) << ')';
|
||||
buf << "Tensor(shape=" << ShapeToString(shape_) << ", dtype=" << dtype->ToString()
|
||||
<< ", value=" << ((data().ndim() > 1) ? '\n' : ' ') << data().ToString(data_type_, shape_, true) << ')';
|
||||
return buf.str();
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
# Copyright 2020 Huawei Technologies Co., Ltd
|
||||
#
|
||||
# Licensed 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.
|
||||
# ============================================================================
|
||||
import numpy as np
|
||||
|
||||
import mindspore
|
||||
import mindspore.context as context
|
||||
import mindspore.nn as nn
|
||||
from mindspore import Tensor
|
||||
from mindspore.ops import operations as P
|
||||
|
||||
context.set_context(mode=context.GRAPH_MODE, device_target="Ascend")
|
||||
|
||||
class TensorPrint(nn.Cell):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.print = P.Print()
|
||||
|
||||
def construct(self, *inputs):
|
||||
self.print(*inputs)
|
||||
return inputs[0]
|
||||
|
||||
def get_tensor(is_scalar, input_type):
|
||||
if is_scalar == 'scalar':
|
||||
if input_type == mindspore.bool_:
|
||||
return Tensor(True, dtype=input_type)
|
||||
if input_type in [mindspore.uint8, mindspore.uint16, mindspore.uint32, mindspore.uint64]:
|
||||
return Tensor(1, dtype=input_type)
|
||||
if input_type in [mindspore.int8, mindspore.int16, mindspore.int32, mindspore.int64]:
|
||||
return Tensor(-1, dtype=input_type)
|
||||
if input_type in [mindspore.float16, mindspore.float32, mindspore.float64]:
|
||||
return Tensor(0.01, dtype=input_type)
|
||||
else:
|
||||
if input_type == mindspore.bool_:
|
||||
return Tensor(np.array([[True, False], [False, True]]), dtype=input_type)
|
||||
if input_type in [mindspore.uint8, mindspore.uint16, mindspore.uint32, mindspore.uint64]:
|
||||
return Tensor(np.array([[1, 2, 3], [4, 5, 6]]), dtype=input_type)
|
||||
if input_type in [mindspore.int8, mindspore.int16, mindspore.int32, mindspore.int64]:
|
||||
return Tensor(np.array([[-1, 2, -3], [-4, 5, -6]]), dtype=input_type)
|
||||
if input_type in [mindspore.float16, mindspore.float32, mindspore.float64]:
|
||||
return Tensor(np.array([[1.0, -2.0, 3.0], [4.0, -5.0, 6.0]]), dtype=input_type)
|
||||
return Tensor(False, np.bool)
|
||||
|
||||
if __name__ == "__main__":
|
||||
net = TensorPrint()
|
||||
net(get_tensor('scalar', mindspore.bool_), get_tensor('scalar', mindspore.uint8),
|
||||
get_tensor('scalar', mindspore.int8), get_tensor('scalar', mindspore.uint16),
|
||||
get_tensor('scalar', mindspore.int16), get_tensor('scalar', mindspore.uint32),
|
||||
get_tensor('scalar', mindspore.int32), get_tensor('scalar', mindspore.uint64),
|
||||
get_tensor('scalar', mindspore.int64), get_tensor('scalar', mindspore.float16),
|
||||
get_tensor('scalar', mindspore.float32), get_tensor('scalar', mindspore.float64),
|
||||
get_tensor('array', mindspore.bool_), get_tensor('array', mindspore.uint8),
|
||||
get_tensor('array', mindspore.int8), get_tensor('array', mindspore.uint16),
|
||||
get_tensor('array', mindspore.int16), get_tensor('array', mindspore.uint32),
|
||||
get_tensor('array', mindspore.int32), get_tensor('array', mindspore.uint64),
|
||||
get_tensor('array', mindspore.int64), get_tensor('array', mindspore.float16),
|
||||
get_tensor('array', mindspore.float32), get_tensor('array', mindspore.float64))
|
|
@ -0,0 +1,74 @@
|
|||
# Copyright 2020 Huawei Technologies Co., Ltd
|
||||
#
|
||||
# Licensed 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.
|
||||
# ============================================================================
|
||||
import os
|
||||
import re
|
||||
import pytest
|
||||
|
||||
# Defines the expected value of tensor printout, corresponding to different data types.
|
||||
expect_scalar = {'Bool': 'True', 'UInt': '1', 'Int': '-1', 'Float16': '*.*******', 'Float32_64': '*.**'}
|
||||
expect_array = {'Bool': '\n[[ True False]\n [False True]]', 'UInt': '\n[[1 2 3]\n [4 5 6]]',
|
||||
'Int': '\n[[-1 2 -3]\n [-4 5 -6]]',
|
||||
'Float16': '\n[[ *.****e*** **.****e*** *.****e***]\n [ *.****e*** **.****e*** *.****e***]]',
|
||||
'Float32_64': '\n[[ *.********e*** **.********e*** *.********e***]\n ' \
|
||||
'[ *.********e*** **.********e*** *.********e***]]'}
|
||||
|
||||
def get_expect_value(res):
|
||||
if res[0] == '[1]':
|
||||
if res[1] == 'Bool':
|
||||
return expect_scalar['Bool']
|
||||
if res[1] in ['Uint8', 'Uint16', 'Uint32', 'Uint64']:
|
||||
return expect_scalar['UInt']
|
||||
if res[1] in ['Int8', 'Int16', 'Int32', 'Int64']:
|
||||
return expect_scalar['Int']
|
||||
if res[1] == 'Float16':
|
||||
return expect_scalar['Float16']
|
||||
if res[1] in ['Float32', 'Float64']:
|
||||
return expect_scalar['Float32_64']
|
||||
else:
|
||||
if res[1] == 'Bool':
|
||||
return expect_array['Bool']
|
||||
if res[1] in ['UInt8', 'UInt16', 'UInt32', 'UInt64']:
|
||||
return expect_array['UInt']
|
||||
if res[1] in ['Int8', 'Int16', 'Int32', 'Int64']:
|
||||
return expect_array['Int']
|
||||
if res[1] == 'Float16':
|
||||
return expect_array['Float16']
|
||||
if res[1] in ['Float32', 'Float64']:
|
||||
return expect_array['Float32_64']
|
||||
return 'None'
|
||||
|
||||
def num_to_asterisk(data):
|
||||
# Convert number and +/- to asterisk
|
||||
return re.sub(r'\d|\+|\-', '*', data.group())
|
||||
|
||||
@pytest.mark.level0
|
||||
@pytest.mark.platform_arm_ascend_training
|
||||
@pytest.mark.platform_x86_ascend_training
|
||||
@pytest.mark.env_onecard
|
||||
def test_tensor_print():
|
||||
path = os.path.split(os.path.realpath(__file__))[0]
|
||||
cmd = f"python {path}/tensor_print_utils.py"
|
||||
lines = os.popen(cmd).readlines()
|
||||
data = ''.join(lines)
|
||||
result = re.findall(r'Tensor[(]shape=(.*?), dtype=(.*?), value=(.*?)[)]', data, re.DOTALL)
|
||||
assert (result != []), "Output does not meet the requirements."
|
||||
for res in result:
|
||||
assert (len(res) == 3), "Output does not meet the requirements."
|
||||
expect = get_expect_value(res)
|
||||
value = res[2]
|
||||
if value.find('.'):
|
||||
# Convert decimals to asterisks, such as 0.01 --> *.** and 1.0e+2 --> *.*e**
|
||||
value = re.sub(r'-?\d+\.\d+|e[\+|\-]\d+', num_to_asterisk, value, re.DOTALL)
|
||||
assert (repr(value) == repr(expect)), repr("output: " + value + ", expect: " + expect)
|
Loading…
Reference in New Issue