123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617 |
- /**
- * @file VCSVMLight.cpp
- * @brief Simple Nearest Neighbour Implementation
- * @author Erik Rodner
- * @date 10/25/2007
- */
- #ifdef NICE_USELIB_SVMLIGHT
- #include <iostream>
- #include "vislearning/classifier/vclassifier/VCSVMLight.h"
- // just because of some macro problems
- # define SVM_LINEAR 0 /* linear kernel type */
- # define SVM_POLY 1 /* polynoial kernel type */
- # define SVM_RBF 2 /* rbf kernel type */
- # define SVM_SIGMOID 3 /* sigmoid kernel type */
- # define SVM_CUSTOM 4 /* custom kernel type */
- using namespace OBJREC;
- using namespace std;
- using namespace NICE;
- VCSVMLight::VCSVMLight ( const Config *_conf, const string & section )
- : VecClassifier ( _conf )
- {
- finalModel = NULL;
- std::string kernel_type_s = _conf->gS(section, "kernel", "rbf");
- if ( (kernel_type_s == "none") || (kernel_type_s == "linear") )
- {
- kernel_type = SVM_LINEAR;
- } else if ( (kernel_type_s == "poly") ) {
- kernel_type = SVM_POLY;
- } else if ( (kernel_type_s == "rbf") ) {
- kernel_type = SVM_RBF;
- } else if ( (kernel_type_s == "sigmoid") ) {
- kernel_type = SVM_SIGMOID;
- } else if ( (kernel_type_s == "custom") ) {
- kernel_type = SVM_CUSTOM;
- } else {
- fthrow ( Exception, "Kernel method " << kernel_type_s << " not supported." );
- }
- std::string normalization_type_s = _conf->gS(section, "normalization_type", "euclidean" );
- if ( normalization_type_s == "none" ) {
- normalization_type = SVM_NORMALIZATION_NONE;
- } else if ( normalization_type_s == "euclidean" ) {
- normalization_type = SVM_NORMALIZATION_EUCLIDEAN;
- } else if ( normalization_type_s == "maxmin" ) {
- normalization_type = SVM_NORMALIZATION_01;
- } else {
- fthrow ( Exception, "Normalization method " << normalization_type << " not supported." );
- }
- fprintf (stderr, "VCSVMLight: kernel %d (%s) normalization %d (%s)\n", kernel_type, kernel_type_s.c_str(),
- normalization_type, normalization_type_s.c_str() );
- poly_degree = _conf->gI(section, "poly_degree", 1);
- rbf_gamma = _conf->gD(section, "rbf_gamma", 1.0);
- sigmoidpoly_scale = _conf->gD(section, "sigmoidpoly_scale", 1.0);
- sigmoidpoly_bias = _conf->gD(section, "sigmoidpoly_bias", 1.0);
- svm_c = _conf->gD(section, "C", 0.0 );
- use_crossvalidation = _conf->gB(section, "use_crossvalidation", true );
- optimize_parameters = _conf->gB(section, "optimize_parameters", true );
- rbf_gamma_min = _conf->gD(section, "rbf_gamma_min", 0.1 );
- rbf_gamma_max = _conf->gD(section, "rbf_gamma_max", 2.0 );
- rbf_gamma_step = _conf->gD(section, "rbf_gamma_step", 0.3 );
- svm_c_min = _conf->gD(section, "svm_c_min", 0.0 );
- svm_c_max = _conf->gD(section, "svm_c_max", 9.0 );
- svm_c_step = _conf->gD(section, "svm_c_step", 1.0 );
- bool calibrate_probabilities = _conf->gB(section, "calibrate_probabilities", true );
- if ( calibrate_probabilities )
- {
- logreg = new VCLogisticRegression ( _conf );
- } else {
- logreg = NULL;
- }
- // used in SVMLight
- verbosity = _conf->gI(section, "verbose", 1 );
- if ( optimize_parameters && use_crossvalidation )
- {
- fprintf (stderr, "VCSVMLight: SVMLight verbosity activated as a workaround for a bug within SVMLight\n");
- verbosity = 1;
- }
- maxClassNo = 1;
- }
- VCSVMLight::VCSVMLight ( const VCSVMLight & classifier ) : VecClassifier()
- {
- this->max = classifier.max;
- this->min = classifier.min;
- this->normalization_type = classifier.normalization_type;
- this->kernel_type = classifier.kernel_type;
- this->poly_degree = classifier.poly_degree;
- this->rbf_gamma = classifier.rbf_gamma;
- this->sigmoidpoly_scale = classifier.sigmoidpoly_scale;
- this->sigmoidpoly_bias = classifier.sigmoidpoly_bias;
- this->svm_c = classifier.svm_c;
- this->use_crossvalidation = classifier.use_crossvalidation;
- this->optimize_parameters = classifier.optimize_parameters;
- this->rbf_gamma_min = classifier.rbf_gamma_min;
- this->rbf_gamma_max = classifier.rbf_gamma_max;
- this->rbf_gamma_step = classifier.rbf_gamma_step;
- this->svm_c_min = classifier.svm_c_min;
- this->svm_c_max = classifier.svm_c_max;
- this->svm_c_step = classifier.svm_c_step;
- if ( classifier.logreg != NULL )
- this->logreg = classifier.logreg->clone();
- else
- this->logreg = NULL;
- if ( classifier.finalModel != NULL )
- this->finalModel = copy_model ( classifier.finalModel );
- else
- this->finalModel = NULL;
- }
- VCSVMLight::~VCSVMLight()
- {
- }
- void VCSVMLight::normalizeVector ( int normalization_type, NICE::Vector & x ) const
- {
- assert ( x.size() > 0 );
- if ( normalization_type == SVM_NORMALIZATION_EUCLIDEAN )
- {
- double length = x.normL2();
- if ( fabs(length) > 1e-12 )
- for ( uint i = 0 ; i < x.size() ; i++ )
- x[i] /= length;
- } else if ( normalization_type == SVM_NORMALIZATION_01 ) {
- for ( uint i = 0 ; i < x.size() ; i++ )
- if ( fabs(max[i]-min[i]) > 1e-20 )
- x[i] = (x[i] - min[i]) / (max[i] - min[i]);
- }
- }
- double VCSVMLight::getSVMScore ( const NICE::Vector & x ) const
- {
- if ( finalModel != NULL ) {
- DOC *example;
- NICE::Vector x_normalized ( x );
- normalizeVector ( normalization_type, x_normalized );
- WORD *words = (WORD *)my_malloc(sizeof(WORD)*(x.size()+10));
- long wc;
- for(wc=0; wc<(long)x.size(); wc++){
- words[wc].weight = (FVAL)(x_normalized[wc]);
- words[wc].wnum = wc+1;
- }
- words[wc].wnum = 0;
-
- long queryid=0;
- long slackid=0;
- long costfactor=1;
- char comment [2] = "";
- example = create_example(0,queryid,slackid,costfactor,
- create_svector(words,comment,1.0));
- double score = classify_example ( finalModel, example );
- free (words);
- free_example (example,1);
- return score;
- } else {
- fprintf (stderr, "VCSVMLight: no model found !!\n");
- exit(-1);
- }
- return 0.0;
- }
- ClassificationResult VCSVMLight::classify ( const NICE::Vector & x ) const
- {
- double score = getSVMScore ( x );
- if ( logreg != NULL )
- {
- NICE::Vector y (1);
- y[0] = score;
- return logreg->classify ( y );
- } else {
- FullVector scores ( maxClassNo+1 );
- scores[0] = 0.0;
- scores[1] = score;
- // we are dealing only with binary classification
- if ( score < 0.0 )
- return ClassificationResult (0, scores);
- else
- return ClassificationResult (1, scores);
- }
- }
- void VCSVMLight::estimateMaxMin ( const LabeledSetVector & train )
- {
- max.resize(0);
- min.resize(0);
- LOOP_ALL(train)
- {
- EACH(classno,v);
- UNUSED_PARAMETER(classno);
- if ( (max.size()<=0) && (min.size()<=0) )
- {
- max = Vector(v);
- min = Vector(v);
- } else {
- for ( uint i = 0 ; i < v.size() ; i++ )
- {
- if ( v[i] > max[i] ) max[i] = v[i];
- if ( v[i] < min[i] ) min[i] = v[i];
- }
- }
- }
- }
- void VCSVMLight::readDocuments(
- DOC ***docs,
- double **label,
- long int *totwords,
- long int *totdoc,
- const LabeledSetVector & train )
- {
- if ( normalization_type == SVM_NORMALIZATION_01 )
- estimateMaxMin ( train );
- char comment [2] = "";
- WORD *words;
- long vectorcount = train.count();
- long vectorsize = train.dimension();
- long dnum=0,dpos=0,dneg=0,dunlab=0,queryid,slackid,max_docs=vectorcount+2;
- long max_words_doc=vectorsize;
- double costfactor;
- long int wc=0;
- (*docs) = (DOC **)my_malloc(sizeof(DOC *)*max_docs); /* feature vectors */
- (*label) = (double *)my_malloc(sizeof(double)*max_docs); /* target values */
- words = (WORD *)my_malloc(sizeof(WORD)*(max_words_doc+10));
- (*totwords)=0;
-
- (*totwords) = vectorsize+1;
- queryid=0; slackid=0; costfactor=1.;
- dnum=0;
- LOOP_ALL(train)
- {
- EACH(classno,v);
- NICE::Vector v_normalized ( v );
- normalizeVector ( normalization_type, v_normalized );
- for(wc=0; wc<vectorsize; wc++){
- words[wc].weight = (FVAL)(v_normalized[wc]);
- words[wc].wnum = wc+1;
- }
- words[wc].wnum = 0;
- (*label)[dnum]= (classno==1) ? 1 : -1;
- if (classno == 1) dpos++;
- if (classno == 0) dneg++;
- if (classno == 0) dunlab++;
- if((*totwords) > MAXFEATNUM) {
- printf("\nMaximum feature number exceeds limit defined in MAXFEATNUM!\n");
- exit(1);
- }
- (*docs)[dnum] = create_example(dnum,queryid,slackid,costfactor,
- create_svector(words,comment,1.0));
- dnum++;
- }
- free(words);
- (*totdoc)=dnum;
- }
- void VCSVMLight::initMainParameters ( LEARN_PARM *learn_parm,
- KERNEL_PARM *kernel_parm )
- {
- learn_parm->svm_c=svm_c;
- kernel_parm->kernel_type=kernel_type;
- kernel_parm->poly_degree=poly_degree;
- kernel_parm->rbf_gamma=rbf_gamma;
- kernel_parm->coef_lin=sigmoidpoly_scale;
- kernel_parm->coef_const=sigmoidpoly_bias;
- strcpy(kernel_parm->custom,"empty");
- if(learn_parm->svm_c<0) {
- printf("\nThe C parameter must be greater than zero!\n\n");
- exit(-1);
- }
- }
- void VCSVMLight::initParameters (
- LEARN_PARM *learn_parm,
- KERNEL_PARM *kernel_parm )
- {
- /* set default */
- strcpy (learn_parm->predfile, "");
- strcpy (learn_parm->alphafile, "");
- learn_parm->biased_hyperplane=1;
- learn_parm->sharedslack=0;
- learn_parm->remove_inconsistent=0;
- learn_parm->skip_final_opt_check=0;
- learn_parm->svm_maxqpsize=10;
- learn_parm->svm_newvarsinqp=0;
-
- if( kernel_type == SVM_LINEAR )
- learn_parm->svm_iter_to_shrink=2;
- else
- learn_parm->svm_iter_to_shrink=100;
- learn_parm->maxiter=100000;
- learn_parm->kernel_cache_size=40;
- learn_parm->eps=0.1;
- learn_parm->transduction_posratio=-1.0;
- learn_parm->svm_costratio=1.0;
- learn_parm->svm_costratio_unlab=1.0;
- learn_parm->svm_unlabbound=1E-5;
- learn_parm->epsilon_crit=0.001;
- learn_parm->epsilon_a=1E-15;
- learn_parm->rho=1.0;
- learn_parm->xa_depth=0;
- // cross validation
- learn_parm->compute_loo = (use_crossvalidation) ? 1 : 0;
-
- learn_parm->type=CLASSIFICATION;
-
- if((learn_parm->skip_final_opt_check)
- && (kernel_parm->kernel_type == SVM_LINEAR)) {
- printf("\nIt does not make sense to skip the final optimality check for linear kernels.\n\n");
- learn_parm->skip_final_opt_check=0;
- }
- if((learn_parm->skip_final_opt_check)
- && (learn_parm->remove_inconsistent)) {
- printf("\nIt is necessary to do the final optimality check when removing inconsistent \nexamples.\n");
- exit(-1);
- }
- if((learn_parm->svm_maxqpsize<2)) {
- printf("\nMaximum size of QP-subproblems not in valid range: %ld [2..]\n",learn_parm->svm_maxqpsize);
- exit(-1);
- }
- if((learn_parm->svm_maxqpsize<learn_parm->svm_newvarsinqp)) {
- printf("\nMaximum size of QP-subproblems [%ld] must be larger than the number of\n",learn_parm->svm_maxqpsize);
- printf("new variables [%ld] entering the working set in each iteration.\n",learn_parm->svm_newvarsinqp);
- exit(-1);
- }
- if(learn_parm->svm_iter_to_shrink<1) {
- printf("\nMaximum number of iterations for shrinking not in valid range: %ld [1,..]\n",learn_parm->svm_iter_to_shrink);
- exit(-1);
- }
- if(learn_parm->transduction_posratio>1) {
- printf("\nThe fraction of unlabeled examples to classify as positives must\n");
- printf("be less than 1.0 !!!\n\n");
- exit(-1);
- }
- if(learn_parm->svm_costratio<=0) {
- printf("\nThe COSTRATIO parameter must be greater than zero!\n\n");
- exit(-1);
- }
- if(learn_parm->epsilon_crit<=0) {
- printf("\nThe epsilon parameter must be greater than zero!\n\n");
- exit(-1);
- }
- if(learn_parm->rho<0) {
- printf("\nThe parameter rho for xi/alpha-estimates and leave-one-out pruning must\n");
- printf("be greater than zero (typically 1.0 or 2.0, see T. Joachims, Estimating the\n");
- printf("Generalization Performance of an SVM Efficiently, ICML, 2000.)!\n\n");
- exit(-1);
- }
- if((learn_parm->xa_depth<0) || (learn_parm->xa_depth>100)) {
- printf("\nThe parameter depth for ext. xi/alpha-estimates must be in [0..100] (zero\n");
- printf("for switching to the conventional xa/estimates described in T. Joachims,\n");
- printf("Estimating the Generalization Performance of an SVM Efficiently, ICML, 2000.)\n");
- exit(-1);
- }
- }
- MODEL *VCSVMLight::optimizeParameters ( DOC **docs,
- double *target, long int totwords, long int totdoc,
- MODEL *model, KERNEL_PARM *kernel_parm,
- LEARN_PARM *learn_parm )
- {
- double best_error = numeric_limits<double>::max();
- double old_best_error = best_error;
- const int numberOfParameters = (kernel_type == SVM_RBF ? 2 : 1 );
- KERNEL_PARM *currentKernelParm = (KERNEL_PARM *)my_malloc(sizeof(KERNEL_PARM));
- LEARN_PARM *currentLearnParm = (LEARN_PARM *)my_malloc(sizeof(LEARN_PARM));
- MODEL *currentModel = (MODEL *)my_malloc(sizeof(MODEL));
- initParameters ( learn_parm, kernel_parm );
- initMainParameters ( learn_parm, kernel_parm );
-
- double *parametersToOptimize [ numberOfParameters ];
- double minValue [ numberOfParameters ];
- double maxValue [ numberOfParameters ];
- double steps [ numberOfParameters ];
-
- parametersToOptimize[0] = &(currentLearnParm->svm_c);
- minValue[0] = svm_c_min;
- maxValue[0] = svm_c_max;
- steps[0] = svm_c_step;
- if ( kernel_type == SVM_RBF ) {
- parametersToOptimize[1] = &(currentKernelParm->rbf_gamma);
- minValue[1] = rbf_gamma_min;
- maxValue[1] = rbf_gamma_max;
- steps[1] = rbf_gamma_step;
- }
- if ( use_crossvalidation )
- {
- fprintf (stderr, "VCSVMLight: error estimate type: leave-one-out error\n");
- } else {
- fprintf (stderr, "VCSVMLight: error estimate type: Xi/Alpha error\n");
- }
- do {
- old_best_error = best_error;
- for ( int i = 0 ; i < numberOfParameters ; i++ )
- {
- for ( double value = minValue[i] ; value < maxValue[i] ; value += steps[i] )
- {
- memcpy ( currentKernelParm, kernel_parm, sizeof(KERNEL_PARM) );
- memcpy ( currentLearnParm, learn_parm, sizeof(LEARN_PARM) );
- double *parameter = parametersToOptimize[i];
- *parameter = value;
- singleTraining ( docs, target, totwords, totdoc, currentModel,
- currentKernelParm, currentLearnParm );
- fprintf (stderr, "VCSVMLight: optimize rbf_gamma=%f C=%f err=%f xa_err=%f (%f, %f)\n",
- currentKernelParm->rbf_gamma, currentLearnParm->svm_c, currentModel->loo_error,
- currentModel->xa_error,
- kernel_parm->rbf_gamma, learn_parm->svm_c );
- double currentError = (use_crossvalidation) ? currentModel->loo_error : currentModel->xa_error;
- if ( currentError < 0 ) {
- fthrow ( Exception, "Error < 0; check your settings; SVMLight verbosity bug ?");
- }
-
- if ( currentError < best_error )
- {
- fprintf (stderr, "VCSVMLight: new optimum found !\n");
- best_error = currentError;
- memcpy ( kernel_parm, currentKernelParm, sizeof(KERNEL_PARM) );
- memcpy ( learn_parm, currentLearnParm, sizeof(LEARN_PARM) );
- memcpy ( model, currentModel, sizeof(MODEL) );
- }
- }
- }
- } while ( old_best_error > best_error );
- free_model(currentModel,0);
- free (currentLearnParm);
- free (currentKernelParm);
- // rewrite
- rbf_gamma = kernel_parm->rbf_gamma;
- svm_c = learn_parm->svm_c;
- fprintf (stderr, "VCSVMLight: optimimum rbf_gamma=%f C=%f !\n", rbf_gamma, svm_c );
- return model;
- }
- MODEL *VCSVMLight::singleTraining ( DOC **docs,
- double *target, long int totwords, long int totdoc,
- MODEL *model, KERNEL_PARM *kernel_parm,
- LEARN_PARM *learn_parm )
- {
- double *alpha_in=NULL;
- KERNEL_CACHE *kernel_cache;
- if(kernel_parm->kernel_type == SVM_LINEAR) { /* don't need the cache */
- kernel_cache=NULL;
- }
- else {
- /* Always get a new kernel cache. It is not possible to use the
- same cache for two different training runs */
- kernel_cache = kernel_cache_init(totdoc,learn_parm->kernel_cache_size);
- }
- svm_learn_classification(docs,target,totdoc,totwords,learn_parm,
- kernel_parm,kernel_cache,model,alpha_in);
- if(kernel_cache) {
- /* Free the memory used for the cache. */
- kernel_cache_cleanup(kernel_cache);
- }
- free(alpha_in);
- return model;
- }
- void VCSVMLight::svmLightTraining (
- const LabeledSetVector & trainSet )
- {
- double *target;
- long int totwords,totdoc,i;
- DOC **docs; /* training examples */
- LEARN_PARM learn_parm;
- KERNEL_PARM kernel_parm;
- MODEL *model=(MODEL *)my_malloc(sizeof(MODEL));
- readDocuments( &docs,&target,&totwords,&totdoc,
- trainSet);
- initParameters(&learn_parm, &kernel_parm);
- initMainParameters(&learn_parm, &kernel_parm);
- if ( optimize_parameters )
- optimizeParameters ( docs, target, totwords, totdoc, model, &kernel_parm, &learn_parm );
- else
- singleTraining ( docs, target, totwords, totdoc, model, &kernel_parm, &learn_parm );
- finalModel = copy_model(model);
- free_model(model,0);
- for(i=0;i<totdoc;i++)
- free_example(docs[i],1);
- free(docs);
- free(target);
- };
- void VCSVMLight::teach ( const LabeledSetVector & _teachSet )
- {
- maxClassNo = _teachSet.getMaxClassno();
- if ( _teachSet.getMaxClassno() != 1 )
- fthrow ( Exception, "This classifier is only suitable for binary classification problems!");
- svmLightTraining ( _teachSet );
- if ( logreg != NULL )
- {
- LabeledSetVector scoreSet;
- LOOP_ALL ( _teachSet )
- {
- EACH(classno,x);
- double score = getSVMScore ( x );
- Vector y (1);
- y[0] = score;
- scoreSet.add ( classno, y );
- }
- logreg->teach ( scoreSet );
- scoreSet.clear();
- }
- }
- void VCSVMLight::finishTeaching()
- {
- }
- /** clone this object */
- VCSVMLight *VCSVMLight::clone(void) const
- {
- VCSVMLight *classifier = new VCSVMLight( *this );
-
- return classifier;
- }
- void VCSVMLight::clear ()
- {
- if ( finalModel != NULL )
- {
- free(finalModel);
- finalModel = NULL;
- }
- }
- void VCSVMLight::read (const string& s, int format)
- {
- finalModel = read_model ( const_cast<char *>(s.c_str()) );
- }
- void VCSVMLight::save (const string& s, int format) const
- {
- write_model ( const_cast<char *>(s.c_str()), finalModel );
- }
- void VCSVMLight::store ( std::ostream & os, int format ) const
- {
- fprintf (stderr, "VCSVMLight: unable to write to stream! please use read()\n");
- }
- void VCSVMLight::restore ( std::istream & is, int format )
- {
- fprintf (stderr, "VCSVMLight: unable to read from stream! please use save()\n");
- exit (-1);
- }
- #endif
|