Commit 73653ad9 authored by Joe Grimes's avatar Joe Grimes
Browse files

Addition of testing for CH for inds and multi class feats

parent bd2c79da
Loading
Loading
Loading
Loading
Loading
+3 −16
Original line number Diff line number Diff line
@@ -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) {
@@ -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];
@@ -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
+1 −5
Original line number Diff line number Diff line
@@ -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();
}
@@ -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;
@@ -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];
@@ -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);
@@ -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
+26 −1
Original line number Diff line number Diff line
@@ -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)
+8 −12
Original line number Diff line number Diff line
@@ -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)


@@ -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])
+39 −35
Original line number Diff line number Diff line
@@ -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