Changeset 10954:444fb02c6ccb in orange
 Timestamp:
 07/19/12 12:00:55 (21 months ago)
 Branch:
 default
 File:

 1 edited
Legend:
 Unmodified
 Added
 Removed

Orange/classification/svm/__init__.py
r10774 r10954 44 44 45 45 maxNu = max_nu 46 47 48 def is_discrete(feature): 49 return isinstance(feature, Orange.feature.Discrete) 50 51 52 def is_continuous(feature): 53 return isinstance(feature, Orange.feature.Continuous) 54 46 55 47 56 class SVMLearner(_SVMLearner): … … 253 262 return data.translate(newdomain) 254 263 264 255 265 SVMLearner = Orange.utils.deprecated_members({ 256 266 "learnClassifier": "learn_classifier", 257 267 "tuneParameters": "tune_parameters", 258 "kernelFunc" 268 "kernelFunc": "kernel_func", 259 269 }, 260 270 wrap_methods=["__init__", "tune_parameters"])(SVMLearner) 271 261 272 262 273 class SVMClassifier(_SVMClassifier): 263 274 def __new__(cls, *args, **kwargs): 264 275 if args and isinstance(args[0], _SVMClassifier): 265 # Will wrap a C++ object 276 # Will wrap a C++ object 266 277 return _SVMClassifier.__new__(cls, name=args[0].name) 267 278 elif args and isinstance(args[0], variable.Descriptor): 268 279 # The constructor call for the C++ object. 269 # This is a hack to support loading of old pickled classifiers 280 # This is a hack to support loading of old pickled classifiers 270 281 return _SVMClassifier.__new__(_SVMClassifier, *args, **kwargs) 271 282 else: … … 281 292 self.kernel_type = wrapped.kernel_type 282 293 self.__wrapped = wrapped 283 294 284 295 assert(type(wrapped) in [_SVMClassifier, _SVMClassifierSparse]) 285 296 286 297 if self.svm_type in [SVMLearner.C_SVC, SVMLearner.Nu_SVC] \ 287 298 and len(wrapped.support_vectors) > 0: … … 291 302 support_vectors = [] 292 303 for n in wrapped.n_SV: 293 support_vectors.append(wrapped.support_vectors[start: start + n]) 304 support_vectors.append( 305 wrapped.support_vectors[start: start + n] 306 ) 294 307 start += n 295 support_vectors = [support_vectors[i] for i in label_map] 296 self.support_vectors = Orange.data.Table(reduce(add, support_vectors)) 308 support_vectors = [support_vectors[i] for i in label_map \ 309 if i is not None] 310 support_vectors = reduce(add, support_vectors) 311 self.support_vectors = Orange.data.Table(support_vectors) 297 312 else: 298 313 self.support_vectors = wrapped.support_vectors 299 314 300 315 @property 301 316 def coef(self): 302 317 """Coefficients of the underlying svm model. 303 318 304 319 If this is a classification model then this is a list of 305 320 coefficients for each binary 1vs1 classifiers, i.e. 306 321 #Classes * (#Classses  1) list of lists where 307 322 each sublist contains tuples of (coef, support_vector_index) 308 323 309 324 For regression models it is still a list of lists (for consistency) 310 but of length 1 e.g. [[(coef, support_vector_index), ... ]] 311 312 """ 313 if is instance(self.class_var, variable.Discrete):325 but of length 1 e.g. [[(coef, support_vector_index), ... ]] 326 327 """ 328 if is_discrete(self.class_var): 314 329 # We need to reorder the coef values 315 330 # see http://www.csie.ntu.edu.tw/~cjlin/libsvm/faq.html#f804 … … 319 334 c_map = self._get_libsvm_bin_classifier_map() 320 335 label_map = self._get_libsvm_labels_map() 321 libsvm_coef = self.__wrapped.coef 322 coef = [] #[None] * len(c_map) 336 coef = [] 323 337 n_class = len(label_map) 324 338 n_SV = self.__wrapped.n_SV … … 331 345 ni = label_map[i] 332 346 nj = label_map[j] 347 348 if ni is None or nj is None: 349 # One of the classes is missing from the model. 350 continue 351 333 352 bc_index, mult = c_map[p] 334 353 335 354 if ni > nj: 355 # The order in libsvm model is switched. 336 356 ni, nj = nj, ni 337 357 338 358 # Original class indices 339 359 c1_range = range(libsvm_class_indices[ni], 340 360 libsvm_class_indices[ni + 1]) 341 c2_range = range(libsvm_class_indices[nj], 361 c2_range = range(libsvm_class_indices[nj], 342 362 libsvm_class_indices[nj + 1]) 343 363 344 364 coef1 = mult * coef_array[nj  1, c1_range] 345 365 coef2 = mult * coef_array[ni, c2_range] 346 366 347 367 # Mapped class indices 348 368 c1_range = range(class_indices[i], 349 369 class_indices[i + 1]) 350 c2_range = range(class_indices[j], 370 c2_range = range(class_indices[j], 351 371 class_indices[j + 1]) 352 372 if mult == 1.0: 353 373 c1_range, c2_range = c2_range, c1_range 354 374 355 375 nonzero1 = np.abs(coef1) > 0.0 356 376 nonzero2 = np.abs(coef2) > 0.0 357 377 358 378 coef1 = coef1[nonzero1] 359 379 coef2 = coef2[nonzero2] 360 361 c1_range = [sv_i for sv_i, nz in zip(c1_range, nonzero1) if nz] 362 c2_range = [sv_i for sv_i, nz in zip(c2_range, nonzero2) if nz] 363 364 coef.append(list(zip(coef1, c1_range)) + list(zip(coef2, c2_range))) 365 380 381 c1_range = [sv_i for sv_i, nz in zip(c1_range, nonzero1) 382 if nz] 383 c2_range = [sv_i for sv_i, nz in zip(c2_range, nonzero2) 384 if nz] 385 386 coef.append(list(zip(coef1, c1_range)) + \ 387 list(zip(coef2, c2_range))) 388 366 389 p += 1 367 390 else: 368 coef = [zip(self.__wrapped.coef[0], range(len(self.support_vectors)))] 369 391 coef = [zip(self.__wrapped.coef[0], 392 range(len(self.support_vectors)))] 393 370 394 return coef 371 395 372 396 @property 373 397 def rho(self): 374 398 """Constant (bias) terms of the svm model. 375 376 For classification models this is a list of bias terms 399 400 For classification models this is a list of bias terms 377 401 for each binary 1vs1 classifier. 378 402 379 403 For regression models it is a list with a single value. 380 404 381 405 """ 382 406 rho = self.__wrapped.rho 383 if is instance(self.class_var, variable.Discrete):407 if is_discrete(self.class_var): 384 408 c_map = self._get_libsvm_bin_classifier_map() 385 409 return [rho[i] * m for i, m in c_map] 386 410 else: 387 411 return list(rho) 388 412 389 413 @property 390 414 def n_SV(self): 391 415 """Number of support vectors for each class. 392 416 For regression models this is `None`. 393 394 """ 395 if self.__wrapped.n_SV is not None:396 c_map = self._get_libsvm_labels_map()397 n_SV= self.__wrapped.n_SV398 return [n_SV[i] for i in c_map]417 418 """ 419 n_SV = self.__wrapped.n_SV 420 if n_SV is not None: 421 labels_map = self._get_libsvm_labels_map() 422 return [n_SV[i] if i is not None else 0 for i in labels_map] 399 423 else: 400 424 return None 401 425 402 426 # Pairwise probability is expresed as: 403 # 1.0 / (1.0 + exp(dec_val[i] * prob_a[i] + prob_b[i])) 404 # Since dec_val already changes signs if we switch the 427 # 1.0 / (1.0 + exp(dec_val[i] * prob_a[i] + prob_b[i])) 428 # Since dec_val already changes signs if we switch the 405 429 # classifier direction only prob_b must change signs 406 430 @property … … 416 440 else: 417 441 return None 418 442 419 443 @property 420 444 def prob_b(self): … … 426 450 else: 427 451 return None 428 452 429 453 def __call__(self, instance, what=Orange.core.GetValue): 430 454 """Classify a new ``instance`` … … 442 466 """Return the decision values of the binary 1vs1 443 467 classifiers for the ``instance`` (:class:`~Orange.data.Instance`). 444 468 445 469 """ 446 470 instance = Orange.data.Instance(self.domain, instance) … … 453 477 else: 454 478 return list(dec_values) 455 479 456 480 def get_model(self): 457 481 """Return a string representing the model in the libsvm model format. 458 482 """ 459 483 return self.__wrapped.get_model() 460 484 461 485 def _get_libsvm_labels_map(self): 462 """Get the internal libsvm label mapping. 463 """ 464 labels = [line for line in self.__wrapped.get_model().splitlines() \ 486 """Get the mapping from indices in `class_var.values` to 487 internal libsvm labels. If a class value is missing from the libsvm 488 model the returned corresponding entry is `None`) 489 490 """ 491 if is_discrete(self.class_var): 492 n_classes = len(self.class_var.values) 493 else: 494 # OneClass/Regression models 495 n_classes = 1 496 model_string = self.__wrapped.get_model() 497 # Get the labels definition line from the model string 498 # (the labels, if present, are always integer strings 499 # indexing self.class_var.values) 500 labels = [line for line in model_string.splitlines() \ 465 501 if line.startswith("label")] 466 502 labels = labels[0].split(" ")[1:] if labels else ["0"] 467 503 labels = [int(label) for label in labels] 468 return [labels.index(i) for i in range(len(labels))] 504 labels_map = dict((cls_index, i) for i, cls_index in enumerate(labels)) 505 return [labels_map.get(i) for i in range(n_classes)] 469 506 470 507 def _get_libsvm_bin_classifier_map(self): 471 508 """Return the libsvm binary classifier mapping (due to label ordering). 472 509 """ 473 if not is instance(self.class_var, variable.Discrete):510 if not is_discrete(self.class_var): 474 511 raise TypeError("SVM classification model expected") 512 475 513 label_map = self._get_libsvm_labels_map() 476 514 bin_c_map = [] 477 n_class = len(self.class_var.values)478 p = 0479 for i in range(n_class  1):480 for j in range(i + 1, n_class ):515 n_class_values = len(self.class_var.values) 516 nr_class = len([i for i in label_map if i is not None]) 517 for i in range(n_class_values  1): 518 for j in range(i + 1, n_class_values): 481 519 ni = label_map[i] 482 520 nj = label_map[j] 483 521 mult = 1 522 523 if ni is None or nj is None: 524 # One or both classes are missing from the libsvm model. 525 continue 526 484 527 if ni > nj: 528 # The order in libsvm is switched 485 529 ni, nj = nj, ni 486 530 mult = 1 531 487 532 # classifier index 488 cls_index = n_class * (n_class  1) / 2  (n_class  ni  1) * (n_class  ni  2) / 2  (n_class  nj) 533 cls_index = nr_class * (nr_class  1) / 2  \ 534 (nr_class  ni  1) * (nr_class  ni  2) / 2  \ 535 (nr_class  nj) 489 536 bin_c_map.append((cls_index, mult)) 490 537 return bin_c_map 491 538 492 539 def __reduce__(self): 493 540 return SVMClassifier, (self.__wrapped,), dict(self.__dict__)
Note: See TracChangeset
for help on using the changeset viewer.