diff --git a/modules/dnn/src/layers/permute_layer.cpp b/modules/dnn/src/layers/permute_layer.cpp index ffc8540a25..05f8c380cc 100644 --- a/modules/dnn/src/layers/permute_layer.cpp +++ b/modules/dnn/src/layers/permute_layer.cpp @@ -184,21 +184,9 @@ public: computeStrides(shape(inputs[0]), shape(outputs[0])); #ifdef HAVE_OPENCL - if (uorder.empty()) - { - std::vector orderVec(_order.begin(), _order.end());; - Mat morder(1, orderVec.size(), CV_32SC1, &orderVec[0]); - - std::vector oldStrideVec(_oldStride.begin(), _oldStride.end()); - Mat mold_stride(1, _oldStride.size(), CV_32SC1, &oldStrideVec[0]); - - std::vector newStrideVec(_newStride.begin(), _newStride.end()); - Mat mnew_stride(1, newStrideVec.size(), CV_32SC1, &newStrideVec[0]); - - morder.copyTo(uorder); - mold_stride.copyTo(uold_stride); - mnew_stride.copyTo(unew_stride); - } + uorder.release(); + uold_stride.release(); + unew_stride.release(); #endif } @@ -286,6 +274,22 @@ public: if (!_needsPermute) return false; + if (uorder.empty()) + { + std::vector orderVec(_order.begin(), _order.end());; + Mat morder(1, orderVec.size(), CV_32SC1, &orderVec[0]); + + std::vector oldStrideVec(_oldStride.begin(), _oldStride.end()); + Mat mold_stride(1, _oldStride.size(), CV_32SC1, &oldStrideVec[0]); + + std::vector newStrideVec(_newStride.begin(), _newStride.end()); + Mat mnew_stride(1, newStrideVec.size(), CV_32SC1, &newStrideVec[0]); + + morder.copyTo(uorder); + mold_stride.copyTo(uold_stride); + mnew_stride.copyTo(unew_stride); + } + bool use_half = (inps.depth() == CV_16S); String opts = format("-DDtype=%s", use_half ? "half" : "float"); for (size_t i = 0; i < inputs.size(); i++) diff --git a/modules/dnn/src/onnx/onnx_importer.cpp b/modules/dnn/src/onnx/onnx_importer.cpp index 3ae2b0b7bb..4d01076d4a 100644 --- a/modules/dnn/src/onnx/onnx_importer.cpp +++ b/modules/dnn/src/onnx/onnx_importer.cpp @@ -335,6 +335,10 @@ void ONNXImporter::populateNet(Net dstNet) { inpShape[j] = tensorShape.dim(j).dim_value(); } + if (!inpShape.empty()) + { + inpShape[0] = std::max(inpShape[0], 1); // It's OK to have undetermined batch size + } outShapes[valueInfoProto.name()] = inpShape; } diff --git a/modules/dnn/test/test_layers.cpp b/modules/dnn/test/test_layers.cpp index fc19e09b56..13e00de091 100644 --- a/modules/dnn/test/test_layers.cpp +++ b/modules/dnn/test/test_layers.cpp @@ -2388,7 +2388,7 @@ TEST_P(ConvolutionEltwiseActivationFusion, Accuracy) if (eltwiseOp != "sum" && weightedEltwise) throw SkipTestException("weighted eltwise not supported"); LayerParams eltwiseParams; - TestLayerFusion::makeDefaultTestEltwiseLayer(eltwiseParams, eltwiseOp, false); + TestLayerFusion::makeDefaultTestEltwiseLayer(eltwiseParams, eltwiseOp, weightedEltwise); std::string actType = get<3>(GetParam()); LayerParams activationParams; @@ -2398,7 +2398,7 @@ TEST_P(ConvolutionEltwiseActivationFusion, Accuracy) Target targetId = get<1>(get<4>(GetParam())); // bug: https://github.com/opencv/opencv/issues/17945 - if (eltwiseOp != "sum" && backendId == DNN_BACKEND_OPENCV && (targetId == DNN_TARGET_OPENCL || targetId == DNN_TARGET_OPENCL_FP16)) + if ((eltwiseOp != "sum" || weightedEltwise) && backendId == DNN_BACKEND_OPENCV && (targetId == DNN_TARGET_OPENCL || targetId == DNN_TARGET_OPENCL_FP16)) applyTestTag(CV_TEST_TAG_DNN_SKIP_OPENCL); // bug: https://github.com/opencv/opencv/issues/17953 @@ -2485,7 +2485,7 @@ TEST_P(ConvolutionActivationEltwiseFusion, Accuracy) if (eltwiseOp != "sum" && weightedEltwise) throw SkipTestException("weighted eltwise not supported"); LayerParams eltwiseParams; - TestLayerFusion::makeDefaultTestEltwiseLayer(eltwiseParams, eltwiseOp, false); + TestLayerFusion::makeDefaultTestEltwiseLayer(eltwiseParams, eltwiseOp, weightedEltwise); Backend backendId = get<0>(get<4>(GetParam())); Target targetId = get<1>(get<4>(GetParam())); diff --git a/modules/ml/include/opencv2/ml.hpp b/modules/ml/include/opencv2/ml.hpp index 7fdb460eb3..d537ab7759 100644 --- a/modules/ml/include/opencv2/ml.hpp +++ b/modules/ml/include/opencv2/ml.hpp @@ -1294,6 +1294,15 @@ public: */ CV_WRAP virtual void getVotes(InputArray samples, OutputArray results, int flags) const = 0; + /** Returns the OOB error value, computed at the training stage when calcOOBError is set to true. + * If this flag was set to false, 0 is returned. The OOB error is also scaled by sample weighting. + */ +#if CV_VERSION_MAJOR == 4 + CV_WRAP virtual double getOOBError() const { return 0; } +#else + /*CV_WRAP*/ virtual double getOOBError() const = 0; +#endif + /** Creates the empty model. Use StatModel::train to train the model, StatModel::train to create and train the model, Algorithm::load to load the pre-trained model. diff --git a/modules/ml/src/kdtree.cpp b/modules/ml/src/kdtree.cpp index 1ab8400936..a80e12964a 100644 --- a/modules/ml/src/kdtree.cpp +++ b/modules/ml/src/kdtree.cpp @@ -101,7 +101,7 @@ medianPartition( size_t* ofs, int a, int b, const float* vals ) int i0 = a, i1 = (a+b)/2, i2 = b; float v0 = vals[ofs[i0]], v1 = vals[ofs[i1]], v2 = vals[ofs[i2]]; int ip = v0 < v1 ? (v1 < v2 ? i1 : v0 < v2 ? i2 : i0) : - v0 < v2 ? i0 : (v1 < v2 ? i2 : i1); + v0 < v2 ? (v1 == v0 ? i2 : i0): (v1 < v2 ? i2 : i1); float pivot = vals[ofs[ip]]; std::swap(ofs[ip], ofs[i2]); @@ -131,7 +131,6 @@ medianPartition( size_t* ofs, int a, int b, const float* vals ) CV_Assert(vals[ofs[k]] >= pivot); more += vals[ofs[k]] > pivot; } - CV_Assert(std::abs(more - less) <= 1); return vals[ofs[middle]]; } diff --git a/modules/ml/src/knearest.cpp b/modules/ml/src/knearest.cpp index ca23d0f4d6..3d8f9b5d2e 100644 --- a/modules/ml/src/knearest.cpp +++ b/modules/ml/src/knearest.cpp @@ -381,36 +381,23 @@ public: Mat res, nr, d; if( _results.needed() ) { - _results.create(testcount, 1, CV_32F); res = _results.getMat(); } if( _neighborResponses.needed() ) { - _neighborResponses.create(testcount, k, CV_32F); nr = _neighborResponses.getMat(); } if( _dists.needed() ) { - _dists.create(testcount, k, CV_32F); d = _dists.getMat(); } for (int i=0; ii) - { - _res = res.row(i); - } - if (nr.rows>i) - { - _nr = nr.row(i); - } - if (d.rows>i) - { - _d = d.row(i); - } tr.findNearest(test_samples.row(i), k, Emax, _res, _nr, _d, noArray()); + res.push_back(_res.t()); + _results.assign(res); } return result; // currently always 0 diff --git a/modules/ml/src/rtrees.cpp b/modules/ml/src/rtrees.cpp index 9c7ec4f8b3..216b42bab6 100644 --- a/modules/ml/src/rtrees.cpp +++ b/modules/ml/src/rtrees.cpp @@ -216,13 +216,14 @@ public: sample = Mat( nallvars, 1, CV_32F, psamples + sstep0*w->sidx[j], sstep1*sizeof(psamples[0]) ); double val = predictTrees(Range(treeidx, treeidx+1), sample, predictFlags); + double sample_weight = w->sample_weights[w->sidx[j]]; if( !_isClassifier ) { oobres[j] += val; oobcount[j]++; double true_val = w->ord_responses[w->sidx[j]]; double a = oobres[j]/oobcount[j] - true_val; - oobError += a*a; + oobError += sample_weight * a*a; val = (val - true_val)/max_response; ncorrect_responses += std::exp( -val*val ); } @@ -237,7 +238,7 @@ public: if( votes[best_class] < votes[k] ) best_class = k; int diff = best_class != w->cat_responses[w->sidx[j]]; - oobError += diff; + oobError += sample_weight * diff; ncorrect_responses += diff == 0; } } @@ -421,6 +422,10 @@ public: } } + double getOOBError() const { + return oobError; + } + RTreeParams rparams; double oobError; vector varImportance; @@ -500,6 +505,8 @@ public: const vector& getNodes() const CV_OVERRIDE { return impl.getNodes(); } const vector& getSplits() const CV_OVERRIDE { return impl.getSplits(); } const vector& getSubsets() const CV_OVERRIDE { return impl.getSubsets(); } + double getOOBError() const CV_OVERRIDE { return impl.getOOBError(); } + DTreesImplForRTrees impl; }; diff --git a/modules/ml/test/test_knearest.cpp b/modules/ml/test/test_knearest.cpp index 49e6b0d12a..80baed9626 100644 --- a/modules/ml/test/test_knearest.cpp +++ b/modules/ml/test/test_knearest.cpp @@ -37,18 +37,31 @@ TEST(ML_KNearest, accuracy) EXPECT_LE(err, 0.01f); } { - // TODO: broken -#if 0 SCOPED_TRACE("KDTree"); - Mat bestLabels; + Mat neighborIndexes; float err = 1000; Ptr knn = KNearest::create(); knn->setAlgorithmType(KNearest::KDTREE); knn->train(trainData, ml::ROW_SAMPLE, trainLabels); - knn->findNearest(testData, 4, bestLabels); + knn->findNearest(testData, 4, neighborIndexes); + Mat bestLabels; + // The output of the KDTree are the neighbor indexes, not actual class labels + // so we need to do some extra work to get actual predictions + for(int row_num = 0; row_num < neighborIndexes.rows; ++row_num){ + vector labels; + for(int index = 0; index < neighborIndexes.row(row_num).cols; ++index) { + labels.push_back(trainLabels.at(neighborIndexes.row(row_num).at(0, index) , 0)); + } + // computing the mode of the output class predictions to determine overall prediction + std::vector histogram(3,0); + for( int i=0; i<3; ++i ) + ++histogram[ static_cast(labels[i]) ]; + int bestLabel = static_cast(std::max_element( histogram.begin(), histogram.end() ) - histogram.begin()); + bestLabels.push_back(bestLabel); + } + bestLabels.convertTo(bestLabels, testLabels.type()); EXPECT_TRUE(calcErr( bestLabels, testLabels, sizes, err, true )); EXPECT_LE(err, 0.01f); -#endif } } @@ -74,4 +87,26 @@ TEST(ML_KNearest, regression_12347) EXPECT_EQ(2, zBestLabels.at(1,0)); } +TEST(ML_KNearest, bug_11877) +{ + Mat trainData = (Mat_(5,2) << 3, 3, 3, 3, 4, 4, 4, 4, 4, 4); + Mat trainLabels = (Mat_(5,1) << 0, 0, 1, 1, 1); + + Ptr knnKdt = KNearest::create(); + knnKdt->setAlgorithmType(KNearest::KDTREE); + knnKdt->setIsClassifier(true); + + knnKdt->train(trainData, ml::ROW_SAMPLE, trainLabels); + + Mat testData = (Mat_(2,2) << 3.1, 3.1, 4, 4.1); + Mat testLabels = (Mat_(2,1) << 0, 1); + Mat result; + + knnKdt->findNearest(testData, 1, result); + + EXPECT_EQ(1, int(result.at(0, 0))); + EXPECT_EQ(2, int(result.at(1, 0))); + EXPECT_EQ(0, trainLabels.at(result.at(0, 0), 0)); +} + }} // namespace diff --git a/modules/ml/test/test_rtrees.cpp b/modules/ml/test/test_rtrees.cpp index ebf0c46557..1ec9b8d042 100644 --- a/modules/ml/test/test_rtrees.cpp +++ b/modules/ml/test/test_rtrees.cpp @@ -51,4 +51,50 @@ TEST(ML_RTrees, getVotes) EXPECT_EQ(result.at(0, predicted_class), rt->predict(test)); } +TEST(ML_RTrees, 11142_sample_weights_regression) +{ + int n = 3; + // RTrees for regression + Ptr rt = cv::ml::RTrees::create(); + //simple regression problem of x -> 2x + Mat data = (Mat_(n,1) << 1, 2, 3); + Mat values = (Mat_(n,1) << 2, 4, 6); + Mat weights = (Mat_(n, 1) << 10, 10, 10); + + Ptr trainData = TrainData::create(data, ml::ROW_SAMPLE, values); + rt->train(trainData); + double error_without_weights = round(rt->getOOBError()); + rt->clear(); + Ptr trainDataWithWeights = TrainData::create(data, ml::ROW_SAMPLE, values, Mat(), Mat(), weights ); + rt->train(trainDataWithWeights); + double error_with_weights = round(rt->getOOBError()); + // error with weights should be larger than error without weights + EXPECT_GE(error_with_weights, error_without_weights); +} + +TEST(ML_RTrees, 11142_sample_weights_classification) +{ + int n = 12; + // RTrees for classification + Ptr rt = cv::ml::RTrees::create(); + + Mat data(n, 4, CV_32F); + randu(data, 0, 10); + Mat labels = (Mat_(n,1) << 0,0,0,0, 1,1,1,1, 2,2,2,2); + Mat weights = (Mat_(n, 1) << 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10); + + rt->train(data, ml::ROW_SAMPLE, labels); + rt->clear(); + double error_without_weights = round(rt->getOOBError()); + Ptr trainDataWithWeights = TrainData::create(data, ml::ROW_SAMPLE, labels, Mat(), Mat(), weights ); + rt->train(data, ml::ROW_SAMPLE, labels); + double error_with_weights = round(rt->getOOBError()); + std::cout << error_without_weights << std::endl; + std::cout << error_with_weights << std::endl; + // error with weights should be larger than error without weights + EXPECT_GE(error_with_weights, error_without_weights); +} + + + }} // namespace