openGPMP
Open Source Mathematics Package
svc.cpp
Go to the documentation of this file.
1 /*************************************************************************
2  *
3  * Project
4  * _____ _____ __ __ _____
5  * / ____| __ \| \/ | __ \
6  * ___ _ __ ___ _ __ | | __| |__) | \ / | |__) |
7  * / _ \| '_ \ / _ \ '_ \| | |_ | ___/| |\/| | ___/
8  *| (_) | |_) | __/ | | | |__| | | | | | | |
9  * \___/| .__/ \___|_| |_|\_____|_| |_| |_|_|
10  * | |
11  * |_|
12  *
13  * Copyright (C) Akiel Aries, <akiel@akiel.org>, et al.
14  *
15  * This software is licensed as described in the file LICENSE, which
16  * you should have received as part of this distribution. The terms
17  * among other details are referenced in the official documentation
18  * seen here : https://akielaries.github.io/openGPMP/ along with
19  * important files seen in this project.
20  *
21  * You may opt to use, copy, modify, merge, publish, distribute
22  * and/or sell copies of the Software, and permit persons to whom
23  * the Software is furnished to do so, under the terms of the
24  * LICENSE file. As this is an Open Source effort, all implementations
25  * must be of the same methodology.
26  *
27  *
28  *
29  * This software is distributed on an AS IS basis, WITHOUT
30  * WARRANTY OF ANY KIND, either express or implied.
31  *
32  ************************************************************************/
33 #include <cmath>
34 #include <iostream>
35 #include <numeric>
36 #include <openGPMP/ml/svc.hpp>
37 
38 gpmp::ml::SVC::SVC(double C_, double l_rate, int max_iters, double tol)
39  : C(C_), learning_rate(l_rate), max_iterations(max_iters), tolerance(tol) {
40 }
41 
42 void gpmp::ml::SVC::fit(const std::vector<std::vector<double>> &X_train,
43  const std::vector<int> &y_train) {
44  // Initialize weights and bias
45  weights.resize(X_train[0].size(), 0.0);
46  bias = 0.0;
47 
48  // Stochastic Gradient Descent
49  for (int iter = 0; iter < max_iterations; ++iter) {
50  update_weights(X_train, y_train);
51 
52  // Check convergence
53  double loss = compute_loss(X_train, y_train);
54  if (loss < tolerance) {
55  break;
56  }
57  }
58 }
59 
60 std::vector<int>
61 gpmp::ml::SVC::predict(const std::vector<std::vector<double>> &X_test) {
62  std::vector<int> predictions;
63  for (const auto &instance : X_test) {
64  double score = 0.0;
65  for (size_t i = 0; i < instance.size(); ++i) {
66  score += instance[i] * weights[i];
67  }
68  score += bias;
69  int prediction = (score >= 0) ? 1 : -1;
70  predictions.push_back(prediction);
71  }
72  return predictions;
73 }
74 
75 double gpmp::ml::SVC::hinge_loss(double prediction, int label) {
76  return fmax(0, 1 - label * prediction);
77 }
78 
79 double gpmp::ml::SVC::compute_loss(const std::vector<std::vector<double>> &X,
80  const std::vector<int> &y) {
81  double loss = 0.0;
82  for (size_t i = 0; i < X.size(); ++i) {
83  double prediction = 0.0;
84  for (size_t j = 0; j < X[i].size(); ++j) {
85  prediction += X[i][j] * weights[j];
86  }
87  prediction += bias;
88  loss += hinge_loss(prediction, y[i]);
89  }
90  // Add L2 regularization
91  for (double weight : weights) {
92  loss += 0.5 * C * weight * weight;
93  }
94  return loss / X.size();
95 }
96 
97 void gpmp::ml::SVC::update_weights(const std::vector<std::vector<double>> &X,
98  const std::vector<int> &y) {
99  for (size_t i = 0; i < X.size(); ++i) {
100  double prediction = 0.0;
101  for (size_t j = 0; j < X[i].size(); ++j) {
102  prediction += X[i][j] * weights[j];
103  }
104  prediction += bias;
105  double loss_grad = -y[i] * (1 - prediction);
106  if (loss_grad > 0) {
107  // Update weights
108  for (size_t j = 0; j < X[i].size(); ++j) {
109  weights[j] -= learning_rate * (C * weights[j] - y[i] * X[i][j]);
110  }
111  // Update bias
112  bias -= learning_rate * y[i];
113  }
114  }
115 }
116 
117 std::vector<double>
118 gpmp::ml::SVC::predict_proba(const std::vector<std::vector<double>> &X_test) {
119  std::vector<double> probabilities;
120  for (const auto &instance : X_test) {
121  double score = 0.0;
122  for (size_t i = 0; i < instance.size(); ++i) {
123  score += instance[i] * weights[i];
124  }
125  score += bias;
126  double prob = sigmoid(score);
127  probabilities.push_back(prob);
128  }
129  return probabilities;
130 }
131 
132 double gpmp::ml::SVC::score(const std::vector<std::vector<double>> &X_test,
133  const std::vector<int> &y_test) {
134  std::vector<int> predictions = predict(X_test);
135  return accuracy(predictions, y_test);
136 }
137 
138 void gpmp::ml::SVC::set_kernel(const std::string &k_type) {
139  this->kernel_type = k_type;
140 }
141 
143  this->kernel_param = k_param;
144 }
145 
147  this->random_state = seed;
148 }
149 
150 void gpmp::ml::SVC::set_verbose(bool vbose) {
151  this->verbose = vbose;
152 }
153 
154 void gpmp::ml::SVC::set_penalty(const std::string &p_type) {
155  this->penalty_type = p_type;
156 }
157 
158 double gpmp::ml::SVC::cross_val_score(const std::vector<std::vector<double>> &X,
159  const std::vector<int> &y,
160  int cv) {
161  std::vector<int> fold_sizes = k_fold_indices(X.size(), cv);
162  double avg_score = 0.0;
163  for (int i = 0; i < cv; ++i) {
164  std::vector<std::vector<double>> X_train, X_valid;
165  std::vector<int> y_train, y_valid;
166  int start = 0;
167  for (int j = 0; j < cv; ++j) {
168  if (j != i) {
169  int end = start + fold_sizes[j];
170  for (int k = start; k < end; ++k) {
171  X_train.push_back(X[k]);
172  y_train.push_back(y[k]);
173  }
174  } else {
175  int end = start + fold_sizes[j];
176  for (int k = start; k < end; ++k) {
177  X_valid.push_back(X[k]);
178  y_valid.push_back(y[k]);
179  }
180  }
181  start += fold_sizes[j];
182  }
183  fit(X_train, y_train);
184  double score_val = score(X_valid, y_valid);
185  if (verbose) {
186  std::cout << "Cross-validation fold " << i + 1
187  << " accuracy: " << score_val << std::endl;
188  }
189  avg_score += score_val;
190  }
191  return avg_score / cv;
192 }
193 
194 std::vector<double>
195 gpmp::ml::SVC::grid_search(const std::vector<std::vector<double>> &X,
196  const std::vector<int> &y,
197  const std::vector<double> &C_values,
198  const std::vector<double> &kernel_params,
199  int cv) {
200  std::vector<double> best_params;
201  double best_score = 0.0;
202  for (double val : C_values) {
203  for (double param : kernel_params) {
204  set_kernel_parameters(param);
205  set_penalty("l2"); // Default penalty type
206  set_verbose(false); // Suppress verbose output
207  double score = cross_val_score(X, y, cv);
208  if (score > best_score) {
209  best_score = score;
210  best_params = {val, param};
211  }
212  }
213  }
214  return best_params;
215 }
216 
217 double gpmp::ml::SVC::kernel(const std::vector<double> &x1,
218  const std::vector<double> &x2) {
219  if (kernel_type == "linear") {
220  return dot_product(x1, x2);
221  } else {
222  // Default to linear kernel if unknown kernel type
223  return dot_product(x1, x2);
224  }
225 }
226 
227 double gpmp::ml::SVC::dot_product(const std::vector<double> &x1,
228  const std::vector<double> &x2) {
229  double result = 0.0;
230  for (size_t i = 0; i < x1.size(); ++i) {
231  result += x1[i] * x2[i];
232  }
233  return result;
234 }
235 
236 double gpmp::ml::SVC::sigmoid(double z) {
237  return 1.0 / (1.0 + exp(-z));
238 }
239 
240 std::vector<int> gpmp::ml::SVC::k_fold_indices(int num_instances, int k) {
241  std::vector<int> fold_sizes(k, num_instances / k);
242  int remainder = num_instances % k;
243  for (int i = 0; i < remainder; ++i) {
244  fold_sizes[i]++;
245  }
246  return fold_sizes;
247 }
248 
249 double gpmp::ml::SVC::accuracy(const std::vector<int> &predictions,
250  const std::vector<int> &labels) {
251  int correct = 0;
252  for (size_t i = 0; i < predictions.size(); ++i) {
253  if (predictions[i] == labels[i]) {
254  correct++;
255  }
256  }
257  return static_cast<double>(correct) / predictions.size();
258 }
void set_random_state(int seed)
Set the random seed for reproducibility.
Definition: svc.cpp:146
double dot_product(const std::vector< double > &x1, const std::vector< double > &x2)
Compute the dot product between two vectors.
Definition: svc.cpp:227
SVC(double C_=1.0, double l_rate=0.01, int max_iters=1000, double tol=1e-4)
Constructor for SVC class.
Definition: svc.cpp:38
void set_verbose(bool vbose)
Enable or disable verbose output during training.
Definition: svc.cpp:150
void set_kernel(const std::string &k_type)
Set the kernel type for the SVC.
Definition: svc.cpp:138
double score(const std::vector< std::vector< double >> &X_test, const std::vector< int > &y_test)
Calculate the accuracy of the model on given test data.
Definition: svc.cpp:132
std::vector< double > predict_proba(const std::vector< std::vector< double >> &X_test)
Predict class probabilities for given test data.
Definition: svc.cpp:118
void update_weights(const std::vector< std::vector< double >> &X, const std::vector< int > &y)
Update weights and bias using stochastic gradient descent.
Definition: svc.cpp:97
std::vector< int > k_fold_indices(int num_instances, int k)
Generate k-fold indices for cross-validation.
Definition: svc.cpp:240
double sigmoid(double z)
Sigmoid activation function.
Definition: svc.cpp:236
void fit(const std::vector< std::vector< double >> &X_train, const std::vector< int > &y_train)
Fit the SVC model to the training data.
Definition: svc.cpp:42
double cross_val_score(const std::vector< std::vector< double >> &X, const std::vector< int > &y, int cv=5)
Perform k-fold cross-validation on the model.
Definition: svc.cpp:158
void set_kernel_parameters(double k_param)
Set the kernel parameters for the SVC.
Definition: svc.cpp:142
void set_penalty(const std::string &p_type)
Set the penalty type for regularization.
Definition: svc.cpp:154
double kernel(const std::vector< double > &x1, const std::vector< double > &x2)
Compute the kernel function between two vectors.
Definition: svc.cpp:217
double hinge_loss(double prediction, int label)
Compute the hinge loss for a given prediction and true label.
Definition: svc.cpp:75
double accuracy(const std::vector< int > &predictions, const std::vector< int > &labels)
Compute the accuracy of predictions.
Definition: svc.cpp:249
std::vector< double > grid_search(const std::vector< std::vector< double >> &X, const std::vector< int > &y, const std::vector< double > &C_values, const std::vector< double > &kernel_params, int cv=5)
Perform grid search for hyperparameter tuning.
Definition: svc.cpp:195
std::vector< int > predict(const std::vector< std::vector< double >> &X_test)
Predict labels for given test data.
Definition: svc.cpp:61
double compute_loss(const std::vector< std::vector< double >> &X, const std::vector< int > &y)
Compute the total loss (including regularization) for the model.
Definition: svc.cpp:79
int dot_product(const std::vector< int8_t > &vec1, const std::vector< int8_t > &vec2)
Computes the dot product for vectors of signed 8-bit integers.
list C
Definition: linalg.py:24