Propagate half precision float through tflite
PiperOrigin-RevId: 247082214
This commit is contained in:
parent
289a0af9b0
commit
c58ddf2520
@ -252,6 +252,7 @@ cc_test(
|
||||
"//tensorflow/lite/kernels/internal:tensor_utils",
|
||||
"//tensorflow/lite/schema:schema_fbs",
|
||||
"//tensorflow/lite/testing:util",
|
||||
"//third_party/eigen3",
|
||||
"@com_google_googletest//:gtest",
|
||||
],
|
||||
)
|
||||
|
@ -172,6 +172,8 @@ const char* TfLiteTypeGetName(TfLiteType type) {
|
||||
return "COMPLEX64";
|
||||
case kTfLiteString:
|
||||
return "STRING";
|
||||
case kTfLiteFloat16:
|
||||
return "FLOAT16";
|
||||
}
|
||||
return "Unknown type";
|
||||
}
|
||||
|
@ -195,6 +195,11 @@ typedef struct {
|
||||
float re, im; // real and imaginary parts, respectively.
|
||||
} TfLiteComplex64;
|
||||
|
||||
// Half precision data type compatible with the C99 definition.
|
||||
typedef struct {
|
||||
uint16_t data;
|
||||
} TfLiteFloat16;
|
||||
|
||||
// Types supported by tensor
|
||||
typedef enum {
|
||||
kTfLiteNoType = 0,
|
||||
@ -207,6 +212,7 @@ typedef enum {
|
||||
kTfLiteInt16 = 7,
|
||||
kTfLiteComplex64 = 8,
|
||||
kTfLiteInt8 = 9,
|
||||
kTfLiteFloat16 = 10,
|
||||
} TfLiteType;
|
||||
|
||||
// Return the name of a given type, for error reporting purposes.
|
||||
@ -259,6 +265,8 @@ typedef union {
|
||||
int32_t* i32;
|
||||
int64_t* i64;
|
||||
float* f;
|
||||
// Placeholder for 16b float type. Use uint16* in the pointer union for now.
|
||||
TfLiteFloat16* f16;
|
||||
char* raw;
|
||||
const char* raw_const;
|
||||
uint8_t* uint8;
|
||||
|
@ -78,6 +78,7 @@ TEST(Types, TestTypeNames) {
|
||||
};
|
||||
EXPECT_EQ(type_name(kTfLiteNoType), "NOTYPE");
|
||||
EXPECT_EQ(type_name(kTfLiteFloat32), "FLOAT32");
|
||||
EXPECT_EQ(type_name(kTfLiteFloat16), "FLOAT16");
|
||||
EXPECT_EQ(type_name(kTfLiteInt16), "INT16");
|
||||
EXPECT_EQ(type_name(kTfLiteInt32), "INT32");
|
||||
EXPECT_EQ(type_name(kTfLiteUInt8), "UINT8");
|
||||
|
@ -61,9 +61,8 @@ TfLiteStatus ConvertTensorType(TensorType tensor_type, TfLiteType* type,
|
||||
*type = kTfLiteFloat32;
|
||||
break;
|
||||
case TensorType_FLOAT16:
|
||||
error_reporter->Report("Unimplemented data type float16 in tensor\n",
|
||||
tensor_type);
|
||||
return kTfLiteError;
|
||||
*type = kTfLiteFloat16;
|
||||
break;
|
||||
case TensorType_INT16:
|
||||
*type = kTfLiteInt16;
|
||||
break;
|
||||
|
@ -141,6 +141,13 @@ TEST_F(FlatbufferConversionsTest, TestConvertTensorType) {
|
||||
EXPECT_EQ(kTfLiteFloat32, type);
|
||||
}
|
||||
|
||||
TEST_F(FlatbufferConversionsTest, TestConvertTensorTypeFloat16) {
|
||||
TfLiteType type;
|
||||
EXPECT_EQ(kTfLiteOk,
|
||||
ConvertTensorType(TensorType_FLOAT16, &type, &mock_reporter_));
|
||||
EXPECT_EQ(kTfLiteFloat16, type);
|
||||
}
|
||||
|
||||
} // namespace tflite
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
@ -469,6 +469,9 @@ TfLiteStatus Subgraph::BytesRequired(TfLiteType type, const int* dims,
|
||||
case kTfLiteInt8:
|
||||
*bytes = sizeof(int8_t) * count;
|
||||
break;
|
||||
case kTfLiteFloat16:
|
||||
*bytes = sizeof(TfLiteFloat16) * count;
|
||||
break;
|
||||
default:
|
||||
ReportError(
|
||||
"Only float32, int8, int16, int32, int64, uint8, bool, complex64 "
|
||||
|
@ -60,6 +60,8 @@ TF_DataType GetTensorFlowDataType(TfLiteType type) {
|
||||
return TF_FLOAT;
|
||||
case kTfLiteFloat32:
|
||||
return TF_FLOAT;
|
||||
case kTfLiteFloat16:
|
||||
return TF_HALF;
|
||||
case kTfLiteInt16:
|
||||
return TF_INT16;
|
||||
case kTfLiteInt32:
|
||||
@ -83,6 +85,8 @@ TfLiteType GetTensorFlowLiteType(TF_DataType type) {
|
||||
switch (type) {
|
||||
case TF_FLOAT:
|
||||
return kTfLiteFloat32;
|
||||
case TF_HALF:
|
||||
return kTfLiteFloat16;
|
||||
case TF_INT16:
|
||||
return kTfLiteInt16;
|
||||
case TF_INT32:
|
||||
|
@ -101,9 +101,9 @@ TEST(UtilTest, CopyShapeAndType) {
|
||||
|
||||
EXPECT_EQ(
|
||||
CopyShapeAndType(&context, Tensor(tensorflow::DT_HALF, {1, 2}), &dst),
|
||||
kTfLiteError);
|
||||
EXPECT_EQ(context.error,
|
||||
"TF Lite does not support TensorFlow data type: half");
|
||||
kTfLiteOk);
|
||||
EXPECT_THAT(context.new_size, ElementsAre(1, 2));
|
||||
EXPECT_EQ(dst.type, kTfLiteFloat16);
|
||||
}
|
||||
|
||||
TEST(UtilTest, TypeConversionsFromTFLite) {
|
||||
|
@ -29,6 +29,9 @@ typedef NS_ENUM(NSUInteger, TFLTensorDataType) {
|
||||
/** 32-bit single precision floating point. */
|
||||
TFLTensorDataTypeFloat32,
|
||||
|
||||
/** 16-bit half precision floating point. */
|
||||
TFLTensorDataTypeFloat16,
|
||||
|
||||
/** 32-bit signed integer. */
|
||||
TFLTensorDataTypeInt32,
|
||||
|
||||
|
@ -366,6 +366,8 @@ static void TFLInterpreterErrorReporter(void *user_data, const char *format, va_
|
||||
switch (cTensorType) {
|
||||
case kTfLiteFloat32:
|
||||
return TFLTensorDataTypeFloat32;
|
||||
case kTfLiteFloat16:
|
||||
return TFLTensorDataTypeFloat16;
|
||||
case kTfLiteInt32:
|
||||
return TFLTensorDataTypeInt32;
|
||||
case kTfLiteUInt8:
|
||||
|
@ -62,6 +62,8 @@ inline TensorType TfLiteTypeToSchemaType(TfLiteType type) {
|
||||
return TensorType_FLOAT32; // TODO(aselle): Consider an error.
|
||||
case kTfLiteFloat32:
|
||||
return TensorType_FLOAT32;
|
||||
case kTfLiteFloat16:
|
||||
return TensorType_FLOAT16;
|
||||
case kTfLiteInt32:
|
||||
return TensorType_INT32;
|
||||
case kTfLiteUInt8:
|
||||
|
@ -30,6 +30,11 @@ limitations under the License.
|
||||
#include "tensorflow/lite/schema/schema_generated.h"
|
||||
#include "tensorflow/lite/util.h"
|
||||
|
||||
// TODO(b/132087118): move static_assert to c_api_internal when compiled with
|
||||
// C++.
|
||||
static_assert(sizeof(TfLiteFloat16) == sizeof(uint16_t),
|
||||
"Float 16 type must be 16 bits.");
|
||||
|
||||
namespace tflite {
|
||||
|
||||
namespace {
|
||||
|
@ -74,6 +74,10 @@ constexpr TfLiteType typeToTfLiteType<string>() {
|
||||
return kTfLiteString;
|
||||
}
|
||||
|
||||
template <>
|
||||
constexpr TfLiteType typeToTfLiteType<TfLiteFloat16>() {
|
||||
return kTfLiteFloat16;
|
||||
}
|
||||
// An interpreter for a graph of nodes that input and output from tensors.
|
||||
// Each node of the graph processes a set of input tensors and produces a
|
||||
// set of output Tensors. All inputs/output tensors are referenced by index.
|
||||
|
@ -17,6 +17,7 @@ limitations under the License.
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include "third_party/eigen3/Eigen/Core"
|
||||
#include "tensorflow/lite/core/api/error_reporter.h"
|
||||
#include "tensorflow/lite/kernels/internal/compatibility.h"
|
||||
#include "tensorflow/lite/kernels/kernel_util.h"
|
||||
@ -165,7 +166,7 @@ TEST(BasicInterpreter, CheckAllocate) {
|
||||
} cases[] = {
|
||||
{kTfLiteFloat32, sizeof(float)}, {kTfLiteInt32, sizeof(int32_t)},
|
||||
{kTfLiteUInt8, sizeof(uint8_t)}, {kTfLiteInt64, sizeof(int64_t)},
|
||||
{kTfLiteInt16, sizeof(int16_t)},
|
||||
{kTfLiteInt16, sizeof(int16_t)}, {kTfLiteFloat16, sizeof(TfLiteFloat16)},
|
||||
};
|
||||
|
||||
for (auto test : cases) {
|
||||
@ -238,6 +239,8 @@ TEST(BasicInterpreter, CheckResize) {
|
||||
const uint8_t uint8s[] = {3, 4};
|
||||
const int64_t int64s[] = {6, -7};
|
||||
const int16_t int16s[] = {8, -9};
|
||||
const Eigen::half float16s[] = {Eigen::half_impl::float_to_half_rtne(-3.f),
|
||||
Eigen::half_impl::float_to_half_rtne(-4.f)};
|
||||
|
||||
struct {
|
||||
TfLiteType type;
|
||||
@ -249,6 +252,8 @@ TEST(BasicInterpreter, CheckResize) {
|
||||
{kTfLiteUInt8, sizeof(uint8_t), reinterpret_cast<const char*>(uint8s)},
|
||||
{kTfLiteInt64, sizeof(int64_t), reinterpret_cast<const char*>(int64s)},
|
||||
{kTfLiteInt16, sizeof(int16_t), reinterpret_cast<const char*>(int16s)},
|
||||
{kTfLiteFloat16, sizeof(TfLiteFloat16),
|
||||
reinterpret_cast<const char*>(float16s)},
|
||||
};
|
||||
|
||||
for (auto test : cases) {
|
||||
@ -283,10 +288,8 @@ TEST(BasicInterpreter, CheckResize) {
|
||||
TEST(BasicInterpreter, CheckAlignment) {
|
||||
struct {
|
||||
TfLiteType type;
|
||||
} cases[] = {
|
||||
{kTfLiteFloat32}, {kTfLiteInt32}, {kTfLiteUInt8},
|
||||
{kTfLiteInt64}, {kTfLiteInt16},
|
||||
};
|
||||
} cases[] = {{kTfLiteFloat32}, {kTfLiteInt32}, {kTfLiteUInt8},
|
||||
{kTfLiteInt64}, {kTfLiteInt16}, {kTfLiteFloat16}};
|
||||
|
||||
for (auto test : cases) {
|
||||
Interpreter interpreter;
|
||||
|
@ -66,6 +66,11 @@ inline const float* GetTensorData(const TfLiteTensor* tensor) {
|
||||
return tensor != nullptr ? tensor->data.f : nullptr;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline const TfLiteFloat16* GetTensorData(const TfLiteTensor* tensor) {
|
||||
return tensor != nullptr ? tensor->data.f16 : nullptr;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline const uint8_t* GetTensorData(const TfLiteTensor* tensor) {
|
||||
return tensor != nullptr ? tensor->data.uint8 : nullptr;
|
||||
|
@ -20,7 +20,6 @@ limitations under the License.
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "tensorflow/core/platform/logging.h"
|
||||
#include "tensorflow/lite/interpreter.h"
|
||||
#include "tensorflow/lite/kernels/internal/tensor_utils.h"
|
||||
@ -568,6 +567,7 @@ class SingleOpTest : public ::testing::TestWithParam<string> {
|
||||
template <typename T>
|
||||
TensorType GetTensorType() {
|
||||
if (std::is_same<T, float>::value) return TensorType_FLOAT32;
|
||||
if (std::is_same<T, TfLiteFloat16>::value) return TensorType_FLOAT16;
|
||||
if (std::is_same<T, int32_t>::value) return TensorType_INT32;
|
||||
if (std::is_same<T, uint8_t>::value) return TensorType_UINT8;
|
||||
if (std::is_same<T, string>::value) return TensorType_STRING;
|
||||
|
@ -56,6 +56,8 @@ const char* TensorTypeName(TfLiteType type) {
|
||||
return "kTfLiteInt16";
|
||||
case kTfLiteComplex64:
|
||||
return "kTfLiteComplex64";
|
||||
case kTfLiteFloat16:
|
||||
return "kTfLiteFloat16";
|
||||
}
|
||||
return "(invalid)";
|
||||
}
|
||||
|
@ -32,6 +32,8 @@ int TfLiteTypeToPyArrayType(TfLiteType tf_lite_type) {
|
||||
switch (tf_lite_type) {
|
||||
case kTfLiteFloat32:
|
||||
return NPY_FLOAT32;
|
||||
case kTfLiteFloat16:
|
||||
return NPY_FLOAT16;
|
||||
case kTfLiteInt32:
|
||||
return NPY_INT32;
|
||||
case kTfLiteInt16:
|
||||
|
@ -61,6 +61,8 @@ inline TensorType TfLiteTypeToSchemaType(TfLiteType type) {
|
||||
return TensorType_FLOAT32; // TODO(b/129336260): No schema type for none.
|
||||
case kTfLiteFloat32:
|
||||
return TensorType_FLOAT32;
|
||||
case kTfLiteFloat16:
|
||||
return TensorType_FLOAT16;
|
||||
case kTfLiteInt32:
|
||||
return TensorType_INT32;
|
||||
case kTfLiteUInt8:
|
||||
|
@ -31,6 +31,7 @@ from tensorflow.python.training.saver import export_meta_graph as _export_meta_g
|
||||
# Map of tf.dtypes to TFLite types_flag_pb2.
|
||||
_MAP_TF_TO_TFLITE_TYPES = {
|
||||
dtypes.float32: _types_pb2.FLOAT,
|
||||
dtypes.float16: _types_pb2.FLOAT16,
|
||||
dtypes.int32: _types_pb2.INT32,
|
||||
dtypes.int64: _types_pb2.INT64,
|
||||
dtypes.string: _types_pb2.STRING,
|
||||
|
@ -50,6 +50,8 @@ class UtilTest(test_util.TensorFlowTestCase):
|
||||
self.assertEqual(
|
||||
util.convert_dtype_to_tflite_type(dtypes.complex64),
|
||||
_types_pb2.COMPLEX64)
|
||||
self.assertEqual(
|
||||
util.convert_dtype_to_tflite_type(dtypes.half), _types_pb2.FLOAT16)
|
||||
with self.assertRaises(ValueError):
|
||||
util.convert_dtype_to_tflite_type(dtypes.bool)
|
||||
|
||||
|
@ -223,6 +223,7 @@ enum class ArrayDataType : uint8 {
|
||||
kUint64, // 10
|
||||
kString,
|
||||
kComplex64,
|
||||
kFloat16,
|
||||
};
|
||||
|
||||
// Compile-time logic to map ArrayDataType to the corresponding C++ scalar type
|
||||
|
@ -46,4 +46,7 @@ enum IODataType {
|
||||
|
||||
// Int8, quantized based on QuantizationParameters in schema.
|
||||
INT8 = 9;
|
||||
|
||||
// Half precision float, not quantized.
|
||||
FLOAT16 = 10;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user