1 // This is mul/clsfy/tests/test_rbf_svm_smo.cxx
2 // Copyright: (C) 2001 British Telecommunications PLC
3 #include <vector>
4 #include <iostream>
5 #include <iomanip>
6 #include <ios>
7 #include <string>
8 #include "testlib/testlib_test.h"
9 //:
10 // \file
11 // \brief Tests clsfy_rbf_svm and clsfy_rbf_svm_smo_1_builder
12 // \author Ian Scott
13 // Test construction, IO etc.
14 
15 #ifdef _MSC_VER
16 #  include "vcl_msvc_warnings.h"
17 #endif
18 #include <clsfy/clsfy_rbf_svm.h>
19 #include <clsfy/clsfy_rbf_svm_smo_1_builder.h>
20 #include <clsfy/clsfy_k_nearest_neighbour.h>
21 #include <clsfy/clsfy_rbf_parzen.h>
22 #include <vpdfl/vpdfl_axis_gaussian.h>
23 #include <vpdfl/vpdfl_axis_gaussian_sampler.h>
24 #include "vnl/vnl_random.h"
25 #include "vsl/vsl_binary_loader.h"
26 #include <mbl/mbl_data_array_wrapper.h>
27 #include "vul/vul_timer.h"
28 #include "vpl/vpl.h" // vpl_unlink()
29 
30 #ifndef LEAVE_FILES_BEHIND
31 #define LEAVE_FILES_BEHIND 0
32 #endif
33 
34 //=======================================================================
test_rbf_svm_smo()35 void test_rbf_svm_smo()
36 {
37   std::cout << "*************************************\n"
38            << " Testing clsfy_rbf_svm_smo_1_builder\n"
39            << "*************************************\n";
40 
41   std::cout<<"======== TESTING BUILDING ===========\n";
42 
43   std::vector<vpdfl_axis_gaussian_sampler *> generator(4);//
44   constexpr unsigned nDims = 2;
45   vnl_vector<double> mean0(nDims), var0(nDims), mean1(nDims), var1(nDims), mean2(nDims), var2(nDims), mean3(nDims), var3(nDims);
46   vpdfl_axis_gaussian PDF0, PDF1, PDF2, PDF3;
47 
48   mean0.fill(0.0); mean0(0) = 1.5;
49   mean1.fill(0.0); mean1(0) = -1.5;
50   mean2.fill(0.0); mean2(1) = 1.5;
51   mean3.fill(0.0); mean3(1) = -1.5;
52 
53   constexpr double trainPrincipleVariance = 0.5;
54   var0.fill(trainPrincipleVariance/10.0); var0(0) = trainPrincipleVariance;
55   var1.fill(trainPrincipleVariance/10.0); var1(0) = trainPrincipleVariance;
56   var2.fill(trainPrincipleVariance/10.0); var2(1) = trainPrincipleVariance;
57   var3.fill(trainPrincipleVariance/10.0); var3(1) = trainPrincipleVariance;
58 
59   PDF0.set(mean0, var0);
60   PDF1.set(mean1, var1);
61   PDF2.set(mean2, var2);
62   PDF3.set(mean3, var3);
63 
64   generator[0] = (vpdfl_axis_gaussian_sampler *)PDF0.new_sampler();
65   generator[1] = (vpdfl_axis_gaussian_sampler *)PDF1.new_sampler();
66   generator[2] = (vpdfl_axis_gaussian_sampler *)PDF2.new_sampler();
67   generator[3] = (vpdfl_axis_gaussian_sampler *)PDF3.new_sampler();
68   vnl_random rng;
69   rng.reseed(333248);
70   constexpr unsigned nSamples = 200;
71   std::vector<unsigned> labels(nSamples);
72   std::vector<vnl_vector<double> > data(nSamples);
73   vnl_vector<double> s;
74   std::cout << "Generating test data\n";
75   std::vector<unsigned> labelcount(4, 0u);
76   for (unsigned int i=0; i<nSamples; i++)
77   {
78     int c = rng.lrand32(3);
79     labels[i] = c/2;
80     labelcount[c] ++;
81     generator[c]->sample(s);
82     data[i] = s;
83   }
84 
85   constexpr unsigned nTestSamples = 1000;
86   std::vector<unsigned> testLabels(nTestSamples);
87   std::vector<vnl_vector<double> > testingVectors(nTestSamples);
88   for (unsigned int i=0; i<nTestSamples; i++)
89   {
90     int c = rng.lrand32(3);
91     testLabels[i] = c/2;
92     generator[c]->sample(s);
93     testingVectors[i]=s;
94   }
95 
96   delete generator[0];
97   delete generator[1];
98   delete generator[2];
99   delete generator[3];
100 
101   mbl_data_array_wrapper<vnl_vector<double> > trainingVectors(data);
102 
103 
104   std::cout << "****************The Training set****************\n";
105   std::cout << "The number of labels from each generators are respectively ";
106   std::cout << labelcount[0] << ' ' << labelcount[1] << ' ' << labelcount[2] << ' ' << labelcount[3] <<  std::endl;
107 
108   vnl_vector<double> x(nDims);
109   std::vector<double> out(1);
110   x.fill(0.0);
111   std::cout << "x(1) varies across from -2 to + 2\n";
112   std::cout << "x(0) varies down from -2 to + 2\n";
113 
114   clsfy_k_nearest_neighbour knn;
115   knn.set(data, labels);
116   knn.set_k(3);
117   std::cout  << std::endl << "KNN output\n";
118   std::cout << std::setprecision(4);
119   for (x(0) = -2; x(0) <= 2 ; x(0) += 0.25)
120   {
121     for (x(1) = -2; x(1) <= 2 ; x(1) += 0.25)
122     {
123       knn.class_probabilities(out, x);
124       std::cout << std::fixed << std::setw(3) << out[0] << ' ';
125     }
126     std::cout << std::endl;
127   }
128 
129   for (x(0) = -2; x(0) <= 2 ; x(0) += 0.25)
130   {
131     for (x(1) = -2; x(1) <= 2 ; x(1) += 0.25)
132     {
133       std::cout << knn.classify(x);
134     }
135     std::cout << std::endl;
136   }
137 
138   clsfy_rbf_parzen win;
139   win.set(data, labels);
140   win.set_rbf_width(0.08);
141   win.set_power(10);
142   std::cout << std::endl << "Training data distribution\n";
143   std::cout << std::setprecision(1);
144   for (x(0) = -2; x(0) <= 2 ; x(0) += 0.25)
145   {
146     for (x(1) = -2; x(1) <= 2 ; x(1) += 0.25)
147     {
148       double v = win.weightings(x);
149       if (v < 0.01)
150         std::cout << " 0   ";
151       else
152         std::cout << std::fixed << std::setw(4) << v << ' ';
153     }
154     std::cout << std::endl;
155   }
156 
157   std::cout << "\n*********Testing Support Vector Training*********\n";
158   std::cout << std::setprecision(6) << std::resetiosflags(std::ios::floatfield);
159 
160   clsfy_rbf_svm_smo_1_builder builder;
161   builder.set_bound_on_multipliers(1e2);
162   builder.set_rbf_width(1.05);
163 
164   clsfy_rbf_svm classifier3;
165 
166   vul_timer mytimer;
167 
168   double error = builder.build(classifier3, trainingVectors, labels);
169 
170   long realtime = mytimer.real();
171   std::cout << "\nOptimisation took " << realtime/1000.0 << " seconds\n";
172   std::cout << "\nLagrangians ";
173   for (unsigned i=0; i < classifier3.n_support_vectors(); ++i)
174     std::cout << classifier3.lagrangians()[i] << " ";
175   std::cout << std::endl;
176 
177   std::cout << "Training Error " << error << std::endl;
178 #if 0
179   mbl_data_wrapper<vnl_vector<double> > &data = trainingVectors.vectorData();
180   data.first();
181   for (unsigned int i=0; i<nSamples; i++)
182   {
183     std::cout << i << '\t' << std::setw(8) << pClassifier->log_l(data.current()) << '\t' << pClassifier->classify(data.current()) <<
184       " should be " << labels(i) <<std::endl;
185     data.next();
186   }
187 #endif
188   // print input, print output
189 
190   std::cout << std::setprecision(4);
191   for (x(0) = -2; x(0) <= 2 ; x(0) += 0.25)
192   {
193     for (x(1) = -2; x(1) <= 2 ; x(1) += 0.25)
194     {
195       classifier3.class_probabilities(out, x);
196       std::cout << std::fixed << std::setw(3) << out[0] << ' ';
197     }
198     std::cout << std::endl;
199   }
200 
201   for (x(0) = -2; x(0) <= 2 ; x(0) += 0.25)
202   {
203     for (x(1) = -2; x(1) <= 2 ; x(1) += 0.25)
204     {
205       std::cout << classifier3.classify(x);
206     }
207     std::cout << std::endl;
208   }
209 
210   std::cout << "There are " << classifier3.n_support_vectors() << " Support Vectors\n";
211 
212   TEST_NEAR("Training Error < 0.05", error, 0.0, 0.05);
213 
214   std::cout << "\nError on Testing set ";
215   mbl_data_array_wrapper<vnl_vector<double> > test_vector_data(testingVectors);
216   double testError = clsfy_test_error(classifier3, test_vector_data, testLabels);
217   std::cout << testError << std::endl;
218 
219   TEST_NEAR("Test Error < 0.1", testError, 0.0, 0.1);
220 
221   std::cout << "\n****************Testing classifier IO**************\n";
222   vsl_add_to_binary_loader(clsfy_rbf_svm());
223   std::string test_path = "test_rbf_svm.bvl.tmp";
224 
225   vsl_b_ofstream bfs_out(test_path);
226   TEST(("Opened " + test_path + " for writing").c_str(), (!bfs_out ), false);
227   vsl_b_write(bfs_out, classifier3);
228   vsl_b_write(bfs_out, (clsfy_classifier_base *) &classifier3);
229   bfs_out.close();
230 
231   clsfy_rbf_svm svmi;
232   clsfy_classifier_base *pClassifier2=nullptr;
233 
234   vsl_b_ifstream bfs_in(test_path);
235   TEST(("Opened " + test_path + " for reading").c_str(), (!bfs_in ), false);
236   vsl_b_read(bfs_in, svmi);
237   vsl_b_read(bfs_in, pClassifier2);
238   bfs_in.close();
239 #if !LEAVE_FILES_BEHIND
240   vpl_unlink(test_path.c_str());
241 #endif
242 
243   std::cout<<"Saved : " << classifier3 << std::endl;
244   std::cout<<"Loaded: " << svmi << std::endl;
245   std::cout<<"Loaded: " << pClassifier2 << std::endl;
246 
247   clsfy_rbf_svm &svmo = *(clsfy_rbf_svm *)&classifier3;
248 
249   TEST("Saved Classifier = Loaded Classifier",
250        svmo.bias() == svmi.bias() &&
251        svmo.lagrangians() == svmi.lagrangians() &&
252        svmo.support_vectors() == svmi.support_vectors(),
253        true);
254 
255   double ll_o = svmo.log_l(vnl_vector<double>(nDims, 0.3));
256   double ll_i = svmi.log_l(vnl_vector<double>(nDims, 0.3));
257   TEST_NEAR("Saved Classifier.log_l() = Loaded Classifier.log_l()",
258             ll_o, ll_i, 1e-11);
259 
260   TEST("Saved Classifier = Classifier Loaded by Base Class Ptr",
261        svmo.is_a(), pClassifier2->is_a());
262 
263   TEST("Loaded Classifier type = Original Classifier type",
264        pClassifier2->is_class(svmo.is_a()), true);
265 
266   TEST("Loaded Classifier has base class type",
267        pClassifier2->is_class("clsfy_classifier_base"), true);
268 
269   std::cout << std::setprecision(6) << std::resetiosflags(std::ios::floatfield);
270 
271   delete pClassifier2;
272   vsl_delete_all_loaders();
273 
274   std::cout << std::setprecision(6) << std::resetiosflags(std::ios::floatfield);
275 }
276 
277 TESTMAIN(test_rbf_svm_smo);
278