Skip to content

Add support for SVDD (one-class SVM) #2807 #3013

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
192 changes: 189 additions & 3 deletions sklearn/svm/src/libsvm/svm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1494,11 +1494,60 @@ class ONE_CLASS_Q: public Kernel
double *QD;
};

class R2_Qq: public Kernel
{
public:
R2_Qq(const PREFIX(problem) & prob, const svm_parameter& param)
:Kernel(prob.l, prob.x, param)
{
cache = new Cache(prob.l,(int)(param.cache_size*(1<<20)));
this->C = param.C;
QD = new double[prob.l];
for(int i=0;i<prob.l;i++)
QD[i]= (Qfloat)(this->*kernel_function)(i,i) + 1/C;
}

Qfloat *get_Q(int i, int len) const
{
Qfloat *data;
int start;
if((start = cache->get_data(i,&data,len)) < len)
{
for(int j=start;j<len;j++)
data[j] = (Qfloat)(this->*kernel_function)(i,j);
if(i >= start && i < len)
data[i] += 1/C;
}
return data;
}

double *get_QD() const
{
return QD;
}

void swap_index(int i, int j) const
{
cache->swap_index(i,j);
Kernel::swap_index(i,j);
swap(QD[i],QD[j]);
}

~R2_Qq()
{
delete cache;
}
private:
Cache *cache;
double C;
double *QD;
};

class SVR_Q: public Kernel
{
public:
SVR_Q(const PREFIX(problem)& prob, const svm_parameter& param)
:Kernel(prob.l, prob.x, param)
public:
SVR_Q(const PREFIX(problem)& prob, const svm_parameter& param)
:Kernel(prob.l, prob.x, param)
{
l = prob.l;
cache = new Cache(l,(long int)(param.cache_size*(1<<20)));
Expand Down Expand Up @@ -1816,6 +1865,131 @@ static void solve_nu_svr(
delete[] y;
}

static void solve_svdd(
const PREFIX(problem) *prob, const svm_parameter *param,
double *alpha, Solver::SolutionInfo* si)
{
int l = prob->l;
int i,j;
double r_square;
double *C = new double[2*l];
double *QD = new double[l];
double *linear_term = new double[l];
schar *ones = new schar[l];
double sum;

ONE_CLASS_Q Q = ONE_CLASS_Q(*prob, *param);
sum = 0;
for(i=0;i<l;i++)
{
QD[i] = Q.get_QD()[i];
linear_term[i] = -0.5 * Q.get_QD()[i];
C[i] = prob->W[i]*param->C;
sum += C[i];
}
sum /= l;

if(sum > (double)1/l)
{
double sum_alpha = 1;
for(i=0;i<l;i++)
{
alpha[i] = min(C[i],sum_alpha);
sum_alpha -= alpha[i];

ones[i] = 1;
}

Solver s;
s.Solve(l, Q, linear_term, ones, alpha, C,
param->eps, si, param->shrinking, param->max_iter);

// \bar{R} = 2(obj-rho) + sum K_{ii}*alpha_i
// because rho = (a^Ta - \bar{R})/2
r_square = 2*(si->obj-si->rho);
for(i=0;i<l;i++)
r_square += alpha[i]*QD[i];
}
else
{
r_square = 0.0;
double rho = 0;
double obj = 0;

// rho = aTa/2 = sum sum Q_ij /l/l/2
// obj = 0.5*(-sum Q_ii + sum sum Q_ij /l)*C
// 0.5 for consistency with C > 1/l, where dual is divided by 2
for(i=0;i<l;i++)
{
obj -= QD[i]/2;
rho += QD[i]/2;
for(j=i+1;j<l;j++)
#ifdef _DENSE_REP
rho += Kernel::k_function(prob->x+i, prob->x+j,*param);
#else
rho += Kernel::k_function(prob->x[i], prob->x[j],*param);
#endif
}
si->obj = (obj + rho/l)*sum;
si->rho = rho / (l*l);

}

info("R^2 = %f\n",r_square);

delete[] linear_term;
delete[] QD;
delete[] ones;
delete[] C;

}

static void solve_r2(
const PREFIX(problem) *prob, const svm_parameter *param,
double *alpha, Solver::SolutionInfo* si)
{
svm_parameter svdd_param = *param;
svdd_param.C = 2;

solve_svdd(prob,&svdd_param,alpha,si);
}

static void solve_r2q(
const PREFIX(problem) *prob, const svm_parameter *param,
double *alpha, Solver::SolutionInfo* si)
{
int l = prob->l;
double *linear_term = new double[l];
double *C=new double[l];
schar *ones = new schar[l];
int i;

alpha[0] = 1;
for(i=1;i<l;i++)
alpha[i] = 0;

for(i=0;i<l;i++)
{
C[i] = INF;
#ifdef _DENSE_REP
linear_term[i]=-0.5*(Kernel::k_function(prob->x+i,prob->x+i,*param) + 1.0/param->C);
#else
linear_term[i]=-0.5*(Kernel::k_function(prob->x[i],prob->x[i],*param) + 1.0/param->C);
#endif
ones[i] = 1;
}

Solver s;
s.Solve(l, R2_Qq(*prob,*param), linear_term, ones,
alpha, C, param->eps, si, param->shrinking, param->max_iter);

info("R^2 = %f\n", -2 *si->obj);

delete[] linear_term;
delete[] ones;
delete[] C;
}

//
// decision_function
//
Expand Down Expand Up @@ -1853,6 +2027,18 @@ static decision_function svm_train_one(
si.upper_bound = Malloc(double,2*prob->l);
solve_nu_svr(prob,param,alpha,&si);
break;
case SVDD:
si.upper_bound = Malloc(double,prob->l);
solve_svdd(prob,param,alpha,&si);
break;
case R2:
si.upper_bound = Malloc(double,prob->l);
solve_r2(prob,param,alpha,&si);
break;
case R2q:
si.upper_bound = Malloc(double,prob->l);
solve_r2q(prob,param,alpha,&si);
break;
}

*status |= si.solve_timed_out;
Expand Down
2 changes: 1 addition & 1 deletion sklearn/svm/src/libsvm/svm.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ struct svm_csr_problem
};


enum { C_SVC, NU_SVC, ONE_CLASS, EPSILON_SVR, NU_SVR }; /* svm_type */
enum { C_SVC, NU_SVC, ONE_CLASS, EPSILON_SVR, NU_SVR, SVDD, R2, R2q }; /* svm_type */
enum { LINEAR, POLY, RBF, SIGMOID, PRECOMPUTED }; /* kernel_type */

struct svm_parameter
Expand Down