mirror of https://github.com/alibaba/MNN.git
256 lines
8.1 KiB
C++
256 lines
8.1 KiB
C++
//
|
|
// TensorUtils.cpp
|
|
// MNN
|
|
//
|
|
// Created by MNN on 2018/08/11.
|
|
// Copyright © 2018, Alibaba Group Holding Limited
|
|
//
|
|
|
|
#include "core/TensorUtils.hpp"
|
|
#include <math.h>
|
|
#include <stdio.h>
|
|
#include <float.h>
|
|
#include <cmath>
|
|
#include <cstring>
|
|
#include "core/Backend.hpp"
|
|
#include "core/Macro.h"
|
|
|
|
namespace MNN {
|
|
Tensor::InsideDescribe* TensorUtils::getDescribe(const Tensor* tensor) {
|
|
return tensor->mDescribe;
|
|
}
|
|
|
|
void TensorUtils::copyShape(const Tensor* source, Tensor* dest, bool copyFormat) {
|
|
auto& ob = dest->buffer();
|
|
auto& ib = source->buffer();
|
|
ob.dimensions = ib.dimensions;
|
|
::memcpy(ob.dim, ib.dim, ib.dimensions * sizeof(halide_dimension_t));
|
|
if (copyFormat) {
|
|
getDescribe(dest)->dimensionFormat = getDescribe(source)->dimensionFormat;
|
|
}
|
|
}
|
|
|
|
void TensorUtils::setLinearLayout(Tensor* tensor) {
|
|
auto& buffer = tensor->buffer();
|
|
int size = 1;
|
|
for (int i = 0; i < buffer.dimensions; ++i) {
|
|
auto index = buffer.dimensions - i - 1;
|
|
auto extent = buffer.dim[index].extent;
|
|
if (1 == index && tensor->mDescribe->dimensionFormat == MNN_DATA_FORMAT_NC4HW4) {
|
|
extent = ROUND_UP(extent, 4);
|
|
}
|
|
buffer.dim[index].stride = size;
|
|
size *= extent;
|
|
}
|
|
}
|
|
|
|
void TensorUtils::clearHandleData(Tensor* tensor) {
|
|
if (tensor->buffer().type.code != halide_type_handle) {
|
|
return;
|
|
}
|
|
auto handle = tensor->host<void*>();
|
|
if (nullptr == handle) {
|
|
return;
|
|
}
|
|
|
|
MNN_ASSERT(tensor->mDescribe->handleFreeFunction != nullptr);
|
|
for (int i = 0; i < tensor->elementSize(); ++i) {
|
|
if (nullptr != handle[i]) {
|
|
tensor->mDescribe->handleFreeFunction(handle[i]);
|
|
handle[i] = nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
static const Tensor* createHostPlanar(const Tensor* source) {
|
|
// check
|
|
auto bnType = MNN_FORWARD_CPU;
|
|
auto tensorBackend = TensorUtils::getDescribe(source)->backend;
|
|
if(tensorBackend){
|
|
bnType = tensorBackend->type();
|
|
}
|
|
bool device = bnType != MNN_FORWARD_CPU;
|
|
bool chunky = TensorUtils::getDescribe(source)->dimensionFormat == MNN_DATA_FORMAT_NC4HW4;
|
|
|
|
// no convert needed
|
|
if (!device && !chunky) {
|
|
return source;
|
|
}
|
|
|
|
// convert
|
|
if (chunky) {
|
|
Tensor* result = source->createHostTensorFromDevice(source, false);
|
|
if (result->getDimensionType() == MNN::Tensor::TENSORFLOW) {
|
|
TensorUtils::getDescribe(result)->dimensionFormat = MNN_DATA_FORMAT_NHWC;
|
|
} else {
|
|
TensorUtils::getDescribe(result)->dimensionFormat = MNN_DATA_FORMAT_NCHW;
|
|
}
|
|
TensorUtils::setLinearLayout(result);
|
|
|
|
if (device) {
|
|
source->copyToHostTensor(result);
|
|
} else {
|
|
Backend::Info info;
|
|
info.type = MNN_FORWARD_CPU;
|
|
auto backend = MNNGetExtraBackendCreator(MNN_FORWARD_CPU)->onCreate(info);
|
|
backend->onCopyBuffer(source, result);
|
|
delete backend;
|
|
}
|
|
return result;
|
|
} else {
|
|
return source->createHostTensorFromDevice(source, true);
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
static void copyTensorToFloat(const Tensor* source, double* dest) {
|
|
auto srcData = source->host<T>();
|
|
auto size = source->elementSize();
|
|
for (int i = 0; i < size; ++i) {
|
|
dest[i] = srcData[i];
|
|
}
|
|
}
|
|
|
|
static bool equals(const double* pa, const double* pb, size_t size, double tolerance, double epsilon, bool overall,
|
|
bool prints) {
|
|
// get max if using overall torelance
|
|
double max = fabs(pb[0]);
|
|
if (overall) {
|
|
for (int i = 1; i < size; i++) {
|
|
max = std::max(max, fabs(pb[i]));
|
|
}
|
|
}
|
|
|
|
// compare
|
|
for (int i = 0; i < size; i++) {
|
|
float va = pa[i], vb = pb[i];
|
|
if (std::isinf(va) && std::isinf(vb)) {
|
|
continue;
|
|
}
|
|
if (fabs(va) < epsilon && fabs(vb) < epsilon) {
|
|
continue;
|
|
}
|
|
float div = overall ? max : fabsf(vb);
|
|
if (fabsf(va - vb) / div > tolerance) {
|
|
if (prints) {
|
|
MNN_PRINT("%d: %f != %f\n", i, va, vb);
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool TensorUtils::compareTensors(const Tensor* compare, const Tensor* expect, float tolerance, bool overall,
|
|
bool printsErrors, bool printsTensors) {
|
|
// type
|
|
if (compare->getType().code != expect->getType().code || compare->getType().bits != expect->getType().bits) {
|
|
if (printsErrors) {
|
|
MNN_PRINT("NOT equal in type: %d/%d - %d/%d.\n", compare->getType().code, compare->getType().bits,
|
|
expect->getType().code, expect->getType().bits);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// dimensions
|
|
if (compare->dimensions() != expect->dimensions()) {
|
|
if (printsErrors) {
|
|
MNN_PRINT("NOT equal in dimensions: %d - %d.\n", compare->dimensions(), expect->dimensions());
|
|
}
|
|
return false;
|
|
}
|
|
for (int i = 0; i < compare->dimensions(); i++) {
|
|
if (compare->length(i) == expect->length(i)) {
|
|
continue;
|
|
}
|
|
if (printsErrors) {
|
|
MNN_PRINT("NOT equal in dimensions[%d]: %d - %d.\n", i, compare->length(i), expect->length(i));
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// convert to host if needed
|
|
auto a = createHostPlanar(compare), b = createHostPlanar(expect);
|
|
|
|
// get value as double
|
|
auto size = expect->elementSize();
|
|
std::vector<double> expectValue(expect->elementSize(), 0.0f);
|
|
std::vector<double> compareValue(compare->elementSize(), 0.0f);
|
|
|
|
auto result = false;
|
|
if (b->buffer().type.code == halide_type_uint) {
|
|
switch (b->buffer().type.bits) {
|
|
case 8:
|
|
copyTensorToFloat<uint8_t>(a, compareValue.data());
|
|
copyTensorToFloat<uint8_t>(b, expectValue.data());
|
|
break;
|
|
case 16:
|
|
copyTensorToFloat<uint16_t>(a, compareValue.data());
|
|
copyTensorToFloat<uint16_t>(b, expectValue.data());
|
|
break;
|
|
case 32:
|
|
copyTensorToFloat<uint32_t>(a, compareValue.data());
|
|
copyTensorToFloat<uint32_t>(b, expectValue.data());
|
|
break;
|
|
case 64:
|
|
copyTensorToFloat<uint64_t>(a, compareValue.data());
|
|
copyTensorToFloat<uint64_t>(b, expectValue.data());
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} else if (b->buffer().type.code == halide_type_int) {
|
|
switch (b->buffer().type.bits) {
|
|
case 8:
|
|
copyTensorToFloat<int8_t>(a, compareValue.data());
|
|
copyTensorToFloat<int8_t>(b, expectValue.data());
|
|
break;
|
|
case 16:
|
|
copyTensorToFloat<int16_t>(a, compareValue.data());
|
|
copyTensorToFloat<int16_t>(b, expectValue.data());
|
|
break;
|
|
case 32:
|
|
copyTensorToFloat<int32_t>(a, compareValue.data());
|
|
copyTensorToFloat<int32_t>(b, expectValue.data());
|
|
break;
|
|
case 64:
|
|
copyTensorToFloat<int64_t>(a, compareValue.data());
|
|
copyTensorToFloat<int64_t>(b, expectValue.data());
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} else if (b->buffer().type.code == halide_type_float) {
|
|
switch (b->buffer().type.bits) {
|
|
case 32:
|
|
copyTensorToFloat<float>(a, compareValue.data());
|
|
copyTensorToFloat<float>(b, expectValue.data());
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} else {
|
|
if (printsErrors) {
|
|
MNN_PRINT("unsupported data type.");
|
|
}
|
|
}
|
|
auto epsilon = FLT_EPSILON;
|
|
if ((NULL != compareValue.data()) && (NULL != expectValue.data())) {
|
|
result = equals(compareValue.data(), expectValue.data(), size, tolerance, epsilon, overall, printsErrors);
|
|
}
|
|
if (!result && printsTensors) {
|
|
a->print();
|
|
b->print();
|
|
}
|
|
|
|
// clean up
|
|
if (a != compare) {
|
|
delete a;
|
|
}
|
|
if (b != expect) {
|
|
delete b;
|
|
}
|
|
return result;
|
|
}
|
|
} // namespace MNN
|