Commit 9f7c19fe authored by Joe Grimes's avatar Joe Grimes
Browse files

Removal of SVM predictions inside sil score, more dynamic inputs

parent 87a22c47
Loading
Loading
Loading
Loading
+60 −90
Original line number Diff line number Diff line
@@ -4,8 +4,6 @@


#include "LossFunctionSilhouetteScore.hpp"
#include <iostream>
#include <typeinfo>


LossFunctionSilhouetteScore::LossFunctionSilhouetteScore(std::vector<double> prop_train,
@@ -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) {
@@ -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)
@@ -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)
@@ -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;
@@ -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) {
@@ -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;
@@ -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);
@@ -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();
}


+3 −2
Original line number Diff line number Diff line
@@ -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:
    /**
@@ -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;
+19 −44
Original line number Diff line number Diff line
@@ -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;
@@ -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);
@@ -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));

@@ -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
@@ -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);
}

}