source: orange/source/orange/libsvm_interface.cpp @ 11609:8f3dd402b200

Revision 11609:8f3dd402b200, 25.4 KB checked in by Ales Erjavec <ales.erjavec@…>, 10 months ago (diff)

Fixed a memory leak when raising an exception.

RevLine 
[8978]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)
[9039]116            stream << "0:" << (int)(p->value) << " ";
[8978]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);
[9389]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            }
[8978]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
[11606]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
[8978]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--;
389                node++;
390            }
391        }
392    }
393    if(type == 1){ /*one dummy attr so we can pickle the classifier and keep the SV index in the training table*/
394        node->index=1;
395        node->value=last;
396        node++;
397    }
398    //cout<<(node-1)->index<<endl<<(node-2)->index<<endl;
399    node->index=-1;
400    node->value=last;
401    node++;
402    return node;
403}
404
405class SVM_NodeSort{
406public:
407    bool operator() (const svm_node &lhs, const svm_node &rhs){
408        return lhs.index < rhs.index;
409    }
410};
411
412svm_node* example_to_svm_sparse(const TExample &ex, svm_node* node, float last=0.0, bool useNonMeta=false){
413    svm_node *first=node;
414    int j=1;
415    int index=1;
416    if(useNonMeta)
417        for(TExample::iterator i=ex.begin(); i!=ex.end(); i++){
418            if(i->isRegular() && i!=&ex.getClass()){
419                if(i->varType==TValue::FLOATVAR)
420                    node->value=float(*i);
421                else
422                    node->value=int(*i);
423                node->index=index;
424                if(node->value==numeric_limits<float>::signaling_NaN() ||
425                    node->value==numeric_limits<int>::max())
426                    node--;
427                node++;
428            }
429            index++;
430        }
431    for(TMetaValues::const_iterator i=ex.meta.begin(); i!=ex.meta.end();i++,j++){
432        if(i->second.isRegular()){
433            if(i->second.varType==TValue::FLOATVAR)
434                node->value=float(i->second);
435            else
436                node->value=int(i->second);
[11606]437            node->index = index - i->first;
438
[8978]439            if(node->value==numeric_limits<float>::signaling_NaN() ||
440                node->value==numeric_limits<int>::max())
441                node--;
442            node++;
443        }
444    }
445    sort(first, node, SVM_NodeSort());
446    //cout<<first->index<<endl<<(first+1)->index<<endl;
447    node->index=-1;
448    node->value=last;
449    node++;
450    return node;
451}
452
453/*
454 * Precompute Gram matrix row for ex.
455 * Used for prediction when using the PRECOMPUTED kernel.
456 */
457svm_node* example_to_svm_precomputed(const TExample &ex, PExampleGenerator examples, PKernelFunc kernel, svm_node* node){
458    node->index = 0;
[11606]459    node->value = 0.0; // Can be any value.
[8978]460    node++;
461    int k = 0;
462    PEITERATE(iter, examples){
463        node->index = ++k;
464        node->value = kernel.getReference()(*iter, ex);
465        node++;
466    }
467    node->index = -1; // sentry
468    node++;
469    return node;
470}
471
472int getNumOfElements(const TExample &ex, bool meta=false, bool useNonMeta=false){
473    if(!meta)
474        return std::max(ex.domain->attributes->size()+1, 2);
475    else{
476        int count=1; //we need one to indicate the end of a sequence
477        if(useNonMeta)
478            count+=ex.domain->attributes->size();
479        for(TMetaValues::const_iterator i=ex.meta.begin(); i!=ex.meta.end();i++)
480            if(i->second.isRegular())
481                count++;
482        return std::max(count,2);
483    }
484}
485
486int getNumOfElements(PExampleGenerator &examples, bool meta=false, bool useNonMeta=false){
487    if(!meta)
488        return getNumOfElements(*(examples->begin()), meta)*examples->numberOfExamples();
489    else{
490        int count=0;
491        for(TExampleGenerator::iterator ex(examples->begin()); ex!=examples->end(); ++ex){
492            count+=getNumOfElements(*ex, meta, useNonMeta);
493        }
494        return count;
495    }
496}
497
498#include "symmatrix.hpp"
499svm_node* init_precomputed_problem(svm_problem &problem, PExampleTable examples, TKernelFunc &kernel){
500    int n_examples = examples->numberOfExamples();
501    int i,j;
502    PSymMatrix matrix = mlnew TSymMatrix(n_examples, 0.0);
503    for (i = 0; i < n_examples; i++)
504        for (j = 0; j <= i; j++){
505            matrix->getref(i, j) = kernel(examples->at(i), examples->at(j));
506//          cout << i << " " << j << " " << matrix->getitem(i, j) << endl;
507        }
508    svm_node *x_space = Malloc(svm_node, n_examples * (n_examples + 2));
509    svm_node *node = x_space;
510
511    problem.l = n_examples;
512    problem.x = Malloc(svm_node*, n_examples);
513    problem.y = Malloc(double, n_examples);
514
515    for (i = 0; i < n_examples; i++){
516        problem.x[i] = node;
517        if (examples->domain->classVar->varType == TValue::FLOATVAR)
518            problem.y[i] = examples->at(i).getClass().floatV;
519        else
520            problem.y[i] = examples->at(i).getClass().intV;
521
522        node->index = 0;
523        node->value = i + 1; // instance indices are 1 based
524        node++;
525        for (j = 0; j < n_examples; j++){
526            node->index = j + 1;
527            node->value = matrix->getitem(i, j);
528            node++;
529        }
530        node->index = -1; // sentry
531        node++;
532    }
533    return x_space;
534}
535
[11606]536/*
537 * Extract an ExampleTable corresponding to the support vectors from the
538 * trained model.
539 */
540PExampleTable extract_support_vectors(svm_model * model, PExampleTable train_instances)
541{
542    PExampleTable vectors = mlnew TExampleTable(train_instances->domain);
543
544    for (int i = 0; i < model->l; i++) {
545        svm_node *node = model->SV[i];
546        int sv_index = -1;
547        if(model->param.kernel_type != PRECOMPUTED){
548            /* The value of the last node (with index == -1) holds the
549             * index of the training example.
550             */
551            while(node->index != -1) {
552                node++;
553            }
554            sv_index = int(node->value);
555        } else {
556            /* The value of the first node contains the training instance
557             * index (indices 1 based).
558             */
559            sv_index = int(node->value) - 1;
560        }
561        vectors->addExample(mlnew TExample(train_instances->at(sv_index)));
562    }
563
564    return vectors;
565}
566
567
568/*
569 * Consolidate model->SV[1] .. SV[l] vectors into a single contiguous
570 * memory block. The model will 'own' the new *(model->SV) array and
571 * will be freed in destroy_svm_model (model->free_sv == 1). Note that
572 * the original 'x_space' is left intact, it is the caller's
573 * responsibility to free it. However the model->SV array itself is
574 * reused (overwritten).
575 */
576
577void svm_model_consolidate_SV(svm_model * model) {
578    int count = 0;
579    svm_node * x_space = NULL;
580    svm_node * ptr = NULL;
581    svm_node * ptr_source = NULL;
582
583    // Count the number of elements.
584    for (int i = 0; i < model->l; i++) {
585        ptr = model->SV[i];
586        while (ptr->index != -1){
587            count++;
588            ptr++;
589        }
590    }
591    // add the sentinel count
592    count += model->l;
593
594    x_space = Malloc(svm_node, count);
595    ptr = x_space;
596    for (int i = 0; i < model->l; i++) {
597        ptr_source = model->SV[i];
598        model->SV[i] = ptr;
599        while (ptr_source->index != -1) {
600            *(ptr++) = *(ptr_source++);
601        }
602        // copy the sentinel
603        *(ptr++) = *(ptr_source++);
604    }
605    model->free_sv = 1; // XXX
606}
607
[8978]608static void print_string_null(const char* s) {}
609
[11606]610
[8978]611TSVMLearner::TSVMLearner(){
612    svm_type = NU_SVC;
613    kernel_type = RBF;
614    degree = 3;
615    gamma = 0;
616    coef0 = 0;
617    nu = 0.5;
618    cache_size = 250;
619    C = 1;
620    eps = 1e-3f;
621    p = 0.1f;
622    shrinking = 1;
623    probability = 0;
624    verbose = false;
625    nr_weight = 0;
626    weight_label = NULL;
627    weight = NULL;
628};
629
[11606]630
[8978]631PClassifier TSVMLearner::operator ()(PExampleGenerator examples, const int&){
632    svm_parameter param;
633    svm_problem prob;
634    svm_model* model;
635    svm_node* x_space;
636
[11608]637    PDomain domain = examples->domain;
638
[8978]639    int classVarType;
[11608]640    if (domain->classVar)
641        classVarType = domain->classVar->varType;
[11609]642    else {
[11608]643        classVarType = TValue::NONE;
644        if(svm_type != ONE_CLASS)
[8978]645            raiseError("Domain has no class variable");
646    }
[11608]647    if (classVarType == TValue::FLOATVAR && !(svm_type == EPSILON_SVR || svm_type == NU_SVR ||svm_type == ONE_CLASS))
[8978]648        raiseError("Domain has continuous class");
649
[11608]650    if (kernel_type == PRECOMPUTED && !kernelFunc)
[8978]651        raiseError("Custom kernel function not supplied");
652
[11608]653    PExampleTable train_data = mlnew TExampleTable(examples, /* owns= */ false);
[8978]654
[11608]655    if (classVarType == TValue::INTVAR && svm_type != ONE_CLASS) {
656        /* Sort the train data by the class columns so the order of
657         * classVar.values is preserved in libsvm's model.
658         */
659        vector<int> sort_columns(domain->variables->size() - 1);
660        train_data->sort(sort_columns);
661    }
662
[11609]663    // Initialize svm parameters
664    param.svm_type = svm_type;
665    param.kernel_type = kernel_type;
666    param.degree = degree;
667    param.gamma = gamma;
668    param.coef0 = coef0;
669    param.nu = nu;
670    param.C = C;
671    param.eps = eps;
672    param.p = p;
673    param.cache_size = cache_size;
674    param.shrinking = shrinking;
675    param.probability = probability;
676    param.nr_weight = nr_weight;
677
678    if (nr_weight > 0) {
679        param.weight_label = Malloc(int, nr_weight);
680        param.weight = Malloc(double, nr_weight);
681        int i;
682        for (i=0; i<nr_weight; i++) {
683            param.weight_label[i] = weight_label[i];
684            param.weight[i] = weight[i];
685        }
686    } else {
687        param.weight_label = NULL;
688        param.weight = NULL;
689    }
690
[11608]691    int numElements = getNumOfElements(train_data);
692
[11609]693    prob.x = NULL;
694    prob.y = NULL;
695
[11608]696    if (kernel_type != PRECOMPUTED)
697        x_space = init_problem(prob, train_data, numElements);
[8978]698    else // Compute the matrix using the kernelFunc
[11608]699        x_space = init_precomputed_problem(prob, train_data, kernelFunc.getReference());
[8978]700
[11608]701    if (param.gamma==0)
[8978]702        param.gamma=1.0f/(float(numElements)/float(prob.l)-1);
703
[11609]704    const char* error=svm_check_parameter(&prob, &param);
[11608]705    if (error){
[8978]706        free(x_space);
707        free(prob.y);
708        free(prob.x);
[11609]709        svm_destroy_param(&param);
[8978]710        raiseError("LibSVM parameter error: %s", error);
711    }
[9347]712
713    // If a probability model was requested LibSVM uses 5 fold
714    // cross-validation to estimate the prediction errors. This includes a
715    // random shuffle of the data. To make the results reproducible and
716    // consistent with 'svm-train' (which always learns just on one dataset
717    // in a process run) we reset the random seed. This could have unintended
718    // consequences.
719    if (param.probability)
720    {
721        srand(1);
722    }
[8978]723    svm_set_print_string_function((verbose)? NULL : &print_string_null);
724
[11606]725    model = svm_train(&prob, &param);
[8978]726
[11606]727    if ((svm_type==C_SVC || svm_type==NU_SVC) && !model->nSV) {
728        svm_free_and_destroy_model(&model);
[11609]729        free(x_space);
730        free(prob.x);
731        free(prob.y);
732        svm_destroy_param(&param);
[11606]733        raiseError("LibSVM returned no support vectors");
734    }
735
[8978]736    svm_destroy_param(&param);
737    free(prob.y);
738    free(prob.x);
[11606]739
740    // Consolidate the SV so x_space can be safely freed
741    svm_model_consolidate_SV(model);
742
743    free(x_space);
744
[11608]745    PExampleTable supportVectors = extract_support_vectors(model, train_data);
[11606]746
[11608]747    return PClassifier(createClassifier(domain, model, supportVectors, train_data));
[8978]748}
749
750svm_node* TSVMLearner::example_to_svm(const TExample &ex, svm_node* node, float last, int type){
751    return ::example_to_svm(ex, node, last, type);
752}
753
[9187]754svm_node* TSVMLearner::init_problem(svm_problem &problem, PExampleTable examples, int n_elements){
755    problem.l = examples->numberOfExamples();
756    problem.y = Malloc(double ,problem.l);
757    problem.x = Malloc(svm_node*, problem.l);
758    svm_node *x_space = Malloc(svm_node, n_elements);
759    svm_node *node = x_space;
760
761    for (int i = 0; i < problem.l; i++){
762        problem.x[i] = node;
763        node = example_to_svm(examples->at(i), node, i);
764        if (examples->domain->classVar)
765            if (examples->domain->classVar->varType == TValue::FLOATVAR)
766                problem.y[i] = examples->at(i).getClass().floatV;
767            else if (examples->domain->classVar->varType == TValue::INTVAR)
768                problem.y[i] = examples->at(i).getClass().intV;
769    }
770    return x_space;
771}
772
[8978]773int TSVMLearner::getNumOfElements(PExampleGenerator examples){
774    return ::getNumOfElements(examples);
775}
776
[11606]777TSVMClassifier* TSVMLearner::createClassifier(
[11607]778        PDomain domain, svm_model* model, PExampleTable supportVectors, PExampleTable examples) {
[11608]779    PKernelFunc kfunc;
[11606]780    if (kernel_type != PRECOMPUTED) {
[11608]781        // Classifier does not need the train data and the kernelFunc.
[11606]782        examples = NULL;
[11608]783        kfunc = NULL;
784    } else {
785        kfunc = kernelFunc;
[11606]786    }
[11608]787
788    return mlnew TSVMClassifier(domain, model, supportVectors, kfunc, examples);
[8978]789}
790
791TSVMLearner::~TSVMLearner(){
792    if(weight_label)
793        free(weight_label);
794
795    if(weight)
[11608]796        free(weight);
[8978]797}
798
799svm_node* TSVMLearnerSparse::example_to_svm(const TExample &ex, svm_node* node, float last, int type){
800    return ::example_to_svm_sparse(ex, node, last, useNonMeta);
801}
802
803int TSVMLearnerSparse::getNumOfElements(PExampleGenerator examples){
804    return ::getNumOfElements(examples, true, useNonMeta);
805}
806
[11606]807TSVMClassifier* TSVMLearnerSparse::createClassifier(
[11607]808        PDomain domain, svm_model* model, PExampleTable supportVectors, PExampleTable examples) {
[11608]809    PKernelFunc kfunc;
[11606]810    if (kernel_type != PRECOMPUTED) {
[11608]811        // Classifier does not need the train data and the kernelFunc.
[11606]812        examples = NULL;
[11608]813        kfunc = NULL;
814    } else {
815        kfunc = kernelFunc;
[11606]816    }
[11608]817    return mlnew TSVMClassifierSparse(domain, model, useNonMeta, supportVectors, kfunc, examples);
[8978]818}
819
[11606]820
821TSVMClassifier::TSVMClassifier(
[11607]822        PDomain domain, svm_model * model,
[11606]823        PExampleTable supportVectors,
[11607]824        PKernelFunc kernelFunc,
825        PExampleTable examples
826        ) : TClassifierFD(domain) {
827    this->model = model;
828    this->supportVectors = supportVectors;
829    this->kernelFunc = kernelFunc;
[11606]830    this->examples = examples;
831
[9021]832    svm_type = svm_get_svm_type(model);
833    kernel_type = model->param.kernel_type;
834
[11607]835    if (svm_type == ONE_CLASS) {
836        this->classVar = mlnew TFloatVariable("one class");
837    }
838
[8978]839    computesProbabilities = model && svm_check_probability_model(model) && \
[11607]840                (svm_type != NU_SVR && svm_type != EPSILON_SVR); // Disable prob. estimation for regression
[9021]841
[8978]842    int nr_class = svm_get_nr_class(model);
843    int i = 0;
[11606]844
845    /* Expose (copy) the model data (coef, rho, probA) to public
846     * class interface.
847     */
[11607]848    if (svm_type == C_SVC || svm_type == NU_SVC) {
849        nSV = mlnew TIntList(nr_class); // num of SVs for each class (sum(nSV) == model->l)
850        for(i = 0;i < nr_class; i++) {
851            nSV->at(i) = model->nSV[i];
852        }
853    }
[8978]854
855    coef = mlnew TFloatListList(nr_class-1);
[11607]856    for(i = 0; i < nr_class - 1; i++) {
[8978]857        TFloatList *coefs = mlnew TFloatList(model->l);
[11607]858        for(int j = 0;j < model->l; j++) {
[8978]859            coefs->at(j) = model->sv_coef[i][j];
[11607]860        }
861        coef->at(i) = coefs;
[8978]862    }
[11607]863
864    // Number of binary classifiers in the model
865    int nr_bin_cls = nr_class * (nr_class - 1) / 2;
866
867    rho = mlnew TFloatList(nr_bin_cls);
868    for(i = 0; i < nr_bin_cls; i++) {
[8978]869        rho->at(i) = model->rho[i];
[11607]870    }
871
872    if(model->probA) {
873        probA = mlnew TFloatList(nr_bin_cls);
874        if (model->param.svm_type != NU_SVR && model->param.svm_type != EPSILON_SVR && model->probB) {
875            // Regression only has probA
876            probB = mlnew TFloatList(nr_bin_cls);
877        }
878
879        for(i=0; i<nr_bin_cls; i++) {
[8978]880            probA->at(i) = model->probA[i];
[11607]881            if (model->param.svm_type != NU_SVR && model->param.svm_type != EPSILON_SVR && model->probB) {
[8978]882                probB->at(i) = model->probB[i];
[11607]883            }
[8978]884        }
885    }
886}
887
[11607]888
[8978]889TSVMClassifier::~TSVMClassifier(){
[11606]890    if (model) {
[8978]891        svm_free_and_destroy_model(&model);
[11606]892    }
[8978]893}
894
[11607]895
[8978]896PDistribution TSVMClassifier::classDistribution(const TExample & example){
897    if(!model)
898        raiseError("No Model");
899
900    if(!computesProbabilities)
901        return TClassifierFD::classDistribution(example);
902
903    int n_elements;
904    if (model->param.kernel_type != PRECOMPUTED)
905        n_elements = getNumOfElements(example);
906    else
907        n_elements = examples->numberOfExamples() + 2;
908
909    int svm_type = svm_get_svm_type(model);
910    int nr_class = svm_get_nr_class(model);
911
912    svm_node *x = Malloc(svm_node, n_elements);
913    try{
914        if (model->param.kernel_type != PRECOMPUTED)
915            example_to_svm(example, x, -1.0);
916        else
917            example_to_svm_precomputed(example, examples, kernelFunc, x);
918    } catch (...) {
919        free(x);
920        throw;
921    }
922
923    int *labels=(int *) malloc(nr_class*sizeof(int));
924    svm_get_labels(model, labels);
925
926    double *prob_estimates = (double *) malloc(nr_class*sizeof(double));;
927    svm_predict_probability(model, x, prob_estimates);
928
929    PDistribution dist = TDistribution::create(example.domain->classVar);
930    for(int i=0; i<nr_class; i++)
931        dist->setint(labels[i], prob_estimates[i]);
932    free(x);
933    free(prob_estimates);
934    free(labels);
935    return dist;
936}
937
938TValue TSVMClassifier::operator()(const TExample & example){
939    if(!model)
940        raiseError("No Model");
941
942    int n_elements;
943    if (model->param.kernel_type != PRECOMPUTED)
[11606]944        n_elements = getNumOfElements(example);
[8978]945    else
946        n_elements = examples->numberOfExamples() + 2;
947
948    int svm_type = svm_get_svm_type(model);
949    int nr_class = svm_get_nr_class(model);
950
951    svm_node *x = Malloc(svm_node, n_elements);
952    try {
953        if (model->param.kernel_type != PRECOMPUTED)
954            example_to_svm(example, x);
955        else
956            example_to_svm_precomputed(example, examples, kernelFunc, x);
957    } catch (...) {
958        free(x);
959        throw;
960    }
961
962    double v;
963
964    if(svm_check_probability_model(model)){
965        double *prob = (double *) malloc(nr_class*sizeof(double));
966        v = svm_predict_probability(model, x, prob);
967        free(prob);
968    } else
969        v = svm_predict(model, x);
970
971    free(x);
972    if(svm_type==NU_SVR || svm_type==EPSILON_SVR || svm_type==ONE_CLASS)
973        return TValue(v);
974    else
975        return TValue(int(v));
976}
977
978PFloatList TSVMClassifier::getDecisionValues(const TExample &example){
979    if(!model)
980        raiseError("No Model");
981
982    int n_elements;
[11606]983    if (model->param.kernel_type != PRECOMPUTED)
984        n_elements = getNumOfElements(example);
985    else
986        n_elements = examples->numberOfExamples() + 2;
[8978]987
988    int svm_type=svm_get_svm_type(model);
989    int nr_class=svm_get_nr_class(model);
990
991    svm_node *x = Malloc(svm_node, n_elements);
992    try {
993        if (model->param.kernel_type != PRECOMPUTED)
994            example_to_svm(example, x);
995        else
996            example_to_svm_precomputed(example, examples, kernelFunc, x);
997    } catch (...) {
998        free(x);
999        throw;
1000    }
1001
1002    int nDecValues = nr_class*(nr_class-1)/2;
1003    double *dec = (double*) malloc(sizeof(double)*nDecValues);
1004    svm_predict_values(model, x, dec);
1005    PFloatList res = mlnew TFloatList(nDecValues);
1006    for(int i=0; i<nDecValues; i++){
1007        res->at(i) = dec[i];
1008    }
1009    free(x);
1010    free(dec);
1011    return res;
1012}
1013
1014svm_node *TSVMClassifier::example_to_svm(const TExample &ex, svm_node *node, float last, int type){
1015    return ::example_to_svm(ex, node, last, type);
1016}
1017
1018int TSVMClassifier::getNumOfElements(const TExample& example){
1019    return ::getNumOfElements(example);
1020}
[11606]1021svm_node *TSVMClassifierSparse::example_to_svm(const TExample &ex, svm_node *node, float last, int){
[8978]1022    return ::example_to_svm_sparse(ex, node, last, useNonMeta);
1023}
1024
1025int TSVMClassifierSparse::getNumOfElements(const TExample& example){
1026    return ::getNumOfElements(example, true, useNonMeta);
1027}
1028
1029
Note: See TracBrowser for help on using the repository browser.