Loading src/loss_function/LossFunctionCalinskiHarabasz.cpp +3 −16 Original line number Diff line number Diff line Loading @@ -135,7 +135,6 @@ double LossFunctionCalinskiHarabasz::operator()(const std::vector<model_node_ptr return compute_CH(); } void LossFunctionCalinskiHarabasz::get_centroids(){ //populate the centroids for (auto& [key, indices] : _cluster_indices) { Loading @@ -159,13 +158,9 @@ double LossFunctionCalinskiHarabasz::compute_w() { double W = 0.0; for (auto& [cluster_label, indices] : _cluster_indices) { const std::vector<double>& centroid = _centroids[cluster_label]; for (size_t i = 0; i < indices.size(); i += _n_feat) { size_t samp_idx = indices[i] / _n_feat; const double* sample = &_a[samp_idx * _n_feat]; double dist2 = 0.0; for (int f = 0; f < _n_feat; ++f) { double d = sample[f] - centroid[f]; Loading @@ -185,39 +180,31 @@ double LossFunctionCalinskiHarabasz::compute_b() { global_centroid[f] += sample[f]; } } for (int f = 0; f < _n_feat; ++f) global_centroid[f] /= static_cast<double>(_n_samp); for (int f = 0; f < _n_feat; ++f) { global_centroid[f] /= static_cast<double>(_n_samp); } double B = 0.0; for (auto& [cluster_label, indices] : _cluster_indices) { const std::vector<double>& centroid = _centroids[cluster_label]; size_t n_samples = indices.size() / _n_feat; double dist2 = 0.0; for (int f = 0; f < _n_feat; ++f) { double d = centroid[f] - global_centroid[f]; dist2 += d * d; } B += dist2 * static_cast<double>(n_samples); } return B; } double LossFunctionCalinskiHarabasz::compute_CH() { double W = compute_w(); double B = compute_b(); int n = static_cast<int>(_n_samp); int k = static_cast<int>(_cluster_indices.size()); if (k <= 1 || n <= k) { return 0.0; } double ch = (B / static_cast<double>(k - 1)) / (W / static_cast<double>(n - k)); return ch; } No newline at end of file src/loss_function/LossFunctionSilhouetteScore.cpp +1 −5 Original line number Diff line number Diff line Loading @@ -111,7 +111,6 @@ double LossFunctionSilhouetteScore::operator()(const std::vector<int>& inds) { _cluster_indices[cluster_label].push_back(index); } } populate_sil_score(); return get_silhouette_average(); } Loading @@ -127,7 +126,6 @@ double LossFunctionSilhouetteScore::euclidean_distance(const double* p1, const d )); } double LossFunctionSilhouetteScore::calculate_a_i(size_t samp_idx, int cluster_label) { const auto& cluster_points = _cluster_indices[cluster_label]; size_t n_samples_in_cluster = cluster_points.size() / _n_feat; Loading Loading @@ -155,7 +153,6 @@ double LossFunctionSilhouetteScore::calculate_b_i(size_t sample_idx, int cluster continue; } double sum = 0.0; // step by _n_feat to visit each sample once for (size_t i = 0; i < indices.size(); i += _n_feat) { size_t other_sample_idx = indices[i] / _n_feat; const double* p2 = &_a[other_sample_idx * _n_feat]; Loading @@ -173,7 +170,6 @@ double LossFunctionSilhouetteScore::calculate_b_i(size_t sample_idx, int cluster void LossFunctionSilhouetteScore::populate_sil_score() { double a_i, b_i; for (auto& [cluster_label, indices] : _cluster_indices) { // step by _n_feat to visit each sample once for (size_t i = 0; i < indices.size(); i += _n_feat) { size_t sample_idx = indices[i] / _n_feat; a_i = calculate_a_i(sample_idx, cluster_label); Loading Loading @@ -219,7 +215,7 @@ double LossFunctionSilhouetteScore::get_silhouette_average() { //Methods for Davies Bouldin Index //Notes for refactoring into seperate file: //Should be refactored into another file //The cluster mapping needs to be populated before we do this logic double LossFunctionSilhouetteScore::get_dbi() { // centroids first Loading src/loss_function/calinski.py +26 −1 Original line number Diff line number Diff line Loading @@ -2,12 +2,37 @@ from sklearn.metrics import calinski_harabasz_score import numpy as np if __name__ == "__main__": #Test for feats # Calinski for feats if False: labels = np.array([0, 0, 1, 1]) train_feat1 = np.array([4.0, 5.0, 6.0, 7.0]) train_feat2 = np.array([2.0, 3.0, 4.0, 5.0]) norm_feat1 = (train_feat1 - train_feat1.min()) / (train_feat1.max() - train_feat1.min()) norm_feat2 = (train_feat2 - train_feat2.min()) / (train_feat2.max() - train_feat2.min()) X = np.column_stack((train_feat1, train_feat2)) score = calinski_harabasz_score(X, labels) print("Calinski Harabasz Score for feats ", score) # Calinski for inds if False: labels = np.array([0, 0, 1, 1]) dat = np.array([ [-1.86846, -1.9923], [-1.78104, -1.31323], [-1.06531, -1.47307], [-1.96543, -1.29881] ]) score = calinski_harabasz_score(dat, labels) print("Calinski Harabasz for inds", score) #Calinski Multi class for Feats if True: labels = np.array([0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3]) train_feat1 = np.array([1.0, 1.2, 0.8, 5.0, 5.1, 4.9, 9.0, 9.1, 8.9, 2.0, 2.1, 1.9]) train_feat2 = np.array([2.0, 2.1, 1.9, 5.0, 5.2, 4.8, 1.0, 1.1, 0.9, 8.0, 8.2, 7.8]) norm_feat1 = (train_feat1 - train_feat1.min()) / (train_feat1.max() - train_feat1.min()) norm_feat2 = (train_feat2 - train_feat2.min()) / (train_feat2.max() - train_feat2.min()) X = np.column_stack((norm_feat1, norm_feat2)) score = calinski_harabasz_score(X, labels) print("Calinski Harabasz multi class for feats ", score) src/loss_function/silhouette.py +8 −12 Original line number Diff line number Diff line Loading @@ -86,19 +86,15 @@ if __name__ == "__main__": score = silhouette_score(dat, labels) print("Silhouette score for inds:", score) #sil score op feats multi class if False: X = np.array([ [1.0, 2.0], [1.2, 2.1], [0.8, 1.9], [5.0, 5.0], [5.1, 5.2], [4.9, 4.8], [9.0, 1.0], [9.1, 1.1], [8.9, 0.9], [2.0, 8.0], [2.1, 8.2], [1.9, 7.8] ]) X_norm = (X - X.min(axis=0)) / (X.max(axis=0) - X.min(axis=0)) if True: train_feat1 = np.array([1.0, 1.2, 0.8, 5.0, 5.1, 4.9, 9.0, 9.1, 8.9, 2.0, 2.1, 1.9]) train_feat2 = np.array([2.0, 2.1, 1.9, 5.0, 5.2, 4.8, 1.0, 1.1, 0.9, 8.0, 8.2, 7.8]) norm_feat1 = (train_feat1 - train_feat1.min()) / (train_feat1.max() - train_feat1.min()) norm_feat2 = (train_feat2 - train_feat2.min()) / (train_feat2.max() - train_feat2.min()) X = np.column_stack((norm_feat1, norm_feat2)) labels = np.array([0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3]) sil_score = silhouette_score(X_norm, labels) sil_score = silhouette_score(X, labels) print("Silhouette Score:", sil_score) Loading Loading @@ -133,7 +129,7 @@ if __name__ == "__main__": print("Davies bouldin index score for inds:", score) #DBI for multi class testing if True: if False: labels = np.array([0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3]) train_feat1 = np.array([1.0, 1.2, 0.8, 5.0, 5.1, 4.9, 9.0, 9.1, 8.9, 2.0, 2.1, 1.9]) train_feat2 = np.array([2.0, 2.1, 1.9, 5.0, 5.2, 4.8, 1.0, 1.1, 0.9, 8.0, 8.2, 7.8]) Loading tests/googletest/loss_function/test_calinksi_harabasz.cc +39 −35 Original line number Diff line number Diff line Loading @@ -145,43 +145,47 @@ TEST_F(LossFunctionCalinskiHarabaszTests, ManualDeterminationFeats) LossFunctionCalinskiHarabasz loss_copy(std::make_shared<LossFunctionCalinskiHarabasz>(loss)); double CH_score = loss(_model_phi); std::cout << "CH score from feats " << CH_score << std::endl; double CH_score_inds = loss_copy({0,1}); double CH_score_feats = loss(_model_phi); EXPECT_NEAR(CH_score, 8.0, 1e-10); EXPECT_NEAR(CH_score_feats, 8.0, 1e-6); EXPECT_NEAR(CH_score_inds, 0.5099024024738862, 1e-5); } //TEST_F(LossFunctionCalinskiHarabaszTests, MultiClassTest) //{ // _task_sizes_train = {12}; // _task_sizes_test = {12}; // node_value_arrs::finalize_values_arr(); // node_value_arrs::initialize_values_arr(_task_sizes_train, _task_sizes_test, 2, 2, false); // // std::vector<double> train_feat1 = {1.0, 1.2, 0.8, 5.0, 5.1, 4.9, 9.0, 9.1, 8.9, 2.0, 2.1, 1.9}; // std::vector<double> train_feat2 = {2.0, 2.1, 1.9, 5.0, 5.2, 4.8, 1.0, 1.1, 0.9, 8.0, 8.2, 7.8}; // // std::vector<double> test_feat1 = train_feat1; // std::vector<double> test_feat2 = train_feat2; // // _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"))); // // _model_phi.push_back(std::make_shared<ModelNode>(_phi[0])); // _model_phi.push_back(std::make_shared<ModelNode>(_phi[1])); // // // 4 classes // _prop_train = {0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3}; // _prop_test = _prop_train; // // LossFunctionCalinskiHarabasz loss( // _prop_train, _prop_test, _task_sizes_train, _task_sizes_test, false, 2); // //} TEST_F(LossFunctionCalinskiHarabaszTests, MultiClassTest) { _task_sizes_train = {12}; _task_sizes_test = {12}; node_value_arrs::finalize_values_arr(); node_value_arrs::initialize_values_arr(_task_sizes_train, _task_sizes_test, 2, 2, false); std::vector<double> train_feat1 = {1.0, 1.2, 0.8, 5.0, 5.1, 4.9, 9.0, 9.1, 8.9, 2.0, 2.1, 1.9}; std::vector<double> train_feat2 = {2.0, 2.1, 1.9, 5.0, 5.2, 4.8, 1.0, 1.1, 0.9, 8.0, 8.2, 7.8}; std::vector<double> test_feat1 = train_feat1; std::vector<double> test_feat2 = train_feat2; _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"))); _model_phi.push_back(std::make_shared<ModelNode>(_phi[0])); _model_phi.push_back(std::make_shared<ModelNode>(_phi[1])); // 4 classes _prop_train = {0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3}; _prop_test = _prop_train; LossFunctionCalinskiHarabasz loss( _prop_train, _prop_test, _task_sizes_train, _task_sizes_test, false, 2); double CH_score = loss(_model_phi); EXPECT_NEAR(CH_score, 1556.2937293418609, 1e-10); } } Loading
src/loss_function/LossFunctionCalinskiHarabasz.cpp +3 −16 Original line number Diff line number Diff line Loading @@ -135,7 +135,6 @@ double LossFunctionCalinskiHarabasz::operator()(const std::vector<model_node_ptr return compute_CH(); } void LossFunctionCalinskiHarabasz::get_centroids(){ //populate the centroids for (auto& [key, indices] : _cluster_indices) { Loading @@ -159,13 +158,9 @@ double LossFunctionCalinskiHarabasz::compute_w() { double W = 0.0; for (auto& [cluster_label, indices] : _cluster_indices) { const std::vector<double>& centroid = _centroids[cluster_label]; for (size_t i = 0; i < indices.size(); i += _n_feat) { size_t samp_idx = indices[i] / _n_feat; const double* sample = &_a[samp_idx * _n_feat]; double dist2 = 0.0; for (int f = 0; f < _n_feat; ++f) { double d = sample[f] - centroid[f]; Loading @@ -185,39 +180,31 @@ double LossFunctionCalinskiHarabasz::compute_b() { global_centroid[f] += sample[f]; } } for (int f = 0; f < _n_feat; ++f) global_centroid[f] /= static_cast<double>(_n_samp); for (int f = 0; f < _n_feat; ++f) { global_centroid[f] /= static_cast<double>(_n_samp); } double B = 0.0; for (auto& [cluster_label, indices] : _cluster_indices) { const std::vector<double>& centroid = _centroids[cluster_label]; size_t n_samples = indices.size() / _n_feat; double dist2 = 0.0; for (int f = 0; f < _n_feat; ++f) { double d = centroid[f] - global_centroid[f]; dist2 += d * d; } B += dist2 * static_cast<double>(n_samples); } return B; } double LossFunctionCalinskiHarabasz::compute_CH() { double W = compute_w(); double B = compute_b(); int n = static_cast<int>(_n_samp); int k = static_cast<int>(_cluster_indices.size()); if (k <= 1 || n <= k) { return 0.0; } double ch = (B / static_cast<double>(k - 1)) / (W / static_cast<double>(n - k)); return ch; } No newline at end of file
src/loss_function/LossFunctionSilhouetteScore.cpp +1 −5 Original line number Diff line number Diff line Loading @@ -111,7 +111,6 @@ double LossFunctionSilhouetteScore::operator()(const std::vector<int>& inds) { _cluster_indices[cluster_label].push_back(index); } } populate_sil_score(); return get_silhouette_average(); } Loading @@ -127,7 +126,6 @@ double LossFunctionSilhouetteScore::euclidean_distance(const double* p1, const d )); } double LossFunctionSilhouetteScore::calculate_a_i(size_t samp_idx, int cluster_label) { const auto& cluster_points = _cluster_indices[cluster_label]; size_t n_samples_in_cluster = cluster_points.size() / _n_feat; Loading Loading @@ -155,7 +153,6 @@ double LossFunctionSilhouetteScore::calculate_b_i(size_t sample_idx, int cluster continue; } double sum = 0.0; // step by _n_feat to visit each sample once for (size_t i = 0; i < indices.size(); i += _n_feat) { size_t other_sample_idx = indices[i] / _n_feat; const double* p2 = &_a[other_sample_idx * _n_feat]; Loading @@ -173,7 +170,6 @@ double LossFunctionSilhouetteScore::calculate_b_i(size_t sample_idx, int cluster void LossFunctionSilhouetteScore::populate_sil_score() { double a_i, b_i; for (auto& [cluster_label, indices] : _cluster_indices) { // step by _n_feat to visit each sample once for (size_t i = 0; i < indices.size(); i += _n_feat) { size_t sample_idx = indices[i] / _n_feat; a_i = calculate_a_i(sample_idx, cluster_label); Loading Loading @@ -219,7 +215,7 @@ double LossFunctionSilhouetteScore::get_silhouette_average() { //Methods for Davies Bouldin Index //Notes for refactoring into seperate file: //Should be refactored into another file //The cluster mapping needs to be populated before we do this logic double LossFunctionSilhouetteScore::get_dbi() { // centroids first Loading
src/loss_function/calinski.py +26 −1 Original line number Diff line number Diff line Loading @@ -2,12 +2,37 @@ from sklearn.metrics import calinski_harabasz_score import numpy as np if __name__ == "__main__": #Test for feats # Calinski for feats if False: labels = np.array([0, 0, 1, 1]) train_feat1 = np.array([4.0, 5.0, 6.0, 7.0]) train_feat2 = np.array([2.0, 3.0, 4.0, 5.0]) norm_feat1 = (train_feat1 - train_feat1.min()) / (train_feat1.max() - train_feat1.min()) norm_feat2 = (train_feat2 - train_feat2.min()) / (train_feat2.max() - train_feat2.min()) X = np.column_stack((train_feat1, train_feat2)) score = calinski_harabasz_score(X, labels) print("Calinski Harabasz Score for feats ", score) # Calinski for inds if False: labels = np.array([0, 0, 1, 1]) dat = np.array([ [-1.86846, -1.9923], [-1.78104, -1.31323], [-1.06531, -1.47307], [-1.96543, -1.29881] ]) score = calinski_harabasz_score(dat, labels) print("Calinski Harabasz for inds", score) #Calinski Multi class for Feats if True: labels = np.array([0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3]) train_feat1 = np.array([1.0, 1.2, 0.8, 5.0, 5.1, 4.9, 9.0, 9.1, 8.9, 2.0, 2.1, 1.9]) train_feat2 = np.array([2.0, 2.1, 1.9, 5.0, 5.2, 4.8, 1.0, 1.1, 0.9, 8.0, 8.2, 7.8]) norm_feat1 = (train_feat1 - train_feat1.min()) / (train_feat1.max() - train_feat1.min()) norm_feat2 = (train_feat2 - train_feat2.min()) / (train_feat2.max() - train_feat2.min()) X = np.column_stack((norm_feat1, norm_feat2)) score = calinski_harabasz_score(X, labels) print("Calinski Harabasz multi class for feats ", score)
src/loss_function/silhouette.py +8 −12 Original line number Diff line number Diff line Loading @@ -86,19 +86,15 @@ if __name__ == "__main__": score = silhouette_score(dat, labels) print("Silhouette score for inds:", score) #sil score op feats multi class if False: X = np.array([ [1.0, 2.0], [1.2, 2.1], [0.8, 1.9], [5.0, 5.0], [5.1, 5.2], [4.9, 4.8], [9.0, 1.0], [9.1, 1.1], [8.9, 0.9], [2.0, 8.0], [2.1, 8.2], [1.9, 7.8] ]) X_norm = (X - X.min(axis=0)) / (X.max(axis=0) - X.min(axis=0)) if True: train_feat1 = np.array([1.0, 1.2, 0.8, 5.0, 5.1, 4.9, 9.0, 9.1, 8.9, 2.0, 2.1, 1.9]) train_feat2 = np.array([2.0, 2.1, 1.9, 5.0, 5.2, 4.8, 1.0, 1.1, 0.9, 8.0, 8.2, 7.8]) norm_feat1 = (train_feat1 - train_feat1.min()) / (train_feat1.max() - train_feat1.min()) norm_feat2 = (train_feat2 - train_feat2.min()) / (train_feat2.max() - train_feat2.min()) X = np.column_stack((norm_feat1, norm_feat2)) labels = np.array([0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3]) sil_score = silhouette_score(X_norm, labels) sil_score = silhouette_score(X, labels) print("Silhouette Score:", sil_score) Loading Loading @@ -133,7 +129,7 @@ if __name__ == "__main__": print("Davies bouldin index score for inds:", score) #DBI for multi class testing if True: if False: labels = np.array([0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3]) train_feat1 = np.array([1.0, 1.2, 0.8, 5.0, 5.1, 4.9, 9.0, 9.1, 8.9, 2.0, 2.1, 1.9]) train_feat2 = np.array([2.0, 2.1, 1.9, 5.0, 5.2, 4.8, 1.0, 1.1, 0.9, 8.0, 8.2, 7.8]) Loading
tests/googletest/loss_function/test_calinksi_harabasz.cc +39 −35 Original line number Diff line number Diff line Loading @@ -145,43 +145,47 @@ TEST_F(LossFunctionCalinskiHarabaszTests, ManualDeterminationFeats) LossFunctionCalinskiHarabasz loss_copy(std::make_shared<LossFunctionCalinskiHarabasz>(loss)); double CH_score = loss(_model_phi); std::cout << "CH score from feats " << CH_score << std::endl; double CH_score_inds = loss_copy({0,1}); double CH_score_feats = loss(_model_phi); EXPECT_NEAR(CH_score, 8.0, 1e-10); EXPECT_NEAR(CH_score_feats, 8.0, 1e-6); EXPECT_NEAR(CH_score_inds, 0.5099024024738862, 1e-5); } //TEST_F(LossFunctionCalinskiHarabaszTests, MultiClassTest) //{ // _task_sizes_train = {12}; // _task_sizes_test = {12}; // node_value_arrs::finalize_values_arr(); // node_value_arrs::initialize_values_arr(_task_sizes_train, _task_sizes_test, 2, 2, false); // // std::vector<double> train_feat1 = {1.0, 1.2, 0.8, 5.0, 5.1, 4.9, 9.0, 9.1, 8.9, 2.0, 2.1, 1.9}; // std::vector<double> train_feat2 = {2.0, 2.1, 1.9, 5.0, 5.2, 4.8, 1.0, 1.1, 0.9, 8.0, 8.2, 7.8}; // // std::vector<double> test_feat1 = train_feat1; // std::vector<double> test_feat2 = train_feat2; // // _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"))); // // _model_phi.push_back(std::make_shared<ModelNode>(_phi[0])); // _model_phi.push_back(std::make_shared<ModelNode>(_phi[1])); // // // 4 classes // _prop_train = {0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3}; // _prop_test = _prop_train; // // LossFunctionCalinskiHarabasz loss( // _prop_train, _prop_test, _task_sizes_train, _task_sizes_test, false, 2); // //} TEST_F(LossFunctionCalinskiHarabaszTests, MultiClassTest) { _task_sizes_train = {12}; _task_sizes_test = {12}; node_value_arrs::finalize_values_arr(); node_value_arrs::initialize_values_arr(_task_sizes_train, _task_sizes_test, 2, 2, false); std::vector<double> train_feat1 = {1.0, 1.2, 0.8, 5.0, 5.1, 4.9, 9.0, 9.1, 8.9, 2.0, 2.1, 1.9}; std::vector<double> train_feat2 = {2.0, 2.1, 1.9, 5.0, 5.2, 4.8, 1.0, 1.1, 0.9, 8.0, 8.2, 7.8}; std::vector<double> test_feat1 = train_feat1; std::vector<double> test_feat2 = train_feat2; _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"))); _model_phi.push_back(std::make_shared<ModelNode>(_phi[0])); _model_phi.push_back(std::make_shared<ModelNode>(_phi[1])); // 4 classes _prop_train = {0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3}; _prop_test = _prop_train; LossFunctionCalinskiHarabasz loss( _prop_train, _prop_test, _task_sizes_train, _task_sizes_test, false, 2); double CH_score = loss(_model_phi); EXPECT_NEAR(CH_score, 1556.2937293418609, 1e-10); } }