source: orange/source/orange/libsvm_interface.cpp @ 11649:6d0ec9cce6a2

Revision 11649:6d0ec9cce6a2, 25.5 KB checked in by markotoplak, 9 months ago (diff)

SVM interface does not pass 0 values to libsvm.

Line 
1/*
2    This file is part of Orange.
3
4    Copyright 1996-2011 Faculty of Computer and Information Science, University of Ljubljana
5    Contact: janez.demsar@fri.uni-lj.si
6
7    Orange is free software: you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation, either version 3 of the License, or
10    (at your option) any later version.
11
12    Orange is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with Orange.  If not, see <http://www.gnu.org/licenses/>.
19*/
20
21#include <iostream>
22#include <sstream>
23
24#include "libsvm_interface.ppp"
25
26// Defined in svm.cpp. If new svm or kernel types are added this should be updated.
27
28static const char *svm_type_table[] =
29{
30    "c_svc","nu_svc","one_class","epsilon_svr","nu_svr",NULL
31};
32
33static const char *kernel_type_table[]=
34{
35    "linear","polynomial","rbf","sigmoid","precomputed",NULL
36};
37
38#define Malloc(type,n) (type *)malloc((n)*sizeof(type))
39
40/*
41 * Save load functions for use with orange pickling.
42 * They are a copy of the standard libSVM save-load functions
43 * except that they read/write from/to std::iostream objects.
44 */
45
46int svm_save_model_alt(std::ostream& stream, const svm_model *model){
47    const svm_parameter& param = model->param;
48    stream.precision(17);
49
50    stream << "svm_type " << svm_type_table[param.svm_type] << endl;
51    stream << "kernel_type " << kernel_type_table[param.kernel_type] << endl;
52
53    if(param.kernel_type == POLY)
54        stream << "degree " << param.degree << endl;
55
56    if(param.kernel_type == POLY || param.kernel_type == RBF || param.kernel_type == SIGMOID)
57        stream << "gamma " << param.gamma << endl;
58
59    if(param.kernel_type == POLY || param.kernel_type == SIGMOID)
60        stream << "coef0 " << param.coef0 << endl;
61
62    int nr_class = model->nr_class;
63    int l = model->l;
64    stream << "nr_class " << nr_class << endl;
65    stream << "total_sv " << l << endl;
66    {
67        stream << "rho";
68        for(int i=0;i<nr_class*(nr_class-1)/2;i++)
69            stream << " " << model->rho[i];
70        stream << endl;
71    }
72
73    if(model->label)
74    {
75        stream << "label";
76        for(int i=0;i<nr_class;i++)
77            stream << " " << model->label[i];
78        stream << endl;
79    }
80
81    if(model->probA) // regression has probA only
82    {
83        stream << "probA";
84        for(int i=0;i<nr_class*(nr_class-1)/2;i++)
85            stream << " " << model->probA[i];
86        stream << endl;
87    }
88    if(model->probB)
89    {
90        stream << "probB";
91        for(int i=0;i<nr_class*(nr_class-1)/2;i++)
92            stream << " " << model->probB[i];
93        stream << endl;
94    }
95
96    if(model->nSV)
97    {
98        stream << "nr_sv";
99        for(int i=0;i<nr_class;i++)
100            stream << " " << model->nSV[i];
101        stream << endl;
102    }
103
104    stream << "SV" << endl;
105    const double * const *sv_coef = model->sv_coef;
106    const svm_node * const *SV = model->SV;
107
108    for(int i=0;i<l;i++)
109    {
110        for(int j=0;j<nr_class-1;j++)
111            stream << sv_coef[j][i] << " ";
112
113        const svm_node *p = SV[i];
114
115        if(param.kernel_type == PRECOMPUTED)
116            stream << "0:" << (int)(p->value) << " ";
117        else
118            while(p->index != -1)
119            {
120                stream << (int)(p->index) << ":" << p->value << " ";
121                p++;
122            }
123        stream << endl;
124    }
125
126    if (!stream.fail())
127        return 0;
128    else
129        return 1;
130}
131
132int svm_save_model_alt(std::string& buffer, const svm_model *model){
133    std::ostringstream strstream;
134    int ret = svm_save_model_alt(strstream, model);
135    buffer = strstream.rdbuf()->str();
136    return ret;
137}
138
139
140#include <algorithm>
141
142svm_model *svm_load_model_alt(std::istream& stream)
143{
144    svm_model *model = Malloc(svm_model,1);
145    svm_parameter& param = model->param;
146    model->rho = NULL;
147    model->probA = NULL;
148    model->probB = NULL;
149    model->label = NULL;
150    model->nSV = NULL;
151
152    char cmd[81];
153    stream.width(80);
154    while (stream.good())
155    {
156        stream >> cmd;
157
158        if(strcmp(cmd, "svm_type") == 0)
159        {
160            stream >> cmd;
161            int i;
162            for(i=0; svm_type_table[i]; i++)
163            {
164                if(strcmp(cmd, svm_type_table[i]) == 0)
165                {
166                    param.svm_type=i;
167                    break;
168                }
169            }
170            if(svm_type_table[i] == NULL)
171            {
172                fprintf(stderr, "unknown svm type.\n");
173                free(model->rho);
174                free(model->label);
175                free(model->nSV);
176                free(model);
177                return NULL;
178            }
179        }
180        else if(strcmp(cmd, "kernel_type") == 0)
181        {
182            stream >> cmd;
183            int i;
184            for(i=0;kernel_type_table[i];i++)
185            {
186                if(strcmp(kernel_type_table[i], cmd)==0)
187                {
188                    param.kernel_type=i;
189                    break;
190                }
191            }
192            if(kernel_type_table[i] == NULL)
193            {
194                fprintf(stderr,"unknown kernel function.\n");
195                free(model->rho);
196                free(model->label);
197                free(model->nSV);
198                free(model);
199                return NULL;
200            }
201        }
202        else if(strcmp(cmd,"degree")==0)
203            stream >> param.degree;
204        else if(strcmp(cmd,"gamma")==0)
205            stream >> param.gamma;
206        else if(strcmp(cmd,"coef0")==0)
207            stream >> param.coef0;
208        else if(strcmp(cmd,"nr_class")==0)
209            stream >> model->nr_class;
210        else if(strcmp(cmd,"total_sv")==0)
211            stream >> model->l;
212        else if(strcmp(cmd,"rho")==0)
213        {
214            int n = model->nr_class * (model->nr_class-1)/2;
215            model->rho = Malloc(double,n);
216            string rho_str;
217            for(int i=0;i<n;i++){
218                // Read the number into a string and then use strtod
219                // for proper handling of NaN's
220                stream >> rho_str;
221                model->rho[i] = strtod(rho_str.c_str(), NULL);
222            }
223        }
224        else if(strcmp(cmd,"label")==0)
225        {
226            int n = model->nr_class;
227            model->label = Malloc(int,n);
228            for(int i=0;i<n;i++)
229                stream >> model->label[i];
230        }
231        else if(strcmp(cmd,"probA")==0)
232        {
233            int n = model->nr_class * (model->nr_class-1)/2;
234            model->probA = Malloc(double,n);
235            for(int i=0;i<n;i++)
236                stream >> model->probA[i];
237        }
238        else if(strcmp(cmd,"probB")==0)
239        {
240            int n = model->nr_class * (model->nr_class-1)/2;
241            model->probB = Malloc(double,n);
242            for(int i=0;i<n;i++)
243                stream >> model->probB[i];
244        }
245        else if(strcmp(cmd,"nr_sv")==0)
246        {
247            int n = model->nr_class;
248            model->nSV = Malloc(int,n);
249            for(int i=0;i<n;i++)
250                stream >> model->nSV[i];
251        }
252        else if(strcmp(cmd,"SV")==0)
253        {
254            while(1)
255            {
256                int c = stream.get();
257                if(stream.eof() || c=='\n') break;
258            }
259            break;
260        }
261        else
262        {
263            fprintf(stderr,"unknown text in model file: [%s]\n",cmd);
264            free(model->rho);
265            free(model->label);
266            free(model->nSV);
267            free(model);
268            return NULL;
269        }
270    }
271    if (stream.fail()){
272        free(model->rho);
273        free(model->label);
274        free(model->nSV);
275        free(model);
276        return NULL;
277
278    }
279
280    // read sv_coef and SV
281
282    int elements = 0;
283    long pos = stream.tellg();
284
285    char *p,*endptr,*idx,*val;
286    string str_line;
287    while (!stream.eof() && !stream.fail())
288    {
289        getline(stream, str_line);
290        elements += std::count(str_line.begin(), str_line.end(), ':');
291    }
292
293    elements += model->l;
294
295    stream.clear();
296    stream.seekg(pos, ios::beg);
297
298    int m = model->nr_class - 1;
299    int l = model->l;
300    model->sv_coef = Malloc(double *,m);
301    int i;
302    for(i=0;i<m;i++)
303        model->sv_coef[i] = Malloc(double,l);
304    model->SV = Malloc(svm_node*,l);
305    svm_node *x_space = NULL;
306    if(l>0) x_space = Malloc(svm_node,elements);
307
308    int j=0;
309    char *line;
310    for(i=0;i<l;i++)
311    {
312        getline(stream, str_line);
313        if (str_line.size() == 0)
314            continue;
315
316        line = (char *) Malloc(char, str_line.size() + 1);
317        // Copy the line for strtok.
318        strcpy(line, str_line.c_str());
319
320        model->SV[i] = &x_space[j];
321
322        p = strtok(line, " \t");
323        model->sv_coef[0][i] = strtod(p,&endptr);
324        for(int k=1;k<m;k++)
325        {
326            p = strtok(NULL, " \t");
327            model->sv_coef[k][i] = strtod(p,&endptr);
328        }
329
330        while(1)
331        {
332            idx = strtok(NULL, ":");
333            val = strtok(NULL, " \t");
334
335            if(val == NULL)
336                break;
337            x_space[j].index = (int) strtol(idx,&endptr,10);
338            x_space[j].value = strtod(val,&endptr);
339
340            ++j;
341        }
342        x_space[j++].index = -1;
343        free(line);
344    }
345
346    if (stream.fail())
347        return NULL;
348
349    model->free_sv = 1; // XXX
350    return model;
351}
352
353svm_model *svm_load_model_alt(std::string& stream)
354{
355    std::istringstream strstream(stream);
356    return svm_load_model_alt(strstream);
357}
358
359
360/*
361 * Return a formated string representing a svm data instance (svm_node *)
362 * (useful for debugging)
363 */
364string svm_node_to_string(svm_node * node) {
365    std::ostringstream strstream;
366    strstream.precision(17);
367    while (node->index != -1) {
368        strstream << node->index << ":" << node->value << " ";
369        node++;
370    }
371    strstream << node->index << ":" << node->value << " ";
372    return strstream.rdbuf()->str();
373}
374
375
376svm_node* example_to_svm(const TExample &ex, svm_node* node, float last=0.0, int type=0){
377    if(type==0){
378        int index=1;
379        for(TExample::iterator i=ex.begin(); i!=ex.end(); i++){
380            if(i->isRegular() && i!=&ex.getClass()){
381                if(i->varType==TValue::FLOATVAR)
382                    node->value=float(*i);
383                else
384                    node->value=int(*i);
385                node->index=index++;
386                if(node->value==numeric_limits<float>::signaling_NaN() ||
387                    node->value==numeric_limits<int>::max() || 
388                    node->value == 0)
389                    node--;
390                node++;
391            }
392        }
393    }
394    if(type == 1){ /*one dummy attr so we can pickle the classifier and keep the SV index in the training table*/
395        node->index=1;
396        node->value=last;
397        node++;
398    }
399    //cout<<(node-1)->index<<endl<<(node-2)->index<<endl;
400    node->index=-1;
401    node->value=last;
402    node++;
403    return node;
404}
405
406class SVM_NodeSort{
407public:
408    bool operator() (const svm_node &lhs, const svm_node &rhs){
409        return lhs.index < rhs.index;
410    }
411};
412
413svm_node* example_to_svm_sparse(const TExample &ex, svm_node* node, float last=0.0, bool useNonMeta=false){
414    svm_node *first=node;
415    int j=1;
416    int index=1;
417    if(useNonMeta)
418        for(TExample::iterator i=ex.begin(); i!=ex.end(); i++){
419            if(i->isRegular() && i!=&ex.getClass()){
420                if(i->varType==TValue::FLOATVAR)
421                    node->value=float(*i);
422                else
423                    node->value=int(*i);
424                node->index=index;
425                if(node->value==numeric_limits<float>::signaling_NaN() ||
426                    node->value==numeric_limits<int>::max() ||
427                    node->value == 0)
428                    node--;
429                node++;
430            }
431            index++;
432        }
433    for(TMetaValues::const_iterator i=ex.meta.begin(); i!=ex.meta.end();i++,j++){
434        if(i->second.isRegular()){
435            if(i->second.varType==TValue::FLOATVAR)
436                node->value=float(i->second);
437            else
438                node->value=int(i->second);
439            node->index = index - i->first;
440
441            if(node->value==numeric_limits<float>::signaling_NaN() ||
442                node->value==numeric_limits<int>::max())
443                node--;
444            node++;
445        }
446    }
447    sort(first, node, SVM_NodeSort());
448    //cout<<first->index<<endl<<(first+1)->index<<endl;
449    node->index=-1;
450    node->value=last;
451    node++;
452    return node;
453}
454
455/*
456 * Precompute Gram matrix row for ex.
457 * Used for prediction when using the PRECOMPUTED kernel.
458 */
459svm_node* example_to_svm_precomputed(const TExample &ex, PExampleGenerator examples, PKernelFunc kernel, svm_node* node){
460    node->index = 0;
461    node->value = 0.0; // Can be any value.
462    node++;
463    int k = 0;
464    PEITERATE(iter, examples){
465        node->index = ++k;
466        node->value = kernel.getReference()(*iter, ex);
467        node++;
468    }
469    node->index = -1; // sentry
470    node++;
471    return node;
472}
473
474int getNumOfElements(const TExample &ex, bool meta=false, bool useNonMeta=false){
475    if(!meta)
476        return std::max(ex.domain->attributes->size()+1, 2);
477    else{
478        int count=1; //we need one to indicate the end of a sequence
479        if(useNonMeta)
480            count+=ex.domain->attributes->size();
481        for(TMetaValues::const_iterator i=ex.meta.begin(); i!=ex.meta.end();i++)
482            if(i->second.isRegular())
483                count++;
484        return std::max(count,2);
485    }
486}
487
488int getNumOfElements(PExampleGenerator &examples, bool meta=false, bool useNonMeta=false){
489    if(!meta)
490        return getNumOfElements(*(examples->begin()), meta)*examples->numberOfExamples();
491    else{
492        int count=0;
493        for(TExampleGenerator::iterator ex(examples->begin()); ex!=examples->end(); ++ex){
494            count+=getNumOfElements(*ex, meta, useNonMeta);
495        }
496        return count;
497    }
498}
499
500#include "symmatrix.hpp"
501svm_node* init_precomputed_problem(svm_problem &problem, PExampleTable examples, TKernelFunc &kernel){
502    int n_examples = examples->numberOfExamples();
503    int i,j;
504    PSymMatrix matrix = mlnew TSymMatrix(n_examples, 0.0);
505    for (i = 0; i < n_examples; i++)
506        for (j = 0; j <= i; j++){
507            matrix->getref(i, j) = kernel(examples->at(i), examples->at(j));
508//          cout << i << " " << j << " " << matrix->getitem(i, j) << endl;
509        }
510    svm_node *x_space = Malloc(svm_node, n_examples * (n_examples + 2));
511    svm_node *node = x_space;
512
513    problem.l = n_examples;
514    problem.x = Malloc(svm_node*, n_examples);
515    problem.y = Malloc(double, n_examples);
516
517    for (i = 0; i < n_examples; i++){
518        problem.x[i] = node;
519        if (examples->domain->classVar->varType == TValue::FLOATVAR)
520            problem.y[i] = examples->at(i).getClass().floatV;
521        else
522            problem.y[i] = examples->at(i).getClass().intV;
523
524        node->index = 0;
525        node->value = i + 1; // instance indices are 1 based
526        node++;
527        for (j = 0; j < n_examples; j++){
528            node->index = j + 1;
529            node->value = matrix->getitem(i, j);
530            node++;
531        }
532        node->index = -1; // sentry
533        node++;
534    }
535    return x_space;
536}
537
538/*
539 * Extract an ExampleTable corresponding to the support vectors from the
540 * trained model.
541 */
542PExampleTable extract_support_vectors(svm_model * model, PExampleTable train_instances)
543{
544    PExampleTable vectors = mlnew TExampleTable(train_instances->domain);
545
546    for (int i = 0; i < model->l; i++) {
547        svm_node *node = model->SV[i];
548        int sv_index = -1;
549        if(model->param.kernel_type != PRECOMPUTED){
550            /* The value of the last node (with index == -1) holds the
551             * index of the training example.
552             */
553            while(node->index != -1) {
554                node++;
555            }
556            sv_index = int(node->value);
557        } else {
558            /* The value of the first node contains the training instance
559             * index (indices 1 based).
560             */
561            sv_index = int(node->value) - 1;
562        }
563        vectors->addExample(mlnew TExample(train_instances->at(sv_index)));
564    }
565
566    return vectors;
567}
568
569
570/*
571 * Consolidate model->SV[1] .. SV[l] vectors into a single contiguous
572 * memory block. The model will 'own' the new *(model->SV) array and
573 * will be freed in destroy_svm_model (model->free_sv == 1). Note that
574 * the original 'x_space' is left intact, it is the caller's
575 * responsibility to free it. However the model->SV array itself is
576 * reused (overwritten).
577 */
578
579void svm_model_consolidate_SV(svm_model * model) {
580    int count = 0;
581    svm_node * x_space = NULL;
582    svm_node * ptr = NULL;
583    svm_node * ptr_source = NULL;
584
585    // Count the number of elements.
586    for (int i = 0; i < model->l; i++) {
587        ptr = model->SV[i];
588        while (ptr->index != -1){
589            count++;
590            ptr++;
591        }
592    }
593    // add the sentinel count
594    count += model->l;
595
596    x_space = Malloc(svm_node, count);
597    ptr = x_space;
598    for (int i = 0; i < model->l; i++) {
599        ptr_source = model->SV[i];
600        model->SV[i] = ptr;
601        while (ptr_source->index != -1) {
602            *(ptr++) = *(ptr_source++);
603        }
604        // copy the sentinel
605        *(ptr++) = *(ptr_source++);
606    }
607    model->free_sv = 1; // XXX
608}
609
610static void print_string_null(const char* s) {}
611
612
613TSVMLearner::TSVMLearner(){
614    svm_type = NU_SVC;
615    kernel_type = RBF;
616    degree = 3;
617    gamma = 0;
618    coef0 = 0;
619    nu = 0.5;
620    cache_size = 250;
621    C = 1;
622    eps = 1e-3f;
623    p = 0.1f;
624    shrinking = 1;
625    probability = 0;
626    verbose = false;
627    nr_weight = 0;
628    weight_label = NULL;
629    weight = NULL;
630};
631
632
633PClassifier TSVMLearner::operator ()(PExampleGenerator examples, const int&){
634    svm_parameter param;
635    svm_problem prob;
636    svm_model* model;
637    svm_node* x_space;
638
639    PDomain domain = examples->domain;
640
641    int classVarType;
642    if (domain->classVar)
643        classVarType = domain->classVar->varType;
644    else {
645        classVarType = TValue::NONE;
646        if(svm_type != ONE_CLASS)
647            raiseError("Domain has no class variable");
648    }
649    if (classVarType == TValue::FLOATVAR && !(svm_type == EPSILON_SVR || svm_type == NU_SVR ||svm_type == ONE_CLASS))
650        raiseError("Domain has continuous class");
651
652    if (kernel_type == PRECOMPUTED && !kernelFunc)
653        raiseError("Custom kernel function not supplied");
654
655    PExampleTable train_data = mlnew TExampleTable(examples, /* owns= */ false);
656
657    if (classVarType == TValue::INTVAR && svm_type != ONE_CLASS) {
658        /* Sort the train data by the class columns so the order of
659         * classVar.values is preserved in libsvm's model.
660         */
661        vector<int> sort_columns(domain->variables->size() - 1);
662        train_data->sort(sort_columns);
663    }
664
665    // Initialize svm parameters
666    param.svm_type = svm_type;
667    param.kernel_type = kernel_type;
668    param.degree = degree;
669    param.gamma = gamma;
670    param.coef0 = coef0;
671    param.nu = nu;
672    param.C = C;
673    param.eps = eps;
674    param.p = p;
675    param.cache_size = cache_size;
676    param.shrinking = shrinking;
677    param.probability = probability;
678    param.nr_weight = nr_weight;
679
680    if (nr_weight > 0) {
681        param.weight_label = Malloc(int, nr_weight);
682        param.weight = Malloc(double, nr_weight);
683        int i;
684        for (i=0; i<nr_weight; i++) {
685            param.weight_label[i] = weight_label[i];
686            param.weight[i] = weight[i];
687        }
688    } else {
689        param.weight_label = NULL;
690        param.weight = NULL;
691    }
692
693    int numElements = getNumOfElements(train_data);
694
695    prob.x = NULL;
696    prob.y = NULL;
697
698    if (kernel_type != PRECOMPUTED)
699        x_space = init_problem(prob, train_data, numElements);
700    else // Compute the matrix using the kernelFunc
701        x_space = init_precomputed_problem(prob, train_data, kernelFunc.getReference());
702
703    if (param.gamma==0)
704        param.gamma=1.0f/(float(numElements)/float(prob.l)-1);
705
706    const char* error=svm_check_parameter(&prob, &param);
707    if (error){
708        free(x_space);
709        free(prob.y);
710        free(prob.x);
711        svm_destroy_param(&param);
712        raiseError("LibSVM parameter error: %s", error);
713    }
714
715    // If a probability model was requested LibSVM uses 5 fold
716    // cross-validation to estimate the prediction errors. This includes a
717    // random shuffle of the data. To make the results reproducible and
718    // consistent with 'svm-train' (which always learns just on one dataset
719    // in a process run) we reset the random seed. This could have unintended
720    // consequences.
721    if (param.probability)
722    {
723        srand(1);
724    }
725    svm_set_print_string_function((verbose)? NULL : &print_string_null);
726
727    model = svm_train(&prob, &param);
728
729    if ((svm_type==C_SVC || svm_type==NU_SVC) && !model->nSV) {
730        svm_free_and_destroy_model(&model);
731        free(x_space);
732        free(prob.x);
733        free(prob.y);
734        svm_destroy_param(&param);
735        raiseError("LibSVM returned no support vectors");
736    }
737
738    svm_destroy_param(&param);
739    free(prob.y);
740    free(prob.x);
741
742    // Consolidate the SV so x_space can be safely freed
743    svm_model_consolidate_SV(model);
744
745    free(x_space);
746
747    PExampleTable supportVectors = extract_support_vectors(model, train_data);
748
749    return PClassifier(createClassifier(domain, model, supportVectors, train_data));
750}
751
752svm_node* TSVMLearner::example_to_svm(const TExample &ex, svm_node* node, float last, int type){
753    return ::example_to_svm(ex, node, last, type);
754}
755
756svm_node* TSVMLearner::init_problem(svm_problem &problem, PExampleTable examples, int n_elements){
757    problem.l = examples->numberOfExamples();
758    problem.y = Malloc(double ,problem.l);
759    problem.x = Malloc(svm_node*, problem.l);
760    svm_node *x_space = Malloc(svm_node, n_elements);
761    svm_node *node = x_space;
762
763    for (int i = 0; i < problem.l; i++){
764        problem.x[i] = node;
765        node = example_to_svm(examples->at(i), node, i);
766        if (examples->domain->classVar)
767            if (examples->domain->classVar->varType == TValue::FLOATVAR)
768                problem.y[i] = examples->at(i).getClass().floatV;
769            else if (examples->domain->classVar->varType == TValue::INTVAR)
770                problem.y[i] = examples->at(i).getClass().intV;
771    }
772    return x_space;
773}
774
775int TSVMLearner::getNumOfElements(PExampleGenerator examples){
776    return ::getNumOfElements(examples);
777}
778
779TSVMClassifier* TSVMLearner::createClassifier(
780        PDomain domain, svm_model* model, PExampleTable supportVectors, PExampleTable examples) {
781    PKernelFunc kfunc;
782    if (kernel_type != PRECOMPUTED) {
783        // Classifier does not need the train data and the kernelFunc.
784        examples = NULL;
785        kfunc = NULL;
786    } else {
787        kfunc = kernelFunc;
788    }
789
790    return mlnew TSVMClassifier(domain, model, supportVectors, kfunc, examples);
791}
792
793TSVMLearner::~TSVMLearner(){
794    if(weight_label)
795        free(weight_label);
796
797    if(weight)
798        free(weight);
799}
800
801svm_node* TSVMLearnerSparse::example_to_svm(const TExample &ex, svm_node* node, float last, int type){
802    return ::example_to_svm_sparse(ex, node, last, useNonMeta);
803}
804
805int TSVMLearnerSparse::getNumOfElements(PExampleGenerator examples){
806    return ::getNumOfElements(examples, true, useNonMeta);
807}
808
809TSVMClassifier* TSVMLearnerSparse::createClassifier(
810        PDomain domain, svm_model* model, PExampleTable supportVectors, PExampleTable examples) {
811    PKernelFunc kfunc;
812    if (kernel_type != PRECOMPUTED) {
813        // Classifier does not need the train data and the kernelFunc.
814        examples = NULL;
815        kfunc = NULL;
816    } else {
817        kfunc = kernelFunc;
818    }
819    return mlnew TSVMClassifierSparse(domain, model, useNonMeta, supportVectors, kfunc, examples);
820}
821
822
823TSVMClassifier::TSVMClassifier(
824        PDomain domain, svm_model * model,
825        PExampleTable supportVectors,
826        PKernelFunc kernelFunc,
827        PExampleTable examples
828        ) : TClassifierFD(domain) {
829    this->model = model;
830    this->supportVectors = supportVectors;
831    this->kernelFunc = kernelFunc;
832    this->examples = examples;
833
834    svm_type = svm_get_svm_type(model);
835    kernel_type = model->param.kernel_type;
836
837    if (svm_type == ONE_CLASS) {
838        this->classVar = mlnew TFloatVariable("one class");
839    }
840
841    computesProbabilities = model && svm_check_probability_model(model) && \
842                (svm_type != NU_SVR && svm_type != EPSILON_SVR); // Disable prob. estimation for regression
843
844    int nr_class = svm_get_nr_class(model);
845    int i = 0;
846
847    /* Expose (copy) the model data (coef, rho, probA) to public
848     * class interface.
849     */
850    if (svm_type == C_SVC || svm_type == NU_SVC) {
851        nSV = mlnew TIntList(nr_class); // num of SVs for each class (sum(nSV) == model->l)
852        for(i = 0;i < nr_class; i++) {
853            nSV->at(i) = model->nSV[i];
854        }
855    }
856
857    coef = mlnew TFloatListList(nr_class-1);
858    for(i = 0; i < nr_class - 1; i++) {
859        TFloatList *coefs = mlnew TFloatList(model->l);
860        for(int j = 0;j < model->l; j++) {
861            coefs->at(j) = model->sv_coef[i][j];
862        }
863        coef->at(i) = coefs;
864    }
865
866    // Number of binary classifiers in the model
867    int nr_bin_cls = nr_class * (nr_class - 1) / 2;
868
869    rho = mlnew TFloatList(nr_bin_cls);
870    for(i = 0; i < nr_bin_cls; i++) {
871        rho->at(i) = model->rho[i];
872    }
873
874    if(model->probA) {
875        probA = mlnew TFloatList(nr_bin_cls);
876        if (model->param.svm_type != NU_SVR && model->param.svm_type != EPSILON_SVR && model->probB) {
877            // Regression only has probA
878            probB = mlnew TFloatList(nr_bin_cls);
879        }
880
881        for(i=0; i<nr_bin_cls; i++) {
882            probA->at(i) = model->probA[i];
883            if (model->param.svm_type != NU_SVR && model->param.svm_type != EPSILON_SVR && model->probB) {
884                probB->at(i) = model->probB[i];
885            }
886        }
887    }
888}
889
890
891TSVMClassifier::~TSVMClassifier(){
892    if (model) {
893        svm_free_and_destroy_model(&model);
894    }
895}
896
897
898PDistribution TSVMClassifier::classDistribution(const TExample & example){
899    if(!model)
900        raiseError("No Model");
901
902    if(!computesProbabilities)
903        return TClassifierFD::classDistribution(example);
904
905    int n_elements;
906    if (model->param.kernel_type != PRECOMPUTED)
907        n_elements = getNumOfElements(example);
908    else
909        n_elements = examples->numberOfExamples() + 2;
910
911    int svm_type = svm_get_svm_type(model);
912    int nr_class = svm_get_nr_class(model);
913
914    svm_node *x = Malloc(svm_node, n_elements);
915    try{
916        if (model->param.kernel_type != PRECOMPUTED)
917            example_to_svm(example, x, -1.0);
918        else
919            example_to_svm_precomputed(example, examples, kernelFunc, x);
920    } catch (...) {
921        free(x);
922        throw;
923    }
924
925    int *labels=(int *) malloc(nr_class*sizeof(int));
926    svm_get_labels(model, labels);
927
928    double *prob_estimates = (double *) malloc(nr_class*sizeof(double));;
929    svm_predict_probability(model, x, prob_estimates);
930
931    PDistribution dist = TDistribution::create(example.domain->classVar);
932    for(int i=0; i<nr_class; i++)
933        dist->setint(labels[i], prob_estimates[i]);
934    free(x);
935    free(prob_estimates);
936    free(labels);
937    return dist;
938}
939
940TValue TSVMClassifier::operator()(const TExample & example){
941    if(!model)
942        raiseError("No Model");
943
944    int n_elements;
945    if (model->param.kernel_type != PRECOMPUTED)
946        n_elements = getNumOfElements(example);
947    else
948        n_elements = examples->numberOfExamples() + 2;
949
950    int svm_type = svm_get_svm_type(model);
951    int nr_class = svm_get_nr_class(model);
952
953    svm_node *x = Malloc(svm_node, n_elements);
954    try {
955        if (model->param.kernel_type != PRECOMPUTED)
956            example_to_svm(example, x);
957        else
958            example_to_svm_precomputed(example, examples, kernelFunc, x);
959    } catch (...) {
960        free(x);
961        throw;
962    }
963
964    double v;
965
966    if(svm_check_probability_model(model)){
967        double *prob = (double *) malloc(nr_class*sizeof(double));
968        v = svm_predict_probability(model, x, prob);
969        free(prob);
970    } else
971        v = svm_predict(model, x);
972
973    free(x);
974    if(svm_type==NU_SVR || svm_type==EPSILON_SVR || svm_type==ONE_CLASS)
975        return TValue(v);
976    else
977        return TValue(int(v));
978}
979
980PFloatList TSVMClassifier::getDecisionValues(const TExample &example){
981    if(!model)
982        raiseError("No Model");
983
984    int n_elements;
985    if (model->param.kernel_type != PRECOMPUTED)
986        n_elements = getNumOfElements(example);
987    else
988        n_elements = examples->numberOfExamples() + 2;
989
990    int svm_type=svm_get_svm_type(model);
991    int nr_class=svm_get_nr_class(model);
992
993    svm_node *x = Malloc(svm_node, n_elements);
994    try {
995        if (model->param.kernel_type != PRECOMPUTED)
996            example_to_svm(example, x);
997        else
998            example_to_svm_precomputed(example, examples, kernelFunc, x);
999    } catch (...) {
1000        free(x);
1001        throw;
1002    }
1003
1004    int nDecValues = nr_class*(nr_class-1)/2;
1005    double *dec = (double*) malloc(sizeof(double)*nDecValues);
1006    svm_predict_values(model, x, dec);
1007    PFloatList res = mlnew TFloatList(nDecValues);
1008    for(int i=0; i<nDecValues; i++){
1009        res->at(i) = dec[i];
1010    }
1011    free(x);
1012    free(dec);
1013    return res;
1014}
1015
1016svm_node *TSVMClassifier::example_to_svm(const TExample &ex, svm_node *node, float last, int type){
1017    return ::example_to_svm(ex, node, last, type);
1018}
1019
1020int TSVMClassifier::getNumOfElements(const TExample& example){
1021    return ::getNumOfElements(example);
1022}
1023svm_node *TSVMClassifierSparse::example_to_svm(const TExample &ex, svm_node *node, float last, int){
1024    return ::example_to_svm_sparse(ex, node, last, useNonMeta);
1025}
1026
1027int TSVMClassifierSparse::getNumOfElements(const TExample& example){
1028    return ::getNumOfElements(example, true, useNonMeta);
1029}
1030
1031
Note: See TracBrowser for help on using the repository browser.