Changeset 11016:e9a6850a6818 in orange


Ignore:
Timestamp:
11/08/12 17:12:25 (18 months ago)
Author:
Ales Erjavec <ales.erjavec@…>
Branch:
default
Message:

Fix the weight vector exposed to python if liblinear internally reorders the values of the binary class.

(references #1239)

Files:
2 edited

Legend:

Unmodified
Added
Removed
  • Orange/testing/unit/tests/test_linear.py

    r10773 r11016  
     1import cPickle 
     2 
    13import Orange 
    24from Orange.testing import testing 
     
    1012import numpy as np 
    1113 
    12 def multiclass_from_1_vs_rest(dec_values, class_var): 
     14 
     15def decision_values(classifier, instance): 
     16    """Return the decision values (numpy.array) for classifying `instance`. 
     17    """ 
     18    instance = Orange.data.Table(classifier.domain, [instance]) 
     19    (instance,) = instance.to_numpy_MA("A") 
     20 
     21    x = instance.filled(0.0) 
     22    if classifier.bias > 0.0: 
     23        x = np.hstack([x, [[classifier.bias]]]) 
     24 
     25    w = np.array(classifier.weights) 
     26 
     27    return np.dot(x, w.T).ravel() 
     28 
     29 
     30def classify_from_weights(classifier, instance): 
     31    """Classify the instance using classifier's weights. 
     32    """ 
     33    dec_values = decision_values(classifier, instance) 
     34 
     35    class_var = classifier.class_var 
    1336    if len(class_var.values) > 2: 
     37        # TODO: Check how liblinear handles ties 
    1438        return class_var(int(np.argmax(dec_values))) 
    1539    else: 
    1640        return class_var(0 if dec_values[0] > 0 else 1) 
    1741 
    18 def binary_classifier_test(self, data): 
     42 
     43def classify_from_weights_test(self, classifier, data): 
    1944    class_var = data.domain.class_var 
    2045    if isinstance(class_var, Orange.feature.Discrete): 
    21         cl_values = class_var.values 
    22         if self.classifier.bias >= 0: 
    23             bias = [self.classifier.bias] 
    24         else: 
    25             bias = [] 
    2646        for inst in data[:]: 
    27             dec_values = [] 
    28             inst_v = [float(v) if not v.is_special() else 0.0 \ 
    29                       for v in Orange.data.Instance(self.classifier.domain, inst)] 
    30             inst_v = inst_v[:-1] + bias 
    31             for w in self.classifier.weights: 
    32                 dec_values.append(np.dot(inst_v, w)) 
    33             pval1 = self.classifier(inst) 
    34             pval2 = multiclass_from_1_vs_rest(dec_values, class_var) 
    35             if len(cl_values) > 2: 
    36                 self.assertEqual(pval1, pval2) 
    37             else: 
    38                 #TODO: handle order switch 
    39                 pass 
     47            pval1 = classifier(inst) 
     48            pval2 = classify_from_weights(classifier, inst) 
     49            self.assertEqual(pval1, pval2, 
     50                             msg="classifier and classify_from_weights return " 
     51                                 "different values") 
     52 
    4053 
    4154@testing.test_on_data 
    4255def test_learner_on(self, dataset): 
    4356    testing.LearnerTestCase.test_learner_on(self, dataset) 
     57 
    4458    n_vals = len(dataset.domain.class_var.values) 
    4559    if n_vals > 2: 
     
    4761    else: 
    4862        self.assertEquals(len(self.classifier.weights), 1) 
     63 
    4964    n_features = len(self.classifier.domain.attributes) 
    5065    if self.classifier.bias >= 0: 
    5166        n_features += 1 
    52      
     67 
    5368    self.assertTrue(all(len(w) == n_features \ 
    5469                        for w in self.classifier.weights 
    5570                        )) 
    56      
    57     binary_classifier_test(self, dataset) 
     71 
     72    classify_from_weights_test(self, self.classifier, dataset) 
     73 
    5874 
    5975@testing.test_on_data 
    6076def test_learner_with_bias_on(self, dataset): 
    61     import cPickle 
    6277    learner = self.learner 
    6378    learner_b = cPickle.loads(cPickle.dumps(learner)) 
     
    6580    try: 
    6681        self.learner = learner_b 
     82        test_learner_on(self, dataset) 
    6783    finally: 
    6884        self.learner = learner 
    69     test_learner_on(self, dataset) 
    70           
     85 
     86 
     87def split(data, value): 
     88    pos = [inst for inst in data if inst.get_class() == value] 
     89    neg = [inst for inst in data if inst.get_class() != value] 
     90    return Orange.data.Table(pos), Orange.data.Table(neg) 
     91 
     92 
     93def test_missing_instances(self): 
     94    """Test the learner on a dataset with no instances for 
     95    some class. 
     96 
     97    """ 
     98    data = Orange.data.Table("iris") 
     99    class_var = data.domain.class_var 
     100 
     101    for i, value in enumerate(class_var.values): 
     102        _, train = split(data, value) 
     103        classifier = self.learner(train) 
     104 
     105        self.assertEqual(len(classifier.weights), len(class_var.values), 
     106                        msg="Number of weight vectors differs from the number " 
     107                            "of class values") 
     108 
     109        dec_values = [decision_values(classifier, instance) \ 
     110                      for instance in data] 
     111 
     112        self.assertTrue(all(val[i] == 0.0 for val in dec_values), 
     113                        msg="Non zero decision value for unseen class") 
     114 
     115        classify_from_weights_test(self, classifier, data) 
     116 
    71117 
    72118@datasets_driven(datasets=testing.CLASSIFICATION_DATASETS) 
     
    76122    test_learner_on = test_learner_on 
    77123    test_learner_with_bias_on = test_learner_with_bias_on 
     124    test_missing_instances = test_missing_instances 
     125 
    78126 
    79127@datasets_driven(datasets=testing.CLASSIFICATION_DATASETS) 
    80128class TestLinearSVMLearnerL2R_L2LOSS(testing.LearnerTestCase): 
    81129    LEARNER = LinearSVMLearner(sover_type=LinearSVMLearner.L2R_L2LOSS) 
    82      
     130 
    83131    test_learner_on = test_learner_on 
    84132    test_learner_with_bias_on = test_learner_with_bias_on 
     133    test_missing_instances = test_missing_instances 
     134 
    85135 
    86136@datasets_driven(datasets=testing.CLASSIFICATION_DATASETS) 
    87137class TestLinearSVMLearnerL2R_L1LOSS_DUAL(testing.LearnerTestCase): 
    88138    LEARNER = LinearSVMLearner(sover_type=LinearSVMLearner.L2R_L1LOSS_DUAL) 
    89      
     139 
    90140    test_learner_on = test_learner_on 
    91141    test_learner_with_bias_on = test_learner_with_bias_on 
     142    test_missing_instances = test_missing_instances 
     143 
    92144 
    93145@datasets_driven(datasets=testing.CLASSIFICATION_DATASETS) 
    94146class TestLinearSVMLearnerL2R_L1LOSS(testing.LearnerTestCase): 
    95147    LEARNER = LinearSVMLearner(sover_type=LinearSVMLearner.L2R_L2LOSS) 
    96          
     148 
    97149    test_learner_on = test_learner_on 
    98150    test_learner_with_bias_on = test_learner_with_bias_on 
     151    test_missing_instances = test_missing_instances 
     152 
    99153 
    100154@datasets_driven(datasets=testing.CLASSIFICATION_DATASETS) 
    101155class TestLinearSVMLearnerL1R_L2LOSS(testing.LearnerTestCase): 
    102156    LEARNER = LinearSVMLearner(sover_type=LinearSVMLearner.L1R_L2LOSS) 
    103      
     157 
    104158    test_learner_on = test_learner_on 
    105159    test_learner_with_bias_on = test_learner_with_bias_on 
     160    test_missing_instances = test_missing_instances 
     161 
    106162 
    107163@datasets_driven(datasets=testing.CLASSIFICATION_DATASETS) 
    108 class TestLinearSVMLearnerL1R_L2LOSS(testing.LearnerTestCase): 
     164class TestLinearSVMLearnerMCSVM_CSS(testing.LearnerTestCase): 
    109165    LEARNER = LinearSVMLearner(sover_type=LinearSVMLearner.MCSVM_CS) 
    110      
     166 
    111167    test_learner_on = test_learner_on 
    112168    test_learner_with_bias_on = test_learner_with_bias_on 
     169    test_missing_instances = test_missing_instances 
     170 
    113171 
    114172if __name__ == "__main__": 
  • source/orange/liblinear_interface.cpp

    r10951 r11016  
    402402    else 
    403403    { 
     404        /* If the order of the liblinear internaly stored classes 
     405         * is different from the order of orange's class values, 
     406         * we reverse the weight vector. 
     407         */ 
     408        float factor = (labels[0] == 0)? 1.0f : -1.0f; 
     409 
    404410        for (int j = 0; j < nr_feature; j++) 
    405411        { 
    406             /* If there are more than 2 class values 
    407              */ 
    408412            if (nr_orange_weights > 1) 
    409413            { 
     414               /* There are more than 2 orange class values. This means 
     415                * there were no instances for one or more classed in the training 
     416                * data set. 
     417                */ 
    410418                weights->at(labels[0])->at(j) = linmodel->w[j]; 
    411419                weights->at(labels[1])->at(j) = - linmodel->w[j]; 
     
    413421            else 
    414422            { 
    415                 weights->at(0)->at(j) = linmodel->w[j]; 
     423                weights->at(0)->at(j) = factor * linmodel->w[j]; 
    416424            } 
    417425        } 
Note: See TracChangeset for help on using the changeset viewer.