svm.cpp

Go to the documentation of this file.
00001 
00005 #include <assert.h>
00006 #include <cmath>
00007 #include <svm.h>
00008 #include "svm.h"
00009 
00010 REGISTER_CREATOR(lemga::SVM);
00011 
00012 // In order to access nSV, we have to copy svm_model here.
00013 // Comments removed. Please see svm.cpp for details.
00014 // Any direct use of svm_model is marked with /* direct access svm_model */
00015 struct svm_model {
00016     svm_parameter param;
00017     int nr_class;
00018     int l;
00019     svm_node **SV;
00020     double **sv_coef;
00021     double *rho;
00022     double *probA;
00023     double *probB;
00024     int *label;
00025     int *nSV;
00026     int free_sv;
00027 };
00028 
00029 namespace lemga {
00030 
00031 typedef struct svm_node* p_svm_node;
00032 
00033 #ifndef NDEBUG
00034 static bool param_equal (const struct svm_parameter& a,
00035                          const struct svm_parameter& b) {
00036     struct svm_parameter a2 = a, b2 = b;
00037     a2.C = b2.C = 0;
00038     return !std::memcmp(&a2, &b2, sizeof(struct svm_parameter));
00039 }
00040 #endif
00041 
00042 struct SVM_detail {
00043     struct svm_parameter param;
00044     struct svm_problem prob;
00045     struct svm_model *model;
00046     struct svm_node *x_space;
00047     UINT n_class;
00048     int *labels;
00049     bool trained; // the wrapper was not loaded from file (ONLY FOR DEBUG)
00050 
00051     SVM_detail ();
00052     ~SVM_detail () {
00053         clean_model(); clean_data(); svm_destroy_param(&param); }
00054     bool fill_svm_problem (const pDataSet&, const pDataWgt&);
00055     bool train (const pDataSet&, const pDataWgt&);
00056     void clean_model ();
00057     void clean_data ();
00058 };
00059 
00060 SVM_detail::SVM_detail () : model(0), x_space(0), trained(false) {
00061     // default LIBSVM parameters, copied from svm-train.c
00062     param.svm_type = C_SVC;
00063     param.degree = 3;
00064     param.gamma = 0;
00065     param.coef0 = 0;
00066     param.nu = 0.5;
00067     param.cache_size = 128;
00068     param.eps = 1e-3;
00069     param.p = 0.1;
00070     param.shrinking = 1;
00071     param.probability = 0;
00072     param.nr_weight = 0;
00073     param.weight_label = NULL;
00074     param.weight = NULL;
00075 }
00076 
00077 void SVM_detail::clean_model () {
00078     trained = false;
00079     if (!model) return;
00080     svm_destroy_model(model);
00081     delete[] labels;
00082     model = 0;
00083 }
00084 
00085 void SVM_detail::clean_data () {
00086     if (!x_space) return;
00087     delete[] prob.x; delete[] prob.y;
00088     delete[] prob.W;
00089     delete[] x_space; x_space = 0;
00090 }
00091 
00092 p_svm_node fill_svm_node (const Input& x, struct svm_node *pool) {
00093     for (UINT j = 0; j < x.size(); ++j, ++pool) {
00094         pool->index = j+1;
00095         pool->value = x[j];
00096     }
00097     pool->index = -1;
00098     return ++pool;
00099 }
00100 
00101 bool SVM_detail::fill_svm_problem (const pDataSet& ptd, const pDataWgt& ptw) {
00102     assert(ptd->size() == ptw->size());
00103     const UINT n_samples = ptd->size();
00104     if (n_samples == 0 || ptd->y(0).size() != 1) return false;
00105     const UINT n_in = ptd->x(0).size();
00106 
00107     clean_data();
00108     prob.l = n_samples;
00109     prob.x = new p_svm_node[n_samples];
00110     prob.y = new double[n_samples];
00111     prob.W = new double[n_samples];
00112     x_space = new struct svm_node[n_samples*(n_in+1)];
00113     if (!x_space) return false;
00114 
00115     struct svm_node *psn = x_space;
00116     for (UINT i = 0; i < n_samples; ++i) {
00117         prob.x[i] = psn;
00118         prob.y[i] = ptd->y(i)[0];
00119         psn = fill_svm_node(ptd->x(i), psn);
00120         prob.W[i] = (*ptw)[i] * n_samples;
00121     }
00122     return true;
00123 }
00124 
00125 bool SVM_detail::train (const pDataSet& ptd, const pDataWgt& ptw) {
00126     if (!fill_svm_problem(ptd, ptw)) {
00127         std::cerr << "Error in filling SVM problem (training data)\n";
00128         return false;
00129     }
00130     const char* error_msg = svm_check_parameter(&prob,&param);
00131     if (error_msg) {
00132         std::cerr << "Error: " << error_msg << '\n';
00133         return false;
00134     }
00135 
00136     clean_model();
00137     model = svm_train(&prob, &param);
00138     trained = true;
00139     n_class = svm_get_nr_class(model);
00140     labels = new int[n_class];
00141     svm_get_labels(model, labels);
00142     return true;
00143 }
00144 
00145 namespace kernel {
00146 
00147 void Linear::set_params (SVM_detail* sd) const {
00148     sd->param.kernel_type = ::LINEAR;
00149 }
00150 
00151 void Polynomial::set_params (SVM_detail* sd) const {
00152     sd->param.kernel_type = ::POLY;
00153     sd->param.degree = degree;
00154     sd->param.gamma = gamma;
00155     sd->param.coef0 = coef0;
00156 }
00157 
00158 void RBF::set_params (SVM_detail* sd) const {
00159     sd->param.kernel_type = ::RBF;
00160     sd->param.gamma = gamma;
00161 }
00162 
00163 void Sigmoid::set_params (SVM_detail* sd) const {
00164     sd->param.kernel_type = ::SIGMOID;
00165     sd->param.gamma = gamma;
00166     sd->param.coef0 = coef0;
00167 }
00168 
00169 void Stump::set_params (SVM_detail* sd) const {
00170     sd->param.kernel_type = ::STUMP;
00171 }
00172 
00173 void Perceptron::set_params (SVM_detail* sd) const {
00174     sd->param.kernel_type = ::PERCEPTRON;
00175 }
00176 
00177 } // namespace kernel
00178 
00179 bool SVM::serialize (std::ostream& os, ver_list& vl) const {
00180     SERIALIZE_PARENT(LearnModel, os, vl, 1);
00181 
00182     // we will not save the detail
00183     assert(sv.size() == coef.size());
00184     assert(ker != 0 || sv.size() == 0);
00185     if (!(os << (ker != 0) << ' ' << regC << ' ' << sv.size() << '\n'))
00186         return false;
00187     // the kernel
00188     if (ker != 0)
00189         if (!(os << *ker)) return false;
00190     // support vectors and coefficients
00191     for (UINT i = 0; i < sv.size(); ++i) {
00192         assert(sv[i].size() == _n_in);
00193         for (UINT j = 0; j < _n_in; ++j)
00194             if (!(os << sv[i][j] << ' ')) return false;
00195         if (!(os << coef[i] << '\n')) return false;
00196     }
00197     // the bias
00198     return (os << coef0 << '\n');
00199 }
00200 
00201 bool SVM::unserialize (std::istream& is, ver_list& vl, const id_t& d) {
00202     if (d != id() && d != NIL_ID) return false;
00203     UNSERIALIZE_PARENT(LearnModel, is, vl, 1, v);
00204     assert(v > 0);
00205 
00206     assert(detail != 0);
00207     reset_model();
00208     if (ker != 0) delete ker; ker = 0;
00209 
00210     UINT has_kernel, nsv;
00211     if (!(is >> has_kernel >> regC >> nsv) || has_kernel > 1 || regC < 0)
00212         return false;
00213     if (!has_kernel && nsv > 0) return false;
00214 
00215     if (has_kernel) {
00216         kernel::Kernel* k = (kernel::Kernel*) Object::create(is);
00217         if (k == 0) return false;
00218         set_kernel(*k); // don't directly assign the kernel
00219         delete k;
00220     }
00221     sv.resize(nsv); coef.resize(nsv);
00222     for (UINT i = 0; i < nsv; ++i) {
00223         Input svi(_n_in);
00224         for (UINT j = 0; j < _n_in; ++j)
00225             if (!(is >> svi[j])) return false;
00226         sv[i].swap(svi);
00227         if (!(is >> coef[i])) return false;
00228     }
00229     return (is >> coef0);
00230 }
00231 
00232 SVM::SVM (UINT n_in) : LearnModel(n_in, 1), ker(0), regC(1), coef0(0) {
00233     detail = new struct SVM_detail;
00234 }
00235 
00236 SVM::SVM (const kernel::Kernel& k, UINT n_in)
00237     : LearnModel(n_in, 1), ker(0), regC(1), coef0(0) {
00238     detail = new struct SVM_detail;
00239     set_kernel(k);
00240 }
00241 
00242 SVM::SVM (const SVM& s) : LearnModel(s),
00243     ker(0), regC(s.regC), sv(s.sv), coef(s.coef), coef0(s.coef0) {
00244     detail = new struct SVM_detail;
00245     if (s.ker != 0) set_kernel(*s.ker);
00246     assert(param_equal(detail->param, s.detail->param));
00247 }
00248 
00249 SVM::SVM (std::istream& is) : LearnModel(), ker(0) {
00250     detail = new struct SVM_detail;
00251     is >> *this;
00252 }
00253 
00254 SVM::~SVM () {
00255     delete detail;
00256     if (ker != 0) delete ker;
00257 }
00258 
00259 const SVM& SVM::operator= (const SVM& s) {
00260     if (&s == this) return *this;
00261     assert(ker != s.ker);
00262 
00263     delete detail;
00264     if (ker != 0) delete ker; ker = 0;
00265 
00266     LearnModel::operator=(s);
00267     regC = s.regC;
00268     sv = s.sv; coef = s.coef; coef0 = s.coef0;
00269     detail = new struct SVM_detail;
00270     if (s.ker != 0) set_kernel(*s.ker);
00271     assert(param_equal(detail->param, s.detail->param));
00272     return *this;
00273 }
00274 
00275 REAL SVM::kernel (const Input& x1, const Input& x2) const {
00276     assert(ker != 0);
00277 #ifndef NDEBUG
00278     assert(detail);
00279     struct svm_node sx1[n_input()+1], sx2[n_input()+1];
00280     fill_svm_node(x1, sx1);
00281     fill_svm_node(x2, sx2);
00282     REAL svmk = svm_kernel(sx1, sx2, detail->param);
00283 #endif
00284     REAL k = (*ker)(x1, x2);
00285     assert(std::fabs(svmk - k) < EPSILON);
00286     return k;
00287 }
00288 
00289 void SVM::set_kernel (const kernel::Kernel& k) {
00290     if (&k == ker) return;
00291 
00292     if (ker != 0) {
00293         delete ker;
00294         reset_model();  // since kernel is also used for testing
00295     }
00296     ker = k.clone();
00297     ker->set_params(detail);
00298 }
00299 
00300 void SVM::initialize () {
00301     reset_model();
00302 }
00303 
00304 void SVM::reset_model () {
00305     detail->clean_model(); detail->clean_data();
00306     sv.clear(); coef.clear(); coef0 = 0;
00307 }
00308 
00309 void SVM::train () {
00310     assert(ptd && n_samples == ptd->size());
00311     assert(ker != 0);
00312     set_dimensions(*ptd);
00313 
00314     reset_model();
00315     detail->param.C = regC;
00316     if (!detail->train(ptd, ptw)) exit(-1);
00317     assert(detail && detail->model && detail->x_space);
00318     assert(detail->n_class > 0 && detail->n_class <= 2);
00319 
00320     // copy the trained model to local
00321     /* direct access svm_model */
00322     const UINT nsv = detail->model->l;
00323     sv.resize(nsv); coef.resize(nsv);
00324     bool flip = false; // whether to flip coefficients and bias
00325     if (detail->n_class == 1) {
00326         assert(nsv == 0);
00327         coef0 = detail->labels[0];
00328     } else {
00329         flip = (detail->labels[0] < detail->labels[1]);
00330         REAL rho = detail->model->rho[0];
00331         coef0 = flip? rho : -rho;
00332     }
00333     for (UINT i = 0; i < nsv; ++i) {
00334         svm_node *SVi = detail->model->SV[i];
00335         Input svi(_n_in, 0);
00336         for (; SVi->index != -1; ++SVi) {
00337             assert(SVi->index > 0 && (UINT) SVi->index <= _n_in);
00338             svi[SVi->index-1] = SVi->value;
00339         }
00340         sv[i].swap(svi);
00341         REAL ci = detail->model->sv_coef[0][i];
00342         coef[i] = flip? -ci : ci;
00343     }
00344 
00345 #ifdef NDEBUG
00346     // destroy the original model (but keep the param) to save memory
00347     detail->clean_model(); detail->clean_data();
00348 #endif
00349 }
00350 
00351 REAL SVM::margin_of (const Input& x, const Input& y) const {
00352     assert(std::fabs(std::fabs(y[0]) - 1) < INFINITESIMAL);
00353     return signed_margin(x) * y[0];
00354 }
00355 
00356 REAL SVM::signed_margin (const Input& x) const {
00357     assert(x.size() == n_input());
00358     assert(ker != 0);
00359 
00360     const UINT nsv = n_support_vectors();
00361     REAL sum = bias();
00362     for (UINT i = 0; i < nsv; ++i)
00363         sum += support_vector_coef(i) * (*ker)(support_vector(i), x);
00364 
00365 #ifndef NDEBUG
00366     assert(detail);
00367     if (!detail->trained) return sum;
00368 
00369     assert(detail->model && detail->x_space);
00370     struct svm_node sx[n_input()+1];
00371     fill_svm_node(x, sx);
00372     double m = detail->labels[0];
00373     if (nsv > 0) {
00374         svm_predict_values(detail->model, sx, &m);
00375         if (detail->labels[0] < detail->labels[1]) m = -m;
00376     }
00377     assert(std::fabs(sum - m) < EPSILON);
00378 #endif
00379 
00380     return sum;
00381 }
00382 
00383 REAL SVM::w_norm () const {
00384     assert(ker != 0);
00385     const UINT nsv = n_support_vectors();
00386 
00387     REAL sum = 0;
00388     for (UINT i = 0; i < nsv; ++i) {
00389         for (UINT j = i; j < nsv; ++j) {
00390             REAL ip = (*ker)(support_vector(i), support_vector(j))
00391                 * support_vector_coef(i) * support_vector_coef(j);
00392             sum += ip + (i==j? 0 : ip);
00393 #ifndef NDEBUG
00394             assert(detail);
00395             if (!detail->trained) continue;
00396 
00397             assert(detail->model && detail->x_space);
00398             /* direct access svm_model */
00399             REAL ve = svm_kernel(detail->model->SV[i],
00400                                  detail->model->SV[j], detail->param)
00401                 * detail->model->sv_coef[0][i] * detail->model->sv_coef[0][j];
00402             assert(std::fabs(ip - ve) < EPSILON);
00403 #endif
00404         }
00405     }
00406     assert(sum >= 0);
00407     return std::sqrt(sum);
00408 }
00409 
00410 Output SVM::operator() (const Input& x) const {
00411     assert(x.size() == n_input());
00412 
00413     REAL y = (signed_margin(x) > 0? 1 : -1);
00414 #ifndef NDEBUG
00415     assert(detail);
00416     if (!detail->trained) return Output(1, y);
00417 
00418     assert(detail->model && detail->x_space);
00419     struct svm_node sx[n_input()+1];
00420     fill_svm_node(x, sx);
00421     REAL l = svm_predict(detail->model, sx);
00422     assert(std::fabs(y - l) < INFINITESIMAL);
00423 #endif
00424 
00425     return Output(1, y);
00426 }
00427 
00428 } // namespace lemga

Generated on Wed Nov 8 08:15:22 2006 for LEMGA by  doxygen 1.4.6