Shark Machine Learning Library
  • About Shark
  • Sourceforge
    • Project Summary
    • Downloads
    • Subversion Repository
  • Getting Started
  • Tutorials
  • FAQ
  • Main Modules
    • ReClaM
    • EALib
    • MOO-EALib
    • Fuzzy
  • Tools
    • Mixture
    • Array
    • Rng
    • LinAlg
    • FileUtil
  • Main Page
  • Related Pages
  • Classes

Svm.cpp

Go to the documentation of this file.
00001 //===========================================================================
00038 //===========================================================================
00039 
00040 #define _SCL_SECURE_NO_WARNINGS
00041 
00042 #include <math.h>
00043 #include <fstream>
00044 #include <iostream>
00045 #include <LinAlg/LinAlg.h>
00046 #include <LinAlg/VecMat.h>
00047 #include <ReClaM/Svm.h>
00048 #include <ReClaM/QuadraticProgram.h>
00049 #include <ReClaM/GaussianProcess.h>
00050 
00051 
00052 using namespace std;
00053 
00054 
00056 
00057 
00058 SVM::SVM(KernelFunction* pKernel, bool bSignOutput)
00059 {
00060     kernel = pKernel;
00061     signOutput = bSignOutput;
00062 
00063     x = NULL;
00064     bOwnMemory = false;
00065 
00066     examples = 0;
00067     inputDimension = 0;
00068     outputDimension = 1;
00069 
00070     parameter.resize(1, false);
00071     parameter = 0.0;
00072 }
00073 
00074 SVM::SVM(KernelFunction* pKernel, const Array<double>& input, bool bSignOutput)
00075 {
00076     kernel = pKernel;
00077     signOutput = bSignOutput;
00078 
00079     x = NULL;
00080     bOwnMemory = false;
00081 
00082     examples = 0;
00083     inputDimension = 0;
00084     outputDimension = 1;
00085 
00086     SetTrainingData(input);
00087 }
00088 
00089 SVM::~SVM()
00090 {
00091     if (bOwnMemory) delete x;
00092 }
00093 
00094 
00095 void SVM::SetTrainingData(const Array<double>& input, bool copy)
00096 {
00097     if (bOwnMemory) delete x;
00098 
00099     if (copy)
00100     {
00101         x = new Array<double> (input);
00102         bOwnMemory = true;
00103     }
00104     else
00105     {
00106         x = &input;
00107         bOwnMemory = false;
00108     }
00109 
00110     examples = input.dim(0);
00111     inputDimension = input.dim(1);
00112 
00113     parameter.resize(examples + 1, false);
00114     parameter = 0.0;
00115 }
00116 
00117 void SVM::model(const Array<double>& input, Array<double>& output)
00118 {
00119     unsigned int i;
00120     double a;
00121     double alpha;
00122     if (input.ndim() == 1)
00123     {
00124         output.resize(1, false);
00125         a = parameter(examples);
00126         for (i = 0; i < examples; i++)
00127         {
00128             alpha = parameter(i);
00129             if (alpha != 0.0)
00130             {
00131                 a += alpha * kernel->eval((*x)[i], input);
00132             }
00133         }
00134         if (signOutput) output(0) = (a > 0.0) ? 1.0 : -1.0;
00135         else output(0) = a;
00136     }
00137     else if (input.ndim() == 2)
00138     {
00139         int j, jc = input.dim(0);
00140         output.resize(jc, 1, false);
00141         for (j = 0; j < jc; j++)
00142         {
00143             a = parameter(examples);
00144             const ArrayReference<double> ii = input[j];
00145             for (i = 0; i < examples; i++)
00146             {
00147                 alpha = parameter(i);
00148                 if (alpha != 0.0)
00149                 {
00150                     a += alpha * kernel->eval((*x)[i], ii);
00151                 }
00152             }
00153             if (signOutput) output(j, 0) = (a > 0.0) ? 1.0 : -1.0;
00154             else output(j, 0) = a;
00155         }
00156     }
00157     else throw SHARKEXCEPTION("[SVM::model] invalid dimension");
00158 }
00159 
00160 double SVM::model(const Array<double>& input)
00161 {
00162     unsigned int i;
00163     double a;
00164     double alpha;
00165     if (input.ndim() == 1)
00166     {
00167         a = parameter(examples);
00168         for (i = 0; i < examples; i++)
00169         {
00170             alpha = parameter(i);
00171             if (alpha != 0.0)
00172             {
00173                 a += alpha * kernel->eval((*x)[i], input);
00174             }
00175         }
00176     }
00177     else throw SHARKEXCEPTION("[SVM::model] invalid dimension");
00178     if (signOutput) return(a > 0.0) ? 1.0 : -1.0;
00179     else return a;
00180 }
00181 
00182 void SVM::modelDerivative(const Array<double>& input, Array<double>& derivative)
00183 {
00184     unsigned int i;
00185     double v;
00186     if (input.ndim() == 1)
00187     {
00188         derivative.resize(1, examples + 1, false);
00189         derivative = 0.0;
00190         for (i = 0; i < examples; i++)
00191         {
00192             v = kernel->eval((*x)[i], input);
00193             derivative(0, i) = v;
00194         }
00195         derivative(0, examples) = 1.0;
00196     }
00197     else throw SHARKEXCEPTION("[SVM::modelDerivative] invalid dimension");
00198 }
00199 
00200 void SVM::modelDerivative(const Array<double>& input, Array<double>& output, Array<double>& derivative)
00201 {
00202     unsigned int i;
00203     double a, v;
00204     if (input.ndim() == 1)
00205     {
00206         derivative.resize(1, examples + 1, false);
00207         derivative = 0.0;
00208         output.resize(1, false);
00209         a = parameter(examples);
00210         for (i = 0; i < examples; i++)
00211         {
00212             v = kernel->eval((*x)[i], input);
00213             a += parameter(i) * v;
00214             derivative(0, i) = v;
00215         }
00216         output(0) = a;
00217         derivative(0, examples) = 1.0;
00218     }
00219     else throw SHARKEXCEPTION("[SVM::modelDerivative] invalid dimension");
00220 }
00221 
00222 bool SVM::LoadSVMModel(std::istream& is)
00223 {
00224     char buffer[50];
00225     unsigned int t, d;
00226 
00227     // read the header line
00228     is.read(buffer, 21);
00229 
00230     buffer[21] = 0;
00231     if (strcmp(buffer, "Shark SVM model\r\nSV: ") != 0) return false;
00232     if (! is.good()) return false;
00233 
00234     // read the number of support vectors
00235     is >> examples;
00236 
00237     is.read(buffer, 7); buffer[7] = 0;
00238     if (strcmp(buffer, "\r\nDIM: ") != 0) return false;
00239     if (! is.good()) return false;
00240 
00241     // read the input space dimension
00242     is >> inputDimension;
00243 
00244     is.read(buffer, 10); buffer[10] = 0;
00245     if (strcmp(buffer, "\r\nkernel: ") != 0) return false;
00246     if (! is.good()) return false;
00247 
00248     // read the kernel parameters
00249     is >> (*kernel);
00250 
00251     is.read(buffer, 14); buffer[14] = 0;
00252     if (strcmp(buffer, "coefficients: ") != 0) return false;
00253     if (! is.good()) return false;
00254 
00255     // read alpha and b
00256     parameter.resize(examples + 1, false);
00257     for (t = 0; t < examples; t++)
00258     {
00259         is >> parameter(t);
00260         is.read(buffer, 1);
00261         if (buffer[0] != ' ') return false;
00262     }
00263 
00264     is >> parameter(examples);
00265 
00266     is.read(buffer, 2); buffer[2] = 0;
00267     if (strcmp(buffer, "\r\n") != 0) return false;
00268     if (! is.good()) return false;
00269 
00270     // read the support vectors
00271     Array<double>* sv = new Array<double> (examples, inputDimension);
00272     for (t = 0; t < examples; t++)
00273     {
00274         for (d = 0; d < inputDimension; d++)
00275         {
00276             is >> (*sv)(t, d);
00277             if (d < inputDimension - 1)
00278             {
00279                 is.read(buffer, 1);
00280                 if (buffer[0] != ' ') return false;
00281             }
00282         }
00283 
00284         is.read(buffer, 2); buffer[2] = 0;
00285         if (strcmp(buffer, "\r\n") != 0) return false;
00286 
00287         if (! is.good()) return false;
00288     }
00289 
00290     bOwnMemory = true;
00291     x = sv;
00292 
00293     return true;
00294 }
00295 
00296 bool SVM::SaveSVMModel(std::ostream& os)
00297 {
00298     unsigned int t, d;
00299     unsigned int T = 0;
00300     for (t = 0; t < examples; t++) if (parameter(t) != 0.0) T++;
00301 
00302     // write the header line
00303     os.write("Shark SVM model\r\nSV: ", 21);
00304 
00305     // write the number of support vectors
00306     os << T;
00307 
00308     os.write("\r\nDIM: ", 7);
00309     if (! os.good()) return false;
00310 
00311     // write the input space dimension
00312     os << inputDimension;
00313 
00314     os.write("\r\nkernel: ", 10);
00315     if (! os.good()) return false;
00316 
00317     // write the kernel parameters
00318     os << (*kernel);
00319 
00320     os.write("coefficients: ", 14);
00321     if (! os.good()) return false;
00322 
00323     // write alpha and b
00324     for (t = 0; t < examples; t++) if (parameter(t) != 0.0) os << parameter(t) << " ";
00325     os << parameter(examples) << "\r\n";
00326     if (! os.good()) return false;
00327 
00328     // write the support vectors
00329     for (t = 0; t < examples; t++)
00330     {
00331         if (parameter(t) != 0.0)
00332         {
00333             for (d = 0; d < inputDimension; d++)
00334             {
00335                 os << (*x)(t, d);
00336                 if (d < inputDimension - 1) os << " ";
00337             }
00338             os.write("\r\n", 2);
00339             if (! os.good()) return false;
00340         }
00341     }
00342 
00343     return true;
00344 }
00345 
00346 // static
00347 SVM* SVM::ImportLibsvmModel(std::istream& is)
00348 {
00349     char buffer[256];
00350     int status;
00351     int i;
00352 
00353     LinearKernel* linear = NULL;
00354     PolynomialKernel* poly = NULL;
00355     RBFKernel* rbf = NULL;
00356     KernelFunction* pKernel = NULL;
00357     double b = 0.0;
00358     int examples = -1;
00359     int dimension = -1;
00360     while (true)
00361     {
00362         status = ReadToken(is, buffer, sizeof(buffer), "\n");
00363         if (status > 1000) return NULL;
00364 
00365         if (memcmp(buffer, "svm_type ", 9) == 0)
00366         {
00367             if (strcmp(buffer + 9, "c_svc") != 0) return NULL;
00368         }
00369         else if (memcmp(buffer, "kernel_type ", 12) == 0)
00370         {
00371             if (strcmp(buffer + 12, "linear") == 0)
00372             {
00373                 linear = new LinearKernel();
00374                 pKernel = linear;
00375             }
00376             else if (strcmp(buffer + 12, "rbf") == 0)
00377             {
00378                 rbf = new RBFKernel(1.0);
00379                 pKernel = rbf;
00380             }
00381             else if (strcmp(buffer + 12, "polynomial") == 0)
00382             {
00383                 poly = new PolynomialKernel(1, 1.0);
00384                 pKernel = poly;
00385             }
00386             else return NULL;
00387         }
00388         else if (memcmp(buffer, "degree ", 7) == 0)
00389         {
00390             if (poly != NULL)
00391             {
00392                 poly->setParameter(0, atof(buffer + 7));
00393             }
00394         }
00395         else if (memcmp(buffer, "gamma ", 6) == 0)
00396         {
00397             if (rbf != NULL)
00398             {
00399                 rbf->setParameter(0, atof(buffer + 6));
00400             }
00401         }
00402         else if (memcmp(buffer, "coef0 ", 6) == 0)
00403         {
00404             if (poly != NULL)
00405             {
00406                 poly->setParameter(1, atof(buffer + 6));
00407             }
00408         }
00409         else if (memcmp(buffer, "nr_class ", 9) == 0)
00410         {
00411             if (strcmp(buffer + 9, "2") != 0) return NULL;
00412         }
00413         else if (memcmp(buffer, "total_sv ", 9) == 0)
00414         {
00415             examples = atoi(buffer + 9);
00416         }
00417         else if (memcmp(buffer, "rho ", 4) == 0)
00418         {
00419             b = atof(buffer + 4);
00420         }
00421         else if (memcmp(buffer, "label ", 6) == 0)
00422         {
00423             if (strcmp(buffer + 6, "1 -1") != 0) return NULL;
00424         }
00425         else if (strcmp(buffer, "SV") == 0)
00426         {
00427             break;
00428         }
00429         else
00430         {
00431             // just ignore unknown tags
00432         }
00433     }
00434     if (examples == -1) return NULL;
00435     if (pKernel == NULL) return NULL;
00436 
00437     // first pass: determine the data dimension
00438     int start = is.tellg();
00439     for (i = 0; i < examples; i++)
00440     {
00441         // read the coefficient
00442         status = ReadToken(is, buffer, sizeof(buffer), " ");
00443         if (status != ' ') return NULL;
00444 
00445         while (true)
00446         {
00447             // read the index
00448             status = ReadToken(is, buffer, sizeof(buffer), ":\n");
00449             if (status > 1000) return NULL;
00450             if (status == '\n') break;
00451             int index = atoi(buffer);
00452             if (index >= dimension) dimension = index + 1;
00453 
00454             // read the value
00455             status = ReadToken(is, buffer, sizeof(buffer), " \n");
00456             if (status > 1000) return NULL;
00457             if (status == '\n') break;
00458         }
00459     }
00460     if (dimension == -1) return NULL;
00461 
00462     // create the objects
00463     SVM* pSVM = new SVM(pKernel);
00464     Array<double>* sv = new Array<double> (examples, dimension);
00465     *sv = 0.0;
00466     pSVM->parameter.resize(examples + 1, false);
00467     pSVM->parameter(examples) = b;
00468     pSVM->examples = examples;
00469     pSVM->inputDimension = dimension;
00470 
00471     // second pass: read the data
00472     is.seekg(start, ios::beg);
00473     for (i = 0; i < examples; i++)
00474     {
00475         // read the coefficient
00476         status = ReadToken(is, buffer, sizeof(buffer), " ");
00477         if (status != ' ') return NULL;
00478         pSVM->parameter(i) = atof(buffer);
00479 
00480         while (true)
00481         {
00482             // read the index
00483             status = ReadToken(is, buffer, sizeof(buffer), ":\n");
00484             if (status > 1000) return NULL;
00485             if (status == '\n') break;
00486             int index = atoi(buffer);
00487             if (index >= dimension) dimension = index + 1;
00488 
00489             // read the value
00490             status = ReadToken(is, buffer, sizeof(buffer), " \n");
00491             if (status > 1000) return NULL;
00492             (*sv)(i, index) = atof(buffer);
00493             if (status == '\n') break;
00494         }
00495     }
00496 
00497     pSVM->x = sv;
00498     pSVM->bOwnMemory = true;
00499 
00500     return pSVM;
00501 }
00502 
00503 // static
00504 SVM* SVM::ImportSvmlightModel(std::istream& is)
00505 {
00506     char buffer[256];
00507     int status;
00508     KernelFunction* pKernel;
00509 
00510     // first line is a comment
00511     status = DiscardUntil(is, "\n");
00512     if (status != '\n') return NULL;
00513 
00514     // kernel type
00515     status = ReadToken(is, buffer, sizeof(buffer), "#\n");
00516     if (status > 1000) return NULL;
00517     if (status == '#') DiscardUntil(is, "\n");
00518     int kernel = atoi(buffer);
00519 
00520     // degree
00521     status = ReadToken(is, buffer, sizeof(buffer), "#\n");
00522     if (status > 1000) return NULL;
00523     if (status == '#') DiscardUntil(is, "\n");
00524     int degree = atoi(buffer);
00525 
00526     // gamma
00527     status = ReadToken(is, buffer, sizeof(buffer), "#\n");
00528     if (status > 1000) return NULL;
00529     if (status == '#') DiscardUntil(is, "\n");
00530     double gamma = atof(buffer);
00531 
00532     // poly coeff s
00533     status = ReadToken(is, buffer, sizeof(buffer), "#\n");
00534     if (status > 1000) return NULL;
00535     if (status == '#') DiscardUntil(is, "\n");
00536     double s = atof(buffer);
00537 
00538     // poly coeff c/r
00539     status = ReadToken(is, buffer, sizeof(buffer), "#\n");
00540     if (status > 1000) return NULL;
00541     if (status == '#') DiscardUntil(is, "\n");
00542     double r = atof(buffer);
00543 
00544     // string parameter u
00545     status = DiscardUntil(is, "\n");
00546     if (status > 1000) return NULL;
00547 
00548     // highest feature index
00549     status = ReadToken(is, buffer, sizeof(buffer), "#\n");
00550     if (status > 1000) return NULL;
00551     if (status == '#') DiscardUntil(is, "\n");
00552     int dimension = atoi(buffer) + 1;
00553 
00554     // number of training documents
00555     status = DiscardUntil(is, "\n");
00556     if (status > 1000) return NULL;
00557 
00558     // number of support vectors plus 1
00559     status = ReadToken(is, buffer, sizeof(buffer), "#\n");
00560     if (status > 1000) return NULL;
00561     if (status == '#') DiscardUntil(is, "\n");
00562     int examples = atoi(buffer) - 1;
00563 
00564     // threshold b
00565     status = ReadToken(is, buffer, sizeof(buffer), "#\n");
00566     if (status > 1000) return NULL;
00567     if (status == '#') DiscardUntil(is, "\n");
00568     double b = atof(buffer);
00569 
00570     // create the objects
00571     if (kernel == 0) pKernel = new LinearKernel();
00572     else if (kernel == 1)
00573     {
00574         if (s == 0.0) return NULL;
00575         pKernel = new PolynomialKernel(degree, r / s);
00576     }
00577     else if (kernel == 2) pKernel = new RBFKernel(gamma);
00578     else return NULL;
00579     SVM* pSVM = new SVM(pKernel);
00580     Array<double>* sv = new Array<double> (examples, dimension);
00581     *sv = 0.0;
00582     pSVM->parameter.resize(examples + 1, false);
00583     pSVM->parameter(examples) = b;
00584     pSVM->examples = examples;
00585     pSVM->inputDimension = dimension;
00586 
00587     // read the data
00588     int i, index;
00589     for (i = 0; i < examples; i++)
00590     {
00591         // read the coefficient
00592         status = ReadToken(is, buffer, sizeof(buffer), " ");
00593         if (status > 1000) return NULL;
00594         pSVM->parameter(i) = atof(buffer);
00595 
00596         while (true)
00597         {
00598             // read the index
00599             status = ReadToken(is, buffer, sizeof(buffer), ":#\n");
00600             if (status > 1000) return NULL;
00601             if (status == '#')
00602             {
00603                 if (DiscardUntil(is, "\n") > 1000) return NULL;
00604                 break;
00605             }
00606             else if (status == '\n') break;
00607             index = atoi(buffer);
00608 
00609             // read the value
00610             status = ReadToken(is, buffer, sizeof(buffer), " #\n");
00611             if (status > 1000) return NULL;
00612             (*sv)(i, index) = atof(buffer);
00613             if (status == '#')
00614             {
00615                 if (DiscardUntil(is, "\n") > 1000) return NULL;
00616                 break;
00617             }
00618             else if (status == '\n') break;
00619         }
00620     }
00621 
00622     pSVM->x = sv;
00623     pSVM->bOwnMemory = true;
00624 
00625     return pSVM;
00626 }
00627 
00628 void SVM::MakeSparse()
00629 {
00630     if (! bOwnMemory) return;
00631 
00632     // count support vectors and compute a map
00633     int i, ic = getExamples();
00634     std::vector<int> map;
00635     for (i = 0; i < ic; i++)
00636     {
00637         if (parameter(i) != 0.0)
00638         {
00639             map.push_back(i);
00640         }
00641     }
00642     int s, sc = map.size();
00643 
00644     // copy relevant coefficients and training data
00645     int dim = x->dim(1);
00646     Array<double> p(sc + 1);
00647     Array<double>* xx = new Array<double>(sc, dim);
00648 
00649     for (s = 0; s < sc; s++)
00650     {
00651         i = map[s];
00652         (*xx)[s] = (*x)[i];
00653         p(s) = parameter(i);
00654     }
00655     p(sc) = parameter(ic);
00656 
00657     parameter = p;
00658     examples = sc;
00659     delete x;
00660     x = xx;
00661 }
00662 
00663 // static
00664 int SVM::ReadToken(std::istream& is, char* buffer, int maxlength, const char* separators)
00665 {
00666     int i;
00667     int s, sc = strlen(separators);
00668     char c;
00669     for (i = 0; i < maxlength - 1; i++)
00670     {
00671         if (is.readsome(&c, 1) == 0) return 1001;
00672         if (is.bad()) return 1002;
00673         for (s = 0; s < sc; s++) if (separators[s] == c) break;
00674         if (s < sc)
00675         {
00676             buffer[i] = 0;
00677             return separators[s];
00678         }
00679         buffer[i] = c;
00680     }
00681     buffer[i] = 0;
00682     return 1003;
00683 }
00684 
00685 // static
00686 int SVM::DiscardUntil(std::istream& is, const char* separators)
00687 {
00688     int s, sc = strlen(separators);
00689     char c;
00690     while (true)
00691     {
00692         if (is.readsome(&c, 1) == 0) return 1001;
00693         if (is.bad()) return 1002;
00694         for (s = 0; s < sc; s++) if (separators[s] == c) return c;
00695     }
00696 }
00697 
00698 
00700 
00701 
00702 MultiClassSVM::MultiClassSVM(KernelFunction* pKernel, unsigned int numberOfClasses, bool bOrthogonalVectors, bool bNumberOutput)
00703 {
00704     this->kernel = pKernel;
00705     this->classes = numberOfClasses;
00706     this->examples = 0;
00707     this->bOwnMemory = false;
00708     this->numberOutput = bNumberOutput;
00709 
00710     prototypes.resize(classes, classes, false);
00711     if (bOrthogonalVectors)
00712     {
00713         prototypes = 0.0;
00714         unsigned int i;
00715         for (i = 0; i < classes; i++) prototypes(i, i) = 1.0;
00716     }
00717     else
00718     {
00719         throw SHARKEXCEPTION("[MultiClassSVM::MultiClassSVM] simplex vector initialization not implemented yet");
00720     }
00721 
00722     inputDimension = 0;
00723     outputDimension = (numberOutput ? 1 : classes);
00724     x = NULL;
00725 }
00726 
00727 MultiClassSVM::MultiClassSVM(KernelFunction* pKernel, Array<double> prototypes, bool bNumberOutput)
00728 {
00729     SIZE_CHECK(prototypes.ndim() == 2);
00730     SIZE_CHECK(prototypes.dim(0) == prototypes.dim(1));
00731 
00732     this->kernel = pKernel;
00733     this->classes = prototypes.dim(0);
00734     this->examples = 0;
00735     this->bOwnMemory = false;
00736     this->numberOutput = bNumberOutput;
00737     this->prototypes = prototypes;
00738 
00739     inputDimension = 0;
00740     outputDimension = (numberOutput ? 1 : classes);
00741     x = NULL;
00742 }
00743 
00744 MultiClassSVM::~MultiClassSVM()
00745 {
00746     if (bOwnMemory) delete x;
00747 }
00748 
00749 
00750 void MultiClassSVM::SetTrainingData(const Array<double>& input, bool copy)
00751 {
00752     SIZE_CHECK(input.ndim() == 2);
00753     if (bOwnMemory) delete x;
00754 
00755     examples = input.dim(0);
00756     inputDimension = input.dim(1);
00757 
00758     parameter.resize(examples * classes + classes, false);
00759     parameter = 0.0;
00760 
00761     if (copy)
00762     {
00763         x = new Array<double>(input);
00764         bOwnMemory = true;
00765     }
00766     else
00767     {
00768         x = &input;
00769         bOwnMemory = false;
00770     }
00771 }
00772 
00773 void MultiClassSVM::model(const Array<double>& input, Array<double>& output)
00774 {
00775     if (numberOutput)
00776     {
00777         Array<double> tmp(classes);
00778         if (input.ndim() == 1)
00779         {
00780             output.resize(1, false);
00781             Predict(input, tmp);
00782             output(0) = VectorToClass(tmp);
00783         }
00784         else if (input.ndim() == 2)
00785         {
00786             unsigned int i, ic = input.dim(0);
00787             output.resize(ic, 1, false);
00788             for (i = 0; i < ic; i++)
00789             {
00790                 Predict(input[i], tmp);
00791                 output(i, 0) = VectorToClass(tmp);
00792             }
00793         }
00794         else throw SHARKEXCEPTION("[MultiClassSVM::model] invalid dimension");
00795     }
00796     else
00797     {
00798         if (input.ndim() == 1)
00799         {
00800             output.resize(classes, false);
00801             Predict(input, output);
00802         }
00803         else if (input.ndim() == 2)
00804         {
00805             unsigned int i, ic = input.dim(0);
00806             output.resize(ic, classes, false);
00807             for (i = 0; i < ic; i++)
00808             {
00809                 Predict(input[i], output[i]);
00810             }
00811         }
00812         else throw SHARKEXCEPTION("[MultiClassSVM::model] invalid dimension");
00813     }
00814 }
00815 
00816 unsigned int MultiClassSVM::model(const Array<double>& input)
00817 {
00818     SIZE_CHECK(input.ndim() == 1);
00819     SIZE_CHECK(input.dim(0) == inputDimension);
00820 
00821     Array<double> tmp(classes);
00822     Predict(input, tmp);
00823     return VectorToClass(tmp);
00824 }
00825 
00826 void MultiClassSVM::Normalize()
00827 {
00828     unsigned int c;
00829     unsigned int i, j;
00830     for (c = 0; c < classes; c++)
00831     {
00832         // compute the squared norm
00833         double norm2 = 0.0;
00834         for (i = 0; i < examples; i++)
00835         {
00836             for (j = 0; j < i; j++)
00837             {
00838                 double k = kernel->eval((*x)[i], (*x)[j]);
00839                 norm2 += 2.0 * parameter(classes * i + c) * parameter(classes * j + c) * k;
00840             }
00841             double k = kernel->eval((*x)[i], (*x)[i]);
00842             norm2 += parameter(classes * i + c) * parameter(classes * i + c) * k;
00843         }
00844 
00845         // normalize
00846         double norm = sqrt(norm2);
00847         for (i = 0; i < examples; i++) parameter(classes*i + c) /= norm;
00848     }
00849 }
00850 
00851 unsigned int MultiClassSVM::VectorToClass(const Array<double>& v)
00852 {
00853     SIZE_CHECK(v.ndim() == 1);
00854     SIZE_CHECK(v.dim(0) == classes);
00855 
00856     unsigned int c, i, best = 0;
00857     double scp, bestscp = -1e100;
00858     for (c = 0; c < classes; c++)
00859     {
00860         scp = 0.0;
00861         for (i = 0; i < classes; i++)
00862         {
00863             scp += v(i) * prototypes(c, i);
00864         }
00865         if (scp > bestscp)
00866         {
00867             bestscp = scp;
00868             best = c;
00869         }
00870     }
00871 
00872     return best;
00873 }
00874 
00875 // We assume that the output array has correct size.
00876 void MultiClassSVM::Predict(const Array<double>& input, Array<double>& output)
00877 {
00878     unsigned int c, e, m;
00879     output = 0.0;
00880 
00881     for (e = 0; e < examples; e++)
00882     {
00883         double k = kernel->eval((*x)[e], input);
00884         for (c = 0; c < classes; c++)
00885         {
00886             for (m = 0; m < classes; m++)
00887             {
00888                 output(m) += getAlpha(e, c) * k * prototypes(c, m);
00889             }
00890         }
00891     }
00892 
00893     for (c = 0; c < classes; c++)
00894     {
00895         for (m = 0; m < classes; m++)
00896         {
00897             output(m) += getOffset(c) * prototypes(c, m);
00898         }
00899     }
00900 }
00901 
00902 // We assume that the output array has correct size.
00903 void MultiClassSVM::Predict(const Array<double>& input, ArrayReference<double> output)
00904 {
00905     unsigned int c, e, m;
00906     output = 0.0;
00907 
00908     for (e = 0; e < examples; e++)
00909     {
00910         double k = kernel->eval((*x)[e], input);
00911         for (c = 0; c < classes; c++)
00912         {
00913             for (m = 0; m < classes; m++)
00914             {
00915                 output(m) += getAlpha(e, c) * k * prototypes(c, m);
00916             }
00917         }
00918     }
00919 
00920     for (c = 0; c < classes; c++)
00921     {
00922         for (m = 0; m < classes; m++)
00923         {
00924             output(m) += getOffset(c) * prototypes(c, m);
00925         }
00926     }
00927 }
00928 
00929 
00931 
00932 
00933 MetaSVM::MetaSVM(SVM* pSVM, unsigned int numberOfHyperParameters)
00934 {
00935     svm = pSVM;
00936     kernel = pSVM->getKernel();
00937     hyperparameters = numberOfHyperParameters;
00938 
00939     unsigned int k, kc = kernel->getParameterDimension();
00940     parameter.resize(hyperparameters + kc, false);
00941     parameter = 0.0;
00942     for (k = 0; k < kc; k++) parameter(k + hyperparameters) = kernel->getParameter(k);
00943 }
00944 
00945 MetaSVM::MetaSVM(MultiClassSVM* pSVM, unsigned int numberOfHyperParameters)
00946 {
00947     svm = pSVM;
00948     kernel = pSVM->getKernel();
00949     hyperparameters = numberOfHyperParameters;
00950 
00951     unsigned int k, kc = kernel->getParameterDimension();
00952     parameter.resize(hyperparameters + kc, false);
00953     parameter = 0.0;
00954     for (k = 0; k < kc; k++) parameter(k + hyperparameters) = kernel->getParameter(k);
00955 }
00956 
00957 MetaSVM::~MetaSVM()
00958 {
00959 }
00960 
00961 
00962 void MetaSVM::model(const Array<double>& input, Array<double>& output)
00963 {
00964     svm->model(input, output);
00965 }
00966 
00967 void MetaSVM::setParameter(unsigned int index, double value)
00968 {
00969     Model::setParameter(index, value);
00970     if (index >= hyperparameters)
00971     {
00972         // set a kernel parameter
00973         kernel->setParameter(index - hyperparameters, value);
00974     }
00975 }
00976 
00977 bool MetaSVM::isFeasible()
00978 {
00979     return (kernel->isFeasible());
00980 }
00981 
00982 
00984 
00985 
00986 C_SVM::C_SVM(SVM* pSVM, double Cplus, double Cminus, bool norm2, bool unconst)
00987         : MetaSVM(pSVM, 1)
00988 {
00989     C_ratio = Cminus / Cplus;
00990     norm2penalty = norm2;
00991     exponential = unconst;
00992 
00993     setParameter(0, (exponential) ? log(Cplus) : Cplus);
00994 }
00995 
00996 C_SVM::~C_SVM()
00997 {
00998 }
00999 
01000 
01001 void C_SVM::PrepareDerivative()
01002 {
01003     if (norm2penalty) throw SHARKEXCEPTION("[C_SVM::PrepareDerivative] PrepareDerivative works only in the 1-norm case.");
01004 
01005     SVM* svm = getSVM();
01006     const Array<double>& pt = svm->getPoints();
01007     int a, ac = svm->getExamples();
01008     int s, sc = svm->getParameterDimension();
01009     int k, kc = kernel->getParameterDimension();
01010     int pc = getParameterDimension();
01011     Array<double> der;
01012 
01013     alpha_b_Derivative.resize(sc, pc, false);
01014     alpha_b_Derivative = 0.0;
01015 
01016     // determine free and bounded support vectors
01017     std::vector<int> fi;
01018     std::vector<int> ri;
01019     for (a = 0; a < ac; a++)
01020     {
01021         double alpha = svm->getAlpha(a);
01022         if (alpha != 0.0)
01023         {
01024             if ((alpha == -C_minus) || (alpha == C_plus)) ri.push_back(a);
01025             else fi.push_back(a);
01026         }
01027     }
01028     int f, fc = fi.size();
01029     int r, rc = ri.size();
01030     fi.push_back(ac);
01031 
01032     Array2D<double> H(fc + 1, fc + 1);
01033     Array2D<double> H_inv(fc + 1, fc + 1);
01034     Array2D<double> R(fc + 1, rc);
01035     Array<double> dH(kc, fc + 1, fc + 1);
01036     Array<double> dR(kc, fc + 1, rc);
01037     Array<double> alpha_f(fc + 1);
01038     Array<double> alpha_r(rc);
01039     for (f = 0; f < fc; f++) alpha_f(f) = svm->getAlpha(fi[f]);
01040     alpha_f(fc) = svm->getOffset();
01041     for (r = 0; r < rc; r++) alpha_r(r) = svm->getAlpha(ri[r]);
01042 
01043     // initialize H and dH
01044     for (f = 0; f < fc; f++)
01045     {
01046         int f2;
01047         for (f2 = 0; f2 < f; f2++)
01048         {
01049             H(f, f2) = H(f2, f) = kernel->evalDerivative(pt[fi[f]], pt[fi[f2]], der);
01050             for (k = 0; k < kc; k++)
01051             {
01052                 dH(k, f, f2) = dH(k, f2, f) = der(k);
01053             }
01054         }
01055         H(f, f) = kernel->evalDerivative(pt[fi[f]], pt[fi[f]], der);
01056         for (k = 0; k < kc; k++)
01057         {
01058             dH(k, f, f) = der(k);
01059         }
01060         H(fc, f) = H(f, fc) = 1.0;
01061         for (k = 0; k < kc; k++) dH(k, fc, f) = dH(k, f, fc) = 0.0;
01062     }
01063     H(fc, fc) = 0.0;
01064     for (k = 0; k < kc; k++) dH(k, fc, fc) = 0.0;
01065 
01066     // compute H_inv
01067 //  invertSymm(H_inv, H);
01068     g_inverse(H, H_inv, 200, 1e-10, true);
01069 
01070     // initialize R and dR
01071     for (r = 0; r < rc; r++)
01072     {
01073         for (f = 0; f < fc; f++)
01074         {
01075             R(f, r) = kernel->evalDerivative(pt[fi[f]], pt[ri[r]], der);
01076             for (k = 0; k < kc; k++)
01077             {
01078                 dR(k, f, r) = der(k);
01079             }
01080         }
01081         R(fc, r) = 1.0;
01082         for (k = 0; k < kc; k++) dR(k, fc, r) = 0.0;
01083     }
01084 
01085     der.resize(fc + 1, false);
01086 
01087     // compute the derivative of (\alpha, b) w.r.t. C
01088     if (rc > 0)
01089     {
01090         Array<double> y(rc);
01091         for (r = 0; r < rc; r++) y(r) = ((alpha_r(r) > 0.0) ? 1.0 : -1.0);
01092         Array<double> Ry(fc + 1);
01093         matColVec(Ry, R, y);
01094         matColVec(der, H_inv, Ry);
01095         for (f = 0; f <= fc; f++) alpha_b_Derivative(fi[f], 0) = -der(f);
01096         for (r = 0; r < rc; r++) alpha_b_Derivative(ri[r], 0) = ((alpha_r(r) > 0.0) ? 1.0 : -C_ratio);
01097         if (exponential)
01098         {
01099             for (s = 0; s < sc; s++) alpha_b_Derivative(s, 0) *= C_plus;
01100         }
01101     }
01102 
01103     // compute the derivative of (\alpha, b) w.r.t. the kernel parameters
01104     for (k = 0; k < kc; k++)
01105     {
01106         Array<double> sum(fc + 1);
01107         Array<double> tmp(fc + 1);
01108         matColVec(sum, dH[k], alpha_f);
01109         matColVec(tmp, dR[k], alpha_r);
01110         for (f = 0; f <= fc; f++) sum(f) += tmp(f);
01111         matColVec(der, H_inv, sum);
01112         for (f = 0; f <= fc; f++) alpha_b_Derivative(fi[f], k + 1) = -der(f);
01113     }
01114 }
01115 
01116 void C_SVM::modelDerivative(const Array<double>& input, Array<double>& derivative)
01117 {
01118     SVM* svm = getSVM();
01119     const Array<double>& pt = svm->getPoints();
01120     int k, kc = kernel->getParameterDimension();
01121     int a, ac = svm->getExamples();
01122     int pc = getParameterDimension();
01123 
01124     derivative.resize(1, pc, false);
01125     for (k = 0; k <= kc; k++) derivative(0, k) = alpha_b_Derivative(ac, k);
01126 
01127     Array<double> der(pc);
01128 
01129     for (a = 0; a < ac; a++)
01130     {
01131         double alpha = svm->getAlpha(a);
01132         double ker = kernel->evalDerivative(input, pt[a], der);
01133         derivative(0, 0) += ker * alpha_b_Derivative(a, 0);
01134         for (k = 0; k < kc; k++)
01135         {
01136             derivative(0, k + 1) += alpha * der(k) + ker * alpha_b_Derivative(a, k + 1);
01137         }
01138     }
01139 }
01140 
01141 void C_SVM::setParameter(unsigned int index, double value)
01142 {
01143     MetaSVM::setParameter(index, value);
01144     if (index == 0)
01145     {
01146         // set C_+ and C_-
01147         if (exponential)
01148         {
01149             value = exp(value);
01150         }
01151         C_plus = value;
01152         C_minus = C_ratio * value;
01153     }
01154 }
01155 
01156 bool C_SVM::isFeasible()
01157 {
01158     return (C_plus > 0.0 && MetaSVM::isFeasible());
01159 }
01160 
01161 
01163 
01164 
01165 OneClassSVM::OneClassSVM(SVM* pSVM, double fractionNu)
01166         : MetaSVM(pSVM, 1)
01167 {
01168     setParameter(0, fractionNu);
01169 }
01170 
01171 OneClassSVM::~OneClassSVM()
01172 {
01173 }
01174 
01175 
01176 void OneClassSVM::setParameter(unsigned int index, double value)
01177 {
01178     MetaSVM::setParameter(index, value);
01179     if (index == 0)
01180     {
01181         // There is no way to set this value correctly if this function is calles by the standard constructor.
01182         // That is, because of the dependency on the number of training examples.
01183 
01184         // The correct initialization takes place in SVM_Optimizer::optimize(...)
01185         nu = value;
01186     }
01187 }
01188 
01189 bool OneClassSVM::isFeasible()
01190 {
01191     return (nu > 0.0 && MetaSVM::isFeasible());
01192 }
01193 
01194 
01196 
01197 
01198 Epsilon_SVM::Epsilon_SVM(SVM* pSVM, double C, double epsilon, bool unconst)
01199         : MetaSVM(pSVM, 2)
01200 {
01201     this->C = C;
01202     this->epsilon = epsilon;
01203 
01204     exponential = unconst;
01205 
01206     setParameter(0, (exponential) ? log(C) : C);
01207     setParameter(1, (exponential) ? log(epsilon) : epsilon);
01208 }
01209 
01210 Epsilon_SVM::~Epsilon_SVM()
01211 {
01212 }
01213 
01214 
01215 void Epsilon_SVM::setParameter(unsigned int index, double value)
01216 {
01217     if (index < hyperparameters)
01218     {
01219         Model::setParameter(index, value);
01220         if (index == 0)
01221         {
01222             if (exponential) C = exp(value); else C = value;
01223         }
01224         else
01225         {
01226             if (exponential) epsilon = exp(value); else epsilon = value;
01227         }
01228     }
01229     else
01230     {
01231         MetaSVM::setParameter(index, value);
01232     }
01233 }
01234 
01235 bool Epsilon_SVM::isFeasible()
01236 {
01237     return (C > 0.0 && epsilon > 0.0 && MetaSVM::isFeasible());
01238 }
01239 
01240 
01242 
01243 
01244 RegularizationNetwork::RegularizationNetwork(SVM* pSVM, double gamma)
01245         : MetaSVM(pSVM, 1)
01246 {
01247     parameter(0) = gamma;
01248 }
01249 
01250 RegularizationNetwork::~RegularizationNetwork()
01251 {
01252 }
01253 
01254 
01255 bool RegularizationNetwork::isFeasible()
01256 {
01257     return (parameter(0) > 0.0 && MetaSVM::isFeasible());
01258 }
01259 
01260 
01262 
01263 
01264 AllInOneMcSVM::AllInOneMcSVM(MultiClassSVM* pSVM, double C)
01265         : MetaSVM(pSVM, 1)
01266 {
01267     parameter(0) = C;
01268 }
01269 
01270 AllInOneMcSVM::~AllInOneMcSVM()
01271 {
01272 }
01273 
01274 
01275 bool AllInOneMcSVM::isFeasible()
01276 {
01277     return (parameter(0) > 0.0 && MetaSVM::isFeasible());
01278 }
01279 
01280 
01282 
01283 
01284 CrammerSingerMcSVM::CrammerSingerMcSVM(MultiClassSVM* pSVM, double beta)
01285         : MetaSVM(pSVM, 1)
01286 {
01287     parameter(0) = beta;
01288 }
01289 
01290 CrammerSingerMcSVM::~CrammerSingerMcSVM()
01291 {
01292 }
01293 
01294 
01295 bool CrammerSingerMcSVM::isFeasible()
01296 {
01297     return (parameter(0) > 0.0 && MetaSVM::isFeasible());
01298 }
01299 
01300 
01302 
01303 
01304 OVAMcSVM::OVAMcSVM(MultiClassSVM* pSVM, double C)
01305         : MetaSVM(pSVM, 1)
01306 {
01307     parameter(0) = C;
01308 }
01309 
01310 OVAMcSVM::~OVAMcSVM()
01311 {
01312 }
01313 
01314 
01315 bool OVAMcSVM::isFeasible()
01316 {
01317     return (parameter(0) > 0.0 && MetaSVM::isFeasible());
01318 }
01319 
01320 
01322 
01323 
01324 OCCMcSVM::OCCMcSVM(MultiClassSVM* pSVM, double C)
01325         : MetaSVM(pSVM, 1)
01326 {
01327     parameter(0) = C;
01328 }
01329 
01330 OCCMcSVM::~OCCMcSVM()
01331 {
01332 }
01333 
01334 
01335 bool OCCMcSVM::isFeasible()
01336 {
01337     return (parameter(0) > 0.0 && MetaSVM::isFeasible());
01338 }
01339 
01340 
01342 
01343 
01344 SVM_Optimizer::SVM_Optimizer()
01345 {
01346     solver = NULL;
01347     matrix = NULL;
01348     cache = NULL;
01349 
01350     printInfo = false;
01351     accuracy = 0.001;
01352     cacheMB = 100;
01353     maxIter = -1;
01354     maxSeconds = -1;
01355 
01356     optimal = true;
01357 }
01358 
01359 SVM_Optimizer::~SVM_Optimizer()
01360 {
01361     if (solver != NULL)
01362     {
01363         delete solver;
01364         solver = NULL;
01365     }
01366     if (matrix != NULL)
01367     {
01368         delete matrix;
01369         matrix = NULL;
01370     }
01371     if (cache != NULL)
01372     {
01373         delete cache;
01374         cache = NULL;
01375     }
01376 }
01377 
01378 
01379 void SVM_Optimizer::init(Model& model)
01380 {
01381     C_SVM* csvm = dynamic_cast<C_SVM*>(&model);
01382     Epsilon_SVM* esvm = dynamic_cast<Epsilon_SVM*>(&model);
01383     OneClassSVM* OCsvm = dynamic_cast<OneClassSVM*>(&model);
01384     RegularizationNetwork* rn = dynamic_cast<RegularizationNetwork*>(&model);
01385     GaussianProcess* gaussproc = dynamic_cast<GaussianProcess*>(&model);
01386     AllInOneMcSVM* aio = dynamic_cast<AllInOneMcSVM*>(&model);
01387     CrammerSingerMcSVM* cs = dynamic_cast<CrammerSingerMcSVM*>(&model);
01388     OVAMcSVM* ova = dynamic_cast<OVAMcSVM*>(&model);
01389     OCCMcSVM* bc = dynamic_cast<OCCMcSVM*>(&model);
01390 
01391     if (csvm != NULL)
01392     {
01393         if (csvm->is2norm()) mode = eC2;
01394         else mode = eC1;
01395         Cplus = csvm->get_Cplus();
01396         Cminus = csvm->get_Cminus();
01397     }
01398     else if (esvm != NULL)
01399     {
01400         mode = eEpsilon;
01401         C = esvm->get_C();
01402         epsilon = esvm->get_epsilon();
01403     }
01404     else if (OCsvm != NULL)
01405     {
01406         mode = e1Class;
01407         fractionOfOutliers = OCsvm->getNu();
01408     }
01409     else if (rn != NULL)
01410     {
01411         mode = eRegularizationNetwork;
01412         gamma = rn->get_gamma();
01413     }
01414     else if (gaussproc != NULL)
01415     {
01416         mode = eGaussianProcess;
01417     }
01418     else if (aio != NULL)
01419     {
01420         mode = eAllInOne;
01421         C = aio->get_C();
01422     }
01423     else if (cs != NULL)
01424     {
01425         mode = eCrammerSinger;
01426         beta = cs->get_beta();
01427     }
01428     else if (ova != NULL)
01429     {
01430         mode = eOVA;
01431         C = ova->get_C();
01432     }
01433     else if (bc != NULL)
01434     {
01435         mode = eOCC;
01436         C = bc->get_C();
01437     }
01438     else throw SHARKEXCEPTION("[SVM_Optimizer::init] The model is not a valid support vector machine meta model.");
01439 
01440     if (solver != NULL)
01441     {
01442         delete solver;
01443         solver = NULL;
01444     }
01445     if (matrix != NULL)
01446     {
01447         delete matrix;
01448         matrix = NULL;
01449     }
01450     if (cache != NULL)
01451     {
01452         delete cache;
01453         cache = NULL;
01454     }
01455 }
01456 
01457 double SVM_Optimizer::optimize(Model& model, ErrorFunction& error, const Array<double>& input, const Array<double>& target)
01458 {
01459     switch (mode)
01460     {
01461     case eC1:
01462     case eC2:
01463     case eEpsilon:
01464     case eNu:
01465     case eRegularizationNetwork:
01466     case e1Class:
01467     {
01468         SVM* svm = dynamic_cast<SVM*>(&model);
01469         if (svm == NULL)
01470         {
01471             MetaSVM* meta = dynamic_cast<MetaSVM*>(&model);;
01472             if (meta != NULL) svm = meta->getSVM();
01473         }
01474         if (svm == NULL) throw SHARKEXCEPTION("[SVM_Optimizer::optimize] The model is not a valid SVM or SVM meta model.");
01475 
01476         return optimize(*svm, input, target);
01477     }
01478 
01479     case eGaussianProcess:
01480     {
01481         GaussianProcess* gaussproc = dynamic_cast<GaussianProcess*>(&model);
01482         gaussproc->train(input, target);
01483         return 0.0;
01484     }
01485 
01486     case eAllInOne:
01487     case eCrammerSinger:
01488     case eOVA:
01489     case eOCC:
01490     {
01491         MultiClassSVM* svm = dynamic_cast<MultiClassSVM*>(&model);
01492         if (svm == NULL)
01493         {
01494             MetaSVM* meta = dynamic_cast<MetaSVM*>(&model);;
01495             if (meta != NULL) svm = meta->getMultiClassSVM();
01496         }
01497         if (svm == NULL) throw SHARKEXCEPTION("[SVM_Optimizer::optimize] The model is not a valid MultiClassSVM or SVM meta model.");
01498 
01499         optimize(*svm, input, target);
01500         return 0.0;
01501     }
01502 
01503     default:
01504     {
01505         throw SHARKEXCEPTION("[SVM_Optimizer::optimize] call init(...) before optimize(...)");
01506         return 0.0;
01507     }
01508     }
01509 }
01510 
01511 double SVM_Optimizer::optimize(SVM& model, const Array<double>& input, const Array<double>& target, bool copy)
01512 {
01513     if (solver != NULL)
01514     {
01515         delete solver;
01516         solver = NULL;
01517     }
01518     if (matrix != NULL)
01519     {
01520         delete matrix;
01521         matrix = NULL;
01522     }
01523     if (cache != NULL)
01524     {
01525         delete cache;
01526         cache = NULL;
01527     }
01528 
01529     unsigned int e, examples = input.dim(0);
01530 
01531     model.SetTrainingData(input, copy);
01532     KernelFunction* kernel = model.getKernel();
01533 
01534     Array<double> alpha(examples);
01535     double b = 0.0;
01536     double ret = 0.0;
01537 
01538     if (mode == eC1)
01539     {
01540         // C-SVM with 1-norm penalty
01541         Array<double> linear(examples);
01542         Array<double> lower(examples);
01543         Array<double> upper(examples);
01544         for (e = 0; e < examples; e++)
01545         {
01546             alpha(e) = model.getParameter(e);
01547             linear(e) = target(e, 0);
01548             if (target(e, 0) > 0.0)
01549             {
01550                 lower(e) = 0.0;
01551                 upper(e) = Cplus;
01552             }
01553             else
01554             {
01555                 lower(e) = -Cminus;
01556                 upper(e) = 0.0;
01557             }
01558         }
01559 
01560         matrix = new KernelMatrix(kernel, input);
01561         cache = new CachedMatrix(matrix, 1048576 * cacheMB / sizeof(float));
01562 
01563         QpSvmDecomp* solver = new QpSvmDecomp(*cache);
01564         this->solver = solver;
01565         solver->setVerbose(printInfo);
01566         solver->setMaxIterations(maxIter);
01567         solver->setMaxSeconds(maxSeconds);
01568 
01569         ret = solver->Solve(linear, lower, upper, alpha, accuracy);
01570 
01571         // computation of b
01572         Array<double> gradient;
01573         solver->getGradient(gradient);
01574         double lowerBound = -1e100;
01575         double upperBound = 1e100;
01576         double sum = 0.0;
01577         unsigned int freeVars = 0;
01578         double value;
01579         for (e = 0; e < examples; e++)
01580         {
01581             value = gradient(e);
01582             if (alpha(e) == lower(e))
01583             {
01584                 if (value > lowerBound) lowerBound = value;
01585             }
01586             else if (alpha(e) == upper(e))
01587             {
01588                 if (value < upperBound) upperBound = value;
01589             }
01590             else
01591             {
01592                 sum += value;
01593                 freeVars++;
01594             }
01595         }
01596 
01597         if (freeVars > 0)
01598             b = sum / freeVars;                     // stabilized exact value
01599         else
01600             b = 0.5 * (lowerBound + upperBound);    // best estimate
01601     }
01602     else if (mode == eC2)
01603     {
01604         // C-SVM with 2-norm penalty
01605         Array<double> linear(examples);
01606         Array<double> diag(examples);
01607         Array<double> lower(examples);
01608         Array<double> upper(examples);
01609         for (e = 0; e < examples; e++)
01610         {
01611             alpha(e) = model.getParameter(e);
01612             linear(e) = target(e, 0);
01613             if (target(e, 0) > 0.0)
01614             {
01615                 diag(e) = 1.0 / Cplus;
01616                 lower(e) = 0.0;
01617                 upper(e) = 1e100;
01618             }
01619             else
01620             {
01621                 diag(e) = 1.0 / Cminus;
01622                 lower(e) = -1e100;
01623                 upper(e) = 0.0;
01624             }
01625         }
01626 
01627         matrix = new RegularizedKernelMatrix(kernel, input, diag);
01628         cache = new CachedMatrix(matrix, 1048576 * cacheMB / sizeof(float));
01629 
01630         QpSvmDecomp* solver = new QpSvmDecomp(*cache);
01631         this->solver = solver;
01632         solver->setVerbose(printInfo);
01633         solver->setMaxIterations(maxIter);
01634         solver->setMaxSeconds(maxSeconds);
01635 
01636         ret = solver->Solve(linear, lower, upper, alpha, accuracy);
01637 
01638         // computation of b
01639         Array<double> gradient;
01640         solver->getGradient(gradient);
01641         double lowerBound = -1e100;
01642         double upperBound = 1e100;
01643         double sum = 0.0;
01644         unsigned int freeVars = 0;
01645         double value;
01646         for (e = 0; e < examples; e++)
01647         {
01648             value = gradient(e);
01649             if (alpha(e) == lower(e))
01650             {
01651                 if (value > lowerBound) lowerBound = value;
01652             }
01653             else if (alpha(e) == upper(e))
01654             {
01655                 if (value < upperBound) upperBound = value;
01656             }
01657             else
01658             {
01659                 sum += value;
01660                 freeVars++;
01661             }
01662         }
01663 
01664         if (freeVars > 0)
01665             b = sum / freeVars;                     // stabilized exact value
01666         else
01667             b = 0.5 * (lowerBound + upperBound);    // best estimate
01668     }
01669     else if (mode == eEpsilon)
01670     {
01671         // epsilon-SVM
01672         Array<double> solution(2*examples);
01673         Array<double> linear(2*examples);
01674         Array<double> lower(2*examples);
01675         Array<double> upper(2*examples);
01676         for (e = 0; e < examples; e++)
01677         {
01678             double p = model.getParameter(e);
01679             if (p >= 0.0)
01680             {
01681                 solution(e) = p;
01682                 solution(e + examples) = 0.0;
01683             }
01684             else
01685             {
01686                 solution(e) = 0.0;
01687                 solution(e + examples) = -p;
01688             }
01689             linear(e) = target(e, 0) - epsilon;
01690             linear(e + examples) = target(e, 0) + epsilon;
01691             lower(e) = 0.0;
01692             lower(e + examples) = -C;
01693             upper(e) = C;
01694             upper(e + examples) = 0.0;
01695         }
01696 
01697         KernelMatrix* kernelmatrix = new KernelMatrix(kernel, input);
01698         matrix = new QPMatrix2(kernelmatrix);
01699         cache = new CachedMatrix(matrix, 1048576 * cacheMB / sizeof(float));
01700 
01701         QpSvmDecomp* solver = new QpSvmDecomp(*cache);
01702         this->solver = solver;
01703         solver->setVerbose(printInfo);
01704         solver->setMaxIterations(maxIter);
01705         solver->setMaxSeconds(maxSeconds);
01706 
01707         ret = solver->Solve(linear, lower, upper, solution);
01708         for (e = 0; e < examples; e++)
01709         {
01710             alpha(e) = solution(e) + solution(e + examples);
01711         }
01712 
01713         // computation of b
01714         Array<double> gradient;
01715         solver->getGradient(gradient);
01716         double lowerBound = -1e100;
01717         double upperBound = 1e100;
01718         double sum = 0.0;
01719         unsigned int freeVars = 0;
01720         double value;
01721         for (e = 0; e < examples; e++)
01722         {
01723             if (solution(e) > 0.0)
01724             {
01725                 value = gradient(e);
01726                 if (solution(e) < C)
01727                 {
01728                     sum += value;
01729                     freeVars++;
01730                 }
01731                 else
01732                 {
01733                     if (value > lowerBound) lowerBound = value;
01734                 }
01735             }
01736             if (solution(e + examples) < 0.0)
01737             {
01738                 value = gradient(e + examples);
01739                 if (solution(e + examples) > -C)
01740                 {
01741                     sum += value;
01742                     freeVars++;
01743                 }
01744                 else
01745                 {
01746                     if (value < upperBound) upperBound = value;
01747                 }
01748             }
01749         }
01750 
01751         if (freeVars > 0)
01752             b = sum / freeVars;                     // stabilized exact value
01753         else
01754             b = 0.5 * (lowerBound + upperBound);    // best estimate
01755     }
01756     else if (mode == e1Class)
01757     {
01758         Array<double> linear(examples);
01759         Array<double> lower(examples);
01760         Array<double> upper(examples);
01761 
01762         unsigned int NrInitialSVs = (unsigned int)(examples * fractionOfOutliers);
01763         double sumAlpha = 0;
01764         double upperBox;
01765 
01766         // NOT NORMALIZED
01767 //      double upperBox = 1 / (examples * fractionOfOutliers);
01768 
01769         // NORMALIZED VERSION, that is \sum_{i}^\ell \alpha_i = NrInitialSVs
01770         upperBox  = 1.0;
01771 
01772         // initialize all patterns
01773         for (e = 0; e < examples; e++)
01774         {
01775             // linear part of Quadratic Program = 0
01776             linear(e) = 0.0;
01777 
01778             // both Box constraints (aka. Cplus and Cminus)
01779             lower(e)  = 0.0;
01780             upper(e)  = upperBox;
01781 
01782             // coefficients for optimization
01783             alpha(e)  = upperBox;
01784         }
01785 
01786         // INITIALIZATION: Check if too much SVs are at bounds
01787         if (NrInitialSVs != examples)
01788         {
01789             int index;
01790             int diff = examples - NrInitialSVs;
01791 
01792             for (int s = 0; s < diff; s++)
01793             {
01794                 // choose randomly a example
01795                 index = Rng::discrete(0, examples - 1);
01796 
01797                 while (alpha(index) == 0)
01798                 {
01799                     // try again if the example has already been used
01800                     index = Rng::discrete(0, examples - 1);
01801                 }
01802                 // and free the SV
01803                 alpha(index) = 0;
01804             }
01805 
01806             // Check if sumAlpha < e*nu
01807             sumAlpha = 0.0;
01808             for (unsigned int e = 0; e < examples; e++)
01809                 sumAlpha += alpha(e);
01810 
01811             if (sumAlpha < (examples * fractionOfOutliers))
01812             {
01813                 // if yes, find a free example
01814                 index = Rng::discrete(0, examples - 1);
01815                 while (alpha(index) != 0)
01816                 {
01817                     // try again if the example already has been used
01818                     index = Rng::discrete(0, examples - 1);
01819                 }
01820                 // and now we get sumAlpha = 1
01821                 alpha(index) = (examples * fractionOfOutliers) - sumAlpha;
01822             }
01823         }
01824 
01825         matrix = new KernelMatrix(kernel, input);
01826         cache = new CachedMatrix(matrix, 1048576 * cacheMB / sizeof(float));
01827 
01828         QpSvmDecomp* solver = new QpSvmDecomp(*cache);
01829         this->solver = solver;
01830         solver->setVerbose(printInfo);
01831         solver->setMaxIterations(maxIter);
01832         solver->setMaxSeconds(maxSeconds);
01833 
01834         // solve the QP problem
01835         ret = solver->Solve(linear, lower, upper, alpha, accuracy);
01836 
01837         // computation of b
01838         Array<double> gradient;
01839         solver->getGradient(gradient);
01840         double lowerBound = -1e100;
01841         double upperBound = 1e100;
01842         double sum = 0.0;
01843         unsigned int freeVars = 0;
01844         double value;
01845 
01846         for (e = 0; e < examples; e++)
01847         {
01848             value = gradient(e);
01849             if (alpha(e) == lower(e))   // no SVs
01850             {
01851                 if (value > lowerBound) lowerBound = value;
01852             }
01853             else if (alpha(e) == upper(e))  // bounded SVs
01854             {
01855                 if (value < upperBound) upperBound = value;
01856             }
01857             else    // free SVs
01858             {
01859                 sum += value;
01860                 freeVars++;
01861             }
01862         }
01863 
01864         if (freeVars > 0)
01865             b = sum / freeVars; // stabilized exact value
01866         else
01867             b = 0.5 * (lowerBound + upperBound);    // best estimate
01868     }
01869     else if (mode == eRegularizationNetwork)
01870     {
01871         unsigned int f;
01872         Array2D<double> K(examples, examples);
01873         Array2D<double> invK(examples, examples);
01874         for (e = 0; e < examples; e++)
01875         {
01876             for (f = 0; f < e; f++)
01877             {
01878                 double k = kernel->eval(input[e], input[f]);
01879                 K(e, f) = K(f, e) = k;
01880             }
01881             double k = kernel->eval(input[e], input[e]);
01882             K(e, e) = k + gamma;
01883         }
01884         invertSymm(invK, K);
01885         matColVec(alpha, invK, target, 0);
01886 
01887         b = 0.0;
01888         ret = 0.0;
01889     }
01890 
01891     for (e = 0; e < examples; e++) model.setParameter(e, alpha(e));
01892     model.setParameter(examples, b);
01893 
01894     model.MakeSparse();
01895 
01896     return ret;
01897 }
01898 
01899 void SVM_Optimizer::optimize(MultiClassSVM& model, const Array<double>& input, const Array<double>& target, bool copy)
01900 {
01901     if (solver != NULL)
01902     {
01903         delete solver;
01904         solver = NULL;
01905     }
01906     if (matrix != NULL)
01907     {
01908         delete matrix;
01909         matrix = NULL;
01910     }
01911     if (cache != NULL)
01912     {
01913         delete cache;
01914         cache = NULL;
01915     }
01916 
01917     unsigned int i;
01918     unsigned int examples = input.dim(0);
01919     unsigned int classes = model.getClasses();
01920     unsigned int variables = examples * classes;
01921 
01922     model.SetTrainingData(input, copy);
01923     KernelFunction* kernel = model.getKernel();
01924 
01925     if (mode == eAllInOne)
01926     {
01927         Array<double> alpha(variables);
01928         for (i = 0; i < variables; i++) alpha(i) = model.getParameter(i);
01929 
01930         matrix = new KernelMatrix(kernel, input);
01931         cache = new CachedMatrix(matrix, 1048576 * cacheMB / sizeof(float));
01932         Array<double> prototypes(Matrix::unitmatrix(classes));
01933 
01934         QpBoxAllInOneDecomp* solver = new QpBoxAllInOneDecomp(*cache);
01935         this->solver = solver;
01936         solver->setMaxIterations(maxIter);
01937         solver->Solve(classes, target, prototypes, C, alpha, accuracy);
01938 
01939         i = 0;
01940         unsigned int e, c, p, m;
01941         for (e = 0; e < examples; e++)
01942         {
01943             double sum = 0.0;
01944             p = (unsigned int)target(e, 0);
01945             for (c = 0; c < classes; c++)
01946             {
01947                 if (c == p)
01948                 {
01949                     i++;
01950                     continue;
01951                 }
01952                 double len2 = 0.0;
01953                 for (m = 0; m < classes; m++)
01954                 {
01955                     double diff = prototypes(p, m) - prototypes(c, m);
01956                     len2 += diff * diff;
01957                 }
01958                 double value = 0.5 * alpha(i) / sqrt(len2);
01959                 model.setParameter(i, -value);
01960                 sum += value;
01961                 i++;
01962             }
01963 
01964             model.setParameter(e * classes + p, sum);
01965         }
01966         optimal = solver->isOptimal();
01967 
01968     }
01969     else if (mode == eCrammerSinger)
01970     {
01971         Array<double> alpha(variables);
01972         for (i = 0; i < variables; i++) alpha(i) = model.getParameter(i);
01973 
01974         matrix = new KernelMatrix(kernel, input);
01975         cache = new CachedMatrix(matrix, 1048576 * cacheMB / sizeof(float));
01976 
01977         QpCrammerSingerDecomp* solver = new QpCrammerSingerDecomp(*cache, target, classes);
01978         solver->setMaxIterations(maxIter);
01979         this->solver = solver;
01980 
01981         // The (beta/2 * accuracy) rule gives the standard accuracy in the binary case.
01982         solver->Solve(alpha, beta, beta / 2.0 * accuracy);
01983         optimal = solver->isOptimal();
01984 
01985         for (i = 0; i < variables; i++) model.setParameter(i, alpha(i));
01986     }
01987     else if (mode == eOVA)
01988     {
01989         Array<double> alpha(examples);
01990         Array<double> linear(examples);
01991         Array<double> lower(examples);
01992         Array<double> upper(examples);
01993 
01994         // train a set of binary classifiers
01995         unsigned int c, e;
01996         matrix = new KernelMatrix(kernel, input);
01997         cache = new CachedMatrix(matrix, 1048576 * cacheMB / sizeof(float));
01998 
01999         QpBoxDecomp* solver = new QpBoxDecomp(*cache);
02000         solver->setMaxIterations(maxIter);
02001         this->solver = solver;
02002 
02003         for (c = 0; c < classes; c++)
02004         {
02005             for (e = 0; e < examples; e++)
02006             {
02007                 alpha(e) = model.getParameter(e * classes + c);
02008                 if (target(e, 0) == c)
02009                 {
02010                     linear(e) = 1.0;
02011                     lower(e) = 0.0;
02012                     upper(e) = C;
02013                 }
02014                 else
02015                 {
02016                     linear(e) = -1.0;
02017                     lower(e) = -C;
02018                     upper(e) = 0.0;
02019                 }
02020             }
02021             solver->Solve(linear, lower, upper, alpha, accuracy);
02022             optimal = solver->isOptimal();
02023 
02024             for (e = 0; e < examples; e++)
02025             {
02026                 model.setParameter(e * classes + c, alpha(e));
02027             }
02028         }
02029     }
02030     else if (mode == eOCC)
02031     {
02032         Array<double> alpha(examples);
02033         Array<double> linear(examples);
02034         Array<double> lower(examples);
02035         Array<double> upper(examples);
02036 
02037         unsigned int c, e;
02038         Array<double> prototypes(Matrix::unitmatrix(classes));
02039         matrix = new InputLabelMatrix(kernel, input, target, prototypes);
02040         cache = new CachedMatrix(matrix, 1048576 * cacheMB / sizeof(float));
02041 
02042         QpBoxDecomp* solver = new QpBoxDecomp(*cache);
02043         solver->setMaxIterations(maxIter);
02044         this->solver = solver;
02045 
02046         for (e = 0; e < examples; e++)
02047         {
02048             c = (unsigned int)target(e, 0);
02049             alpha(e) = model.getParameter(e * classes + c);
02050             linear(e) = 1.0;
02051             lower(e) = 0.0;
02052             upper(e) = C;
02053         }
02054         solver->Solve(linear, lower, upper, alpha, accuracy);
02055         optimal = solver->isOptimal();
02056 
02057         for (e = 0; e < examples; e++)
02058         {
02059             unsigned int label = (unsigned int)target(e, 0);
02060             for (c = 0; c < classes; c++)
02061             {
02062                 double value = (label == c) ? alpha(e) : 0.0;
02063                 model.setParameter(e * classes + c, value);
02064             }
02065         }
02066     }
02067 
02068     // set the bias term to zero
02069     for (i = 0; i < classes; i++) model.setParameter(variables + i, 0.0);
02070 
02071     // TODO:
02072 //  model.MakeSparse();
02073 }
02074 
02075 // static dummy member
02076 ErrorFunction& SVM_Optimizer::dummyError = * ((ErrorFunction*) NULL);
02077