Package mvpa :: Package clfs :: Package sg :: Module svm

[hide private]
[frames] | no frames]

# Source Code for Module mvpa.clfs.sg.svm

      1  # emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
      2  # vi: set ft=python sts=4 ts=4 sw=4 et:
      3  ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
      4  #
      5  #   See COPYING file distributed along with the PyMVPA package for the
      6  #   copyright and license terms.
      7  #
      8  ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
      9  """Wrap the libsvm package into a very simple class interface."""
     10
     11  __docformat__ = 'restructuredtext'
     12
     13
     14  _DEV__doc__ = """
     15
     16  TODOs:
     17   * dual-license under GPL for use of SG?
     18   * for recent versions add ability to specify/parametrize normalization
     19     scheme for the kernel, and reuse 'scale' now for the normalizer
     20   * Add support for simplified linear classifiers (which do not require
     21     storing all training SVs/samples to make classification in predict())
     22  """
     23
     24  import numpy as N
     25
     26  from mvpa import _random_seed
     27
     28  # Rely on SG
     29  from mvpa.base import externals, warning
     30  if externals.exists('shogun', raiseException=True):
     31      import shogun.Features
     32      import shogun.Classifier
     33      import shogun.Regression
     34      import shogun.Kernel
     35      import shogun.Library
     36
     37      # Figure out debug IDs once and for all
     38      if hasattr(shogun.Kernel, 'M_DEBUG'):
     39          _M_DEBUG = shogun.Kernel.M_DEBUG
     40          _M_ERROR = shogun.Kernel.M_ERROR
     41      elif hasattr(shogun.Kernel, 'MSG_DEBUG'):
     42          _M_DEBUG = shogun.Kernel.MSG_DEBUG
     43          _M_ERROR = shogun.Kernel.MSG_ERROR
     44      else:
     45          _M_DEBUG, _M_ERROR = None, None
     46          warning("Could not figure out debug IDs within shogun. "
     47                  "No control over shogun verbosity would be provided")
     48
     49      try:
     50          # reuse the same seed for shogun
     51          shogun.Library.Math_init_random(_random_seed)
     52          # and do it twice to stick ;) for some reason is necessary
     53          # atm
     54          shogun.Library.Math_init_random(_random_seed)
     55      except Exception, e:
     56          warning('Shogun cannot be seeded due to %s' % (e,))
     57
     58  import operator
     59
     60  from mvpa.misc.param import Parameter
     61  from mvpa.base import warning
     62
     63  from mvpa.clfs.base import FailedToTrainError
     64  from mvpa.clfs.meta import MulticlassClassifier
     65  from mvpa.clfs._svmbase import _SVM
     66  from mvpa.misc.state import StateVariable
     67  from mvpa.measures.base import Sensitivity
     68
     69  from sens import *
     70
     71  if __debug__:
     72      from mvpa.base import debug
     73
     74
     75 -def _setdebug(obj, partname):
     76      """Helper to set level of debugging output for SG
     77      :Parameters:
     78        obj
     79          In SG debug output seems to be set per every object
     80        partname : basestring
     81          For what kind of object we are talking about... could be automated
     82          later on (TODO)
     83      """
     84      if _M_DEBUG is None:
     85          return
     86      debugname = "SG_%s" % partname.upper()
     87
     88      switch = {True: (_M_DEBUG, 'M_DEBUG', "enable"),
     89                False: (_M_ERROR, 'M_ERROR', "disable")}
     90
     91      key = __debug__ and debugname in debug.active
     92
     93      sglevel, slevel, progressfunc = switch[key]
     94
     95      if __debug__:
     96          debug("SG_", "Setting verbosity for shogun.%s instance: %s to %s" %
     97                (partname, `obj`, slevel))
     98      obj.io.set_loglevel(sglevel)
     99      try:
    100          exec "obj.io.%s_progress()" % progressfunc
    101      except:
    102          warning("Shogun version installed has no way to enable progress" +
    103                  " reports")
    104
    105
    106 -def _tosg(data):
    107      """Draft helper function to convert data we have into SG suitable format
    108
    109      TODO: Support different datatypes
    110      """
    111
    112      if __debug__:
    113          debug("SG_", "Converting data for shogun into RealFeatures")
    114
    115      features = shogun.Features.RealFeatures(data.astype('double').T)
    116
    117      if __debug__:
    118          debug("SG__", "Done converting data for shogun into RealFeatures")
    119      _setdebug(features, 'Features')
    120      return features
    121
    122
    123 -class SVM(_SVM):
    124      """Support Vector Machine Classifier(s) based on Shogun
    125
    126      This is a simple base interface
    127      """
    128
    129      num_threads = Parameter(1,
    130                              min=1,
    131                              doc='Number of threads to utilize')
    132
    133      # NOTE: gamma is width in SG notation for RBF(Gaussian)
    134      _KERNELS = {}
    135      if externals.exists('shogun', raiseException=True):
    136          _KERNELS = { "linear": (shogun.Kernel.LinearKernel,
    137                                 ('scale',), LinearSVMWeights),
    138                       "rbf" :   (shogun.Kernel.GaussianKernel,
    139                                 ('gamma',), None),
    140                       "rbfshift": (shogun.Kernel.GaussianShiftKernel,
    141                                   ('gamma', 'max_shift', 'shift_step'), None),
    142                       "sigmoid": (shogun.Kernel.SigmoidKernel,
    143                                  ('cache_size', 'gamma', 'coef0'), None),
    144                      }
    145
    146      _KNOWN_PARAMS = [ 'epsilon' ]
    147      _KNOWN_KERNEL_PARAMS = [ ]
    148
    149      _clf_internals = _SVM._clf_internals + [ 'sg', 'retrainable' ]
    150
    151      if externals.exists('sg ge 0.6.4'):
    152          _KERNELS['linear'] = (shogun.Kernel.LinearKernel, (), LinearSVMWeights)
    153
    154      # Some words of wisdom from shogun author:
    155      # XXX remove after proper comments added to implementations
    156      """
    157      If you'd like to train linear SVMs use SGD or OCAS. These are (I am
    158      serious) the fastest linear SVM-solvers to date. (OCAS cannot do SVMs
    159      with standard additive bias, but will L2 reqularize it - though it
    160      should not matter much in practice (although it will give slightly
    161      different solutions)). Note that SGD has no stopping criterion (you
    162      simply have to specify the number of iterations) and that OCAS has a
    163      different stopping condition than svmlight for example which may be more
    164      tight and more loose depending on the problem - I sugeest 1e-2 or 1e-3
    165      for epsilon.
    166
    167      If you would like to train kernel SVMs use libsvm/gpdt/svmlight -
    168      depending on the problem one is faster than the other (hard to say when,
    169      I *think* when your dataset is very unbalanced chunking methods like
    170      svmlight/gpdt are better), for smaller problems definitely libsvm.
    171
    172      If you use string kernels then gpdt/svmlight have a special 'linadd'
    173      speedup for this (requires sg 0.6.2 - there was some inefficiency in the
    174      code for python-modular before that). This is effective for big datasets
    175      and (I trained on 10 million strings based on this).
    176
    177      And yes currently we only implemented parallel training for svmlight,
    178      however all SVMs can be evaluated in parallel.
    179      """
    180      _KNOWN_IMPLEMENTATIONS = {}
    181      if externals.exists('shogun', raiseException=True):
    182          _KNOWN_IMPLEMENTATIONS = {
    183              "libsvm" : (shogun.Classifier.LibSVM, ('C',),
    184                         ('multiclass', 'binary'),
    185                          "LIBSVM's C-SVM (L2 soft-margin SVM)"),
    186              "gmnp" : (shogun.Classifier.GMNPSVM, ('C',),
    187                       ('multiclass', 'binary'),
    188                        "Generalized Nearest Point Problem SVM"),
    189              # XXX should have been GPDT, shogun has it fixed since some version
    190              "gpbt" : (shogun.Classifier.GPBTSVM, ('C',), ('binary',),
    191                        "Gradient Projection Decomposition Technique for " \
    192                        "large-scale SVM problems"),
    193              "gnpp" : (shogun.Classifier.GNPPSVM, ('C',), ('binary',),
    194                        "Generalized Nearest Point Problem SVM"),
    195
    196              ## TODO: Needs sparse features...
    197              # "svmlin" : (shogun.Classifier.SVMLin, ''),
    198              # "liblinear" : (shogun.Classifier.LibLinear, ''),
    199              # "subgradient" : (shogun.Classifier.SubGradientSVM, ''),
    200              ## good 2-class linear SVMs
    201              # "ocas" : (shogun.Classifier.SVMOcas, ''),
    202              # "sgd" : ( shogun.Classifier.SVMSGD, ''),
    203
    204              # regressions
    205              "libsvr": (shogun.Regression.LibSVR, ('C', 'tube_epsilon',),
    206                        ('regression',),
    207                         "LIBSVM's epsilon-SVR"),
    208              }
    209
    210
    211 -    def __init__(self,
    212                   kernel_type='linear',
    213                   **kwargs):
    214          """Interface class to Shogun's classifiers and regressions.
    215
    216          Default implementation is 'libsvm'.
    217          """
    218
    219          svm_impl = kwargs.get('svm_impl', 'libsvm').lower()
    220          kwargs['svm_impl'] = svm_impl
    221
    222          # init base class
    223          _SVM.__init__(self, kernel_type=kernel_type, **kwargs)
    224
    225          self.__svm = None
    226          """Holds the trained svm."""
    227          self.__svm_apply = None
    228          """Compatibility convenience to bind to the classify/apply method
    229             of __svm"""
    230          # Need to store original data...
    231          # TODO: keep 1 of them -- just __traindata or __traindataset
    232          # For now it is needed for computing sensitivities
    233          self.__traindataset = None
    234
    235          # internal SG swig proxies
    236          self.__traindata = None
    237          self.__kernel = None
    238          self.__kernel_test = None
    239          self.__testdata = None
    240
    241
    242 -    def __condition_kernel(self, kernel):
    243          # XXX I thought that it is needed only for retrainable classifier,
    244          #     but then krr gets confused, and svrlight needs it to provide
    245          #     meaningful results even without 'retraining'
    246          if self._svm_impl in ['svrlight', 'lightsvm']:
    247              try:
    248                  kernel.set_precompute_matrix(True, True)
    249              except Exception, e:
    250                  # N/A in shogun 0.9.1... TODO: RF
    251                  if __debug__:
    252                      debug('SG_', "Failed call to set_precompute_matrix for %s: %s"
    253                            % (self, e))
    254
    255
    256 -    def _train(self, dataset):
    257          """Train SVM
    258          """
    259          # XXX watchout
    260          # self.untrain()
    261          newkernel, newsvm = False, False
    262          # local bindings for faster lookup
    263          retrainable = self.params.retrainable
    264
    265          if retrainable:
    266              _changedData = self._changedData
    267
    268          # LABELS
    269          ul = None
    270          self.__traindataset = dataset
    271
    272
    273          # OK -- we have to map labels since
    274          #  binary ones expect -1/+1
    275          #  Multiclass expect labels starting with 0, otherwise they puke
    276          #   when ran from ipython... yikes
    277          if __debug__:
    278              debug("SG_", "Creating labels instance")
    279
    280          if 'regression' in self._clf_internals:
    281              labels_ = N.asarray(dataset.labels, dtype='double')
    282          else:
    283              ul = dataset.uniquelabels
    284              ul.sort()
    285
    286              if len(ul) == 2:
    287                  # assure that we have -1/+1
    288                  _labels_dict = {ul[0]:-1.0, ul[1]:+1.0}
    289              elif len(ul) < 2:
    290                  raise FailedToTrainError, \
    291                        "We do not have 1-class SVM brought into SG yet"
    292              else:
    293                  # can't use plain enumerate since we need them swapped
    294                  _labels_dict = dict([ (ul[i], i) for i in range(len(ul))])
    295
    296              # reverse labels dict for back mapping in _predict
    297              _labels_dict_rev = dict([(x[1], x[0])
    298                                       for x in _labels_dict.items()])
    299
    300              # bind to instance as well
    301              self._labels_dict = _labels_dict
    302              self._labels_dict_rev = _labels_dict_rev
    303
    304              # Map labels
    305              #
    306              # TODO: top level classifier should take care about labels
    307              # mapping if that is needed
    308              if __debug__:
    309                  debug("SG__", "Mapping labels using dict %s" % _labels_dict)
    310              labels_ = N.asarray([ _labels_dict[x] for x in dataset.labels ], dtype='double')
    311
    312          labels = shogun.Features.Labels(labels_)
    313          _setdebug(labels, 'Labels')
    314
    315
    316          # KERNEL
    317          if not retrainable or _changedData['traindata'] or _changedData['kernel_params']:
    318              # If needed compute or just collect arguments for SVM and for
    319              # the kernel
    320              kargs = []
    321              for arg in self._KERNELS[self._kernel_type_literal][1]:
    322                  value = self.kernel_params[arg].value
    323                  # XXX Unify damn automagic gamma value
    324                  if arg == 'gamma' and value == 0.0:
    325                      value = self._getDefaultGamma(dataset)
    326                  kargs += [value]
    327
    328              if retrainable and __debug__:
    329                  if _changedData['traindata']:
    330                      debug("SG",
    331                            "Re-Creating kernel since training data has changed")
    332
    333                  if _changedData['kernel_params']:
    334                      debug("SG",
    335                            "Re-Creating kernel since params %s has changed" %
    336                            _changedData['kernel_params'])
    337
    338              # create training data
    339              if __debug__: debug("SG_", "Converting input data for shogun")
    340              self.__traindata = _tosg(dataset.samples)
    341
    342              if __debug__:
    343                  debug("SG", "Creating kernel instance of %s giving arguments %s" %
    344                        (`self._kernel_type`, kargs))
    345
    346              self.__kernel = kernel = \
    347                              self._kernel_type(self.__traindata, self.__traindata,
    348                                                *kargs)
    349
    350              if externals.exists('sg ge 0.6.4'):
    351                   kernel.set_normalizer(shogun.Kernel.IdentityKernelNormalizer())
    352
    353              newkernel = True
    354              self.kernel_params.reset()  # mark them as not-changed
    355              _setdebug(kernel, 'Kernels')
    356
    357              self.__condition_kernel(kernel)
    358              if retrainable:
    359                  if __debug__:
    360                      debug("SG_", "Resetting test kernel for retrainable SVM")
    361                  self.__kernel_test = None
    362                  self.__kernel_args = kargs
    363
    364          # TODO -- handle _changedData['params'] correctly, ie without recreating
    365          # whole SVM
    366          Cs = None
    367          if not retrainable or self.__svm is None or _changedData['params']:
    368              # SVM
    369              if self.params.isKnown('C'):
    370                  C = self.params.C
    371                  if not operator.isSequenceType(C):
    372                      # we were not given a tuple for balancing between classes
    373                      C = [C]
    374
    375                  Cs = list(C[:])               # copy
    376                  for i in xrange(len(Cs)):
    377                      if Cs[i]<0:
    378                          Cs[i] = self._getDefaultC(dataset.samples)*abs(Cs[i])
    379                      if __debug__:
    380                          debug("SG_", "Default C for %s was computed to be %s" %
    381                                (C[i], Cs[i]))
    382
    383                  # XXX do not jump over the head and leave it up to the user
    384                  #     ie do not rescale automagically by the number of samples
    385                  #if len(Cs) == 2 and not ('regression' in self._clf_internals) and len(ul) == 2:
    386                  #    # we were given two Cs
    387                  #    if N.max(C) < 0 and N.min(C) < 0:
    388                  #        # and both are requested to be 'scaled' TODO :
    389                  #        # provide proper 'features' to the parameters,
    390                  #        # so we could specify explicitely if to scale
    391                  #        # them by the number of samples here
    392                  #        nl = [N.sum(labels_ == _labels_dict[l]) for l in ul]
    393                  #        ratio = N.sqrt(float(nl[1]) / nl[0])
    394                  #        #ratio = (float(nl[1]) / nl[0])
    395                  #        Cs[0] *= ratio
    396                  #        Cs[1] /= ratio
    397                  #        if __debug__:
    398                  #            debug("SG_", "Rescaled Cs to %s to accomodate the "
    399                  #                  "difference in number of training samples" %
    400                  #                  Cs)
    401
    402              # Choose appropriate implementation
    403              svm_impl_class = self.__get_implementation(ul)
    404
    405              if __debug__:
    406                  debug("SG", "Creating SVM instance of %s" % `svm_impl_class`)
    407
    408              if self._svm_impl in ['libsvr', 'svrlight']:
    409                  # for regressions constructor a bit different
    410                  self.__svm = svm_impl_class(Cs[0], self.params.epsilon, self.__kernel, labels)
    411              elif self._svm_impl in ['krr']:
    412                  self.__svm = svm_impl_class(self.params.tau, self.__kernel, labels)
    413              else:
    414                  self.__svm = svm_impl_class(Cs[0], self.__kernel, labels)
    415                  self.__svm.set_epsilon(self.params.epsilon)
    416
    417              # To stay compatible with versions across API changes in sg 1.0.0
    418              self.__svm_apply = hasattr(self.__svm, 'apply') \
    419                                 and self.__svm.apply \
    420                                 or  self.__svm.classify # the last one for old API
    421
    422              # Set shrinking
    423              if self.params.isKnown('shrinking'):
    424                  shrinking = self.params.shrinking
    425                  if __debug__:
    426                      debug("SG_", "Setting shrinking to %s" % shrinking)
    427                  self.__svm.set_shrinking_enabled(shrinking)
    428
    429              if Cs is not None and len(Cs) == 2:
    430                  if __debug__:
    431                      debug("SG_", "Since multiple Cs are provided: %s, assign them" % Cs)
    432                  self.__svm.set_C(Cs[0], Cs[1])
    433
    434              self.params.reset()  # mark them as not-changed
    435              newsvm = True
    436              _setdebug(self.__svm, 'SVM')
    437              # Set optimization parameters
    438              if self.params.isKnown('tube_epsilon') and \
    439                     hasattr(self.__svm, 'set_tube_epsilon'):
    440                  self.__svm.set_tube_epsilon(self.params.tube_epsilon)
    441              self.__svm.parallel.set_num_threads(self.params.num_threads)
    442          else:
    443              if __debug__:
    444                  debug("SG_", "SVM instance is not re-created")
    445              if _changedData['labels']:          # labels were changed
    446                  if __debug__: debug("SG__", "Assigning new labels")
    447                  self.__svm.set_labels(labels)
    448              if newkernel:               # kernel was replaced
    449                  if __debug__: debug("SG__", "Assigning new kernel")
    450                  self.__svm.set_kernel(self.__kernel)
    451              assert(_changedData['params'] is False)  # we should never get here
    452
    453          if retrainable:
    454              # we must assign it only if it is retrainable
    455              self.states.retrained = not newsvm or not newkernel
    456
    457          # Train
    458          if __debug__ and 'SG' in debug.active:
    459              if not self.regression:
    460                  lstr = " with labels %s" % dataset.uniquelabels
    461              else:
    462                  lstr = ""
    463              debug("SG", "%sTraining %s on data%s" %
    464                    (("","Re-")[retrainable and self.states.retrained],
    465                     self, lstr))
    466
    467          self.__svm.train()
    468
    469          if __debug__:
    470              debug("SG_", "Done training SG_SVM %s" % self._kernel_type)
    471
    472          # Report on training
    473          if (__debug__ and 'SG__' in debug.active) or \
    474             self.states.isEnabled('training_confusion'):
    475              trained_labels = self.__svm_apply().get_labels()
    476          else:
    477              trained_labels = None
    478
    479          if __debug__ and "SG__" in debug.active:
    480                  debug("SG__", "Original labels: %s, Trained labels: %s" %
    481                                (dataset.labels, trained_labels))
    482
    483          # Assign training confusion right away here since we are ready
    484          # to do so.
    485          # XXX TODO use some other state variable like 'trained_labels' and
    486          #     use it within base Classifier._posttrain to assign predictions
    487          #     instead of duplicating code here
    488          # XXX For now it can be done only for regressions since labels need to
    489          #     be remapped and that becomes even worse if we use regression
    490          #     as a classifier so mapping happens upstairs
    491          if self.regression and self.states.isEnabled('training_confusion'):
    492              self.states.training_confusion = self._summaryClass(
    493                  targets=dataset.labels,
    494                  predictions=trained_labels)
    495
    496 -    def _predict(self, data):
    497          """Predict values for the data
    498          """
    499
    500          retrainable = self.params.retrainable
    501
    502          if retrainable:
    503              changed_testdata = self._changedData['testdata'] or \
    504                                 self.__kernel_test is None
    505
    506          if not retrainable or changed_testdata:
    507              testdata = _tosg(data)
    508
    509          if not retrainable:
    510              if __debug__:
    511                  debug("SG__",
    512                        "Initializing SVMs kernel of %s with training/testing samples"
    513                        % self)
    514              # We can just reuse kernel used for training
    515              self.__kernel.init(self.__traindata, testdata)
    516              self.__condition_kernel(self.__kernel)
    517          else:
    518              if changed_testdata:
    519                  if __debug__:
    520                      debug("SG__",
    521                            "Re-creating testing kernel of %s giving "
    522                            "arguments %s" %
    523                            (`self._kernel_type`, self.__kernel_args))
    524                  kernel_test = self._kernel_type(self.__traindata, testdata,
    525                                                  *self.__kernel_args)
    526                  _setdebug(kernel_test, 'Kernels')
    527
    528                  custk_args = ([self.__traindata, testdata], [])[
    529                      int(externals.exists('sg ge 0.6.4'))]
    530                  if __debug__:
    531                      debug("SG__",
    532                            "Re-creating custom testing kernel giving "
    533                            "arguments %s" % (str(custk_args)))
    534                  kernel_test_custom = shogun.Kernel.CustomKernel(*custk_args)
    535
    536                  _setdebug(kernel_test_custom, 'Kernels')
    537                  self.__kernel_test = kernel_test_custom
    538                  self.__kernel_test.set_full_kernel_matrix_from_full(
    539                      kernel_test.get_kernel_matrix())
    540              elif __debug__:
    541                  debug("SG__", "Re-using testing kernel")
    542
    543              assert(self.__kernel_test is not None)
    544              self.__svm.set_kernel(self.__kernel_test)
    545
    546          if __debug__:
    547              debug("SG_", "Classifying testing data")
    548
    549          # doesn't do any good imho although on unittests helps tiny bit... hm
    550          #self.__svm.init_kernel_optimization()
    551          values_ = self.__svm_apply()
    552          if values_ is None:
    553              raise RuntimeError, "We got empty list of values from %s" % self
    554
    555          values = values_.get_labels()
    556
    557          if retrainable:
    558              # we must assign it only if it is retrainable
    559              self.states.repredicted = repredicted = not changed_testdata
    560              if __debug__:
    561                  debug("SG__", "Re-assigning learing kernel. Repredicted is %s"
    562                        % repredicted)
    563              # return back original kernel
    564              self.__svm.set_kernel(self.__kernel)
    565
    566          if __debug__:
    567              debug("SG__", "Got values %s" % values)
    568
    569          if ('regression' in self._clf_internals):
    570              predictions = values
    571          else:
    572              # local bindings
    573              _labels_dict = self._labels_dict
    574              _labels_dict_rev = self._labels_dict_rev
    575
    576              if len(_labels_dict) == 2:
    577                  predictions = 1.0 - 2*N.signbit(values)
    578              else:
    579                  predictions = values
    580
    581              # assure that we have the same type
    582              label_type = type(_labels_dict.values()[0])
    583
    584              # remap labels back adjusting their type
    585              predictions = [_labels_dict_rev[label_type(x)]
    586                             for x in predictions]
    587
    588              if __debug__:
    589                  debug("SG__", "Tuned predictions %s" % predictions)
    590
    591          # store state variable
    592          # TODO: extract values properly for multiclass SVMs --
    593          #       ie 1 value per label or pairs for all 1-vs-1 classifications
    594          self.values = values
    595
    596          ## to avoid leaks with not yet properly fixed shogun
    597          if not retrainable:
    598              try:
    599                  testdata.free_features()
    600              except:
    601                  pass
    602
    603          return predictions
    604
    605
    606 -    def untrain(self):
    607          super(SVM, self).untrain()
    608          if not self.params.retrainable:
    609              if __debug__:
    610                  debug("SG__", "Untraining %(clf)s and destroying sg's SVM",
    611                        msgargs={'clf':self})
    612
    613              # to avoid leaks with not yet properly fixed shogun
    614              # XXX make it nice... now it is just stable ;-)
    615              if True: # not self.__traindata is None:
    616                  if True:
    617                  # try:
    618                      if self.__kernel is not None:
    619                          del self.__kernel
    620                          self.__kernel = None
    621
    622                      if self.__kernel_test is not None:
    623                          del self.__kernel_test
    624                          self.__kernel_test = None
    625
    626                      if self.__svm is not None:
    627                          del self.__svm
    628                          self.__svm = None
    629                          self.__svm_apply = None
    630
    631                      if self.__traindata is not None:
    632                          # Let in for easy demonstration of the memory leak in shogun
    633                          #for i in xrange(10):
    634                          #    debug("SG__", "cachesize pre free features %s" %
    635                          #          (self.__svm.get_kernel().get_cache_size()))
    636                          self.__traindata.free_features()
    637                          del self.__traindata
    638                          self.__traindata = None
    639
    640                      self.__traindataset = None
    641
    642
    643                  #except:
    644                  #    pass
    645
    646              if __debug__:
    647                  debug("SG__",
    648                        "Done untraining %(self)s and destroying sg's SVM",
    649                        msgargs=locals())
    650          elif __debug__:
    651              debug("SG__", "Not untraining %(self)s since it is retrainable",
    652                    msgargs=locals())
    653
    654
    655 -    def __get_implementation(self, ul):
    656          if 'regression' in self._clf_internals or len(ul) == 2:
    657              svm_impl_class = SVM._KNOWN_IMPLEMENTATIONS[self._svm_impl][0]
    658          else:
    659              if self._svm_impl == 'libsvm':
    660                  svm_impl_class = shogun.Classifier.LibSVMMultiClass
    661              elif self._svm_impl == 'gmnp':
    662                  svm_impl_class = shogun.Classifier.GMNPSVM
    663              else:
    664                  raise RuntimeError, \
    665                        "Shogun: Implementation %s doesn't handle multiclass " \
    666                        "data. Got labels %s. Use some other classifier" % \
    667                        (self._svm_impl, self.__traindataset.uniquelabels)
    668              if __debug__:
    669                  debug("SG_", "Using %s for multiclass data of %s" %
    670                        (svm_impl_class, self._svm_impl))
    671
    672          return svm_impl_class
    673
    674
    675      svm = property(fget=lambda self: self.__svm)
    676      """Access to the SVM model."""
    677
    678      traindataset = property(fget=lambda self: self.__traindataset)
    679      """Dataset which was used for training
    680
    681      TODO -- might better become state variable I guess"""
    682
    683
    684
    685  # Conditionally make some of the implementations available if they are
    686  # present in the present shogun
    687  for name, item, params, descr in \
    688          [('mpd', "shogun.Classifier.MPDSVM", "('C',), ('binary',)",
    689            "MPD classifier from shogun"),
    690           ('lightsvm', "shogun.Classifier.SVMLight", "('C',), ('binary',)",
    691            "SVMLight classification http://svmlight.joachims.org/"),
    692           ('svrlight', "shogun.Regression.SVRLight", "('C','tube_epsilon',), ('regression',)",
    693            "SVMLight regression http://svmlight.joachims.org/"),
    694           ('krr', "shogun.Regression.KRR", "('tau',), ('regression',)",
    695            "Kernel Ridge Regression"),
    696           ]:
    697      if externals.exists('shogun.%s' % name):
    698          exec "SVM._KNOWN_IMPLEMENTATIONS[\"%s\"] = (%s, %s, \"%s\")" % (name, item, params, descr)
    699
    700  # Assign SVM class to limited set of LinearSVMWeights
    701  LinearSVMWeights._LEGAL_CLFS = [SVM]
    702