Skip to content

Commit

Permalink
add two functions get_decfun_coef and get_decfun_bias
Browse files Browse the repository at this point in the history
  • Loading branch information
Hsiang-Fu Yu authored and ycjuan committed Oct 19, 2014
1 parent 56d69b2 commit fb7a0a5
Show file tree
Hide file tree
Showing 8 changed files with 139 additions and 14 deletions.
25 changes: 25 additions & 0 deletions README
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,21 @@ Library Usage
This function outputs the name of labels into an array called label.
For a regression model, label is unchanged.

- Function: double get_decfun_coef(const struct model *model_, int feat_idx,
int label_idx);

This function gives the coefficient for the feature with feature index =
feat_idx and the class with label index = label_idx. Note that feat_idx
starts from 1, while label_idx starts from 0. If label_idx is not in the
valid range (0 to nr_class-1), then a NaN will be returned; and if feat_idx
is not in the valid range (1 to nr_feature), then a zero value will be
returned. For regression models, label_idx is ignored.

- Function: double get_decfun_bias(const struct model *model_, int label_idx);

This function gives the bias term corresponding to the class with the
label_idx. For regression models, label_idx is ignored.

- Function: const char *check_parameter(const struct problem *prob,
const struct parameter *param);

Expand All @@ -456,6 +471,16 @@ Library Usage
train() and cross_validation(). It returns NULL if the
parameters are feasible, otherwise an error message is returned.

- Function: int check_probability_model(const struct model *model);

This function returns 1 if the model supports probability output;
otherwise, it returns 0.

- Function: int check_regression_model(const struct model *model);

This function returns 1 if the model is a regression model; otherwise
it returns 0.

- Function: int save_model(const char *model_file_name,
const struct model *model_);

Expand Down
65 changes: 59 additions & 6 deletions linear.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2305,9 +2305,7 @@ model* train(const problem *prob, const parameter *param)
model_->param = *param;
model_->bias = prob->bias;

if(param->solver_type == L2R_L2LOSS_SVR ||
param->solver_type == L2R_L1LOSS_SVR_DUAL ||
param->solver_type == L2R_L2LOSS_SVR_DUAL)
if(check_regression_model(model_))
{
model_->w = Malloc(double, w_size);
model_->nr_class = 2;
Expand Down Expand Up @@ -2512,9 +2510,7 @@ double predict_values(const struct model *model_, const struct feature_node *x,

if(nr_class==2)
{
if(model_->param.solver_type == L2R_L2LOSS_SVR ||
model_->param.solver_type == L2R_L1LOSS_SVR_DUAL ||
model_->param.solver_type == L2R_L2LOSS_SVR_DUAL)
if(check_regression_model(model_))
return dec_values[0];
else
return (dec_values[0]>0)?model_->label[0]:model_->label[1];
Expand Down Expand Up @@ -2764,6 +2760,56 @@ void get_labels(const model *model_, int* label)
label[i] = model_->label[i];
}

// use inline here for better performance (around 20% faster than the non-inline one)
static inline double get_w_value(const struct model *model_, int idx, int label_idx)
{
int nr_class = model_->nr_class;
int solver_type = model_->param.solver_type;
const double *w = model_->w;

if(!check_regression_model(model_) && (label_idx < 0 || label_idx >= nr_class))
{
const double nan = 0.0/0.0;
return nan;
}
if(idx < 0 || idx > model_->nr_feature)
return 0;
if(check_regression_model(model_))
return w[idx];
else
{
if(nr_class == 2 && solver_type != MCSVM_CS)
{
if(label_idx == 0)
return w[idx];
else
return -w[idx];
}
else
return w[idx*nr_class+label_idx];
}
}

// feat_idx: starting from 1 to nr_feature
// label_idx: starting from 0 to nr_class-1 for classification models;
// for regression models, label_idx is ignored.
double get_decfun_coef(const struct model *model_, int feat_idx, int label_idx)
{
if(feat_idx > model_->nr_feature)
return 0;
return get_w_value(model_, feat_idx-1, label_idx);
}

double get_decfun_bias(const struct model *model_, int label_idx)
{
int bias_idx = model_->nr_feature;
double bias = model_->bias;
if(bias <= 0)
return 0;
else
return bias*get_w_value(model_, bias_idx, label_idx);
}

void free_model_content(struct model *model_ptr)
{
if(model_ptr->w != NULL)
Expand Down Expand Up @@ -2824,6 +2870,13 @@ int check_probability_model(const struct model *model_)
model_->param.solver_type==L1R_LR);
}

int check_regression_model(const struct model *model_)
{
return (model_->param.solver_type==L2R_L2LOSS_SVR ||
model_->param.solver_type==L2R_L1LOSS_SVR_DUAL ||
model_->param.solver_type==L2R_L2LOSS_SVR_DUAL);
}

void set_print_string_function(void (*print_func)(const char*))
{
if (print_func == NULL)
Expand Down
3 changes: 3 additions & 0 deletions linear.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,16 @@ struct model *load_model(const char *model_file_name);
int get_nr_feature(const struct model *model_);
int get_nr_class(const struct model *model_);
void get_labels(const struct model *model_, int* label);
double get_decfun_coef(const struct model *model_, int feat_idx, int label_idx);
double get_decfun_bias(const struct model *model_, int label_idx);

void free_model_content(struct model *model_ptr);
void free_and_destroy_model(struct model **model_ptr_ptr);
void destroy_param(struct parameter *param);

const char *check_parameter(const struct problem *prob, const struct parameter *param);
int check_probability_model(const struct model *model);
int check_regression_model(const struct model *model);
void set_print_string_function(void (*print_func) (const char*));

#ifdef __cplusplus
Expand Down
4 changes: 1 addition & 3 deletions matlab/predict.c
Original file line number Diff line number Diff line change
Expand Up @@ -176,9 +176,7 @@ void do_predict(int nlhs, mxArray *plhs[], const mxArray *prhs[], struct model *
++total;
}

if(model_->param.solver_type==L2R_L2LOSS_SVR ||
model_->param.solver_type==L2R_L1LOSS_SVR_DUAL ||
model_->param.solver_type==L2R_L2LOSS_SVR_DUAL)
if(check_regression_model(model_))
{
info("Mean squared error = %g (regression)\n",error/total);
info("Squared correlation coefficient = %g (regression)\n",
Expand Down
4 changes: 1 addition & 3 deletions predict.c
Original file line number Diff line number Diff line change
Expand Up @@ -158,9 +158,7 @@ void do_predict(FILE *input, FILE *output)
sumpt += predict_label*target_label;
++total;
}
if(model_->param.solver_type==L2R_L2LOSS_SVR ||
model_->param.solver_type==L2R_L1LOSS_SVR_DUAL ||
model_->param.solver_type==L2R_L2LOSS_SVR_DUAL)
if(check_regression_model(model_))
{
info("Mean squared error = %g (regression)\n",error/total);
info("Squared correlation coefficient = %g (regression)\n",
Expand Down
30 changes: 30 additions & 0 deletions python/README
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,36 @@ LIBLINEAR shared library:
>>> nr_class = model_.get_nr_class()
>>> class_labels = model_.get_labels()
>>> is_prob_model = model_.is_probability_model()
>>> is_regression_model = model_.is_regression_model()

The decision function is W*x + b, where
W is an nr_class-by-nr_feature matrix, and
b is a vector of size nr_class.
To access W_kj (i.e., coefficient for the k-th class and the j-th feature)
and b_k (i.e., bias for the k-th class), use the following functions.

>>> W_kj = model_.get_decfun_coef(feat_idx=j, label_idx=k)
>>> b_k = model_.get_decfun_bias(label_idx=k)

We also provide a function to extract w_k (i.e., the k-th row of W) and
b_k directly as follows.

>>> [w_k, b_k] = model_.get_decfun(label_idx=k)

Note that w_k is a Python list of length nr_feature, which means that
w_k[0] = W_k1.
For regression models, W is just a vector of length nr_feature. Either
set label_idx=0 or omit the label_idx parameter to access the coefficients.

>>> W_j = model_.get_decfun_coef(feat_idx=j)
>>> b = model_.get_decfun_bias()
>>> [W, b] = model_.get_decfun()

Note that in get_decfun_coef, get_decfun_bias, and get_decfun, feat_idx
starts from 1, while label_idx starts from 0. If label_idx is not in the
valid range (0 to nr_class-1), then a NaN will be returned; and if feat_idx
is not in the valid range (1 to nr_feature), then a zero value will be
returned. For regression models, label_idx is ignored.

Utility Functions
=================
Expand Down
17 changes: 17 additions & 0 deletions python/liblinear.py
Original file line number Diff line number Diff line change
Expand Up @@ -250,9 +250,23 @@ def get_labels(self):
liblinear.get_labels(self, labels)
return labels[:nr_class]

def get_decfun_coef(self, feat_idx, label_idx=0):
return liblinear.get_decfun_coef(self, feat_idx, label_idx)

def get_decfun_bias(self, label_idx=0):
return liblinear.get_decfun_bias(self, label_idx)

def get_decfun(self, label_idx=0):
w = [liblinear.get_decfun_coef(self, feat_idx, label_idx) for feat_idx in range(1, self.nr_feature+1)]
b = liblinear.get_decfun_bias(self, label_idx)
return (w, b)

def is_probability_model(self):
return (liblinear.check_probability_model(self) == 1)

def is_regression_model(self):
return (liblinear.check_regression_model(self) == 1)

def toPyModel(model_ptr):
"""
toPyModel(model_ptr) -> model
Expand All @@ -278,10 +292,13 @@ def toPyModel(model_ptr):
fillprototype(liblinear.get_nr_feature, c_int, [POINTER(model)])
fillprototype(liblinear.get_nr_class, c_int, [POINTER(model)])
fillprototype(liblinear.get_labels, None, [POINTER(model), POINTER(c_int)])
fillprototype(liblinear.get_decfun_coef, c_double, [POINTER(model), c_int, c_int])
fillprototype(liblinear.get_decfun_bias, c_double, [POINTER(model), c_int])

fillprototype(liblinear.free_model_content, None, [POINTER(model)])
fillprototype(liblinear.free_and_destroy_model, None, [POINTER(POINTER(model))])
fillprototype(liblinear.destroy_param, None, [POINTER(parameter)])
fillprototype(liblinear.check_parameter, c_char_p, [POINTER(problem), POINTER(parameter)])
fillprototype(liblinear.check_probability_model, c_int, [POINTER(model)])
fillprototype(liblinear.check_regression_model, c_int, [POINTER(model)])
fillprototype(liblinear.set_print_string_function, None, [CFUNCTYPE(None, c_char_p)])
5 changes: 3 additions & 2 deletions python/liblinearutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
import os, sys
sys.path = [os.path.dirname(os.path.abspath(__file__))] + sys.path
from liblinear import *
from liblinear import __all__ as liblinear_all
from ctypes import c_double

__all__ = ['svm_read_problem', 'load_model', 'save_model', 'evaluations',
'train', 'predict']
'train', 'predict'] + liblinear_all


def svm_read_problem(data_file_name):
Expand Down Expand Up @@ -246,7 +247,7 @@ def info(s):
y = [0] * len(x)
ACC, MSE, SCC = evaluations(y, pred_labels)
l = len(y)
if solver_type in [L2R_L2LOSS_SVR, L2R_L2LOSS_SVR_DUAL, L2R_L1LOSS_SVR_DUAL]:
if m.is_regression_model():
info("Mean squared error = %g (regression)" % MSE)
info("Squared correlation coefficient = %g (regression)" % SCC)
else:
Expand Down

0 comments on commit fb7a0a5

Please sign in to comment.