Fix MIN and MAX aggregation functions (#1022)

This commit is contained in:
jignparm 2019-05-15 10:19:25 -07:00 committed by Changming Sun
parent 875c4c2f3f
commit fc17a8bc08
3 changed files with 90 additions and 15 deletions

View file

@ -147,7 +147,7 @@ TreeEnsembleRegressor<T>::TreeEnsembleRegressor(const OpKernelInfo& info)
}
template <typename T>
common::Status TreeEnsembleRegressor<T>::ProcessTreeNode(std::unordered_map<int64_t, float>& classes, int64_t treeindex, const T* Xdata, int64_t feature_base) const {
common::Status TreeEnsembleRegressor<T>::ProcessTreeNode(std::unordered_map < int64_t, std::tuple<float, float, float>>& classes, int64_t treeindex, const T* Xdata, int64_t feature_base) const {
//walk down tree to the leaf
::onnxruntime::ml::NODE_MODE mode = static_cast<::onnxruntime::ml::NODE_MODE>(nodes_modes_[treeindex]);
int64_t loopcount = 0;
@ -197,9 +197,13 @@ common::Status TreeEnsembleRegressor<T>::ProcessTreeNode(std::unordered_map<int6
float weight = std::get<3>(leafnode_data_[index]);
auto it_classes = classes.find(classid);
if (it_classes != classes.end()) {
it_classes->second += weight;
auto& tuple = it_classes->second;
std::get<0>(tuple) += weight;
if (weight < std::get<1>(tuple)) std::get<1>(tuple) = weight;
if (weight > std::get<2>(tuple)) std::get<2>(tuple) = weight;
} else {
auto p1 = std::make_pair(classid, weight);
std::tuple<float, float, float> tuple = std::make_tuple(weight, weight, weight);
auto p1 = std::make_pair(classid, tuple);
classes.insert(p1);
}
index++;
@ -232,7 +236,7 @@ common::Status TreeEnsembleRegressor<T>::Compute(OpKernelContext* context) const
for (int64_t i = 0; i < N; i++) //for each class
{
int64_t current_weight_0 = i * stride;
std::unordered_map<int64_t, float> scores;
std::unordered_map<int64_t, std::tuple<float, float, float>> scores; // sum, min, max
//for each tree
for (size_t j = 0; j < roots_.size(); j++) {
//walk each tree from its root
@ -246,13 +250,13 @@ common::Status TreeEnsembleRegressor<T>::Compute(OpKernelContext* context) const
float val = base_values_.size() == (size_t)n_targets_ ? base_values_[j] : 0.f;
if (it_scores != scores.end()) {
if (aggregate_function_ == ::onnxruntime::ml::AGGREGATE_FUNCTION::AVERAGE) {
val += scores[j] / roots_.size();
val += std::get<0>(scores[j]) / roots_.size(); //first element of tuple is already a sum
} else if (aggregate_function_ == ::onnxruntime::ml::AGGREGATE_FUNCTION::SUM) {
val += scores[j];
val += std::get<0>(scores[j]);
} else if (aggregate_function_ == ::onnxruntime::ml::AGGREGATE_FUNCTION::MIN) {
if (scores[j] < val) val = scores[j];
val += std::get<1>(scores[j]); // second element of tuple is min
} else if (aggregate_function_ == ::onnxruntime::ml::AGGREGATE_FUNCTION::MAX) {
if (scores[j] > val) val = scores[j];
val += std::get<2>(scores[j]); // third element of tuple is max
}
}
outputs.push_back(val);

View file

@ -15,7 +15,7 @@ class TreeEnsembleRegressor final : public OpKernel {
common::Status Compute(OpKernelContext* context) const override;
private:
common::Status ProcessTreeNode(std::unordered_map<int64_t, float>& classes, int64_t treeindex, const T* Xdata, int64_t feature_base) const;
common::Status ProcessTreeNode(std::unordered_map < int64_t, std::tuple<float, float, float>>& classes, int64_t treeindex, const T* Xdata, int64_t feature_base) const;
std::vector<int64_t> nodes_treeids_;
std::vector<int64_t> nodes_nodeids_;

View file

@ -7,7 +7,8 @@
namespace onnxruntime {
namespace test {
TEST(MLOpTest, TreeRegressorMultiTarget) {
void GenTreeAndRunTest(const std::vector<float>& X, const std::vector<float>& base_values, const std::vector<float>& results, const std::string& aggFunction)
{
OpTester test("TreeEnsembleRegressor", 1, onnxruntime::kMLDomain);
//tree
@ -25,9 +26,78 @@ TEST(MLOpTest, TreeRegressorMultiTarget) {
std::vector<float> target_weights = {1.5f, 27.5f, 2.25f, 20.75f, 2.f, 23.f, 3.f, 14.f, 0.f, 41.f, 1.83333333f, 24.5f, 0.f, 41.f, 2.75f, 16.25f, 2.f, 23.f, 3.f, 14.f, 2.66666667f, 17.f, 2.f, 23.f, 3.f, 14.f};
std::vector<int64_t> classes = {0, 1};
//test data
//add attributes
test.AddAttribute("nodes_truenodeids", lefts);
test.AddAttribute("nodes_falsenodeids", rights);
test.AddAttribute("nodes_treeids", treeids);
test.AddAttribute("nodes_nodeids", nodeids);
test.AddAttribute("nodes_featureids", featureids);
test.AddAttribute("nodes_values", thresholds);
test.AddAttribute("nodes_modes", modes);
test.AddAttribute("target_treeids", target_treeids);
test.AddAttribute("target_nodeids", target_nodeids);
test.AddAttribute("target_ids", target_classids);
test.AddAttribute("target_weights", target_weights);
test.AddAttribute("base_values", base_values);
test.AddAttribute("n_targets", (int64_t)2);
if (aggFunction == "AVERAGE") {
test.AddAttribute("aggregate_function", "AVERAGE");
} else if (aggFunction == "MIN") {
test.AddAttribute("aggregate_function", "MIN");
} else if (aggFunction == "MAX") {
test.AddAttribute("aggregate_function", "MAX");
} // default function is SUM
//fill input data
test.AddInput<float>("X", {8, 3}, X);
test.AddOutput<float>("Y", {8, 2}, results);
test.Run();
}
TEST(MLOpTest, TreeRegressorMultiTargetAverage) {
std::vector<float> X = {1.f, 0.0f, 0.4f, 3.0f, 44.0f, -3.f, 12.0f, 12.9f, -312.f, 23.0f, 11.3f, -222.f, 23.0f, 11.3f, -222.f, 23.0f, 3311.3f, -222.f, 23.0f, 11.3f, -222.f, 43.0f, 413.3f, -114.f};
std::vector<float> results = {1.33333333f, 29.f, 3.f, 14.f, 2.f, 23.f, 2.f, 23.f, 2.f, 23.f, 2.66666667f, 17.f, 2.f, 23.f, 3.f, 14.f};
std::vector<float> base_values{0.f, 0.f};
GenTreeAndRunTest(X, base_values, results, "AVERAGE");
}
TEST(MLOpTest, TreeRegressorMultiTargetMin) {
std::vector<float> X = {1.f, 0.0f, 0.4f, 3.0f, 44.0f, -3.f, 12.0f, 12.9f, -312.f, 23.0f, 11.3f, -222.f, 23.0f, 11.3f, -222.f, 23.0f, 3311.3f, -222.f, 23.0f, 11.3f, -222.f, 43.0f, 413.3f, -114.f};
std::vector<float> results = {5.f, 28.f, 8.f, 19.f, 7.f, 28.f, 7.f, 28.f, 7.f, 28.f, 7.f, 19.f, 7.f, 28.f, 8.f, 19.f};
std::vector<float> base_values{5.f, 5.f};
GenTreeAndRunTest(X, base_values, results, "MIN");
}
TEST(MLOpTest, TreeRegressorMultiTargetMax) {
std::vector<float> X = {1.f, 0.0f, 0.4f, 3.0f, 44.0f, -3.f, 12.0f, 12.9f, -312.f, 23.0f, 11.3f, -222.f, 23.0f, 11.3f, -222.f, 23.0f, 3311.3f, -222.f, 23.0f, 11.3f, -222.f, 43.0f, 413.3f, -114.f};
std::vector<float> results = {2.f, 41.f, 3.f, 14.f, 2.f, 23.f, 2.f, 23.f, 2.f, 23.f, 3.f, 23.f, 2.f, 23.f, 3.f, 14.f};
std::vector<float> base_values{0.f, 0.f};
GenTreeAndRunTest(X, base_values, results, "MAX");
}
TEST(MLOpTest, TreeRegressorSingleTargetSum) {
OpTester test("TreeEnsembleRegressor", 1, onnxruntime::kMLDomain);
//tree
std::vector<int64_t> lefts = {1, 0, 0, 1, 0, 0, 1, 0 ,0};
std::vector<int64_t> rights = {2,0,0,2,0,0,2,0,0};
std::vector<int64_t> treeids = {0,0,0,1,1,1,2,2,2};
std::vector<int64_t> nodeids = {0,1,2,0,1,2,0,1,2};
std::vector<int64_t> featureids = {0,0,0,0,0,0,1,0,0};
std::vector<float> thresholds = {1,0,0,0.5,0,0,0.5,0,0 };
std::vector<std::string> modes = {"BRANCH_LEQ", "LEAF", "LEAF", "BRANCH_LEQ", "LEAF", "LEAF", "BRANCH_LEQ", "LEAF", "LEAF"};
std::vector<int64_t> target_treeids = {0,0,1,1,2,2};
std::vector<int64_t> target_nodeids = {1,2,1,2,1,2};
std::vector<int64_t> target_classids = {0,0,0,0,0,0};
std::vector<float> target_weights = {33.33333f, 16.66666f, 33.33333f, -3.33333f, 16.66666f, -3.333333f};
std::vector<int64_t> classes = {0, 1};
//test data
std::vector<float> X = {0,1,1,1,2,0};
std::vector<float> results = {63.33333333f, 26.66666667f, 30.0f};
//add attributes
test.AddAttribute("nodes_truenodeids", lefts);
@ -42,11 +112,12 @@ TEST(MLOpTest, TreeRegressorMultiTarget) {
test.AddAttribute("target_ids", target_classids);
test.AddAttribute("target_weights", target_weights);
test.AddAttribute("n_targets", (int64_t)2);
test.AddAttribute("aggregate_function", "AVERAGE");
test.AddAttribute("n_targets", (int64_t)1);
// SUM aggregation by default -- no need to add explicitly
//fill input data
test.AddInput<float>("X", {8, 3}, X);
test.AddOutput<float>("Y", {8, 2}, results);
test.AddInput<float>("X", {3, 2}, X);
test.AddOutput<float>("Y", {3, 1}, results);
test.Run();
}