Mercurial > pylearn
view pylearn/external/wrap_libsvm.py @ 1205:5525cf3faaa2
requirements: Question about the serialization requirement
author | Olivier Delalleau <delallea@iro> |
---|---|
date | Tue, 21 Sep 2010 10:48:01 -0400 |
parents | 80123baa4544 |
children |
line wrap: on
line source
"""Run an experiment using libsvm. """ import numpy from ..datasets import make_dataset # libsvm currently has no python installation instructions/convention. # # This module uses a specific convention for libsvm's installation. # I base this on installing libsvm-2.88. # To install libsvm's python module, do the following: # 1. Build libsvm (run make in both the root dir and the python subdir). # 2. touch a '__init__.py' file in the python subdir # 3. add a symbolic link to a PYTHONPATH location that looks like this: # libsvm -> <your root path>/libsvm-2.88/python/ # 4. modify the svm_model class in python/svm.py to inherit from object # # That is the sort of thing that this module expects from 'import libsvm' import libsvm class svm_model(libsvm.svm_model): """ This class is a picklable drop-in replacement for libsvm.svm_model. """ def __getstate__(self): return PicklableSVM.svm_to_str(self) def __setstate__(self, svm_str): PicklableSVM.str_to_svm(svm_str, self=self) @staticmethod def str_to_svm(s, self=None): fname = tempfile.mktemp() f = open(fname,'w') f.write(s) f.close() rval = self try: if self: self.__init__(fname) else: rval = libsvm.svm_model(fname) finally: os.remove(fname) return rval @staticmethod def svm_to_str(svm): fname = tempfile.mktemp() svm.save(fname) rval = open(fname, 'r').read() os.remove(fname) return rval def predict(self, x): if type(x) != numpy.ndarray: raise TypeError(x) if x.ndim != 1: raise TypeError(x) return libsvm.svm_model.predict(self, numpy.asarray(x, dtype='float64')) def predict_probability(self, x): if x.ndim != 1: raise TypeError(x) return libsvm.svm_model.predict_probability(self, numpy.asarray(x, dtype='float64')) svm_problem = libsvm.svm_problem svm_parameter = libsvm.svm_parameter RBF = libsvm.RBF #################################### # Extra stuff that is less essential # # TODO: Move stuff below to a file # in algorithms #################################### def score_01(x, y, model): assert len(x) == len(y) size = len(x) errors = 0 for i in range(size): prediction = model.predict(x[i]) #probability = model.predict_probability if (y[i] != prediction): errors = errors + 1 return float(errors)/size #this is the dbdict experiment interface... if you happen to use dbdict class State(object): #TODO: parametrize to get all the kernel types, not hardcode for RBF dataset = 'MNIST_1k' C = 10.0 kernel = 'RBF' # rel_gamma is related to the procedure Jerome used. He mentioned why in # quadratic_neurons/neuropaper/draft3.pdf. rel_gamma = 1.0 def __init__(self, **kwargs): for k, v in kwargs: setattr(self, k, type(getattr(self, k))(v)) def state_run_svm_experiment(state, channel=lambda *args, **kwargs:None): """Parameters are described in state, and returned in state. :param state: object instance to store parameters and return values :param channel: not used :returns: None This is the kind of function that dbdict-run can use. """ dataset = make_dataset(**state.dataset) #libsvm needs stuff in int32 on a 32bit machine #TODO: test this on a 64bit machine # -> Both int32 and int64 (default) seem to be OK train_y = numpy.asarray(dataset.train.y, dtype='int32') valid_y = numpy.asarray(dataset.valid.y, dtype='int32') test_y = numpy.asarray(dataset.test.y, dtype='int32') problem = libsvm.svm_problem(train_y, dataset.train.x); gamma0 = 0.5 / numpy.sum(numpy.var(dataset.train.x, axis=0)) param = libsvm.svm_parameter(C=state['C'], kernel_type=getattr(libsvm, state['kernel']), gamma=state['rel_gamma'] * gamma0) model = libsvm.svm_model(problem, param) #this is the expensive part state['train_01'] = score_01(dataset.train.x, train_y, model) state['valid_01'] = score_01(dataset.valid.x, valid_y, model) state['test_01'] = score_01(dataset.test.x, test_y, model) state['n_train'] = len(train_y) state['n_valid'] = len(valid_y) state['n_test'] = len(test_y) def run_svm_experiment(**kwargs): """Python-friendly interface to dbdict_run_svm_experiment Parameters are used to construct a `State` instance, which is returned after running `dbdict_run_svm_experiment` on it. .. code-block:: python results = run_svm_experiment(dataset='MNIST_1k', C=100.0, rel_gamma=0.01) print results.n_train # 1000 print results.valid_01, results.test_01 # 0.14, 0.10 #.. or something... """ state_run_svm_experiment(state=kwargs) return kwargs def train_rbf_model(train_X, train_Y, C, gamma): param = libsvm.svm_parameter(C=C, kernel_type=libsvm.RBF, gamma=gamma) problem = libsvm.svm_problem(train_Y, train_X) model = svm_model(problem, param) #save_filename = state.save_filename #model.save(save_filename) def jobman_train_model(state, channel): """ According to the given validation set, What is the best libsvm parameter setting to train on? """ (train_X, train_Y) = jobman.tools.make(state.train_set) (valid_X, valid_Y) = jobman.tools.make(state.valid_set) C_grid = [1,2,3] gamma_grid = [0.1, 1, 10] grid = [dict( train_set=None, svm_param=dict(kernel='RBF', C=C, gamma=g), save_filename='model_RBF_C%f_G%f.libsvm') for C in C_grid for g in gamma_grid] # will return quickly if jobs have already run # and the rootpath is populated with results grid = jobman.map( jobman_train_model_given_all_params, grid, path=jobman.rootpath(state)+'/gridmap', cleanup=False) # evaluate all these sub_state models on our validation_set valid_perf = [] for sub_state in grid: # create a file in this state-space called model.tmp # with the same contents as the # save_filename file in the sub_state jobman.link('model.tmp', jobman.rootpath(sub_state)+'/'+sub_state.save_filename) model = svm.model('model.tmp') valid_perf.append((score_01(valid_X, valid_Y, model), sub_state)) jobman.unlink('model.tmp') # calculate the return value valid_perf.sort() #lowest first state.lowest_valid_err = valid_perf[0][0] state.lowest_valid_svm_param = valid_perf[0][1].svm_param