Loading src/loss_function/LossFunctionSilhouetteScore.cpp +60 −90 Original line number Diff line number Diff line Loading @@ -4,8 +4,6 @@ #include "LossFunctionSilhouetteScore.hpp" #include <iostream> #include <typeinfo> LossFunctionSilhouetteScore::LossFunctionSilhouetteScore(std::vector<double> prop_train, Loading @@ -17,9 +15,7 @@ LossFunctionSilhouetteScore::LossFunctionSilhouetteScore(std::vector<double> pro : LossFunction(prop_train, prop_test, task_sizes_train, std::move(task_sizes_test), false, n_feat), _n_class_per_task(task_sizes_train.size(), 0), _n_class(0) //ripped setup from convex hull { for (auto& pt : _prop_test) { if (std::none_of(_prop_train.begin(), _prop_train.end(), [&pt](double pp) { Loading @@ -30,7 +26,6 @@ LossFunctionSilhouetteScore::LossFunctionSilhouetteScore(std::vector<double> pro "A class in the property vector (test set) is not in the training set."); } } int start = 0.0; std::vector<double> unique_classes; for (size_t tt = 0; tt < _task_sizes_train.size(); ++tt) Loading @@ -48,7 +43,6 @@ LossFunctionSilhouetteScore::LossFunctionSilhouetteScore(std::vector<double> pro } start += _task_sizes_train[tt]; } unique_classes = vector_utils::unique<double>(_prop_train); _n_class = unique_classes.size(); for (int ccprop_test = 0; ccprop_test < _n_class; ++ccprop_test) Loading Loading @@ -94,7 +88,6 @@ LossFunctionSilhouetteScore::LossFunctionSilhouetteScore(const std::shared_ptr<L set_nfeat(_n_feat); } void LossFunctionSilhouetteScore::set_nfeat(int n_feat) { _n_feat = n_feat; Loading @@ -104,89 +97,72 @@ void LossFunctionSilhouetteScore::set_nfeat(int n_feat) return tot + nc * (nc - 1) / 2; }); _coefs.resize(n_class_combos * _n_dim, 0.0); int start = 0; _svm.clear(); for (int tt = 0; tt < _n_task; ++tt) { _svm.push_back(std::make_shared<SVMWrapper>( _n_class_per_task[tt], _n_feat, _task_sizes_train[tt], &_prop_train[start])); start += _task_sizes_train[tt]; } } //operate under the assumption that operator(feats) has been called before the call to inds() double LossFunctionSilhouetteScore::operator()(const std::vector<int>& inds) { std::vector<model_node_ptr> feats; _a.clear(); _a.resize(_n_feat * _n_samp); for (int ii = 0; ii < inds.size(); ii++) { for (int ii = 0; ii < inds.size(); ++ii) { std::copy_n( node_value_arrs::get_d_matrix_ptr(inds[ii]), _n_samp, _a.data() + ii * _n_samp ); } std::vector<double*> feat_ptrs(_n_feat); for (size_t i = 0; i < _n_feat; i++){ feat_ptrs[i] = &_a[i * _n_samp]; } _svm[0]->train(feat_ptrs); _prop_test_est = _svm[0]->predict(_n_samp, feat_ptrs); std::vector<model_node_ptr> feats; //I think this should also return the silhouette score //Maybe make dummy modelnodes and rip the svm values into them, then call the same pipeline //as feat inputs // for (size_t i = 0; i < _n_feat; i++){ // double* train_ptr = &_a[i * _n_samp]; // std::vector<double> train_vec(train_ptr, train_ptr + _n_samp); // std::vector<double> test_vec = train_vec; // auto fnode = std::make_shared<FeatureNode>(i, "feat_" + std::to_string(inds[i]), // train_vec, test_vec, Unit("")); // feats.push_back(std::make_shared<ModelNode>(fnode)); //Might be a better way to do this than creating new model nodes from the d_matrix values. //Could rewire the map to take <*double, double> instead of <ModelNode, double> for (size_t i = 0; i < _n_feat; ++i) { double* train_ptr = &_a[i * _n_samp]; std::vector<double> train_vec(train_ptr, train_ptr + _n_samp); std::vector<double> test_vec(train_ptr, train_ptr + node_value_arrs::N_SAMPLES_TEST); auto fnode = std::make_shared<FeatureNode>( i, "feat_" + std::to_string(inds[i]), train_vec, test_vec, Unit("") ); feats.push_back(std::make_shared<ModelNode>(fnode)); } // silhouette_scores.clear(); // auto centroids = get_all_centroids(feats); // calculate_centroid_differences(feats, centroids); // return get_silhouette_average(); return 0.0; return (*this)(feats); } double LossFunctionSilhouetteScore::operator()(const std::vector<model_node_ptr>& feats) { int start = 0, start_test = 0, start_coefs = 0; for (size_t tt = 0; tt < _task_sizes_train.size(); ++tt) { std::vector<double*> node_val_ptrs(_n_feat); std::vector<double*> node_test_val_ptrs(_n_feat); for (int dd = 0; dd < _n_feat; ++dd) { node_val_ptrs[dd] = feats[dd]->value_ptr() + start; node_test_val_ptrs[dd] = feats[dd]->test_value_ptr() + start_test; } _svm[tt]->train(node_val_ptrs); const auto& coefs = _svm[tt]->coefs(); for (size_t cc = 0; cc < coefs.size(); ++cc) { std::copy_n(coefs[cc].data(), coefs[cc].size(), &_coefs[start_coefs * _n_dim]); _coefs[start_coefs * _n_dim + _n_feat] = _svm[tt]->intercept()[cc]; ++start_coefs; } std::copy_n(_svm[tt]->y_estimate().begin(), _task_sizes_train[tt], _prop_train_est.begin() + start); std::copy_n(_svm[tt]->predict(_task_sizes_test[tt], node_test_val_ptrs).begin(), _task_sizes_test[tt], _prop_test_est.begin() + start_test); start += _task_sizes_train[tt]; start_test += _task_sizes_test[tt]; } std::map<const ModelNode*, double> centroids = get_all_centroids(feats); calculate_centroid_differences(feats, centroids); get_all_centroids(feats); calculate_centroid_differences(_prop_train); return get_silhouette_average(); //this should be moved to the classifier //int start = 0, start_test = 0, start_coefs = 0; // for (size_t tt = 0; tt < _task_sizes_train.size(); ++tt) // { // std::vector<double*> node_val_ptrs(_n_feat); // std::vector<double*> node_test_val_ptrs(_n_feat); // // for (int dd = 0; dd < _n_feat; ++dd) // { // node_val_ptrs[dd] = feats[dd]->value_ptr() + start; // node_test_val_ptrs[dd] = feats[dd]->test_value_ptr() + start_test; // } // // _svm[tt]->train(node_val_ptrs); // // const auto& coefs = _svm[tt]->coefs(); // for (size_t cc = 0; cc < coefs.size(); ++cc) // { // std::copy_n(coefs[cc].data(), coefs[cc].size(), &_coefs[start_coefs * _n_dim]); // _coefs[start_coefs * _n_dim + _n_feat] = _svm[tt]->intercept()[cc]; // ++start_coefs; // } // // std::copy_n(_svm[tt]->y_estimate().begin(), _task_sizes_train[tt], _prop_train_est.begin() + start); // std::copy_n(_svm[tt]->predict(_task_sizes_test[tt], node_test_val_ptrs).begin(), // _task_sizes_test[tt], // _prop_test_est.begin() + start_test); // // start += _task_sizes_train[tt]; // start_test += _task_sizes_test[tt]; } double LossFunctionSilhouetteScore::test_loss(const std::vector<model_node_ptr>& feats) { Loading @@ -195,9 +171,8 @@ double LossFunctionSilhouetteScore::test_loss(const std::vector<model_node_ptr>& //Assuming that each model node corresponds to a unique feature //Add all of the model nodes values to a hashmap, to map the nodes to their centroids, //Afterwards we can re-iterate and calculate the differences to grab the silhouettescore std::map<const ModelNode*, double> LossFunctionSilhouetteScore::get_all_centroids(const std::vector<model_node_ptr>& feats) void LossFunctionSilhouetteScore::get_all_centroids(const std::vector<model_node_ptr>& feats) { std::map<const ModelNode*, double> centroid_map; for (model_node_ptr model : feats) { double curr_total = 0; Loading @@ -208,22 +183,22 @@ std::map<const ModelNode*, double> LossFunctionSilhouetteScore::get_all_centroid //grab the raw pointer from the shared pointer centroid_map[model.get()] = curr_total / model->svm_value().size(); } return centroid_map; } void LossFunctionSilhouetteScore::calculate_centroid_differences(const std::vector<model_node_ptr>feats, std::map<const ModelNode*, double> centroid_map) //now iterate over _prop_test and centroid map, and use the difference from the closest centroid value void LossFunctionSilhouetteScore::calculate_centroid_differences(std::vector<double> input_values) { for (int i = 0; i < _prop_test_est.size(); i++) for (int i = 0; i < input_values.size(); i++) { double min = std::numeric_limits<double>::max(); for (const auto& pair : centroid_map) { double centroid_value = pair.second; double curr_diff = std::abs(_prop_test_est[i] - centroid_value); //Find the closest centroid double curr_diff = std::abs(input_values[i] - centroid_value); if (curr_diff < min) { min = curr_diff; // b - a / max(a,b) min = curr_diff/std::max(input_values[i], centroid_value); } } silhouette_scores.push_back(min); Loading @@ -231,12 +206,7 @@ void LossFunctionSilhouetteScore::calculate_centroid_differences(const std::vect } double LossFunctionSilhouetteScore::get_silhouette_average() { double avg = 0; for (int i = 0; i < silhouette_scores.size(); i++){ avg+= silhouette_scores[i]; if (silhouette_scores.empty()) return 0.0; return std::accumulate(silhouette_scores.begin(), silhouette_scores.end(), 0.0) / static_cast<double>(silhouette_scores.size()); } No newline at end of file return avg/silhouette_scores.size(); } src/loss_function/LossFunctionSilhouetteScore.hpp +3 −2 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ protected: std::vector<int> _n_class_per_task; //!< Number of classes in the property int _n_class; //!< Number of classes in the property std::vector<double> _a; //!< matrix to copy values from the D_Matrix std::map<const ModelNode*, double> centroid_map; public: /** Loading Loading @@ -85,11 +86,11 @@ public: inline std::shared_ptr<SVMWrapper> svm(int tt) override { return _svm[tt]; } virtual std::map<const ModelNode*, double> get_all_centroids(const std::vector<model_node_ptr>& feats); virtual void get_all_centroids(const std::vector<model_node_ptr>& feats); virtual double get_silhouette_average(); virtual void calculate_centroid_differences(const std::vector<model_node_ptr> feats, std::map<const ModelNode*, double> centroid_map); virtual void calculate_centroid_differences(std::vector<double> input_values); std::vector<double> get_silhouette_scores(){ return silhouette_scores; Loading tests/googletest/loss_function/test_silhouette_score_loss.cc +19 −44 Original line number Diff line number Diff line Loading @@ -107,10 +107,7 @@ protected: std::fill_n(_prop_test.begin() + 15, 5, 3.0); } void TearDown() override { node_value_arrs::finalize_values_arr(); } void TearDown() override { node_value_arrs::finalize_values_arr(); } std::vector<node_ptr> _phi; std::vector<model_node_ptr> _model_phi; Loading @@ -122,15 +119,10 @@ protected: std::vector<int> _task_sizes_test; }; TEST_F(LossFunctionSilhouetteScoreTests, NoFixIntercept) { TEST_F(LossFunctionSilhouetteScoreTests, NoFixIntercept) { LossFunctionSilhouetteScore loss( _prop_train, _prop_test, _task_sizes_train, _task_sizes_test, false, 2 ); _prop_train, _prop_test, _task_sizes_train, _task_sizes_test, false, 2); EXPECT_GE(loss(_model_phi), -1.0); EXPECT_LE(loss(_model_phi), 1.0); EXPECT_GE(loss({0, 1}), -1.0); Loading @@ -140,15 +132,10 @@ TEST_F(LossFunctionSilhouetteScoreTests, NoFixIntercept) { EXPECT_EQ(loss.type(), LOSS_TYPE::SILHOUETTE_SCORE); } TEST_F(LossFunctionSilhouetteScoreTests, CopyNoFixIntercept) { TEST_F(LossFunctionSilhouetteScoreTests, CopyNoFixIntercept) { LossFunctionSilhouetteScore loss( _prop_train, _prop_test, _task_sizes_train, _task_sizes_test, false, 2 ); _prop_train, _prop_test, _task_sizes_train, _task_sizes_test, false, 2); LossFunctionSilhouetteScore loss_copy(std::make_shared<LossFunctionSilhouetteScore>(loss)); Loading @@ -161,15 +148,12 @@ TEST_F(LossFunctionSilhouetteScoreTests, CopyNoFixIntercept) { EXPECT_EQ(loss_copy.type(), LOSS_TYPE::SILHOUETTE_SCORE); } TEST_F(LossFunctionSilhouetteScoreTests, ManualDetermination) { TEST_F(LossFunctionSilhouetteScoreTests, ManualDetermination) { _task_sizes_train = {4}; _task_sizes_test = {4}; node_value_arrs::finalize_values_arr(); node_value_arrs::initialize_values_arr(_task_sizes_train, _task_sizes_test, 2, 2, false); //not division by the maximum //model node does x[i] - min(x) / (max(x) - min(x)) // the lowest val will always result to 0 // std::vector<double> train_feat1 = {1.0, 2.0, 3.0, 4.0}; //normalized scored Loading @@ -186,39 +170,30 @@ TEST_F(LossFunctionSilhouetteScoreTests, ManualDetermination) { _phi.clear(); _model_phi.clear(); _phi.push_back(std::make_shared<FeatureNode>(0, "Feature1", train_feat1, test_feat1, Unit("m"))); _phi.push_back(std::make_shared<FeatureNode>(1, "Feature2", train_feat2, test_feat2, Unit("m"))); _phi.push_back( std::make_shared<FeatureNode>(0, "Feature1", train_feat1, test_feat1, Unit("m"))); _phi.push_back( std::make_shared<FeatureNode>(1, "Feature2", train_feat2, test_feat2, Unit("m"))); _model_phi.push_back(std::make_shared<ModelNode>(_phi[0])); _model_phi.push_back(std::make_shared<ModelNode>(_phi[1])); _prop_train = {0.0, 0.0, 1.0, 1.0}; // abs(0-.46)/max(0,.46) = .46/.46 = 1 // abs(1-.5)/max(1,.5) = .5/1 = .5 //sil score values should be 1,1,.5.5 = 3/4 = 0.75 _prop_test = _prop_train; _task_sizes_train = {4}; _task_sizes_test = {4}; LossFunctionSilhouetteScore loss( _prop_train, _prop_test, _task_sizes_train, _task_sizes_test, false, 2 ); _prop_train, _prop_test, _task_sizes_train, _task_sizes_test, false, 2); double silhouette_score = loss(_model_phi); EXPECT_NEAR(silhouette_score, 0.48, 0.01); EXPECT_EQ(silhouette_score,0.75); } } Loading Loading
src/loss_function/LossFunctionSilhouetteScore.cpp +60 −90 Original line number Diff line number Diff line Loading @@ -4,8 +4,6 @@ #include "LossFunctionSilhouetteScore.hpp" #include <iostream> #include <typeinfo> LossFunctionSilhouetteScore::LossFunctionSilhouetteScore(std::vector<double> prop_train, Loading @@ -17,9 +15,7 @@ LossFunctionSilhouetteScore::LossFunctionSilhouetteScore(std::vector<double> pro : LossFunction(prop_train, prop_test, task_sizes_train, std::move(task_sizes_test), false, n_feat), _n_class_per_task(task_sizes_train.size(), 0), _n_class(0) //ripped setup from convex hull { for (auto& pt : _prop_test) { if (std::none_of(_prop_train.begin(), _prop_train.end(), [&pt](double pp) { Loading @@ -30,7 +26,6 @@ LossFunctionSilhouetteScore::LossFunctionSilhouetteScore(std::vector<double> pro "A class in the property vector (test set) is not in the training set."); } } int start = 0.0; std::vector<double> unique_classes; for (size_t tt = 0; tt < _task_sizes_train.size(); ++tt) Loading @@ -48,7 +43,6 @@ LossFunctionSilhouetteScore::LossFunctionSilhouetteScore(std::vector<double> pro } start += _task_sizes_train[tt]; } unique_classes = vector_utils::unique<double>(_prop_train); _n_class = unique_classes.size(); for (int ccprop_test = 0; ccprop_test < _n_class; ++ccprop_test) Loading Loading @@ -94,7 +88,6 @@ LossFunctionSilhouetteScore::LossFunctionSilhouetteScore(const std::shared_ptr<L set_nfeat(_n_feat); } void LossFunctionSilhouetteScore::set_nfeat(int n_feat) { _n_feat = n_feat; Loading @@ -104,89 +97,72 @@ void LossFunctionSilhouetteScore::set_nfeat(int n_feat) return tot + nc * (nc - 1) / 2; }); _coefs.resize(n_class_combos * _n_dim, 0.0); int start = 0; _svm.clear(); for (int tt = 0; tt < _n_task; ++tt) { _svm.push_back(std::make_shared<SVMWrapper>( _n_class_per_task[tt], _n_feat, _task_sizes_train[tt], &_prop_train[start])); start += _task_sizes_train[tt]; } } //operate under the assumption that operator(feats) has been called before the call to inds() double LossFunctionSilhouetteScore::operator()(const std::vector<int>& inds) { std::vector<model_node_ptr> feats; _a.clear(); _a.resize(_n_feat * _n_samp); for (int ii = 0; ii < inds.size(); ii++) { for (int ii = 0; ii < inds.size(); ++ii) { std::copy_n( node_value_arrs::get_d_matrix_ptr(inds[ii]), _n_samp, _a.data() + ii * _n_samp ); } std::vector<double*> feat_ptrs(_n_feat); for (size_t i = 0; i < _n_feat; i++){ feat_ptrs[i] = &_a[i * _n_samp]; } _svm[0]->train(feat_ptrs); _prop_test_est = _svm[0]->predict(_n_samp, feat_ptrs); std::vector<model_node_ptr> feats; //I think this should also return the silhouette score //Maybe make dummy modelnodes and rip the svm values into them, then call the same pipeline //as feat inputs // for (size_t i = 0; i < _n_feat; i++){ // double* train_ptr = &_a[i * _n_samp]; // std::vector<double> train_vec(train_ptr, train_ptr + _n_samp); // std::vector<double> test_vec = train_vec; // auto fnode = std::make_shared<FeatureNode>(i, "feat_" + std::to_string(inds[i]), // train_vec, test_vec, Unit("")); // feats.push_back(std::make_shared<ModelNode>(fnode)); //Might be a better way to do this than creating new model nodes from the d_matrix values. //Could rewire the map to take <*double, double> instead of <ModelNode, double> for (size_t i = 0; i < _n_feat; ++i) { double* train_ptr = &_a[i * _n_samp]; std::vector<double> train_vec(train_ptr, train_ptr + _n_samp); std::vector<double> test_vec(train_ptr, train_ptr + node_value_arrs::N_SAMPLES_TEST); auto fnode = std::make_shared<FeatureNode>( i, "feat_" + std::to_string(inds[i]), train_vec, test_vec, Unit("") ); feats.push_back(std::make_shared<ModelNode>(fnode)); } // silhouette_scores.clear(); // auto centroids = get_all_centroids(feats); // calculate_centroid_differences(feats, centroids); // return get_silhouette_average(); return 0.0; return (*this)(feats); } double LossFunctionSilhouetteScore::operator()(const std::vector<model_node_ptr>& feats) { int start = 0, start_test = 0, start_coefs = 0; for (size_t tt = 0; tt < _task_sizes_train.size(); ++tt) { std::vector<double*> node_val_ptrs(_n_feat); std::vector<double*> node_test_val_ptrs(_n_feat); for (int dd = 0; dd < _n_feat; ++dd) { node_val_ptrs[dd] = feats[dd]->value_ptr() + start; node_test_val_ptrs[dd] = feats[dd]->test_value_ptr() + start_test; } _svm[tt]->train(node_val_ptrs); const auto& coefs = _svm[tt]->coefs(); for (size_t cc = 0; cc < coefs.size(); ++cc) { std::copy_n(coefs[cc].data(), coefs[cc].size(), &_coefs[start_coefs * _n_dim]); _coefs[start_coefs * _n_dim + _n_feat] = _svm[tt]->intercept()[cc]; ++start_coefs; } std::copy_n(_svm[tt]->y_estimate().begin(), _task_sizes_train[tt], _prop_train_est.begin() + start); std::copy_n(_svm[tt]->predict(_task_sizes_test[tt], node_test_val_ptrs).begin(), _task_sizes_test[tt], _prop_test_est.begin() + start_test); start += _task_sizes_train[tt]; start_test += _task_sizes_test[tt]; } std::map<const ModelNode*, double> centroids = get_all_centroids(feats); calculate_centroid_differences(feats, centroids); get_all_centroids(feats); calculate_centroid_differences(_prop_train); return get_silhouette_average(); //this should be moved to the classifier //int start = 0, start_test = 0, start_coefs = 0; // for (size_t tt = 0; tt < _task_sizes_train.size(); ++tt) // { // std::vector<double*> node_val_ptrs(_n_feat); // std::vector<double*> node_test_val_ptrs(_n_feat); // // for (int dd = 0; dd < _n_feat; ++dd) // { // node_val_ptrs[dd] = feats[dd]->value_ptr() + start; // node_test_val_ptrs[dd] = feats[dd]->test_value_ptr() + start_test; // } // // _svm[tt]->train(node_val_ptrs); // // const auto& coefs = _svm[tt]->coefs(); // for (size_t cc = 0; cc < coefs.size(); ++cc) // { // std::copy_n(coefs[cc].data(), coefs[cc].size(), &_coefs[start_coefs * _n_dim]); // _coefs[start_coefs * _n_dim + _n_feat] = _svm[tt]->intercept()[cc]; // ++start_coefs; // } // // std::copy_n(_svm[tt]->y_estimate().begin(), _task_sizes_train[tt], _prop_train_est.begin() + start); // std::copy_n(_svm[tt]->predict(_task_sizes_test[tt], node_test_val_ptrs).begin(), // _task_sizes_test[tt], // _prop_test_est.begin() + start_test); // // start += _task_sizes_train[tt]; // start_test += _task_sizes_test[tt]; } double LossFunctionSilhouetteScore::test_loss(const std::vector<model_node_ptr>& feats) { Loading @@ -195,9 +171,8 @@ double LossFunctionSilhouetteScore::test_loss(const std::vector<model_node_ptr>& //Assuming that each model node corresponds to a unique feature //Add all of the model nodes values to a hashmap, to map the nodes to their centroids, //Afterwards we can re-iterate and calculate the differences to grab the silhouettescore std::map<const ModelNode*, double> LossFunctionSilhouetteScore::get_all_centroids(const std::vector<model_node_ptr>& feats) void LossFunctionSilhouetteScore::get_all_centroids(const std::vector<model_node_ptr>& feats) { std::map<const ModelNode*, double> centroid_map; for (model_node_ptr model : feats) { double curr_total = 0; Loading @@ -208,22 +183,22 @@ std::map<const ModelNode*, double> LossFunctionSilhouetteScore::get_all_centroid //grab the raw pointer from the shared pointer centroid_map[model.get()] = curr_total / model->svm_value().size(); } return centroid_map; } void LossFunctionSilhouetteScore::calculate_centroid_differences(const std::vector<model_node_ptr>feats, std::map<const ModelNode*, double> centroid_map) //now iterate over _prop_test and centroid map, and use the difference from the closest centroid value void LossFunctionSilhouetteScore::calculate_centroid_differences(std::vector<double> input_values) { for (int i = 0; i < _prop_test_est.size(); i++) for (int i = 0; i < input_values.size(); i++) { double min = std::numeric_limits<double>::max(); for (const auto& pair : centroid_map) { double centroid_value = pair.second; double curr_diff = std::abs(_prop_test_est[i] - centroid_value); //Find the closest centroid double curr_diff = std::abs(input_values[i] - centroid_value); if (curr_diff < min) { min = curr_diff; // b - a / max(a,b) min = curr_diff/std::max(input_values[i], centroid_value); } } silhouette_scores.push_back(min); Loading @@ -231,12 +206,7 @@ void LossFunctionSilhouetteScore::calculate_centroid_differences(const std::vect } double LossFunctionSilhouetteScore::get_silhouette_average() { double avg = 0; for (int i = 0; i < silhouette_scores.size(); i++){ avg+= silhouette_scores[i]; if (silhouette_scores.empty()) return 0.0; return std::accumulate(silhouette_scores.begin(), silhouette_scores.end(), 0.0) / static_cast<double>(silhouette_scores.size()); } No newline at end of file return avg/silhouette_scores.size(); }
src/loss_function/LossFunctionSilhouetteScore.hpp +3 −2 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ protected: std::vector<int> _n_class_per_task; //!< Number of classes in the property int _n_class; //!< Number of classes in the property std::vector<double> _a; //!< matrix to copy values from the D_Matrix std::map<const ModelNode*, double> centroid_map; public: /** Loading Loading @@ -85,11 +86,11 @@ public: inline std::shared_ptr<SVMWrapper> svm(int tt) override { return _svm[tt]; } virtual std::map<const ModelNode*, double> get_all_centroids(const std::vector<model_node_ptr>& feats); virtual void get_all_centroids(const std::vector<model_node_ptr>& feats); virtual double get_silhouette_average(); virtual void calculate_centroid_differences(const std::vector<model_node_ptr> feats, std::map<const ModelNode*, double> centroid_map); virtual void calculate_centroid_differences(std::vector<double> input_values); std::vector<double> get_silhouette_scores(){ return silhouette_scores; Loading
tests/googletest/loss_function/test_silhouette_score_loss.cc +19 −44 Original line number Diff line number Diff line Loading @@ -107,10 +107,7 @@ protected: std::fill_n(_prop_test.begin() + 15, 5, 3.0); } void TearDown() override { node_value_arrs::finalize_values_arr(); } void TearDown() override { node_value_arrs::finalize_values_arr(); } std::vector<node_ptr> _phi; std::vector<model_node_ptr> _model_phi; Loading @@ -122,15 +119,10 @@ protected: std::vector<int> _task_sizes_test; }; TEST_F(LossFunctionSilhouetteScoreTests, NoFixIntercept) { TEST_F(LossFunctionSilhouetteScoreTests, NoFixIntercept) { LossFunctionSilhouetteScore loss( _prop_train, _prop_test, _task_sizes_train, _task_sizes_test, false, 2 ); _prop_train, _prop_test, _task_sizes_train, _task_sizes_test, false, 2); EXPECT_GE(loss(_model_phi), -1.0); EXPECT_LE(loss(_model_phi), 1.0); EXPECT_GE(loss({0, 1}), -1.0); Loading @@ -140,15 +132,10 @@ TEST_F(LossFunctionSilhouetteScoreTests, NoFixIntercept) { EXPECT_EQ(loss.type(), LOSS_TYPE::SILHOUETTE_SCORE); } TEST_F(LossFunctionSilhouetteScoreTests, CopyNoFixIntercept) { TEST_F(LossFunctionSilhouetteScoreTests, CopyNoFixIntercept) { LossFunctionSilhouetteScore loss( _prop_train, _prop_test, _task_sizes_train, _task_sizes_test, false, 2 ); _prop_train, _prop_test, _task_sizes_train, _task_sizes_test, false, 2); LossFunctionSilhouetteScore loss_copy(std::make_shared<LossFunctionSilhouetteScore>(loss)); Loading @@ -161,15 +148,12 @@ TEST_F(LossFunctionSilhouetteScoreTests, CopyNoFixIntercept) { EXPECT_EQ(loss_copy.type(), LOSS_TYPE::SILHOUETTE_SCORE); } TEST_F(LossFunctionSilhouetteScoreTests, ManualDetermination) { TEST_F(LossFunctionSilhouetteScoreTests, ManualDetermination) { _task_sizes_train = {4}; _task_sizes_test = {4}; node_value_arrs::finalize_values_arr(); node_value_arrs::initialize_values_arr(_task_sizes_train, _task_sizes_test, 2, 2, false); //not division by the maximum //model node does x[i] - min(x) / (max(x) - min(x)) // the lowest val will always result to 0 // std::vector<double> train_feat1 = {1.0, 2.0, 3.0, 4.0}; //normalized scored Loading @@ -186,39 +170,30 @@ TEST_F(LossFunctionSilhouetteScoreTests, ManualDetermination) { _phi.clear(); _model_phi.clear(); _phi.push_back(std::make_shared<FeatureNode>(0, "Feature1", train_feat1, test_feat1, Unit("m"))); _phi.push_back(std::make_shared<FeatureNode>(1, "Feature2", train_feat2, test_feat2, Unit("m"))); _phi.push_back( std::make_shared<FeatureNode>(0, "Feature1", train_feat1, test_feat1, Unit("m"))); _phi.push_back( std::make_shared<FeatureNode>(1, "Feature2", train_feat2, test_feat2, Unit("m"))); _model_phi.push_back(std::make_shared<ModelNode>(_phi[0])); _model_phi.push_back(std::make_shared<ModelNode>(_phi[1])); _prop_train = {0.0, 0.0, 1.0, 1.0}; // abs(0-.46)/max(0,.46) = .46/.46 = 1 // abs(1-.5)/max(1,.5) = .5/1 = .5 //sil score values should be 1,1,.5.5 = 3/4 = 0.75 _prop_test = _prop_train; _task_sizes_train = {4}; _task_sizes_test = {4}; LossFunctionSilhouetteScore loss( _prop_train, _prop_test, _task_sizes_train, _task_sizes_test, false, 2 ); _prop_train, _prop_test, _task_sizes_train, _task_sizes_test, false, 2); double silhouette_score = loss(_model_phi); EXPECT_NEAR(silhouette_score, 0.48, 0.01); EXPECT_EQ(silhouette_score,0.75); } } Loading