Initial commit as Claude developed it
Some checks failed
CI/CD Pipeline / Code Linting (push) Failing after 22s
CI/CD Pipeline / Build and Test (Debug, clang, ubuntu-latest) (push) Failing after 5m44s
CI/CD Pipeline / Build and Test (Debug, gcc, ubuntu-latest) (push) Failing after 5m33s
CI/CD Pipeline / Build and Test (Release, clang, ubuntu-20.04) (push) Failing after 6m12s
CI/CD Pipeline / Build and Test (Release, clang, ubuntu-latest) (push) Failing after 5m13s
CI/CD Pipeline / Build and Test (Release, gcc, ubuntu-20.04) (push) Failing after 5m30s
CI/CD Pipeline / Build and Test (Release, gcc, ubuntu-latest) (push) Failing after 5m33s
CI/CD Pipeline / Docker Build Test (push) Failing after 13s
CI/CD Pipeline / Performance Benchmarks (push) Has been skipped
CI/CD Pipeline / Build Documentation (push) Successful in 31s
CI/CD Pipeline / Create Release Package (push) Has been skipped

This commit is contained in:
2025-06-22 12:50:10 +02:00
parent 270c540556
commit d6dc083a5a
38 changed files with 10197 additions and 6 deletions

View File

@@ -0,0 +1,195 @@
#pragma once
#include "types.hpp"
#include <torch/torch.h>
#include <vector>
#include <memory>
// Forward declarations for libsvm and liblinear structures
struct svm_node;
struct svm_problem;
struct feature_node;
struct problem;
namespace svm_classifier {
/**
* @brief Data converter between libtorch tensors and SVM library formats
*
* This class handles the conversion between PyTorch tensors and the data structures
* required by libsvm and liblinear libraries. It manages memory allocation and
* provides efficient conversion methods.
*/
class DataConverter {
public:
/**
* @brief Default constructor
*/
DataConverter();
/**
* @brief Destructor - cleans up allocated memory
*/
~DataConverter();
/**
* @brief Convert PyTorch tensors to libsvm format
* @param X Feature tensor of shape (n_samples, n_features)
* @param y Target tensor of shape (n_samples,) - optional for prediction
* @return Pointer to svm_problem structure
*/
std::unique_ptr<svm_problem> to_svm_problem(const torch::Tensor& X,
const torch::Tensor& y = torch::Tensor());
/**
* @brief Convert PyTorch tensors to liblinear format
* @param X Feature tensor of shape (n_samples, n_features)
* @param y Target tensor of shape (n_samples,) - optional for prediction
* @return Pointer to problem structure
*/
std::unique_ptr<problem> to_linear_problem(const torch::Tensor& X,
const torch::Tensor& y = torch::Tensor());
/**
* @brief Convert single sample to libsvm format
* @param sample Feature tensor of shape (n_features,)
* @return Pointer to svm_node array
*/
svm_node* to_svm_node(const torch::Tensor& sample);
/**
* @brief Convert single sample to liblinear format
* @param sample Feature tensor of shape (n_features,)
* @return Pointer to feature_node array
*/
feature_node* to_feature_node(const torch::Tensor& sample);
/**
* @brief Convert predictions back to PyTorch tensor
* @param predictions Vector of predictions
* @return PyTorch tensor with predictions
*/
torch::Tensor from_predictions(const std::vector<double>& predictions);
/**
* @brief Convert probabilities back to PyTorch tensor
* @param probabilities 2D vector of class probabilities
* @return PyTorch tensor with probabilities of shape (n_samples, n_classes)
*/
torch::Tensor from_probabilities(const std::vector<std::vector<double>>& probabilities);
/**
* @brief Convert decision values back to PyTorch tensor
* @param decision_values 2D vector of decision function values
* @return PyTorch tensor with decision values
*/
torch::Tensor from_decision_values(const std::vector<std::vector<double>>& decision_values);
/**
* @brief Validate input tensors
* @param X Feature tensor
* @param y Target tensor (optional)
* @throws std::invalid_argument if tensors are invalid
*/
void validate_tensors(const torch::Tensor& X, const torch::Tensor& y = torch::Tensor());
/**
* @brief Get number of features from last conversion
* @return Number of features
*/
int get_n_features() const { return n_features_; }
/**
* @brief Get number of samples from last conversion
* @return Number of samples
*/
int get_n_samples() const { return n_samples_; }
/**
* @brief Clean up all allocated memory
*/
void cleanup();
/**
* @brief Set sparse threshold (features with absolute value below this are ignored)
* @param threshold Sparse threshold (default: 1e-8)
*/
void set_sparse_threshold(double threshold) { sparse_threshold_ = threshold; }
/**
* @brief Get sparse threshold
* @return Current sparse threshold
*/
double get_sparse_threshold() const { return sparse_threshold_; }
private:
int n_features_; ///< Number of features
int n_samples_; ///< Number of samples
double sparse_threshold_; ///< Threshold for sparse features
// Memory management for libsvm structures
std::vector<std::vector<svm_node>> svm_nodes_storage_;
std::vector<svm_node*> svm_x_space_;
std::vector<double> svm_y_space_;
// Memory management for liblinear structures
std::vector<std::vector<feature_node>> linear_nodes_storage_;
std::vector<feature_node*> linear_x_space_;
std::vector<double> linear_y_space_;
// Single sample storage (for prediction)
std::vector<svm_node> single_svm_nodes_;
std::vector<feature_node> single_linear_nodes_;
/**
* @brief Convert tensor data to libsvm nodes for multiple samples
* @param X Feature tensor
* @return Vector of svm_node vectors
*/
std::vector<std::vector<svm_node>> tensor_to_svm_nodes(const torch::Tensor& X);
/**
* @brief Convert tensor data to liblinear nodes for multiple samples
* @param X Feature tensor
* @return Vector of feature_node vectors
*/
std::vector<std::vector<feature_node>> tensor_to_linear_nodes(const torch::Tensor& X);
/**
* @brief Convert single tensor sample to svm_node vector
* @param sample Feature tensor of shape (n_features,)
* @return Vector of svm_node structures
*/
std::vector<svm_node> sample_to_svm_nodes(const torch::Tensor& sample);
/**
* @brief Convert single tensor sample to feature_node vector
* @param sample Feature tensor of shape (n_features,)
* @return Vector of feature_node structures
*/
std::vector<feature_node> sample_to_linear_nodes(const torch::Tensor& sample);
/**
* @brief Extract labels from target tensor
* @param y Target tensor
* @return Vector of double labels
*/
std::vector<double> extract_labels(const torch::Tensor& y);
/**
* @brief Check if tensor is on CPU and convert if necessary
* @param tensor Input tensor
* @return Tensor guaranteed to be on CPU
*/
torch::Tensor ensure_cpu_tensor(const torch::Tensor& tensor);
/**
* @brief Validate tensor dimensions and data type
* @param tensor Tensor to validate
* @param expected_dims Expected number of dimensions
* @param name Tensor name for error messages
*/
void validate_tensor_properties(const torch::Tensor& tensor, int expected_dims, const std::string& name);
};
} // namespace svm_classifier

View File

@@ -0,0 +1,195 @@
#pragma once
#include "types.hpp"
#include <torch/torch.h>
#include <vector>
#include <memory>
// Forward declarations for libsvm and liblinear structures
struct svm_node;
struct svm_problem;
struct feature_node;
struct problem;
namespace svm_classifier {
/**
* @brief Data converter between libtorch tensors and SVM library formats
*
* This class handles the conversion between PyTorch tensors and the data structures
* required by libsvm and liblinear libraries. It manages memory allocation and
* provides efficient conversion methods.
*/
class DataConverter {
public:
/**
* @brief Default constructor
*/
DataConverter();
/**
* @brief Destructor - cleans up allocated memory
*/
~DataConverter();
/**
* @brief Convert PyTorch tensors to libsvm format
* @param X Feature tensor of shape (n_samples, n_features)
* @param y Target tensor of shape (n_samples,) - optional for prediction
* @return Pointer to svm_problem structure
*/
std::unique_ptr<svm_problem> to_svm_problem(const torch::Tensor& X,
const torch::Tensor& y = torch::Tensor());
/**
* @brief Convert PyTorch tensors to liblinear format
* @param X Feature tensor of shape (n_samples, n_features)
* @param y Target tensor of shape (n_samples,) - optional for prediction
* @return Pointer to problem structure
*/
std::unique_ptr<problem> to_linear_problem(const torch::Tensor& X,
const torch::Tensor& y = torch::Tensor());
/**
* @brief Convert single sample to libsvm format
* @param sample Feature tensor of shape (n_features,)
* @return Pointer to svm_node array
*/
svm_node* to_svm_node(const torch::Tensor& sample);
/**
* @brief Convert single sample to liblinear format
* @param sample Feature tensor of shape (n_features,)
* @return Pointer to feature_node array
*/
feature_node* to_feature_node(const torch::Tensor& sample);
/**
* @brief Convert predictions back to PyTorch tensor
* @param predictions Vector of predictions
* @return PyTorch tensor with predictions
*/
torch::Tensor from_predictions(const std::vector<double>& predictions);
/**
* @brief Convert probabilities back to PyTorch tensor
* @param probabilities 2D vector of class probabilities
* @return PyTorch tensor with probabilities of shape (n_samples, n_classes)
*/
torch::Tensor from_probabilities(const std::vector<std::vector<double>>& probabilities);
/**
* @brief Convert decision values back to PyTorch tensor
* @param decision_values 2D vector of decision function values
* @return PyTorch tensor with decision values
*/
torch::Tensor from_decision_values(const std::vector<std::vector<double>>& decision_values);
/**
* @brief Validate input tensors
* @param X Feature tensor
* @param y Target tensor (optional)
* @throws std::invalid_argument if tensors are invalid
*/
void validate_tensors(const torch::Tensor& X, const torch::Tensor& y = torch::Tensor());
/**
* @brief Get number of features from last conversion
* @return Number of features
*/
int get_n_features() const { return n_features_; }
/**
* @brief Get number of samples from last conversion
* @return Number of samples
*/
int get_n_samples() const { return n_samples_; }
/**
* @brief Clean up all allocated memory
*/
void cleanup();
/**
* @brief Set sparse threshold (features with absolute value below this are ignored)
* @param threshold Sparse threshold (default: 1e-8)
*/
void set_sparse_threshold(double threshold) { sparse_threshold_ = threshold; }
/**
* @brief Get sparse threshold
* @return Current sparse threshold
*/
double get_sparse_threshold() const { return sparse_threshold_; }
private:
int n_features_; ///< Number of features
int n_samples_; ///< Number of samples
double sparse_threshold_; ///< Threshold for sparse features
// Memory management for libsvm structures
std::vector<std::vector<svm_node>> svm_nodes_storage_;
std::vector<svm_node*> svm_x_space_;
std::vector<double> svm_y_space_;
// Memory management for liblinear structures
std::vector<std::vector<feature_node>> linear_nodes_storage_;
std::vector<feature_node*> linear_x_space_;
std::vector<double> linear_y_space_;
// Single sample storage (for prediction)
std::vector<svm_node> single_svm_nodes_;
std::vector<feature_node> single_linear_nodes_;
/**
* @brief Convert tensor data to libsvm nodes for multiple samples
* @param X Feature tensor
* @return Vector of svm_node vectors
*/
std::vector<std::vector<svm_node>> tensor_to_svm_nodes(const torch::Tensor& X);
/**
* @brief Convert tensor data to liblinear nodes for multiple samples
* @param X Feature tensor
* @return Vector of feature_node vectors
*/
std::vector<std::vector<feature_node>> tensor_to_linear_nodes(const torch::Tensor& X);
/**
* @brief Convert single tensor sample to svm_node vector
* @param sample Feature tensor of shape (n_features,)
* @return Vector of svm_node structures
*/
std::vector<svm_node> sample_to_svm_nodes(const torch::Tensor& sample);
/**
* @brief Convert single tensor sample to feature_node vector
* @param sample Feature tensor of shape (n_features,)
* @return Vector of feature_node structures
*/
std::vector<feature_node> sample_to_linear_nodes(const torch::Tensor& sample);
/**
* @brief Extract labels from target tensor
* @param y Target tensor
* @return Vector of double labels
*/
std::vector<double> extract_labels(const torch::Tensor& y);
/**
* @brief Check if tensor is on CPU and convert if necessary
* @param tensor Input tensor
* @return Tensor guaranteed to be on CPU
*/
torch::Tensor ensure_cpu_tensor(const torch::Tensor& tensor);
/**
* @brief Validate tensor dimensions and data type
* @param tensor Tensor to validate
* @param expected_dims Expected number of dimensions
* @param name Tensor name for error messages
*/
void validate_tensor_properties(const torch::Tensor& tensor, int expected_dims, const std::string& name);
};
} // namespace svm_classifier

View File

@@ -0,0 +1,264 @@
#pragma once
#include "types.hpp"
#include "kernel_parameters.hpp"
#include "data_converter.hpp"
#include <torch/torch.h>
#include <vector>
#include <memory>
#include <unordered_map>
// Forward declarations
struct svm_model;
struct model;
namespace svm_classifier {
/**
* @brief Abstract base class for multiclass classification strategies
*/
class MulticlassStrategyBase {
public:
/**
* @brief Virtual destructor
*/
virtual ~MulticlassStrategyBase() = default;
/**
* @brief Train the multiclass classifier
* @param X Feature tensor of shape (n_samples, n_features)
* @param y Target tensor of shape (n_samples,)
* @param params Kernel parameters
* @param converter Data converter instance
* @return Training metrics
*/
virtual TrainingMetrics fit(const torch::Tensor& X,
const torch::Tensor& y,
const KernelParameters& params,
DataConverter& converter) = 0;
/**
* @brief Predict class labels
* @param X Feature tensor of shape (n_samples, n_features)
* @param converter Data converter instance
* @return Predicted class labels
*/
virtual std::vector<int> predict(const torch::Tensor& X,
DataConverter& converter) = 0;
/**
* @brief Predict class probabilities
* @param X Feature tensor of shape (n_samples, n_features)
* @param converter Data converter instance
* @return Class probabilities for each sample
*/
virtual std::vector<std::vector<double>> predict_proba(const torch::Tensor& X,
DataConverter& converter) = 0;
/**
* @brief Get decision function values
* @param X Feature tensor of shape (n_samples, n_features)
* @param converter Data converter instance
* @return Decision function values
*/
virtual std::vector<std::vector<double>> decision_function(const torch::Tensor& X,
DataConverter& converter) = 0;
/**
* @brief Get unique class labels
* @return Vector of unique class labels
*/
virtual std::vector<int> get_classes() const = 0;
/**
* @brief Check if the model supports probability prediction
* @return True if probabilities are supported
*/
virtual bool supports_probability() const = 0;
/**
* @brief Get number of classes
* @return Number of classes
*/
virtual int get_n_classes() const = 0;
/**
* @brief Get strategy type
* @return Multiclass strategy type
*/
virtual MulticlassStrategy get_strategy_type() const = 0;
protected:
std::vector<int> classes_; ///< Unique class labels
bool is_trained_ = false; ///< Whether the model is trained
};
/**
* @brief One-vs-Rest (OvR) multiclass strategy
*/
class OneVsRestStrategy : public MulticlassStrategyBase {
public:
/**
* @brief Constructor
*/
OneVsRestStrategy();
/**
* @brief Destructor
*/
~OneVsRestStrategy() override;
TrainingMetrics fit(const torch::Tensor& X,
const torch::Tensor& y,
const KernelParameters& params,
DataConverter& converter) override;
std::vector<int> predict(const torch::Tensor& X,
DataConverter& converter) override;
std::vector<std::vector<double>> predict_proba(const torch::Tensor& X,
DataConverter& converter) override;
std::vector<std::vector<double>> decision_function(const torch::Tensor& X,
DataConverter& converter) override;
std::vector<int> get_classes() const override { return classes_; }
bool supports_probability() const override;
int get_n_classes() const override { return static_cast<int>(classes_.size()); }
MulticlassStrategy get_strategy_type() const override { return MulticlassStrategy::ONE_VS_REST; }
private:
std::vector<std::unique_ptr<svm_model>> svm_models_; ///< SVM models (one per class)
std::vector<std::unique_ptr<model>> linear_models_; ///< Linear models (one per class)
KernelParameters params_; ///< Stored parameters
SVMLibrary library_type_; ///< Which library is being used
/**
* @brief Create binary labels for one-vs-rest
* @param y Original labels
* @param positive_class Positive class label
* @return Binary labels (+1 for positive class, -1 for others)
*/
torch::Tensor create_binary_labels(const torch::Tensor& y, int positive_class);
/**
* @brief Train a single binary classifier
* @param X Feature tensor
* @param y_binary Binary labels
* @param params Kernel parameters
* @param converter Data converter
* @param class_idx Index of the class being trained
* @return Training time for this classifier
*/
double train_binary_classifier(const torch::Tensor& X,
const torch::Tensor& y_binary,
const KernelParameters& params,
DataConverter& converter,
int class_idx);
/**
* @brief Clean up all models
*/
void cleanup_models();
};
/**
* @brief One-vs-One (OvO) multiclass strategy
*/
class OneVsOneStrategy : public MulticlassStrategyBase {
public:
/**
* @brief Constructor
*/
OneVsOneStrategy();
/**
* @brief Destructor
*/
~OneVsOneStrategy() override;
TrainingMetrics fit(const torch::Tensor& X,
const torch::Tensor& y,
const KernelParameters& params,
DataConverter& converter) override;
std::vector<int> predict(const torch::Tensor& X,
DataConverter& converter) override;
std::vector<std::vector<double>> predict_proba(const torch::Tensor& X,
DataConverter& converter) override;
std::vector<std::vector<double>> decision_function(const torch::Tensor& X,
DataConverter& converter) override;
std::vector<int> get_classes() const override { return classes_; }
bool supports_probability() const override;
int get_n_classes() const override { return static_cast<int>(classes_.size()); }
MulticlassStrategy get_strategy_type() const override { return MulticlassStrategy::ONE_VS_ONE; }
private:
std::vector<std::unique_ptr<svm_model>> svm_models_; ///< SVM models (one per pair)
std::vector<std::unique_ptr<model>> linear_models_; ///< Linear models (one per pair)
std::vector<std::pair<int, int>> class_pairs_; ///< Class pairs for each model
KernelParameters params_; ///< Stored parameters
SVMLibrary library_type_; ///< Which library is being used
/**
* @brief Extract samples for a specific class pair
* @param X Feature tensor
* @param y Label tensor
* @param class1 First class
* @param class2 Second class
* @return Pair of (filtered_X, filtered_y)
*/
std::pair<torch::Tensor, torch::Tensor> extract_binary_data(const torch::Tensor& X,
const torch::Tensor& y,
int class1,
int class2);
/**
* @brief Train a single pairwise classifier
* @param X Feature tensor
* @param y Labels
* @param class1 First class
* @param class2 Second class
* @param params Kernel parameters
* @param converter Data converter
* @param model_idx Index of the model being trained
* @return Training time for this classifier
*/
double train_pairwise_classifier(const torch::Tensor& X,
const torch::Tensor& y,
int class1,
int class2,
const KernelParameters& params,
DataConverter& converter,
int model_idx);
/**
* @brief Voting mechanism for OvO predictions
* @param decisions Matrix of pairwise decisions
* @return Predicted class for each sample
*/
std::vector<int> vote_predictions(const std::vector<std::vector<double>>& decisions);
/**
* @brief Clean up all models
*/
void cleanup_models();
};
/**
* @brief Factory function to create multiclass strategy
* @param strategy Strategy type
* @return Unique pointer to multiclass strategy
*/
std::unique_ptr<MulticlassStrategyBase> create_multiclass_strategy(MulticlassStrategy strategy);
} // namespace svm_classifier

View File

@@ -0,0 +1,297 @@
#pragma once
#include "types.hpp"
#include "kernel_parameters.hpp"
#include "data_converter.hpp"
#include "multiclass_strategy.hpp"
#include <torch/torch.h>
#include <nlohmann/json.hpp>
#include <memory>
#include <string>
namespace svm_classifier {
/**
* @brief Support Vector Machine Classifier with scikit-learn compatible API
*
* This class provides a unified interface for SVM classification using both
* liblinear (for linear kernels) and libsvm (for non-linear kernels).
* It supports multiclass classification through One-vs-Rest and One-vs-One strategies.
*/
class SVMClassifier {
public:
/**
* @brief Default constructor with default parameters
*/
SVMClassifier();
/**
* @brief Constructor with JSON parameters
* @param config JSON configuration object
*/
explicit SVMClassifier(const nlohmann::json& config);
/**
* @brief Constructor with explicit parameters
* @param kernel Kernel type
* @param C Regularization parameter
* @param multiclass_strategy Multiclass strategy
*/
SVMClassifier(KernelType kernel,
double C = 1.0,
MulticlassStrategy multiclass_strategy = MulticlassStrategy::ONE_VS_REST);
/**
* @brief Destructor
*/
~SVMClassifier();
/**
* @brief Copy constructor (deleted - models are not copyable)
*/
SVMClassifier(const SVMClassifier&) = delete;
/**
* @brief Copy assignment (deleted - models are not copyable)
*/
SVMClassifier& operator=(const SVMClassifier&) = delete;
/**
* @brief Move constructor
*/
SVMClassifier(SVMClassifier&&) noexcept;
/**
* @brief Move assignment
*/
SVMClassifier& operator=(SVMClassifier&&) noexcept;
/**
* @brief Train the SVM classifier
* @param X Feature tensor of shape (n_samples, n_features)
* @param y Target tensor of shape (n_samples,) with class labels
* @return Training metrics
* @throws std::invalid_argument if input data is invalid
* @throws std::runtime_error if training fails
*/
TrainingMetrics fit(const torch::Tensor& X, const torch::Tensor& y);
/**
* @brief Predict class labels for samples
* @param X Feature tensor of shape (n_samples, n_features)
* @return Tensor of predicted class labels
* @throws std::runtime_error if model is not fitted
*/
torch::Tensor predict(const torch::Tensor& X);
/**
* @brief Predict class probabilities for samples
* @param X Feature tensor of shape (n_samples, n_features)
* @return Tensor of shape (n_samples, n_classes) with class probabilities
* @throws std::runtime_error if model is not fitted or doesn't support probabilities
*/
torch::Tensor predict_proba(const torch::Tensor& X);
/**
* @brief Get decision function values
* @param X Feature tensor of shape (n_samples, n_features)
* @return Tensor with decision function values
* @throws std::runtime_error if model is not fitted
*/
torch::Tensor decision_function(const torch::Tensor& X);
/**
* @brief Calculate accuracy score on test data
* @param X Feature tensor of shape (n_samples, n_features)
* @param y_true True labels tensor of shape (n_samples,)
* @return Accuracy score (fraction of correctly predicted samples)
* @throws std::runtime_error if model is not fitted
*/
double score(const torch::Tensor& X, const torch::Tensor& y_true);
/**
* @brief Calculate detailed evaluation metrics
* @param X Feature tensor of shape (n_samples, n_features)
* @param y_true True labels tensor of shape (n_samples,)
* @return Evaluation metrics including precision, recall, F1-score
*/
EvaluationMetrics evaluate(const torch::Tensor& X, const torch::Tensor& y_true);
/**
* @brief Set parameters from JSON configuration
* @param config JSON configuration object
* @throws std::invalid_argument if parameters are invalid
*/
void set_parameters(const nlohmann::json& config);
/**
* @brief Get current parameters as JSON
* @return JSON object with current parameters
*/
nlohmann::json get_parameters() const;
/**
* @brief Check if the model is fitted/trained
* @return True if model is fitted
*/
bool is_fitted() const { return is_fitted_; }
/**
* @brief Get the number of classes
* @return Number of classes (0 if not fitted)
*/
int get_n_classes() const;
/**
* @brief Get unique class labels
* @return Vector of unique class labels
*/
std::vector<int> get_classes() const;
/**
* @brief Get the number of features
* @return Number of features (0 if not fitted)
*/
int get_n_features() const { return n_features_; }
/**
* @brief Get training metrics from last fit
* @return Training metrics
*/
TrainingMetrics get_training_metrics() const { return training_metrics_; }
/**
* @brief Check if the current model supports probability prediction
* @return True if probabilities are supported
*/
bool supports_probability() const;
/**
* @brief Save model to file
* @param filename Path to save the model
* @throws std::runtime_error if saving fails
*/
void save_model(const std::string& filename) const;
/**
* @brief Load model from file
* @param filename Path to load the model from
* @throws std::runtime_error if loading fails
*/
void load_model(const std::string& filename);
/**
* @brief Get kernel type
* @return Current kernel type
*/
KernelType get_kernel_type() const { return params_.get_kernel_type(); }
/**
* @brief Get multiclass strategy
* @return Current multiclass strategy
*/
MulticlassStrategy get_multiclass_strategy() const { return params_.get_multiclass_strategy(); }
/**
* @brief Get SVM library being used
* @return SVM library type
*/
SVMLibrary get_svm_library() const { return get_svm_library(params_.get_kernel_type()); }
/**
* @brief Perform cross-validation
* @param X Feature tensor
* @param y Target tensor
* @param cv Number of folds (default: 5)
* @return Cross-validation scores for each fold
*/
std::vector<double> cross_validate(const torch::Tensor& X,
const torch::Tensor& y,
int cv = 5);
/**
* @brief Find optimal hyperparameters using grid search
* @param X Feature tensor
* @param y Target tensor
* @param param_grid JSON object with parameter grid
* @param cv Number of cross-validation folds
* @return JSON object with best parameters and score
*/
nlohmann::json grid_search(const torch::Tensor& X,
const torch::Tensor& y,
const nlohmann::json& param_grid,
int cv = 5);
/**
* @brief Get feature importance (for linear kernels only)
* @return Tensor with feature weights/importance
* @throws std::runtime_error if not supported for current kernel
*/
torch::Tensor get_feature_importance() const;
/**
* @brief Reset the classifier (clear trained model)
*/
void reset();
private:
KernelParameters params_; ///< Model parameters
std::unique_ptr<MulticlassStrategyBase> multiclass_strategy_; ///< Multiclass strategy
std::unique_ptr<DataConverter> data_converter_; ///< Data converter
bool is_fitted_; ///< Whether model is fitted
int n_features_; ///< Number of features
TrainingMetrics training_metrics_; ///< Last training metrics
/**
* @brief Validate input data
* @param X Feature tensor
* @param y Target tensor (optional)
* @param check_fitted Whether to check if model is fitted
*/
void validate_input(const torch::Tensor& X,
const torch::Tensor& y = torch::Tensor(),
bool check_fitted = false);
/**
* @brief Initialize multiclass strategy based on current parameters
*/
void initialize_multiclass_strategy();
/**
* @brief Calculate confusion matrix
* @param y_true True labels
* @param y_pred Predicted labels
* @return Confusion matrix
*/
std::vector<std::vector<int>> calculate_confusion_matrix(const std::vector<int>& y_true,
const std::vector<int>& y_pred);
/**
* @brief Calculate precision, recall, and F1-score from confusion matrix
* @param confusion_matrix Confusion matrix
* @return Tuple of (precision, recall, f1_score)
*/
std::tuple<double, double, double> calculate_metrics_from_confusion_matrix(
const std::vector<std::vector<int>>& confusion_matrix);
/**
* @brief Split data for cross-validation
* @param X Feature tensor
* @param y Target tensor
* @param fold Current fold
* @param n_folds Total number of folds
* @return Tuple of (X_train, y_train, X_val, y_val)
*/
std::tuple<torch::Tensor, torch::Tensor, torch::Tensor, torch::Tensor>
split_for_cv(const torch::Tensor& X, const torch::Tensor& y, int fold, int n_folds);
/**
* @brief Generate parameter combinations for grid search
* @param param_grid JSON parameter grid
* @return Vector of parameter combinations
*/
std::vector<nlohmann::json> generate_param_combinations(const nlohmann::json& param_grid);
};
} // namespace svm_classifier

View File

@@ -0,0 +1,138 @@
#pragma once
#include <string>
#include <vector>
#include <memory>
namespace svm_classifier {
/**
* @brief Supported kernel types
*/
enum class KernelType {
LINEAR, ///< Linear kernel: <x, y>
RBF, ///< Radial Basis Function: exp(-gamma * ||x - y||^2)
POLYNOMIAL, ///< Polynomial: (gamma * <x, y> + coef0)^degree
SIGMOID ///< Sigmoid: tanh(gamma * <x, y> + coef0)
};
/**
* @brief Multiclass classification strategies
*/
enum class MulticlassStrategy {
ONE_VS_REST, ///< One-vs-Rest (OvR) strategy
ONE_VS_ONE ///< One-vs-One (OvO) strategy
};
/**
* @brief SVM library type selection
*/
enum class SVMLibrary {
LIBLINEAR, ///< Use liblinear (for linear kernels)
LIBSVM ///< Use libsvm (for non-linear kernels)
};
/**
* @brief Training result status
*/
enum class TrainingStatus {
SUCCESS,
INVALID_PARAMETERS,
INSUFFICIENT_DATA,
MEMORY_ERROR,
CONVERGENCE_ERROR
};
/**
* @brief Prediction result structure
*/
struct PredictionResult {
std::vector<int> predictions; ///< Predicted class labels
std::vector<std::vector<double>> probabilities; ///< Class probabilities (if available)
std::vector<std::vector<double>> decision_values; ///< Decision function values
bool has_probabilities = false; ///< Whether probabilities are available
};
/**
* @brief Training metrics structure
*/
struct TrainingMetrics {
double training_time = 0.0; ///< Training time in seconds
int support_vectors = 0; ///< Number of support vectors
int iterations = 0; ///< Number of iterations
double objective_value = 0.0; ///< Final objective value
TrainingStatus status = TrainingStatus::SUCCESS;
};
/**
* @brief Model evaluation metrics
*/
struct EvaluationMetrics {
double accuracy = 0.0; ///< Classification accuracy
double precision = 0.0; ///< Macro-averaged precision
double recall = 0.0; ///< Macro-averaged recall
double f1_score = 0.0; ///< Macro-averaged F1-score
std::vector<std::vector<int>> confusion_matrix; ///< Confusion matrix
};
/**
* @brief Convert kernel type to string
*/
inline std::string kernel_type_to_string(KernelType kernel)
{
switch (kernel) {
case KernelType::LINEAR: return "linear";
case KernelType::RBF: return "rbf";
case KernelType::POLYNOMIAL: return "polynomial";
case KernelType::SIGMOID: return "sigmoid";
default: return "unknown";
}
}
/**
* @brief Convert string to kernel type
*/
inline KernelType string_to_kernel_type(const std::string& kernel_str)
{
if (kernel_str == "linear") return KernelType::LINEAR;
if (kernel_str == "rbf") return KernelType::RBF;
if (kernel_str == "polynomial" || kernel_str == "poly") return KernelType::POLYNOMIAL;
if (kernel_str == "sigmoid") return KernelType::SIGMOID;
throw std::invalid_argument("Unknown kernel type: " + kernel_str);
}
/**
* @brief Convert multiclass strategy to string
*/
inline std::string multiclass_strategy_to_string(MulticlassStrategy strategy)
{
switch (strategy) {
case MulticlassStrategy::ONE_VS_REST: return "ovr";
case MulticlassStrategy::ONE_VS_ONE: return "ovo";
default: return "unknown";
}
}
/**
* @brief Convert string to multiclass strategy
*/
inline MulticlassStrategy string_to_multiclass_strategy(const std::string& strategy_str)
{
if (strategy_str == "ovr" || strategy_str == "one_vs_rest") {
return MulticlassStrategy::ONE_VS_REST;
}
if (strategy_str == "ovo" || strategy_str == "one_vs_one") {
return MulticlassStrategy::ONE_VS_ONE;
}
throw std::invalid_argument("Unknown multiclass strategy: " + strategy_str);
}
/**
* @brief Determine which SVM library to use based on kernel type
*/
inline SVMLibrary get_svm_library(KernelType kernel)
{
return (kernel == KernelType::LINEAR) ? SVMLibrary::LIBLINEAR : SVMLibrary::LIBSVM;
}
} // namespace svm_classifier