diff --git a/inference-engine/src/gna_plugin/backend/make_pwl.cpp b/inference-engine/src/gna_plugin/backend/make_pwl.cpp index e0f71bc7fc7..c4f98e88c2b 100644 --- a/inference-engine/src/gna_plugin/backend/make_pwl.cpp +++ b/inference-engine/src/gna_plugin/backend/make_pwl.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include "runtime/pwl.h" @@ -58,7 +59,72 @@ static void insert_extra_pwl_segments(std::vector& gna_pwl, } } -void make_gna_pwl(const DnnActivation fun, +static void print_segments_header(const DnnActivation& fun) { + gnalog() << "=========================== " << intel_dnn_activation_name[fun] << + " segments ===========================\n"; + gnalog() << std::setw(12) << std::setfill(' ') << "x" << std::setw(12) << std::setfill(' ') << + "y" << std::setw(12) << std::setfill(' ') << "slope" << std::endl; +} + +static void print_segment(double x, double y, double slope) { + gnalog() << std::setw(12) << std::setfill(' ') << x << std::setw(12) << std::setfill(' ') << + y << std::setw(12) << std::setfill(' ') << slope << std::endl; +} + +static std::vector create_multisegment_gna_pwl(const std::vector& pwl, + double in_scale, + double out_scale, + double min_x_val, + double max_x_val, + double min_y_val, + double max_y_val, + bool fake_quantize, + bool add_last_seg) { + std::vector gna_pwl; + + int32_t xbase = static_cast (INT32_MIN & XBASEMASK); // zero out the 2 lsb + int16_t ybase = FLOAT_TO_INT16(min_y_val * out_scale); + int16_t slope = 0; + gna_pwl.push_back({xbase, ybase, slope}); + print_segment(xbase / in_scale, min_y_val, slope); + + if (!fake_quantize && min_x_val > INT32_MIN / in_scale) { + auto s = gna_slope(pwl[0].m, in_scale, out_scale); + slope = FLOAT_TO_INT16(s.slope * s.slope_scale); + xbase = (static_cast(min_x_val * in_scale) & XBASEMASK) | s.slope_scale_index; + ybase = FLOAT_TO_INT16(min_y_val * out_scale); + gna_pwl.push_back({xbase, ybase, slope}); + print_segment(min_x_val, min_y_val, pwl[0].m); + } + + for (uint32_t i = 0; i < pwl.size(); ++i) { + if (!fake_quantize && (pwl[i].alpha <= min_x_val || + pwl[i].alpha <= INT32_MIN / in_scale || + pwl[i].alpha >= max_x_val)) { + continue; + } + + auto s = gna_slope(pwl[i].m, in_scale, out_scale); + xbase = ((static_cast (in_scale * pwl[i].alpha)) & XBASEMASK) | s.slope_scale_index; + ybase = FLOAT_TO_INT16(pwl[i].beta * out_scale); + slope = FLOAT_TO_INT16(s.slope * s.slope_scale); + gna_pwl.push_back({xbase, ybase, slope}); + print_segment(pwl[i].alpha, pwl[i].beta, pwl[i].m); + } + + if (!fake_quantize && add_last_seg) { + // insert extra segment for xvalues > u_bound + xbase = static_cast(max_x_val * in_scale) & XBASEMASK; + ybase = FLOAT_TO_INT16(max_y_val * out_scale); + slope = 0; + gna_pwl.push_back({xbase, ybase, slope}); + print_segment(max_x_val, max_y_val, slope); + } + + return gna_pwl; +} + +void make_gna_pwl(const DnnActivation& fun, const std::vector& pwl, const double l_bound, const double u_bound, @@ -73,199 +139,56 @@ void make_gna_pwl(const DnnActivation fun, gnalog() << "make_gna_pwl\n"; gnalog() << " in_scale " << in_scale << "\n"; gnalog() << " out_scale " << out_scale << "\n"; + print_segments_header(fun); switch (fun) { case kActSigmoid: case kActTanh: case kActSoftSign: { - auto n_segments = static_cast (pwl_size) + 1; - gna_pwl.resize(n_segments); // insert extra segment for x values < l_bound - gna_pwl[0].xBase = static_cast (INT32_MIN & XBASEMASK); // zero out the 2 lsb + double min_x_val; + double min_y_val; if (fun == kActSigmoid) { - gnalog() << "=========================== Sigmoid Segments ===========================\n"; - auto minVal = (fun.fqParams.set && *fun.fqParams.input_low > 0) ? FLOAT_TO_INT16(*fun.fqParams.input_low * out_scale) : 0; - gna_pwl[0].yBase = gna_pwl[1].yBase = minVal; - gna_pwl[1].xBase = (static_cast (in_scale * (-pwl[0].b / pwl[0].m))) & XBASEMASK; + min_y_val = fun.fqParams.set ? pwl[0].beta : 0; + min_x_val = -pwl[0].b / pwl[0].m; } else if (fun == kActTanh) { - gnalog() << "=========================== Tanh Segments ===========================\n"; - auto minVal = (fun.fqParams.set && *fun.fqParams.input_low > -1) ? FLOAT_TO_INT16(*fun.fqParams.input_low * out_scale) : - static_cast(-1.0 * out_scale); - gna_pwl[0].yBase = gna_pwl[1].yBase = minVal; - gna_pwl[1].xBase = (static_cast (in_scale * (-1.0 - pwl[0].b) / pwl[0].m)) & XBASEMASK; + min_y_val = fun.fqParams.set ? pwl[0].beta : -1.0; + min_x_val = (-1.0 - pwl[0].b) / pwl[0].m; } else { - gnalog() << "=========================== SoftSign Segments ===========================\n"; - auto minVal = (fun.fqParams.set && *fun.fqParams.input_low > -1) ? FLOAT_TO_INT16(*fun.fqParams.input_low * out_scale) : - static_cast(-1.0 * out_scale); - gna_pwl[0].yBase = gna_pwl[1].yBase = minVal; - gna_pwl[1].xBase = (static_cast (in_scale * (-1.0 - pwl[0].b) / pwl[0].m)) & XBASEMASK; + min_y_val = fun.fqParams.set ? pwl[0].beta : -1.0; + min_x_val = (-1.0 - pwl[0].b) / pwl[0].m; } - gna_pwl[0].slope = 0; - - gnalog() << (gna_pwl[0].xBase) / in_scale - << " " << (gna_pwl[0].yBase) / out_scale - << " " << 0.0 - << "\n"; - - s = gna_slope(pwl[0].m, in_scale, out_scale); - gna_pwl[1].slope = FLOAT_TO_INT16(s.slope * s.slope_scale); - gna_pwl[1].xBase = gna_pwl[1].xBase | s.slope_scale_index; - - gnalog() << (gna_pwl[1].xBase/in_scale) - << " " << (gna_pwl[1].yBase) / out_scale - << " " << pwl[0].m - << "\n"; - - for (uint32_t i = 1; i < pwl_size - 1; ++i) { - s = gna_slope(pwl[i].m, in_scale, out_scale); - gna_pwl[i + 1].xBase = (static_cast (in_scale * pwl[i].alpha)) & XBASEMASK; - gna_pwl[i + 1].yBase = FLOAT_TO_INT16(pwl[i].beta * out_scale); - gna_pwl[i + 1].slope = FLOAT_TO_INT16(s.slope * s.slope_scale); - gna_pwl[i + 1].xBase = gna_pwl[i + 1].xBase | s.slope_scale_index; - - gnalog() << (pwl[i].alpha) - << " " << pwl[i].beta - << " " << pwl[i].m - << "\n"; - } - // insert extra segment for xvalues > u_bound - auto maxVal = (fun.fqParams.set && *fun.fqParams.input_high <= 1) ? *fun.fqParams.input_high : 1.0; - gna_pwl[n_segments - 1].xBase = - ((uint32_t) (in_scale * (1.0 - pwl[pwl_size - 2].b) / pwl[pwl_size - 2].m)) & XBASEMASK; - gna_pwl[n_segments - 1].yBase = FLOAT_TO_INT16(maxVal * out_scale); - gna_pwl[n_segments - 1].slope = 0; - - gnalog() << (gna_pwl[n_segments - 1].xBase / in_scale) - << " " << 1.0 - << " " << 0.0 - << "\n"; + double max_y_val = fun.fqParams.set ? pwl.back().beta : 1.0; + double max_x_val = fun.srcFQParams.set ? u_bound : (1.0 - pwl[pwl_size - 2].b) / pwl[pwl_size - 2].m; + gna_pwl = create_multisegment_gna_pwl(pwl, in_scale, out_scale, min_x_val, max_x_val, min_y_val, max_y_val, + fun.fqParams.set, true); break; } case kActExp: { - auto n_segments = static_cast (pwl_size) + 1; - gna_pwl.resize(n_segments); - // insert extra segment for x values < l_bound - gna_pwl[0].xBase = static_cast (INT32_MIN & XBASEMASK); // zero out the 2 lsb - gnalog() << "=========================== Exp Segments ===========================\n"; - gna_pwl[0].yBase = gna_pwl[1].yBase = 0; - gna_pwl[1].xBase = (static_cast (in_scale * (-pwl[0].b / pwl[0].m))) & XBASEMASK; - gna_pwl[0].slope = 0; - - gnalog() << (gna_pwl[0].xBase) / in_scale - << " " << (gna_pwl[0].yBase) / out_scale - << " " << 0.0 - << "\n"; - - s = gna_slope(pwl[0].m, in_scale, out_scale); - gna_pwl[1].slope = FLOAT_TO_INT16(s.slope * s.slope_scale); - gna_pwl[1].xBase = gna_pwl[1].xBase | s.slope_scale_index; - - gnalog() << ((int32_t)(gna_pwl[1].xBase & XBASEMASK) / in_scale) - << " " << (gna_pwl[1].yBase) / out_scale - << " " << pwl[0].m - << "\n"; - - for (uint32_t i = 1; i < pwl_size - 1; ++i) { - s = gna_slope(pwl[i].m, in_scale, out_scale); - gna_pwl[i + 1].xBase = (static_cast (in_scale * pwl[i].alpha)) & XBASEMASK; - gna_pwl[i + 1].yBase = FLOAT_TO_INT16(pwl[i].beta * out_scale); - gna_pwl[i + 1].slope = FLOAT_TO_INT16(s.slope * s.slope_scale); - gna_pwl[i + 1].xBase = gna_pwl[i + 1].xBase | s.slope_scale_index; - - gnalog() << (pwl[i].alpha) - << " " << pwl[i].beta - << " " << pwl[i].m - << "\n"; - } - // insert extra segment for xvalues > u_bound - gna_pwl[n_segments - 1].xBase = - ((uint32_t)(in_scale * (y_max/out_scale - pwl[pwl_size - 2].b) / pwl[pwl_size - 2].m)) & XBASEMASK; - gna_pwl[n_segments - 1].yBase = y_max; - gna_pwl[n_segments - 1].slope = 0; - - gnalog() << (gna_pwl[n_segments - 1].xBase / in_scale) - << " " << 1.0 - << " " << 0.0 - << "\n"; + double min_x_val = -pwl[0].b / pwl[0].m; + double max_x_val = (y_max/out_scale - pwl[pwl_size - 2].b) / pwl[pwl_size - 2].m; + double min_y_val = fun.fqParams.set ? pwl[0].beta : 0; + double max_y_val = fun.fqParams.set ? pwl.front().beta : y_max / out_scale; + gna_pwl = create_multisegment_gna_pwl(pwl, in_scale, out_scale, min_x_val, max_x_val, min_y_val, max_y_val, + fun.fqParams.set, true); break; } case kActLog: { - auto n_segments = static_cast (pwl_size); - gna_pwl.resize(n_segments); - // insert extra segment for x values < l_bound - gna_pwl[0].xBase = static_cast (INT32_MIN & XBASEMASK); // zero out the 2 lsb - gnalog() << "=========================== Log Segments ===========================\n"; - gna_pwl[0].yBase = gna_pwl[1].yBase = y_min; - gna_pwl[1].xBase = (static_cast (1 + ~XBASEMASK)); // smallest representable value - gna_pwl[0].slope = 0; - - gnalog() << gna_pwl[0].xBase / in_scale - << " " << (gna_pwl[0].yBase) / out_scale - << " " << 0.0 - << "\n"; - - s = gna_slope(pwl[0].m, in_scale, out_scale); - gna_pwl[1].slope = FLOAT_TO_INT16(s.slope * s.slope_scale); - gna_pwl[1].xBase = gna_pwl[1].xBase | s.slope_scale_index; - - gnalog() << ((int32_t)(gna_pwl[1].xBase & XBASEMASK) / in_scale) - << " " << (gna_pwl[1].yBase) / out_scale - << " " << pwl[0].m - << "\n"; - - for (uint32_t i = 1; i < pwl_size - 1; ++i) { - s = gna_slope(pwl[i].m, in_scale, out_scale); - gna_pwl[i + 1].xBase = (static_cast (in_scale * pwl[i].alpha)) & XBASEMASK; - gna_pwl[i + 1].yBase = FLOAT_TO_INT16(pwl[i].beta * out_scale); - gna_pwl[i + 1].slope = FLOAT_TO_INT16(s.slope * s.slope_scale); - gna_pwl[i + 1].xBase = gna_pwl[i + 1].xBase | s.slope_scale_index; - - gnalog() << (pwl[i].alpha) - << " " << pwl[i].beta - << " " << pwl[i].m - << "\n"; - } + double min_x_val = 1 + ~XBASEMASK; + double max_x_val = INT32_MAX / in_scale; + double min_y_val = y_min / out_scale; + double max_y_val = y_max / out_scale; + gna_pwl = create_multisegment_gna_pwl(pwl, in_scale, out_scale, min_x_val, max_x_val, min_y_val, max_y_val, + fun.fqParams.set, false); break; } case kActNegLog: case kActNegHalfLog: { - auto n_segments = static_cast (pwl_size); - gna_pwl.resize(n_segments); - // insert extra segment for x values < l_bound - gna_pwl[0].xBase = static_cast (INT32_MIN & XBASEMASK); // zero out the 2 lsb - if (fun == kActNegHalfLog) - gnalog() << "=========================== NegHalfLog Segments ===========================\n"; - else - gnalog() << "=========================== NegLog Segments ===========================\n"; - gna_pwl[0].yBase = gna_pwl[1].yBase = y_max; - gna_pwl[1].xBase = (static_cast (1 + ~XBASEMASK)); // smallest representable value - gna_pwl[0].slope = 0; - - gnalog() << gna_pwl[0].xBase / in_scale - << " " << (gna_pwl[0].yBase) / out_scale - << " " << 0.0 - << "\n"; - - s = gna_slope(pwl[0].m, in_scale, out_scale); - gna_pwl[1].slope = FLOAT_TO_INT16(s.slope * s.slope_scale); - gna_pwl[1].xBase = gna_pwl[1].xBase | s.slope_scale_index; - - gnalog() << ((int32_t)(gna_pwl[1].xBase & XBASEMASK) / in_scale) - << " " << (gna_pwl[1].yBase) / out_scale - << " " << pwl[0].m - << "\n"; - - for (uint32_t i = 1; i < pwl_size - 1; ++i) { - s = gna_slope(pwl[i].m, in_scale, out_scale); - gna_pwl[i + 1].xBase = (static_cast (in_scale * pwl[i].alpha)) & XBASEMASK; - gna_pwl[i + 1].yBase = FLOAT_TO_INT16(pwl[i].beta * out_scale); - gna_pwl[i + 1].slope = FLOAT_TO_INT16(s.slope * s.slope_scale); - gna_pwl[i + 1].xBase = gna_pwl[i + 1].xBase | s.slope_scale_index; - - gnalog() << (pwl[i].alpha) - << " " << pwl[i].beta - << " " << pwl[i].m - << "\n"; - } + double min_x_val = 1 + ~XBASEMASK; + double max_x_val = INT32_MAX / in_scale; + double min_y_val = y_max / out_scale; + double max_y_val = y_min / out_scale; + gna_pwl = create_multisegment_gna_pwl(pwl, in_scale, out_scale, min_x_val, max_x_val, min_y_val, max_y_val, + fun.fqParams.set, false); break; } case kActRelu: @@ -273,10 +196,6 @@ void make_gna_pwl(const DnnActivation fun, auto n_segments = 2; gna_pwl.resize(n_segments); - if (fun == kActRelu) - gnalog() << "=========================== ReLU Segments ===========================\n"; - else - gnalog() << "=========================== LeakyReLU Segments ======================\n"; int32_t x_lower = INT32_MIN; int32_t x_upper = INT32_MAX; int32_t y_lower = y_min; @@ -297,19 +216,16 @@ void make_gna_pwl(const DnnActivation fun, gna_pwl[0].xBase = (x_lower & XBASEMASK) | s.slope_scale_index; // zero out the 2 lsb gna_pwl[0].slope = FLOAT_TO_INT16(s.slope * s.slope_scale); - gnalog() << (int32_t)(gna_pwl[0].xBase & XBASEMASK) / in_scale - << " " << gna_pwl[0].yBase / out_scale - << " " << (gna_pwl[0].slope * in_scale) / (out_scale*s.slope_scale) - << "\n"; + print_segment((int32_t)(gna_pwl[0].xBase & XBASEMASK) / in_scale, + gna_pwl[0].yBase / out_scale, + (gna_pwl[0].slope * in_scale) / (out_scale*s.slope_scale)); + gna_pwl[1].xBase = 0; gna_pwl[1].yBase = 0; s = gna_slope(1.0, in_scale, out_scale); gna_pwl[1].slope = FLOAT_TO_INT16(s.slope * s.slope_scale); gna_pwl[1].xBase = gna_pwl[1].xBase | s.slope_scale_index; - gnalog() << 0.0 - << " " << 0.0 - << " " << (gna_pwl[1].slope * in_scale) / (out_scale*s.slope_scale) - << "\n"; + print_segment(0.0, 0.0, (gna_pwl[1].slope * in_scale) / (out_scale*s.slope_scale)); if (fun.fqParams.set) { // need a right segment gna_pwl.push_back({ @@ -317,10 +233,7 @@ void make_gna_pwl(const DnnActivation fun, y_upper, 0 }); - gnalog() << (x_upper & XBASEMASK) / in_scale - << " " << gna_pwl[n_segments].yBase / out_scale - << " " << 0 - << "\n"; + print_segment((x_upper & XBASEMASK) / in_scale, gna_pwl[n_segments].yBase / out_scale, 0.0); } break; } @@ -328,34 +241,28 @@ void make_gna_pwl(const DnnActivation fun, auto n_segments = 3; gna_pwl.resize(n_segments); - gnalog() << "=========================== Sign Segments ===========================\n"; int32_t x_lower = INT32_MIN; int16_t y_lower = static_cast(-1.0 * out_scale); gna_pwl[0].yBase = y_lower; gna_pwl[0].xBase = (x_lower & XBASEMASK); // zero out the 2 lsb gna_pwl[0].slope = 0; - gnalog() << gna_pwl[0].xBase / in_scale - << " " << gna_pwl[0].yBase / out_scale - << " " << (gna_pwl[0].slope * in_scale) / (out_scale*s.slope_scale) - << "\n"; + print_segment(gna_pwl[0].xBase / in_scale, gna_pwl[0].yBase / out_scale, + (gna_pwl[0].slope * in_scale) / (out_scale*s.slope_scale)); gna_pwl[1].xBase = -1; gna_pwl[1].yBase = 0; gna_pwl[1].slope = 0; gna_pwl[1].xBase = gna_pwl[1].xBase & XBASEMASK; - gnalog() << gna_pwl[1].xBase / in_scale - << " " << gna_pwl[1].yBase / out_scale - << " " << (gna_pwl[1].slope * in_scale) / (out_scale*s.slope_scale) - << "\n"; + print_segment(gna_pwl[1].xBase / in_scale, gna_pwl[1].yBase / out_scale, + (gna_pwl[1].slope * in_scale) / (out_scale*s.slope_scale)); + gna_pwl[2].xBase = 1 + ~XBASEMASK; // smallest representable positive number gna_pwl[2].yBase = static_cast(1.0 * out_scale); s = gna_slope(1.0, in_scale, out_scale); gna_pwl[2].slope = 0; gna_pwl[2].xBase = gna_pwl[2].xBase & XBASEMASK; - gnalog() << gna_pwl[2].xBase / in_scale - << " " << gna_pwl[2].yBase / out_scale - << " " << (gna_pwl[2].slope * in_scale) / (out_scale*s.slope_scale) - << "\n"; + print_segment(gna_pwl[2].xBase / in_scale, gna_pwl[2].yBase / out_scale, + (gna_pwl[2].slope * in_scale) / (out_scale*s.slope_scale)); break; } case kActIdentity: @@ -373,7 +280,6 @@ void make_gna_pwl(const DnnActivation fun, } auto n_segments = 2; if (fun == kActKaldiLstmClipping) { - gnalog() << "=========================== Clipping Segments ===========================\n"; if (x_lower < l_bound * in_scale) { if (y_lower < l_bound * out_scale) { x_lower = FLOAT_TO_INT32(l_bound * in_scale); @@ -391,42 +297,32 @@ void make_gna_pwl(const DnnActivation fun, } } } else if (fun == kActIdentity) { - gnalog() << "=========================== Identity Segments ===========================\n"; if (x_lower < y_lower * in_scale / out_scale) x_lower = FLOAT_TO_INT32(y_lower * in_scale / out_scale); if (x_upper > y_upper * in_scale / out_scale) x_upper = FLOAT_TO_INT32(y_upper * in_scale / out_scale); if (y_lower < x_lower * out_scale / in_scale) y_lower = FLOAT_TO_INT16(x_lower * out_scale / in_scale); if (y_upper > x_upper * out_scale / in_scale) y_upper = FLOAT_TO_INT16(x_upper * out_scale / in_scale); - } else if (fun == kActFakeQuantize) { - gnalog() << "=========================== Fake Quantize Segments ===========================\n"; } + gna_pwl.resize(n_segments); gna_pwl[0].xBase = INT32_MIN & XBASEMASK; // zero out the 2 lsb gna_pwl[0].yBase = y_lower; gna_pwl[0].slope = 0; - gnalog() << gna_pwl[0].xBase / in_scale - << " " << gna_pwl[0].yBase / out_scale - << " " << 0 - << "\n"; + print_segment(gna_pwl[0].xBase / in_scale, gna_pwl[0].yBase / out_scale, 0.0); gna_pwl[1].xBase = x_lower & XBASEMASK; // zero out the 2 lsb gna_pwl[1].yBase = y_lower; s = gna_slope(1.0, in_scale, out_scale); gna_pwl[1].slope = FLOAT_TO_INT16(s.slope * s.slope_scale); gna_pwl[1].xBase = gna_pwl[1].xBase | s.slope_scale_index; - gnalog() << (int32_t)(gna_pwl[1].xBase & XBASEMASK) / in_scale - << " " << gna_pwl[1].yBase / out_scale - << " " << 1.0 - << "\n"; + print_segment((int32_t)(gna_pwl[1].xBase & XBASEMASK) / in_scale, gna_pwl[1].yBase / out_scale, 1.0); + if (INT32_MAX > x_upper) { // need a right segment gna_pwl.push_back({ static_cast(x_upper & XBASEMASK), // zero out the 2 lsb y_upper, 0 }); - gnalog() << (x_upper & XBASEMASK) / in_scale - << " " << gna_pwl[n_segments].yBase / out_scale - << " " << 0 - << "\n"; + print_segment((x_upper & XBASEMASK) / in_scale, gna_pwl[n_segments].yBase / out_scale, 0.0); } break; } @@ -440,7 +336,6 @@ void make_gna_pwl(const DnnActivation fun, if (y_upper > x_upper * out_scale / in_scale) y_upper = FLOAT_TO_INT16(x_upper * out_scale / in_scale); if (x_upper > y_upper * in_scale / out_scale) x_upper = FLOAT_TO_INT32(y_upper * in_scale / out_scale); - gnalog() << "=========================== Abs Segments ===========================\n"; if (y_upper == y_max) { // saturation at ends - need one more segment n_segments += 1; gna_pwl.resize(n_segments); @@ -457,19 +352,14 @@ void make_gna_pwl(const DnnActivation fun, s = gna_slope(-1.0, in_scale, out_scale); gna_pwl[i].slope = FLOAT_TO_INT16(s.slope * s.slope_scale); gna_pwl[i].xBase = gna_pwl[i].xBase | s.slope_scale_index; - gnalog() << (int32_t)(gna_pwl[i].xBase & XBASEMASK) / in_scale - << " " << gna_pwl[i].yBase / out_scale - << " " << -1.0 - << "\n"; + print_segment((int32_t)(gna_pwl[i].xBase & XBASEMASK) / in_scale, gna_pwl[i].yBase / out_scale, -1.0); + gna_pwl[i + 1].xBase = 0; gna_pwl[i + 1].yBase = 0; s = gna_slope(1.0, in_scale, out_scale); gna_pwl[i + 1].slope = FLOAT_TO_INT16(s.slope * s.slope_scale); gna_pwl[i + 1].xBase = gna_pwl[i + 1].xBase | s.slope_scale_index; - gnalog() << (int32_t)(gna_pwl[i + 1].xBase & XBASEMASK) / in_scale - << " " << gna_pwl[i + 1].yBase / out_scale - << " " << 1.0 - << "\n"; + print_segment((int32_t)(gna_pwl[i + 1].xBase & XBASEMASK) / in_scale, gna_pwl[i + 1].yBase / out_scale, 1.0); break; } case kActPow: { @@ -551,11 +441,7 @@ void make_gna_pwl(const DnnActivation fun, gna_pwl[0].xBase = INT32_MIN & XBASEMASK; // zero out the 2 lsb gna_pwl[0].yBase = y_lower; gna_pwl[0].slope = 0; - gnalog() << gna_pwl[0].xBase / in_scale - << " " << gna_pwl[0].yBase / out_scale - << " " << 0 - << "\n"; - + print_segment(gna_pwl[0].xBase / in_scale, gna_pwl[0].yBase / out_scale, 0.0); gna_pwl[1].xBase = x_lower & XBASEMASK; // zero out the 2 lsb gna_pwl[1].yBase = y_lower; @@ -563,73 +449,27 @@ void make_gna_pwl(const DnnActivation fun, s = gna_slope(slope, in_scale, out_scale); gna_pwl[1].slope = FLOAT_TO_INT16(s.slope * s.slope_scale); gna_pwl[1].xBase = gna_pwl[1].xBase | s.slope_scale_index; - gnalog() << (int32_t)(gna_pwl[1].xBase & XBASEMASK) / in_scale - << " " << gna_pwl[1].yBase / out_scale - << " " << 1.0 - << "\n"; + print_segment((int32_t)(gna_pwl[1].xBase & XBASEMASK) / in_scale, gna_pwl[1].yBase / out_scale, 1.0); if (INT32_MAX > x_upper) { // need a right segment gna_pwl.push_back({ static_cast(x_upper & XBASEMASK), // zero out the 2 lsb y_upper, 0 }); - gnalog() << (x_upper & XBASEMASK) / in_scale - << " " << gna_pwl[2].yBase / out_scale - << " " << 0 - << "\n"; + print_segment((x_upper & XBASEMASK) / in_scale, gna_pwl[2].yBase / out_scale, 0.0); } } else { - auto n_segments = static_cast (pwl_size) + 1; - gna_pwl.resize(n_segments); - // insert extra segment for x values < l_bound - gna_pwl[0].xBase = static_cast (INT32_MIN & XBASEMASK); // zero out the 2 lsb - gnalog() << "=========================== Exp Segments ===========================\n"; - gna_pwl[0].yBase = gna_pwl[1].yBase = 0; - gna_pwl[1].xBase = (static_cast (in_scale * (-pwl[0].b / pwl[0].m))) & XBASEMASK; - gna_pwl[0].slope = 0; - - gnalog() << (gna_pwl[0].xBase) / in_scale - << " " << (gna_pwl[0].yBase) / out_scale - << " " << 0.0 - << "\n"; - - s = gna_slope(pwl[0].m, in_scale, out_scale); - gna_pwl[1].slope = FLOAT_TO_INT16(s.slope * s.slope_scale); - gna_pwl[1].xBase = gna_pwl[1].xBase | s.slope_scale_index; - - gnalog() << ((int32_t)(gna_pwl[1].xBase & XBASEMASK) / in_scale) - << " " << (gna_pwl[1].yBase) / out_scale - << " " << pwl[0].m - << "\n"; - - for (uint32_t i = 1; i < pwl_size - 1; ++i) { - s = gna_slope(pwl[i].m, in_scale, out_scale); - gna_pwl[i + 1].xBase = (static_cast (in_scale * pwl[i].alpha)) & XBASEMASK; - gna_pwl[i + 1].yBase = FLOAT_TO_INT16(pwl[i].beta * out_scale); - gna_pwl[i + 1].slope = FLOAT_TO_INT16(s.slope * s.slope_scale); - gna_pwl[i + 1].xBase = gna_pwl[i + 1].xBase | s.slope_scale_index; - - gnalog() << (pwl[i].alpha) - << " " << pwl[i].beta - << " " << pwl[i].m - << "\n"; - } - // insert extra segment for xvalues > u_bound - gna_pwl[n_segments - 1].xBase = - ((uint32_t)(in_scale * (y_max / out_scale - pwl[pwl_size - 2].b) / pwl[pwl_size - 2].m)) & XBASEMASK; - gna_pwl[n_segments - 1].yBase = y_max; - gna_pwl[n_segments - 1].slope = 0; - - gnalog() << (gna_pwl[n_segments - 1].xBase / in_scale) - << " " << 1.0 - << " " << 0.0 - << "\n"; + double min_x_val = -pwl[0].b / pwl[0].m; + double max_x_val = (y_max/out_scale - pwl[pwl_size - 2].b) / pwl[pwl_size - 2].m; + double min_y_val = fun.fqParams.set ? pwl[0].beta : 0; + double max_y_val = fun.fqParams.set ? pwl.front().beta : y_max / out_scale; + gna_pwl = create_multisegment_gna_pwl(pwl, in_scale, out_scale, min_x_val, max_x_val, min_y_val, max_y_val, + fun.fqParams.set, true); break; } break; } default: - gnalog() << "Unexpected function activation!\n"; THROW_GNA_EXCEPTION << "Unexpected function activation!" << fun; } insert_extra_pwl_segments(gna_pwl, y_min, y_max); diff --git a/inference-engine/src/gna_plugin/backend/make_pwl.hpp b/inference-engine/src/gna_plugin/backend/make_pwl.hpp index 5cc879d75ec..a436cba26bd 100644 --- a/inference-engine/src/gna_plugin/backend/make_pwl.hpp +++ b/inference-engine/src/gna_plugin/backend/make_pwl.hpp @@ -7,7 +7,7 @@ #include #include "runtime/pwl.h" -void make_gna_pwl(const DnnActivation fun, +void make_gna_pwl(const DnnActivation& fun, const std::vector& pwl, const double l_bound, const double u_bound, diff --git a/inference-engine/tests/functional/plugin/gna/scale_factors_tests/eltwise_act_fq.cpp b/inference-engine/tests/functional/plugin/gna/scale_factors_tests/eltwise_act_fq.cpp new file mode 100644 index 00000000000..b9d54f1308b --- /dev/null +++ b/inference-engine/tests/functional/plugin/gna/scale_factors_tests/eltwise_act_fq.cpp @@ -0,0 +1,167 @@ +// Copyright (C) 2021 Intel Corporation +// SPDX-License-Identifier: Apache-2.0 +// + +#include +#include +#include +#include +#include + +#include + +#include "common_test_utils/common_utils.hpp" +#include "functional_test_utils/plugin_cache.hpp" +#include "shared_test_classes/base/layer_test_utils.hpp" +#include "functional_test_utils/blob_utils.hpp" +#include "ngraph_functions/utils/ngraph_helpers.hpp" +#include "ngraph_functions/builders.hpp" + +#include "ngraph_functions/pass/convert_prc.hpp" + +static std::map activationNames = { + {ngraph::helpers::ActivationTypes::Sigmoid, "Sigmoid"}, + {ngraph::helpers::ActivationTypes::Tanh, "Tanh"}, + {ngraph::helpers::ActivationTypes::Relu, "Relu"}, + {ngraph::helpers::ActivationTypes::Exp, "Exp"}, + {ngraph::helpers::ActivationTypes::Log, "Log"}, + {ngraph::helpers::ActivationTypes::Sign, "Sign"}, + {ngraph::helpers::ActivationTypes::Abs, "Abs"} +}; + +typedef std::tuple< + InferenceEngine::Precision, // Network Precision + std::string, // Target Device + std::map, // Configuration + std::pair, // Input values + ngraph::helpers::ActivationTypes // Activation type +> eltwiseActFqParams; + +namespace LayerTestsDefinitions { + +class EltwiseActFqTest : public testing::WithParamInterface, + public LayerTestsUtils::LayerTestsCommon { +public: + static std::string getTestCaseName(testing::TestParamInfo obj) { + InferenceEngine::Precision netPrecision; + std::string targetDevice; + std::map configuration; + std::pair inputValues; + ngraph::helpers::ActivationTypes act; + std::tie(netPrecision, targetDevice, configuration, inputValues, act) = obj.param; + + std::ostringstream result; + result << "netPRC=" << netPrecision.name() << "_"; + result << "targetDevice=" << targetDevice << "_"; + for (auto const& configItem : configuration) { + result << "_configItem=" << configItem.first << "_" << configItem.second; + } + result << "_range=(" << inputValues.first << ", " << inputValues.second << ")"; + result << "_act=" << activationNames[act]; + + return result.str(); + } + + InferenceEngine::Blob::Ptr GenerateInput(const InferenceEngine::InputInfo& info) const override { + InferenceEngine::Blob::Ptr blob = make_blob_with_precision(info.getTensorDesc()); + blob->allocate(); + + auto* rawBlobDataPtr = blob->buffer().as(); + std::vector values = CommonTestUtils::generate_float_numbers(blob->size(), inputDataMin, inputDataMax); + for (size_t i = 0; i < blob->size(); i++) { + rawBlobDataPtr[i] = values[i]; + } + return blob; + } + +protected: + void SetUp() override { + InferenceEngine::Precision netPrecision; + std::pair inputValues; + ngraph::helpers::ActivationTypes act; + + std::tie(netPrecision, targetDevice, configuration, inputValues, act) = this->GetParam(); + std::tie(inputDataMin, inputDataMax) = inputValues; + if (act == ngraph::helpers::ActivationTypes::Log) { + // clamp not positive values + inputDataMin = 1.0e-3; + } + auto ngPrc = FuncTestUtils::PrecisionUtils::convertIE2nGraphPrc(netPrecision); + + const ngraph::Shape shape = {1, 128}; + auto params = ngraph::builder::makeParams(ngPrc, {shape}); + + auto lowNodeIn = ngraph::builder::makeConstant(ngPrc, {1}, { 100 * inputDataMin }); + auto highNodeIn = ngraph::builder::makeConstant(ngPrc, {1}, { 100 * inputDataMax }); + auto fqIn = std::make_shared(params[0], lowNodeIn, highNodeIn, + lowNodeIn, highNodeIn, levels16); + + auto constant = ngraph::builder::makeConstant(ngPrc, shape, + CommonTestUtils::generate_float_numbers(shape[1], inputDataMin, inputDataMax)); + auto add = std::make_shared(fqIn, constant); + + auto lowNode = ngraph::builder::makeConstant(ngPrc, {1}, { 2 * inputDataMin }); + auto highNode = ngraph::builder::makeConstant(ngPrc, {1}, { 2 * inputDataMax }); + auto fq = std::make_shared(add, lowNode, highNode, + lowNode, highNode, levels32); + + auto tanh = ngraph::builder::makeActivation(fq, ngPrc, act); + + auto lowNodeOut = ngraph::builder::makeConstant(ngPrc, {1}, { std::tanh(2 * inputDataMin) }); + auto highNodeOut = ngraph::builder::makeConstant(ngPrc, {1}, { std::tanh(2 * inputDataMax) }); + auto fqOut = std::make_shared(tanh, lowNodeOut, highNodeOut, + lowNodeOut, highNodeOut, levels16); + + ngraph::ResultVector results{std::make_shared(fqOut)}; + function = std::make_shared(results, params, "TanhFq"); + } + + float inputDataMax = 1.0; + float inputDataMin = -1.0; + const size_t levels16 = std::numeric_limits::max(); + const size_t levels32 = std::numeric_limits::max(); + // to reproduce the problem with quite big distance between min int and min value from stats + const size_t sf_reducer = 100; +}; + +TEST_P(EltwiseActFqTest, CompareWithRefImpl) { + Run(); +}; + +const std::vector netPrecisions = { + InferenceEngine::Precision::FP32, + InferenceEngine::Precision::FP16 +}; + +const std::vector> configs = { + { + {"GNA_DEVICE_MODE", "GNA_SW_EXACT"}, + } +}; + +const std::vector> inputValues = { + {-10.0, 10.0}, + {-5.0, 5.0}, + {-1.0, 1.0}, + {-0.04, 0.04} +}; + +const std::vector activationTypes = { + ngraph::helpers::ActivationTypes::Sigmoid, + ngraph::helpers::ActivationTypes::Tanh, + ngraph::helpers::ActivationTypes::Relu, + ngraph::helpers::ActivationTypes::Exp, + ngraph::helpers::ActivationTypes::Log, + ngraph::helpers::ActivationTypes::Sign, + ngraph::helpers::ActivationTypes::Abs +}; + +INSTANTIATE_TEST_SUITE_P(smoke_base, EltwiseActFqTest, + ::testing::Combine( + ::testing::ValuesIn(netPrecisions), + ::testing::Values(CommonTestUtils::DEVICE_GNA), + ::testing::ValuesIn(configs), + ::testing::ValuesIn(inputValues), + ::testing::ValuesIn(activationTypes)), + EltwiseActFqTest::getTestCaseName); +} // namespace LayerTestsDefinitions \ No newline at end of file diff --git a/inference-engine/tests/functional/plugin/gna/shared_tests_instances/skip_tests_config.cpp b/inference-engine/tests/functional/plugin/gna/shared_tests_instances/skip_tests_config.cpp index 5b306e6a99c..a930e9b2544 100644 --- a/inference-engine/tests/functional/plugin/gna/shared_tests_instances/skip_tests_config.cpp +++ b/inference-engine/tests/functional/plugin/gna/shared_tests_instances/skip_tests_config.cpp @@ -19,6 +19,8 @@ std::vector disabledTestPatterns() { // TODO: FIX BUG 32210 R"(.*ActivationLayerTest.CompareWithRefs/(Sigmoid|Tanh|Exp|Log).*)", R"(.*ActivationFQSubgraph.*activation=(Exp|Log).*)", + // TODO: Issue 68586 + R"(.*EltwiseActFqTest.*act=Log.*)", // TODO: Issue 32542 R"(.*(EltwiseLayerTest).*eltwiseOpType=(Sum|Sub).*opType=SCALAR.*)", R"(.*(EltwiseLayerTest).*eltwiseOpType=Prod.*secondaryInputType=PARAMETER.*opType=SCALAR.*)",