1
2
3
4
5
6
7
8
9 """Wrap the libsvm package into a very simple class interface."""
10
11 __docformat__ = 'restructuredtext'
12
13 import numpy as N
14
15 import operator
16
17 from mvpa.misc.param import Parameter
18 from mvpa.base import warning, externals
19 from mvpa.misc.state import StateVariable
20
21 from mvpa.clfs.base import Classifier
22 from mvpa.clfs._svmbase import _SVM
23 from mvpa.measures.base import Sensitivity
24
25 from mvpa.clfs.libsvmc import _svm as svm
26 from sens import *
27
28 if __debug__:
29 from mvpa.base import debug
30
31
32 from mvpa.clfs.libsvmc._svmc import \
33 C_SVC, NU_SVC, ONE_CLASS, EPSILON_SVR, \
34 NU_SVR, LINEAR, POLY, RBF, SIGMOID, \
35 PRECOMPUTED
36
37
39 """Support Vector Machine Classifier.
40
41 This is a simple interface to the libSVM package.
42 """
43
44
45
46 probabilities = StateVariable(enabled=False,
47 doc="Estimates of samples probabilities as provided by LibSVM")
48
49 _KERNELS = { "linear": (svm.svmc.LINEAR, None, LinearSVMWeights),
50 "rbf" : (svm.svmc.RBF, ('gamma',), None),
51 "poly": (svm.svmc.POLY, ('gamma', 'degree', 'coef0'), None),
52 "sigmoid": (svm.svmc.SIGMOID, ('gamma', 'coef0'), None),
53 }
54
55
56
57 _KNOWN_PARAMS = [ 'epsilon', 'probability', 'shrinking',
58 'weight_label', 'weight']
59
60 _KNOWN_KERNEL_PARAMS = [ 'cache_size' ]
61
62 _KNOWN_IMPLEMENTATIONS = {
63 'C_SVC' : (svm.svmc.C_SVC, ('C',),
64 ('binary', 'multiclass'), 'C-SVM classification'),
65 'NU_SVC' : (svm.svmc.NU_SVC, ('nu',),
66 ('binary', 'multiclass'), 'nu-SVM classification'),
67 'ONE_CLASS' : (svm.svmc.ONE_CLASS, (),
68 ('oneclass',), 'one-class-SVM'),
69 'EPSILON_SVR' : (svm.svmc.EPSILON_SVR, ('C', 'tube_epsilon'),
70 ('regression',), 'epsilon-SVM regression'),
71 'NU_SVR' : (svm.svmc.NU_SVR, ('nu', 'tube_epsilon'),
72 ('regression',), 'nu-SVM regression')
73 }
74
75 _clf_internals = _SVM._clf_internals + [ 'libsvm' ]
76
77 - def __init__(self,
78 kernel_type='linear',
79 **kwargs):
80
81
82
83
84 """Interface class to LIBSVM classifiers and regressions.
85
86 Default implementation (C/nu/epsilon SVM) is chosen depending
87 on the given parameters (C/nu/tube_epsilon).
88 """
89
90 svm_impl = kwargs.get('svm_impl', None)
91
92
93 if svm_impl is None:
94 for arg, impl in [ ('tube_epsilon', 'EPSILON_SVR'),
95 ('C', 'C_SVC'),
96 ('nu', 'NU_SVC') ]:
97 if kwargs.has_key(arg):
98 svm_impl = impl
99 if __debug__:
100 debug('SVM', 'No implementation was specified. Since '
101 '%s is given among arguments, assume %s' %
102 (arg, impl))
103 break
104 if svm_impl is None:
105 svm_impl = 'C_SVC'
106 if __debug__:
107 debug('SVM', 'Assign C_SVC "by default"')
108 kwargs['svm_impl'] = svm_impl
109
110
111 _SVM.__init__(self, kernel_type, **kwargs)
112
113 self._svm_type = self._KNOWN_IMPLEMENTATIONS[svm_impl][0]
114
115 if 'nu' in self._KNOWN_PARAMS and 'epsilon' in self._KNOWN_PARAMS:
116
117
118 self.params['epsilon'].setDefault(0.001)
119
120 self.__model = None
121 """Holds the trained SVM."""
122
123
124
126 """Train SVM
127 """
128
129 if dataset.samples.dtype == 'float64':
130 src = dataset.samples
131 else:
132 src = dataset.samples.astype('double')
133
134 svmprob = svm.SVMProblem( dataset.labels.tolist(), src )
135
136
137 TRANSLATEDICT={'epsilon': 'eps',
138 'tube_epsilon': 'p'}
139 args = []
140 for paramname, param in self.params.items.items() \
141 + self.kernel_params.items.items():
142 if paramname in TRANSLATEDICT:
143 argname = TRANSLATEDICT[paramname]
144 elif paramname in svm.SVMParameter.default_parameters:
145 argname = paramname
146 else:
147 if __debug__:
148 debug("SVM_", "Skipping parameter %s since it is not known"
149 "to libsvm" % paramname)
150 continue
151 args.append( (argname, param.value) )
152
153
154
155 libsvm_param = svm.SVMParameter(
156 kernel_type=self._kernel_type,
157 svm_type=self._svm_type,
158 **dict(args))
159 """Store SVM parameters in libSVM compatible format."""
160
161 if self.params.isKnown('C'):
162 C = self.params.C
163 if not operator.isSequenceType(C):
164
165 C = [C]
166
167 Cs = list(C[:])
168 for i in xrange(len(Cs)):
169 if Cs[i]<0:
170 Cs[i] = self._getDefaultC(dataset.samples)*abs(Cs[i])
171 if __debug__:
172 debug("SVM", "Default C for %s was computed to be %s" %
173 (C[i], Cs[i]))
174
175 libsvm_param._setParameter('C', Cs[0])
176
177 if len(Cs)>1:
178 C0 = abs(C[0])
179 scale = 1.0/(C0)
180
181 if len(Cs) != len(dataset.uniquelabels):
182 raise ValueError, "SVM was parametrized with %d Cs but " \
183 "there are %d labels in the dataset" % \
184 (len(Cs), len(dataset.uniquelabels))
185 weight = [ c*scale for c in Cs ]
186 libsvm_param._setParameter('weight', weight)
187
188 self.__model = svm.SVMModel(svmprob, libsvm_param)
189
190
237
238
240 """Provide quick summary over the SVM classifier"""
241 s = super(SVM, self).summary()
242 if self.trained:
243 s += '\n # of SVs: %d' % self.__model.getTotalNSV()
244 try:
245 prm = svm.svmc.svm_model_param_get(self.__model.model)
246 C = svm.svmc.svm_parameter_C_get(prm)
247
248
249 inside_margin = N.sum(
250
251 N.abs(self.__model.getSVCoef()) >= 0.99*svm.svmc.svm_parameter_C_get(prm))
252 s += ' #bounded SVs:%d' % inside_margin
253 s += ' used C:%5g' % C
254 except:
255 pass
256 return s
257
258
260 if __debug__:
261 debug("SVM", "Untraining %s and destroying libsvm model" % self)
262 super(SVM, self).untrain()
263 del self.__model
264 self.__model = None
265
266 model = property(fget=lambda self: self.__model)
267 """Access to the SVM model."""
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340 try:
341
342 if __debug__ and "LIBSVM" in debug.active:
343 debug("LIBSVM", "Setting verbosity for libsvm to 255")
344 svm.svmc.svm_set_verbosity(255)
345 else:
346 svm.svmc.svm_set_verbosity(0)
347 except AttributeError:
348 warning("Available LIBSVM has no way to control verbosity of the output")
349
350
352 """`SensitivityAnalyzer` for the LIBSVM implementation of a linear SVM.
353 """
354
355 biases = StateVariable(enabled=True,
356 doc="Offsets of separating hyperplanes")
357
358
359 _LEGAL_CLFS = [ SVM ]
360
361
363 """Initialize the analyzer with the classifier it shall use.
364
365 :Parameters:
366 clf: LinearSVM
367 classifier to use. Only classifiers sub-classed from
368 `LinearSVM` may be used.
369 """
370
371 Sensitivity.__init__(self, clf, **kwargs)
372
373
374 - def _call(self, dataset, callables=[]):
375 if self.clf.model.nr_class != 2:
376 warning("You are estimating sensitivity for SVM %s trained on %d" %
377 (str(self.clf), self.clf.model.nr_class) +
378 " classes. Make sure that it is what you intended to do" )
379
380 svcoef = N.matrix(self.clf.model.getSVCoef())
381 svs = N.matrix(self.clf.model.getSV())
382 rhos = N.asarray(self.clf.model.getRho())
383
384 self.biases = rhos
385
386
387
388
389
390
391
392
393
394 weights = svcoef * svs
395
396 if __debug__:
397 debug('SVM',
398 "Extracting weights for %d-class SVM: #SVs=%s, " % \
399 (self.clf.model.nr_class, str(self.clf.model.getNSV())) + \
400 " SVcoefshape=%s SVs.shape=%s Rhos=%s." % \
401 (svcoef.shape, svs.shape, rhos) + \
402 " Result: min=%f max=%f" % (N.min(weights), N.max(weights)))
403
404 return N.asarray(weights.T)
405
406
407 _customizeDocInherit = True
408