00001
00005 #include <assert.h>
00006 #include <cmath>
00007 #include <svm.h>
00008 #include "svm.h"
00009
00010 REGISTER_CREATOR(lemga::SVM);
00011
00012
00013
00014
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;
00050
00051 SVM_detail ();
00052 ~SVM_detail () {
00053 clean_model(); clean_data(); svm_destroy_param(¶m); }
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
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,¶m);
00131 if (error_msg) {
00132 std::cerr << "Error: " << error_msg << '\n';
00133 return false;
00134 }
00135
00136 clean_model();
00137 model = svm_train(&prob, ¶m);
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 }
00178
00179 bool SVM::serialize (std::ostream& os, ver_list& vl) const {
00180 SERIALIZE_PARENT(LearnModel, os, vl, 1);
00181
00182
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
00188 if (ker != 0)
00189 if (!(os << *ker)) return false;
00190
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
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);
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();
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
00321
00322 const UINT nsv = detail->model->l;
00323 sv.resize(nsv); coef.resize(nsv);
00324 bool flip = false;
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
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
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 }