Ignore:
Timestamp:
03/26/12 12:11:11 (2 years ago)
Author:
Ales Erjavec <ales.erjavec@…>
Branch:
default
Message:

Fixed coef and rho properties for regression problems. Fixed prob_b mapping (changes signs when binary classifier direction changes).

File:
1 edited

Legend:

Unmodified
Added
Removed
  • Orange/classification/svm/__init__.py

    r10621 r10637  
    293293    @property 
    294294    def coef(self): 
    295         """Coefficients of the underlying binary 1vs1 classifiers. 
    296          
    297         This is a #Classes * (#Classses - 1) list of lists where 
    298         each sublist contains tuples of (alpha, support_vector_index) 
     295        """Coefficients of the underlying svm model. 
     296         
     297        If this is a classification model then this is a list of 
     298        coefficients for each binary 1vs1 classifiers, i.e. 
     299        #Classes * (#Classses - 1) list of lists where 
     300        each sublist contains tuples of (coef, support_vector_index) 
     301         
     302        For regression models it is still a list of lists (for consistency) 
     303        but of length 1 e.g. [[(coef, support_vector_index), ... ]]  
    299304            
    300305        """ 
    301         # We need to reorder the coef values 
    302         # see http://www.csie.ntu.edu.tw/~cjlin/libsvm/faq.html#f804 
    303         # for more information on how the coefs are stored by libsvm 
    304         # internally. 
    305          
    306         import numpy as np 
    307         c_map = self._get_libsvm_bin_classifier_map() 
    308         label_map = self._get_libsvm_labels_map() 
    309         libsvm_coef = self.__wrapped.coef 
    310         coef = [] #[None] * len(c_map) 
    311         n_class = len(label_map) 
    312         n_SV = self.__wrapped.n_SV 
    313         coef_array = np.array(self.__wrapped.coef) 
    314         p = 0 
    315         libsvm_class_indices = np.cumsum([0] + list(n_SV), dtype=int) 
    316         class_indices = np.cumsum([0] + list(self.n_SV), dtype=int) 
    317         for i in range(n_class - 1): 
    318             for j in range(i + 1, n_class): 
    319                 ni = label_map[i] 
    320                 nj = label_map[j] 
    321                 bc_index, mult = c_map[p] 
    322                  
    323                 if ni > nj: 
    324                     ni, nj = nj, ni 
    325                  
    326                 # Original class indices 
    327                 c1_range = range(libsvm_class_indices[ni], 
    328                                  libsvm_class_indices[ni + 1]) 
    329                 c2_range = range(libsvm_class_indices[nj],  
    330                                  libsvm_class_indices[nj + 1]) 
    331                  
    332                 coef1 = mult * coef_array[nj - 1, c1_range] 
    333                 coef2 = mult * coef_array[ni, c2_range] 
    334                  
    335                 # Mapped class indices 
    336                 c1_range = range(class_indices[i], 
    337                                  class_indices[i + 1]) 
    338                 c2_range = range(class_indices[j],  
    339                                  class_indices[j + 1]) 
    340                 if mult == -1.0: 
    341                     c1_range, c2_range = c2_range, c1_range 
     306        if isinstance(self.class_var, variable.Discrete): 
     307            # We need to reorder the coef values 
     308            # see http://www.csie.ntu.edu.tw/~cjlin/libsvm/faq.html#f804 
     309            # for more information on how the coefs are stored by libsvm 
     310            # internally. 
     311            import numpy as np 
     312            c_map = self._get_libsvm_bin_classifier_map() 
     313            label_map = self._get_libsvm_labels_map() 
     314            libsvm_coef = self.__wrapped.coef 
     315            coef = [] #[None] * len(c_map) 
     316            n_class = len(label_map) 
     317            n_SV = self.__wrapped.n_SV 
     318            coef_array = np.array(self.__wrapped.coef) 
     319            p = 0 
     320            libsvm_class_indices = np.cumsum([0] + list(n_SV), dtype=int) 
     321            class_indices = np.cumsum([0] + list(self.n_SV), dtype=int) 
     322            for i in range(n_class - 1): 
     323                for j in range(i + 1, n_class): 
     324                    ni = label_map[i] 
     325                    nj = label_map[j] 
     326                    bc_index, mult = c_map[p] 
    342327                     
    343                 nonzero1 = np.abs(coef1) > 0.0 
    344                 nonzero2 = np.abs(coef2) > 0.0 
    345                  
    346                 coef1 = coef1[nonzero1] 
    347                 coef2 = coef2[nonzero2] 
    348                  
    349                 c1_range = [sv_i for sv_i, nz in zip(c1_range, nonzero1) if nz] 
    350                 c2_range = [sv_i for sv_i, nz in zip(c2_range, nonzero2) if nz] 
    351                  
    352                 coef.append(list(zip(coef1, c1_range)) + list(zip(coef2, c2_range))) 
    353                  
    354                 p += 1 
     328                    if ni > nj: 
     329                        ni, nj = nj, ni 
     330                     
     331                    # Original class indices 
     332                    c1_range = range(libsvm_class_indices[ni], 
     333                                     libsvm_class_indices[ni + 1]) 
     334                    c2_range = range(libsvm_class_indices[nj],  
     335                                     libsvm_class_indices[nj + 1]) 
     336                     
     337                    coef1 = mult * coef_array[nj - 1, c1_range] 
     338                    coef2 = mult * coef_array[ni, c2_range] 
     339                     
     340                    # Mapped class indices 
     341                    c1_range = range(class_indices[i], 
     342                                     class_indices[i + 1]) 
     343                    c2_range = range(class_indices[j],  
     344                                     class_indices[j + 1]) 
     345                    if mult == -1.0: 
     346                        c1_range, c2_range = c2_range, c1_range 
     347                         
     348                    nonzero1 = np.abs(coef1) > 0.0 
     349                    nonzero2 = np.abs(coef2) > 0.0 
     350                     
     351                    coef1 = coef1[nonzero1] 
     352                    coef2 = coef2[nonzero2] 
     353                     
     354                    c1_range = [sv_i for sv_i, nz in zip(c1_range, nonzero1) if nz] 
     355                    c2_range = [sv_i for sv_i, nz in zip(c2_range, nonzero2) if nz] 
     356                     
     357                    coef.append(list(zip(coef1, c1_range)) + list(zip(coef2, c2_range))) 
     358                     
     359                    p += 1 
     360        else: 
     361            coef = [zip(self.__wrapped.coef[0], range(len(self.support_vectors)))] 
     362             
    355363        return coef 
    356364     
    357365    @property 
    358366    def rho(self): 
    359         """Constant (bias) terms in each underlying binary 1vs1 classifier. 
    360         """ 
    361         c_map = self._get_libsvm_bin_classifier_map() 
     367        """Constant (bias) terms of the svm model. 
     368         
     369        For classification models this is a list of bias terms  
     370        for each binary 1vs1 classifier. 
     371         
     372        For regression models it is a list with a single value. 
     373          
     374        """ 
    362375        rho = self.__wrapped.rho 
    363         return [rho[i] * m for i, m in c_map] 
     376        if isinstance(self.class_var, variable.Discrete): 
     377            c_map = self._get_libsvm_bin_classifier_map() 
     378            return [rho[i] * m for i, m in c_map] 
     379        else: 
     380            return list(rho) 
    364381     
    365382    @property 
    366383    def n_SV(self): 
    367384        """Number of support vectors for each class. 
     385        For regression models this is `None`. 
     386         
    368387        """ 
    369388        if self.__wrapped.n_SV is not None: 
     
    374393            return None 
    375394     
     395    # Pairwise probability is expresed as: 
     396    #   1.0 / (1.0 + exp(dec_val[i] * prob_a[i] + prob_b[i]))  
     397    # Since dec_val already changes signs if we switch the  
     398    # classifier direction only prob_b must change signs 
    376399    @property 
    377400    def prob_a(self): 
    378401        if self.__wrapped.prob_a is not None: 
    379             c_map = self._get_libsvm_bin_classifier_map() 
    380             prob_a = self.__wrapped.prob_a 
    381             # TODO: What about order switch? 
    382             return [prob_a[i] for i, _ in c_map] 
     402            if isinstance(self.class_var, variable.Discrete): 
     403                c_map = self._get_libsvm_bin_classifier_map() 
     404                prob_a = self.__wrapped.prob_a 
     405                return [prob_a[i] for i, _ in c_map] 
     406            else: 
     407                # A single value for regression 
     408                return list(self.__wrapped.prob_a) 
    383409        else: 
    384410            return None 
     
    389415            c_map = self._get_libsvm_bin_classifier_map() 
    390416            prob_b = self.__wrapped.prob_b 
    391             # TODO: What about order switch? 
    392             return [prob_b[i] for i, _ in c_map] 
     417            # Change sign when changing the classifier direction 
     418            return [prob_b[i] * m for i, m in c_map] 
    393419        else: 
    394420            return None 
     
    413439        instance = Orange.data.Instance(self.domain, instance) 
    414440        dec_values = self.__wrapped.get_decision_values(instance) 
    415         # decision values are ordered by libsvm internal class values 
    416         # i.e. the order of labels in the data 
    417         c_map = self._get_libsvm_bin_classifier_map() 
    418         return [dec_values[i] * m for i, m in c_map] 
     441        if isinstance(self.class_var, variable.Discrete): 
     442            # decision values are ordered by libsvm internal class values 
     443            # i.e. the order of labels in the data 
     444            c_map = self._get_libsvm_bin_classifier_map() 
     445            return [dec_values[i] * m for i, m in c_map] 
     446        else: 
     447            return list(dec_values) 
    419448         
    420449    def get_model(self): 
     
    433462 
    434463    def _get_libsvm_bin_classifier_map(self): 
    435         """Return the libsvm binary classifier mapping (due to label ordering) 
    436         """ 
     464        """Return the libsvm binary classifier mapping (due to label ordering). 
     465        """ 
     466        if not isinstance(self.class_var, variable.Discrete): 
     467            raise TypeError("SVM classification model expected") 
    437468        label_map = self._get_libsvm_labels_map() 
    438469        bin_c_map = [] 
     
    518549        import itertools 
    519550         
     551        if not isinstance(self.class_var, variable.Discrete): 
     552            raise TypeError("SVM classification model expected") 
     553         
    520554        model = [] 
    521                  
     555         
    522556        # Take the model up to nr_classes 
    523557        libsvm_model = self.__wrapped.get_model() 
Note: See TracChangeset for help on using the changeset viewer.