From: Hsiang-Fu Yu Date: Mon, 28 Jul 2014 18:22:42 +0000 (-0500) Subject: add two functions get_decfun_coef and get_decfun_bias X-Git-Tag: v195~8 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=fb7a0a53b395bced06157a8d69a852ebdc719b2d;p=liblinear add two functions get_decfun_coef and get_decfun_bias --- diff --git a/README b/README index b5e1506..2560287 100644 --- a/README +++ b/README @@ -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); @@ -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_); diff --git a/linear.cpp b/linear.cpp index 76623c1..6efbf10 100644 --- a/linear.cpp +++ b/linear.cpp @@ -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; @@ -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]; @@ -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) @@ -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) diff --git a/linear.h b/linear.h index 22a3567..6b07b47 100644 --- a/linear.h +++ b/linear.h @@ -57,6 +57,8 @@ 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); @@ -64,6 +66,7 @@ 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 diff --git a/matlab/predict.c b/matlab/predict.c index 36c6046..3548bdc 100644 --- a/matlab/predict.c +++ b/matlab/predict.c @@ -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", diff --git a/predict.c b/predict.c index c5b3f1d..a043181 100644 --- a/predict.c +++ b/predict.c @@ -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", diff --git a/python/README b/python/README index e12b298..e6349cf 100644 --- a/python/README +++ b/python/README @@ -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 ================= diff --git a/python/liblinear.py b/python/liblinear.py index ac06a33..9587718 100644 --- a/python/liblinear.py +++ b/python/liblinear.py @@ -250,9 +250,23 @@ class model(Structure): 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 @@ -278,10 +292,13 @@ fillprototype(liblinear.load_model, POINTER(model), [c_char_p]) 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)]) diff --git a/python/liblinearutil.py b/python/liblinearutil.py index 626489b..40de52a 100644 --- a/python/liblinearutil.py +++ b/python/liblinearutil.py @@ -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): @@ -246,7 +247,7 @@ def predict(y, x, m, options=""): 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: