[GNA] Fixed segments creation for multi segment pwl functions (#8044)

* [GNA] Fixed extreme segments creation for tanh pwl

* [GNA] Tests added

* [GNA] Added tests for different activations

* [GNA] Comments apply
This commit is contained in:
Elizaveta Lobanova 2021-10-21 17:54:23 +03:00 committed by GitHub
parent c9dc922f3c
commit 0fb24e8040
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 298 additions and 289 deletions

View File

@ -4,6 +4,7 @@
#include <vector> #include <vector>
#include <iostream> #include <iostream>
#include <iomanip>
#include <cmath> #include <cmath>
#include "runtime/pwl.h" #include "runtime/pwl.h"
@ -58,7 +59,72 @@ static void insert_extra_pwl_segments(std::vector<gna_pwl_segment_t>& 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<gna_pwl_segment_t> create_multisegment_gna_pwl(const std::vector<pwl_t>& 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_segment_t> gna_pwl;
int32_t xbase = static_cast<int32_t> (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<int32_t>(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<int32_t> (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<int32_t>(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_t>& pwl, const std::vector<pwl_t>& pwl,
const double l_bound, const double l_bound,
const double u_bound, const double u_bound,
@ -73,199 +139,56 @@ void make_gna_pwl(const DnnActivation fun,
gnalog() << "make_gna_pwl\n"; gnalog() << "make_gna_pwl\n";
gnalog() << " in_scale " << in_scale << "\n"; gnalog() << " in_scale " << in_scale << "\n";
gnalog() << " out_scale " << out_scale << "\n"; gnalog() << " out_scale " << out_scale << "\n";
print_segments_header(fun);
switch (fun) { switch (fun) {
case kActSigmoid: case kActSigmoid:
case kActTanh: case kActTanh:
case kActSoftSign: { case kActSoftSign: {
auto n_segments = static_cast<int32_t> (pwl_size) + 1;
gna_pwl.resize(n_segments);
// insert extra segment for x values < l_bound // insert extra segment for x values < l_bound
gna_pwl[0].xBase = static_cast<int32_t> (INT32_MIN & XBASEMASK); // zero out the 2 lsb double min_x_val;
double min_y_val;
if (fun == kActSigmoid) { if (fun == kActSigmoid) {
gnalog() << "=========================== Sigmoid Segments ===========================\n"; min_y_val = fun.fqParams.set ? pwl[0].beta : 0;
auto minVal = (fun.fqParams.set && *fun.fqParams.input_low > 0) ? FLOAT_TO_INT16(*fun.fqParams.input_low * out_scale) : 0; min_x_val = -pwl[0].b / pwl[0].m;
gna_pwl[0].yBase = gna_pwl[1].yBase = minVal;
gna_pwl[1].xBase = (static_cast<int32_t> (in_scale * (-pwl[0].b / pwl[0].m))) & XBASEMASK;
} else if (fun == kActTanh) { } else if (fun == kActTanh) {
gnalog() << "=========================== Tanh Segments ===========================\n"; min_y_val = fun.fqParams.set ? pwl[0].beta : -1.0;
auto minVal = (fun.fqParams.set && *fun.fqParams.input_low > -1) ? FLOAT_TO_INT16(*fun.fqParams.input_low * out_scale) : min_x_val = (-1.0 - pwl[0].b) / pwl[0].m;
static_cast<int16_t>(-1.0 * out_scale);
gna_pwl[0].yBase = gna_pwl[1].yBase = minVal;
gna_pwl[1].xBase = (static_cast<int32_t> (in_scale * (-1.0 - pwl[0].b) / pwl[0].m)) & XBASEMASK;
} else { } else {
gnalog() << "=========================== SoftSign Segments ===========================\n"; min_y_val = fun.fqParams.set ? pwl[0].beta : -1.0;
auto minVal = (fun.fqParams.set && *fun.fqParams.input_low > -1) ? FLOAT_TO_INT16(*fun.fqParams.input_low * out_scale) : min_x_val = (-1.0 - pwl[0].b) / pwl[0].m;
static_cast<int16_t>(-1.0 * out_scale);
gna_pwl[0].yBase = gna_pwl[1].yBase = minVal;
gna_pwl[1].xBase = (static_cast<int32_t> (in_scale * (-1.0 - pwl[0].b) / pwl[0].m)) & XBASEMASK;
} }
gna_pwl[0].slope = 0; 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;
gnalog() << (gna_pwl[0].xBase) / in_scale gna_pwl = create_multisegment_gna_pwl(pwl, in_scale, out_scale, min_x_val, max_x_val, min_y_val, max_y_val,
<< " " << (gna_pwl[0].yBase) / out_scale fun.fqParams.set, true);
<< " " << 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<int32_t> (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";
break; break;
} }
case kActExp: { case kActExp: {
auto n_segments = static_cast<int32_t> (pwl_size) + 1; double min_x_val = -pwl[0].b / pwl[0].m;
gna_pwl.resize(n_segments); double max_x_val = (y_max/out_scale - pwl[pwl_size - 2].b) / pwl[pwl_size - 2].m;
// insert extra segment for x values < l_bound double min_y_val = fun.fqParams.set ? pwl[0].beta : 0;
gna_pwl[0].xBase = static_cast<int32_t> (INT32_MIN & XBASEMASK); // zero out the 2 lsb double max_y_val = fun.fqParams.set ? pwl.front().beta : y_max / out_scale;
gnalog() << "=========================== Exp Segments ===========================\n"; gna_pwl = create_multisegment_gna_pwl(pwl, in_scale, out_scale, min_x_val, max_x_val, min_y_val, max_y_val,
gna_pwl[0].yBase = gna_pwl[1].yBase = 0; fun.fqParams.set, true);
gna_pwl[1].xBase = (static_cast<int32_t> (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<int32_t> (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";
break; break;
} }
case kActLog: { case kActLog: {
auto n_segments = static_cast<int32_t> (pwl_size); double min_x_val = 1 + ~XBASEMASK;
gna_pwl.resize(n_segments); double max_x_val = INT32_MAX / in_scale;
// insert extra segment for x values < l_bound double min_y_val = y_min / out_scale;
gna_pwl[0].xBase = static_cast<int32_t> (INT32_MIN & XBASEMASK); // zero out the 2 lsb double max_y_val = y_max / out_scale;
gnalog() << "=========================== Log Segments ===========================\n"; gna_pwl = create_multisegment_gna_pwl(pwl, in_scale, out_scale, min_x_val, max_x_val, min_y_val, max_y_val,
gna_pwl[0].yBase = gna_pwl[1].yBase = y_min; fun.fqParams.set, false);
gna_pwl[1].xBase = (static_cast<int32_t> (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<int32_t> (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";
}
break; break;
} }
case kActNegLog: case kActNegLog:
case kActNegHalfLog: { case kActNegHalfLog: {
auto n_segments = static_cast<int32_t> (pwl_size); double min_x_val = 1 + ~XBASEMASK;
gna_pwl.resize(n_segments); double max_x_val = INT32_MAX / in_scale;
// insert extra segment for x values < l_bound double min_y_val = y_max / out_scale;
gna_pwl[0].xBase = static_cast<int32_t> (INT32_MIN & XBASEMASK); // zero out the 2 lsb double max_y_val = y_min / out_scale;
if (fun == kActNegHalfLog) gna_pwl = create_multisegment_gna_pwl(pwl, in_scale, out_scale, min_x_val, max_x_val, min_y_val, max_y_val,
gnalog() << "=========================== NegHalfLog Segments ===========================\n"; fun.fqParams.set, false);
else
gnalog() << "=========================== NegLog Segments ===========================\n";
gna_pwl[0].yBase = gna_pwl[1].yBase = y_max;
gna_pwl[1].xBase = (static_cast<int32_t> (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<int32_t> (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";
}
break; break;
} }
case kActRelu: case kActRelu:
@ -273,10 +196,6 @@ void make_gna_pwl(const DnnActivation fun,
auto n_segments = 2; auto n_segments = 2;
gna_pwl.resize(n_segments); 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_lower = INT32_MIN;
int32_t x_upper = INT32_MAX; int32_t x_upper = INT32_MAX;
int32_t y_lower = y_min; 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].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); gna_pwl[0].slope = FLOAT_TO_INT16(s.slope * s.slope_scale);
gnalog() << (int32_t)(gna_pwl[0].xBase & XBASEMASK) / in_scale print_segment((int32_t)(gna_pwl[0].xBase & XBASEMASK) / in_scale,
<< " " << gna_pwl[0].yBase / out_scale gna_pwl[0].yBase / out_scale,
<< " " << (gna_pwl[0].slope * in_scale) / (out_scale*s.slope_scale) (gna_pwl[0].slope * in_scale) / (out_scale*s.slope_scale));
<< "\n";
gna_pwl[1].xBase = 0; gna_pwl[1].xBase = 0;
gna_pwl[1].yBase = 0; gna_pwl[1].yBase = 0;
s = gna_slope(1.0, in_scale, out_scale); s = gna_slope(1.0, in_scale, out_scale);
gna_pwl[1].slope = FLOAT_TO_INT16(s.slope * s.slope_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; gna_pwl[1].xBase = gna_pwl[1].xBase | s.slope_scale_index;
gnalog() << 0.0 print_segment(0.0, 0.0, (gna_pwl[1].slope * in_scale) / (out_scale*s.slope_scale));
<< " " << 0.0
<< " " << (gna_pwl[1].slope * in_scale) / (out_scale*s.slope_scale)
<< "\n";
if (fun.fqParams.set) { // need a right segment if (fun.fqParams.set) { // need a right segment
gna_pwl.push_back({ gna_pwl.push_back({
@ -317,10 +233,7 @@ void make_gna_pwl(const DnnActivation fun,
y_upper, y_upper,
0 }); 0 });
gnalog() << (x_upper & XBASEMASK) / in_scale print_segment((x_upper & XBASEMASK) / in_scale, gna_pwl[n_segments].yBase / out_scale, 0.0);
<< " " << gna_pwl[n_segments].yBase / out_scale
<< " " << 0
<< "\n";
} }
break; break;
} }
@ -328,34 +241,28 @@ void make_gna_pwl(const DnnActivation fun,
auto n_segments = 3; auto n_segments = 3;
gna_pwl.resize(n_segments); gna_pwl.resize(n_segments);
gnalog() << "=========================== Sign Segments ===========================\n";
int32_t x_lower = INT32_MIN; int32_t x_lower = INT32_MIN;
int16_t y_lower = static_cast<int16_t>(-1.0 * out_scale); int16_t y_lower = static_cast<int16_t>(-1.0 * out_scale);
gna_pwl[0].yBase = y_lower; gna_pwl[0].yBase = y_lower;
gna_pwl[0].xBase = (x_lower & XBASEMASK); // zero out the 2 lsb gna_pwl[0].xBase = (x_lower & XBASEMASK); // zero out the 2 lsb
gna_pwl[0].slope = 0; gna_pwl[0].slope = 0;
gnalog() << gna_pwl[0].xBase / in_scale print_segment(gna_pwl[0].xBase / in_scale, gna_pwl[0].yBase / out_scale,
<< " " << gna_pwl[0].yBase / out_scale (gna_pwl[0].slope * in_scale) / (out_scale*s.slope_scale));
<< " " << (gna_pwl[0].slope * in_scale) / (out_scale*s.slope_scale)
<< "\n";
gna_pwl[1].xBase = -1; gna_pwl[1].xBase = -1;
gna_pwl[1].yBase = 0; gna_pwl[1].yBase = 0;
gna_pwl[1].slope = 0; gna_pwl[1].slope = 0;
gna_pwl[1].xBase = gna_pwl[1].xBase & XBASEMASK; gna_pwl[1].xBase = gna_pwl[1].xBase & XBASEMASK;
gnalog() << gna_pwl[1].xBase / in_scale print_segment(gna_pwl[1].xBase / in_scale, gna_pwl[1].yBase / out_scale,
<< " " << gna_pwl[1].yBase / out_scale (gna_pwl[1].slope * in_scale) / (out_scale*s.slope_scale));
<< " " << (gna_pwl[1].slope * in_scale) / (out_scale*s.slope_scale)
<< "\n";
gna_pwl[2].xBase = 1 + ~XBASEMASK; // smallest representable positive number gna_pwl[2].xBase = 1 + ~XBASEMASK; // smallest representable positive number
gna_pwl[2].yBase = static_cast<int16_t>(1.0 * out_scale); gna_pwl[2].yBase = static_cast<int16_t>(1.0 * out_scale);
s = gna_slope(1.0, in_scale, out_scale); s = gna_slope(1.0, in_scale, out_scale);
gna_pwl[2].slope = 0; gna_pwl[2].slope = 0;
gna_pwl[2].xBase = gna_pwl[2].xBase & XBASEMASK; gna_pwl[2].xBase = gna_pwl[2].xBase & XBASEMASK;
gnalog() << gna_pwl[2].xBase / in_scale print_segment(gna_pwl[2].xBase / in_scale, gna_pwl[2].yBase / out_scale,
<< " " << gna_pwl[2].yBase / out_scale (gna_pwl[2].slope * in_scale) / (out_scale*s.slope_scale));
<< " " << (gna_pwl[2].slope * in_scale) / (out_scale*s.slope_scale)
<< "\n";
break; break;
} }
case kActIdentity: case kActIdentity:
@ -373,7 +280,6 @@ void make_gna_pwl(const DnnActivation fun,
} }
auto n_segments = 2; auto n_segments = 2;
if (fun == kActKaldiLstmClipping) { if (fun == kActKaldiLstmClipping) {
gnalog() << "=========================== Clipping Segments ===========================\n";
if (x_lower < l_bound * in_scale) { if (x_lower < l_bound * in_scale) {
if (y_lower < l_bound * out_scale) { if (y_lower < l_bound * out_scale) {
x_lower = FLOAT_TO_INT32(l_bound * in_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) { } 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_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 (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_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); 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.resize(n_segments);
gna_pwl[0].xBase = INT32_MIN & XBASEMASK; // zero out the 2 lsb gna_pwl[0].xBase = INT32_MIN & XBASEMASK; // zero out the 2 lsb
gna_pwl[0].yBase = y_lower; gna_pwl[0].yBase = y_lower;
gna_pwl[0].slope = 0; gna_pwl[0].slope = 0;
gnalog() << gna_pwl[0].xBase / in_scale print_segment(gna_pwl[0].xBase / in_scale, gna_pwl[0].yBase / out_scale, 0.0);
<< " " << gna_pwl[0].yBase / out_scale
<< " " << 0
<< "\n";
gna_pwl[1].xBase = x_lower & XBASEMASK; // zero out the 2 lsb gna_pwl[1].xBase = x_lower & XBASEMASK; // zero out the 2 lsb
gna_pwl[1].yBase = y_lower; gna_pwl[1].yBase = y_lower;
s = gna_slope(1.0, in_scale, out_scale); s = gna_slope(1.0, in_scale, out_scale);
gna_pwl[1].slope = FLOAT_TO_INT16(s.slope * s.slope_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; gna_pwl[1].xBase = gna_pwl[1].xBase | s.slope_scale_index;
gnalog() << (int32_t)(gna_pwl[1].xBase & XBASEMASK) / in_scale print_segment((int32_t)(gna_pwl[1].xBase & XBASEMASK) / in_scale, gna_pwl[1].yBase / out_scale, 1.0);
<< " " << gna_pwl[1].yBase / out_scale
<< " " << 1.0
<< "\n";
if (INT32_MAX > x_upper) { // need a right segment if (INT32_MAX > x_upper) { // need a right segment
gna_pwl.push_back({ gna_pwl.push_back({
static_cast<int32_t>(x_upper & XBASEMASK), // zero out the 2 lsb static_cast<int32_t>(x_upper & XBASEMASK), // zero out the 2 lsb
y_upper, y_upper,
0 }); 0 });
gnalog() << (x_upper & XBASEMASK) / in_scale print_segment((x_upper & XBASEMASK) / in_scale, gna_pwl[n_segments].yBase / out_scale, 0.0);
<< " " << gna_pwl[n_segments].yBase / out_scale
<< " " << 0
<< "\n";
} }
break; 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 (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); 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 if (y_upper == y_max) { // saturation at ends - need one more segment
n_segments += 1; n_segments += 1;
gna_pwl.resize(n_segments); 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); s = gna_slope(-1.0, in_scale, out_scale);
gna_pwl[i].slope = FLOAT_TO_INT16(s.slope * s.slope_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; gna_pwl[i].xBase = gna_pwl[i].xBase | s.slope_scale_index;
gnalog() << (int32_t)(gna_pwl[i].xBase & XBASEMASK) / in_scale print_segment((int32_t)(gna_pwl[i].xBase & XBASEMASK) / in_scale, gna_pwl[i].yBase / out_scale, -1.0);
<< " " << gna_pwl[i].yBase / out_scale
<< " " << -1.0
<< "\n";
gna_pwl[i + 1].xBase = 0; gna_pwl[i + 1].xBase = 0;
gna_pwl[i + 1].yBase = 0; gna_pwl[i + 1].yBase = 0;
s = gna_slope(1.0, in_scale, out_scale); 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].slope = FLOAT_TO_INT16(s.slope * s.slope_scale);
gna_pwl[i + 1].xBase = gna_pwl[i + 1].xBase | s.slope_scale_index; 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 print_segment((int32_t)(gna_pwl[i + 1].xBase & XBASEMASK) / in_scale, gna_pwl[i + 1].yBase / out_scale, 1.0);
<< " " << gna_pwl[i + 1].yBase / out_scale
<< " " << 1.0
<< "\n";
break; break;
} }
case kActPow: { 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].xBase = INT32_MIN & XBASEMASK; // zero out the 2 lsb
gna_pwl[0].yBase = y_lower; gna_pwl[0].yBase = y_lower;
gna_pwl[0].slope = 0; gna_pwl[0].slope = 0;
gnalog() << gna_pwl[0].xBase / in_scale print_segment(gna_pwl[0].xBase / in_scale, gna_pwl[0].yBase / out_scale, 0.0);
<< " " << gna_pwl[0].yBase / out_scale
<< " " << 0
<< "\n";
gna_pwl[1].xBase = x_lower & XBASEMASK; // zero out the 2 lsb gna_pwl[1].xBase = x_lower & XBASEMASK; // zero out the 2 lsb
gna_pwl[1].yBase = y_lower; 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); s = gna_slope(slope, in_scale, out_scale);
gna_pwl[1].slope = FLOAT_TO_INT16(s.slope * s.slope_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; gna_pwl[1].xBase = gna_pwl[1].xBase | s.slope_scale_index;
gnalog() << (int32_t)(gna_pwl[1].xBase & XBASEMASK) / in_scale print_segment((int32_t)(gna_pwl[1].xBase & XBASEMASK) / in_scale, gna_pwl[1].yBase / out_scale, 1.0);
<< " " << gna_pwl[1].yBase / out_scale
<< " " << 1.0
<< "\n";
if (INT32_MAX > x_upper) { // need a right segment if (INT32_MAX > x_upper) { // need a right segment
gna_pwl.push_back({ gna_pwl.push_back({
static_cast<int32_t>(x_upper & XBASEMASK), // zero out the 2 lsb static_cast<int32_t>(x_upper & XBASEMASK), // zero out the 2 lsb
y_upper, y_upper,
0 }); 0 });
gnalog() << (x_upper & XBASEMASK) / in_scale print_segment((x_upper & XBASEMASK) / in_scale, gna_pwl[2].yBase / out_scale, 0.0);
<< " " << gna_pwl[2].yBase / out_scale
<< " " << 0
<< "\n";
} }
} else { } else {
auto n_segments = static_cast<int32_t> (pwl_size) + 1; double min_x_val = -pwl[0].b / pwl[0].m;
gna_pwl.resize(n_segments); double max_x_val = (y_max/out_scale - pwl[pwl_size - 2].b) / pwl[pwl_size - 2].m;
// insert extra segment for x values < l_bound double min_y_val = fun.fqParams.set ? pwl[0].beta : 0;
gna_pwl[0].xBase = static_cast<int32_t> (INT32_MIN & XBASEMASK); // zero out the 2 lsb double max_y_val = fun.fqParams.set ? pwl.front().beta : y_max / out_scale;
gnalog() << "=========================== Exp Segments ===========================\n"; gna_pwl = create_multisegment_gna_pwl(pwl, in_scale, out_scale, min_x_val, max_x_val, min_y_val, max_y_val,
gna_pwl[0].yBase = gna_pwl[1].yBase = 0; fun.fqParams.set, true);
gna_pwl[1].xBase = (static_cast<int32_t> (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<int32_t> (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";
break; break;
} }
break; break;
} }
default: default:
gnalog() << "Unexpected function activation!\n";
THROW_GNA_EXCEPTION << "Unexpected function activation!" << fun; THROW_GNA_EXCEPTION << "Unexpected function activation!" << fun;
} }
insert_extra_pwl_segments(gna_pwl, y_min, y_max); insert_extra_pwl_segments(gna_pwl, y_min, y_max);

View File

@ -7,7 +7,7 @@
#include <vector> #include <vector>
#include "runtime/pwl.h" #include "runtime/pwl.h"
void make_gna_pwl(const DnnActivation fun, void make_gna_pwl(const DnnActivation& fun,
const std::vector<pwl_t>& pwl, const std::vector<pwl_t>& pwl,
const double l_bound, const double l_bound,
const double u_bound, const double u_bound,

View File

@ -0,0 +1,167 @@
// Copyright (C) 2021 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//
#include <vector>
#include <memory>
#include <tuple>
#include <vector>
#include <string>
#include <ie_core.hpp>
#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<ngraph::helpers::ActivationTypes, std::string> 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<std::string, std::string>, // Configuration
std::pair<float, float>, // Input values
ngraph::helpers::ActivationTypes // Activation type
> eltwiseActFqParams;
namespace LayerTestsDefinitions {
class EltwiseActFqTest : public testing::WithParamInterface<eltwiseActFqParams>,
public LayerTestsUtils::LayerTestsCommon {
public:
static std::string getTestCaseName(testing::TestParamInfo<eltwiseActFqParams> obj) {
InferenceEngine::Precision netPrecision;
std::string targetDevice;
std::map<std::string, std::string> configuration;
std::pair<float, float> 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<float*>();
std::vector<float> 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<float, float> 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<float>(ngPrc, {1}, { 100 * inputDataMin });
auto highNodeIn = ngraph::builder::makeConstant<float>(ngPrc, {1}, { 100 * inputDataMax });
auto fqIn = std::make_shared<ngraph::opset8::FakeQuantize>(params[0], lowNodeIn, highNodeIn,
lowNodeIn, highNodeIn, levels16);
auto constant = ngraph::builder::makeConstant<float>(ngPrc, shape,
CommonTestUtils::generate_float_numbers(shape[1], inputDataMin, inputDataMax));
auto add = std::make_shared<ngraph::opset8::Add>(fqIn, constant);
auto lowNode = ngraph::builder::makeConstant<float>(ngPrc, {1}, { 2 * inputDataMin });
auto highNode = ngraph::builder::makeConstant<float>(ngPrc, {1}, { 2 * inputDataMax });
auto fq = std::make_shared<ngraph::opset8::FakeQuantize>(add, lowNode, highNode,
lowNode, highNode, levels32);
auto tanh = ngraph::builder::makeActivation(fq, ngPrc, act);
auto lowNodeOut = ngraph::builder::makeConstant<float>(ngPrc, {1}, { std::tanh(2 * inputDataMin) });
auto highNodeOut = ngraph::builder::makeConstant<float>(ngPrc, {1}, { std::tanh(2 * inputDataMax) });
auto fqOut = std::make_shared<ngraph::opset8::FakeQuantize>(tanh, lowNodeOut, highNodeOut,
lowNodeOut, highNodeOut, levels16);
ngraph::ResultVector results{std::make_shared<ngraph::opset8::Result>(fqOut)};
function = std::make_shared<ngraph::Function>(results, params, "TanhFq");
}
float inputDataMax = 1.0;
float inputDataMin = -1.0;
const size_t levels16 = std::numeric_limits<uint16_t>::max();
const size_t levels32 = std::numeric_limits<uint32_t>::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<InferenceEngine::Precision> netPrecisions = {
InferenceEngine::Precision::FP32,
InferenceEngine::Precision::FP16
};
const std::vector<std::map<std::string, std::string>> configs = {
{
{"GNA_DEVICE_MODE", "GNA_SW_EXACT"},
}
};
const std::vector<std::pair<float, float>> inputValues = {
{-10.0, 10.0},
{-5.0, 5.0},
{-1.0, 1.0},
{-0.04, 0.04}
};
const std::vector<ngraph::helpers::ActivationTypes> 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

View File

@ -19,6 +19,8 @@ std::vector<std::string> disabledTestPatterns() {
// TODO: FIX BUG 32210 // TODO: FIX BUG 32210
R"(.*ActivationLayerTest.CompareWithRefs/(Sigmoid|Tanh|Exp|Log).*)", R"(.*ActivationLayerTest.CompareWithRefs/(Sigmoid|Tanh|Exp|Log).*)",
R"(.*ActivationFQSubgraph.*activation=(Exp|Log).*)", R"(.*ActivationFQSubgraph.*activation=(Exp|Log).*)",
// TODO: Issue 68586
R"(.*EltwiseActFqTest.*act=Log.*)",
// TODO: Issue 32542 // TODO: Issue 32542
R"(.*(EltwiseLayerTest).*eltwiseOpType=(Sum|Sub).*opType=SCALAR.*)", R"(.*(EltwiseLayerTest).*eltwiseOpType=(Sum|Sub).*opType=SCALAR.*)",
R"(.*(EltwiseLayerTest).*eltwiseOpType=Prod.*secondaryInputType=PARAMETER.*opType=SCALAR.*)", R"(.*(EltwiseLayerTest).*eltwiseOpType=Prod.*secondaryInputType=PARAMETER.*opType=SCALAR.*)",