blob: 71464324bf5821894404fde8224caa0719a95b88 [file] [log] [blame]
/* ----------------------------------------------------------------------- *//**
*
* @file linear_svm.hpp
*
*//* ----------------------------------------------------------------------- */
#ifndef MADLIB_MODULES_CONVEX_TASK_LINEAR_SVM_HPP_
#define MADLIB_MODULES_CONVEX_TASK_LINEAR_SVM_HPP_
namespace madlib {
namespace modules {
namespace convex {
// Use Eigen
using namespace madlib::dbal::eigen_integration;
template <class Model, class Tuple>
class LinearSVM {
public:
typedef Model model_type;
typedef Tuple tuple_type;
typedef typename Tuple::independent_variables_type independent_variables_type;
typedef typename Tuple::dependent_variable_type dependent_variable_type;
// Model is assumed to be base Eigen type or Eigen map and the 'EigenType'
// variable infers the actual type from the Model definition.
// For eg. SVMModel is defined as a ColumnVectorTransparentHandleMap which
// has a ColumnVector as its EigenType.
typedef typename model_type::PlainEigenType coefficient_type;
static double epsilon;
static bool is_svc;
static void gradient(
const model_type &model,
const independent_variables_type &x,
const dependent_variable_type &y,
model_type &gradient);
static void gradientInPlace(
model_type &model,
const independent_variables_type &x,
const dependent_variable_type &y,
const double &stepsize);
static double getLossAndUpdateModel(
model_type &model,
const independent_variables_type &x,
const dependent_variable_type &y,
const double &stepsize);
static double loss(
const model_type &model,
const independent_variables_type &x,
const dependent_variable_type &y);
static dependent_variable_type predict(
const model_type &model,
const independent_variables_type &x);
};
template <class Model, class Tuple>
double LinearSVM<Model, Tuple >::epsilon = 0.;
template <class Model, class Tuple>
bool LinearSVM<Model, Tuple >::is_svc = false;
template <class Model, class Tuple>
void
LinearSVM<Model, Tuple>::gradient(
const model_type &model,
const independent_variables_type &x,
const dependent_variable_type &y,
model_type &gradient) {
double wx = dot(model, x);
if (is_svc) {
if (1 - wx * y > 0) {
double c = -y; // minus for "-loglik"
gradient += c * x;
}
}
else {
double wx_y = wx - y;
double c = wx_y > 0 ? 1. : -1.;
if (c*wx_y - epsilon > 0.)
gradient += c * x;
}
}
template <class Model, class Tuple>
void
LinearSVM<Model, Tuple>::gradientInPlace(
model_type &model,
const independent_variables_type &x,
const dependent_variable_type &y,
const double &stepsize) {
double wx = dot(model, x);
if (is_svc) {
if (1. - wx * y > 0.) {
double c = -y; // minus for "-loglik"
model -= stepsize * c * x;
}
}
else {
double wx_y = wx - y;
double c = wx_y > 0 ? 1. : -1.;
if (c*wx_y - epsilon > 0.)
model -= stepsize * c * x;
}
}
/**
* @brief This function will update the model for a single batch and return the loss
* @param model Model to update
* @param x Batch of independent variables
* @param y Batch of dependent variables
* @param stepsize Learning rate for model update
* @return Total loss in the batch
*/
template <class Model, class Tuple>
double
LinearSVM<Model, Tuple>::getLossAndUpdateModel(
model_type &model,
const independent_variables_type &x,
const dependent_variable_type &y,
const double &stepsize){
// This function is called by the minibatch transition function to update
// the model for each batch. x and y in the function signature are defined
// as generic variables to ensure a consistent interface across all modules.
// ASSUMPTION: 'gradient' will always be of the same type as the
// coefficients. In SVM, the model is just the coefficients, but can be
// more complex with other modules like MLP.
coefficient_type gradient = model;
gradient.setZero();
coefficient_type w_transpose_x = x * model;
double loss = 0.0;
int batch_size = x.rows();
double dist_from_hyperplane = 0.0;
double c = 0.0;
int n_points_with_positive_dist = 0;
for (int i = 0; i < batch_size; i++) {
if (is_svc) {
c = -y(i); // minus for "-loglik"
dist_from_hyperplane = 1.0 - w_transpose_x(i) * y(i);
} else {
double wx_y = w_transpose_x(i) - y(i);
c = wx_y > 0 ? 1.0 : -1.0;
dist_from_hyperplane = c * wx_y - epsilon;
}
if (dist_from_hyperplane > 0.) {
gradient += c * x.row(i);
loss += dist_from_hyperplane;
n_points_with_positive_dist++;
}
}
gradient.array() /= n_points_with_positive_dist;
model -= stepsize * gradient;
return loss;
}
template <class Model, class Tuple>
double
LinearSVM<Model, Tuple>::loss(
const model_type &model,
const independent_variables_type &x,
const dependent_variable_type &y) {
double wx = dot(model, x);
double distance = 0.;
if (is_svc) {
distance = 1. - wx * y;
}
else {
distance = fabs(wx - y) - epsilon;
}
return distance > 0. ? distance : 0.;
}
template <class Model, class Tuple>
typename Tuple::dependent_variable_type
LinearSVM<Model, Tuple>::predict(
const model_type &model,
const independent_variables_type &x) {
return dot(model, x);
}
} // namespace convex
} // namespace modules
} // namespace madlib
#endif