source: orange/source/orange/libsvm_interface.cpp @ 11664:4bb73da41192

Revision 11664:4bb73da41192, 26.2 KB checked in by Ales Erjavec <ales.erjavec@…>, 8 months ago (diff)

Updated the included LIBSVM to version 3.17.

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