source: trunk/test/knn_test.cc @ 2338

Last change on this file since 2338 was 2338, checked in by Peter, 11 years ago

adding an archetype class for distance concept and use that class in KNN and NCC. Adding CopyConstructible? to requirement for Distance concept

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 11.1 KB
Line 
1// $Id: knn_test.cc 2338 2010-10-16 05:00:12Z peter $
2
3/*
4  Copyright (C) 2007, 2008 Jari Häkkinen, Peter Johansson, Markus Ringnér
5
6  This file is part of the yat library, http://dev.thep.lu.se/yat
7
8  The yat library is free software; you can redistribute it and/or
9  modify it under the terms of the GNU General Public License as
10  published by the Free Software Foundation; either version 3 of the
11  License, or (at your option) any later version.
12
13  The yat library is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  General Public License for more details.
17
18  You should have received a copy of the GNU General Public License
19  along with yat. If not, see <http://www.gnu.org/licenses/>.
20*/
21
22#include "Suite.h"
23
24#include "yat/classifier/KNN.h"
25#include "yat/classifier/KNN_ReciprocalDistance.h"
26#include "yat/classifier/KNN_ReciprocalRank.h"
27#include "yat/classifier/MatrixLookup.h"
28#include "yat/classifier/MatrixLookupWeighted.h"
29#include "yat/statistics/EuclideanDistance.h"
30#include "yat/utility/DataIterator.h"
31#include "yat/utility/Matrix.h"
32#include "yat/utility/MatrixWeighted.h"
33
34
35#include <cassert>
36#include <fstream>
37#include <iostream>
38#include <list>
39#include <string>
40#include <vector>
41
42
43using namespace theplu::yat;
44
45void compile_test(test::Suite&);
46utility::Matrix data(void);
47utility::MatrixWeighted data_weighted(void);
48double deviation(const utility::Matrix& a, const utility::Matrix& b);
49void test_unweighted(test::Suite&);
50void test_unweighted_weighted(test::Suite&);
51void test_weighted(test::Suite&);
52void test_reciprocal_ranks(test::Suite&);
53void test_reciprocal_distance(test::Suite&);
54void test_no_samples(test::Suite&);
55void test_no_features(test::Suite&);
56std::vector<std::string> vec_target(void);
57
58
59int main(int argc, char* argv[])
60{ 
61  test::Suite suite(argc, argv);
62  suite.err() << "testing knn" << std::endl;
63  test_unweighted(suite);
64  test_unweighted_weighted(suite);
65  test_weighted(suite);
66  test_reciprocal_ranks(suite);
67  test_reciprocal_distance(suite);
68  test_no_samples(suite);
69  test_no_features(suite);
70  compile_test(suite);
71  return suite.return_value();
72} 
73
74
75void compile_test(test::Suite& suite)
76{
77  if (false) {
78    boost::detail::dummy_constructor dummy;
79    test::distance_archetype distance(dummy);
80    classifier::KNN<test::distance_archetype> knn(distance);
81    knn.k(3);
82    knn.k();
83    classifier::SupervisedClassifier* knn2 = knn.make_classifier();
84    delete knn2;
85    utility::Matrix result;
86    knn.train(classifier::MatrixLookup(data()), classifier::Target());
87    knn.train(classifier::MatrixLookupWeighted(data_weighted()), 
88              classifier::Target());
89    knn.predict(classifier::MatrixLookup(data()), result);
90    knn.predict(classifier::MatrixLookupWeighted(data_weighted()), result);
91  }
92}
93
94
95utility::Matrix data(void)
96{
97  utility::Matrix data1(3,4);
98  for(size_t i=0;i<3;i++) {
99    data1(i,0)=3-i;
100    data1(i,1)=5-i;
101    data1(i,2)=i+1;
102    data1(i,3)=i+3;
103  }
104  return data1;
105}
106
107
108utility::MatrixWeighted data_weighted(void)
109{
110  utility::Matrix x = data();
111  utility::MatrixWeighted result(x.rows(), x.columns());
112  std::copy(x.begin(), x.end(), utility::data_iterator(result.begin()));
113  return result;
114}
115
116
117double deviation(const utility::Matrix& a, const utility::Matrix& b) {
118  assert(a.rows()==b.rows());
119  assert(b.columns()==b.columns());
120  double sl=0;
121  for (size_t i=0; i<a.rows(); i++){
122    for (size_t j=0; j<a.columns(); j++){
123      sl += std::abs(a(i,j)-b(i,j));
124    }
125  }
126  sl /= (a.columns()*a.rows());
127  return sl;
128}
129
130void test_unweighted(test::Suite& suite)
131{
132  ////////////////////////////////////////////////////////////////
133  // A test of training and predictions using unweighted data
134  ////////////////////////////////////////////////////////////////
135  suite.err() << "test of predictions using unweighted training " 
136              << "and test data\n";
137  utility::Matrix data1 = data();
138  classifier::MatrixLookup ml1(data1);
139  classifier::Target target1(vec_target());
140 
141  classifier::KNN<statistics::EuclideanDistance> knn1;
142  knn1.k(3);
143  knn1.train(ml1,target1);
144  utility::Matrix prediction1;
145  knn1.predict(ml1,prediction1);
146  utility::Matrix result1(2,4);
147  result1(0,0)=result1(0,1)=result1(1,2)=result1(1,3)=2.0;
148  result1(0,2)=result1(0,3)=result1(1,0)=result1(1,1)=1.0;
149  suite.add(suite.equal_range(result1.begin(), result1.end(), 
150                              prediction1.begin(), 1));
151}
152
153void test_unweighted_weighted(test::Suite& suite)
154{
155  suite.err() << "test of predictions using unweighted training "
156              << "and weighted test data\n";
157  utility::MatrixWeighted xw = data_weighted();
158  xw(2,0).weight()=0;
159 
160  classifier::MatrixLookupWeighted mlw1(xw);
161  classifier::KNN<statistics::EuclideanDistance> knn1;
162  knn1.k(3);
163  utility::Matrix data1 = data();
164  classifier::MatrixLookup ml1(data1);
165  classifier::Target target1(vec_target());
166  knn1.train(ml1,target1);
167  utility::Matrix prediction1;
168  knn1.predict(mlw1,prediction1); 
169  utility::Matrix result1(2,4);
170  result1(0,0)=result1(0,1)=result1(1,2)=result1(1,3)=2.0;
171  result1(0,2)=result1(0,3)=result1(1,0)=result1(1,1)=1.0;
172  result1(0,0)=1.0;
173  result1(1,0)=2.0;
174  suite.add(suite.equal_range(result1.begin(), result1.end(), 
175                              prediction1.begin(), 1));
176}
177
178void test_weighted(test::Suite& suite)
179{
180  ////////////////////////////////////////////////////////////////
181  // A test of training and test both weighted
182  ////////////////////////////////////////////////////////////////
183  suite.err() << "test of predictions using weighted training and test data\n";
184  suite.err() << "... uniform neighbor weighting" << std::endl;
185  utility::MatrixWeighted xw = data_weighted();
186  xw(2,0).weight()=0;
187  xw(0,1).weight()=0;
188  classifier::MatrixLookupWeighted mlw1(xw);
189   
190  utility::MatrixWeighted xw2 = data_weighted();
191  xw2(2,3).weight()=0;
192  classifier::MatrixLookupWeighted mlw2(xw2);
193  classifier::KNN<statistics::EuclideanDistance> knn2;
194  knn2.k(3);
195  classifier::Target target1(vec_target());
196  knn2.train(mlw2,target1);
197  utility::Matrix prediction1;
198  knn2.predict(mlw1,prediction1); 
199  utility::Matrix result1(2,4);
200  result1(0,0)=result1(0,1)=result1(1,2)=result1(1,3)=2.0;
201  result1(0,2)=result1(0,3)=result1(1,0)=result1(1,1)=1.0;
202  result1(0,0)=1.0;
203  result1(1,0)=2.0;
204  result1(0,1)=1.0;
205  result1(1,1)=2.0;
206  suite.add(suite.equal_range(result1.begin(), result1.end(), 
207                              prediction1.begin(), 1));
208}
209
210
211void test_reciprocal_ranks(test::Suite& suite)
212{
213  ////////////////////////////////////////////////////////////////
214  // A test of reciprocal ranks weighting with training and test both weighted
215  ////////////////////////////////////////////////////////////////
216  suite.err() << "... reciprokal rank neighbor weighting" << std::endl;
217  utility::MatrixWeighted xw2 = data_weighted();
218  xw2(2,3).weight()=0;
219  classifier::MatrixLookupWeighted mlw2(xw2);
220  utility::MatrixWeighted xw3 = data_weighted();
221  xw3(1,3).data()=7;
222  xw3(2,3).weight()=0;
223  classifier::MatrixLookupWeighted mlw3(xw3);
224  classifier::KNN<statistics::EuclideanDistance
225    ,classifier::KNN_ReciprocalRank> knn3;
226  knn3.k(3);
227  classifier::Target target1(vec_target());
228  knn3.train(mlw2,target1);
229  utility::Matrix prediction1;
230  knn3.predict(mlw3,prediction1); 
231  utility::Matrix result1(2,4);
232  result1(0,0)=result1(1,3)=1.0;
233  result1(0,3)=result1(1,0)=5.0/6.0;
234  result1(0,2)=result1(1,1)=1.0/2.0;
235  result1(0,1)=result1(1,2)=4.0/3.0;
236  suite.add(suite.equal_range(result1.begin(), result1.end(), 
237                              prediction1.begin(), 1));
238}
239
240void test_reciprocal_distance(test::Suite& suite)
241{
242  ////////////////////////////////////////////////////////////////
243  // A test of reciprocal distance weighting with training and test both weighted
244  ////////////////////////////////////////////////////////////////
245  suite.err() << "... reciprocal distance neighbor weighting" << std::endl;
246  classifier::KNN<statistics::EuclideanDistance,classifier::KNN_ReciprocalDistance> 
247    knn4;
248  knn4.k(3);
249  utility::MatrixWeighted xw2 = data_weighted();
250  xw2(2,3).weight()=0;
251  classifier::MatrixLookupWeighted mlw2(xw2);
252  utility::MatrixWeighted xw3 = data_weighted();
253  xw3(1,3).data()=7;
254  xw3(2,3).weight()=0;
255  classifier::MatrixLookupWeighted mlw3(xw3);
256  classifier::Target target1(vec_target());
257  knn4.train(mlw2,target1);
258  utility::Matrix prediction1;
259  knn4.predict(mlw3,prediction1); 
260  if (!(std::isinf(prediction1(0,0)) && std::isinf(prediction1(0,1)) && 
261        std::isinf(prediction1(1,2)) && 
262        suite.equal_fix(prediction1(1,3), 1.0/3.6742346141747673, 1e-16) &&
263        suite.equal_fix(prediction1(1,0), 
264                        1.0/2.82842712475+1.0/2.4494897427831779, 1e-11)
265        )){
266    suite.err() << "Difference to expected prediction too large\n";
267    suite.add(false);
268  }
269}
270
271
272void test_no_samples(test::Suite& suite)
273{
274  ////////////////////////////////////////////////////////////////
275  // A test of when a class has no training samples, should give nan
276  // in predictions. Also tests that k is reduced if not enough
277  // training samples.
278  ////////////////////////////////////////////////////////////////
279  //Keep only the second class in the training samples
280  std::vector<size_t> ind(2,2);
281  ind[1]=3;
282  classifier::Target target1(vec_target());
283  classifier::Target target2(target1,utility::Index(ind));
284
285  utility::MatrixWeighted xw = data_weighted();
286  xw(2,3).weight()=0.0;
287
288  classifier::MatrixLookupWeighted mlw4(xw, utility::Index(xw.rows()),
289                                        utility::Index(ind));
290  classifier::KNN<statistics::EuclideanDistance> knn5;
291  knn5.k(3);
292  knn5.train(mlw4,target2);
293  utility::MatrixWeighted xw3 = data_weighted();
294  xw3(1,3).data()=7;
295  xw3(2,3).weight()=0;
296  classifier::MatrixLookupWeighted mlw3(xw3);
297  utility::Matrix prediction1;
298  knn5.predict(mlw3,prediction1); 
299  if (!(std::isnan(prediction1(0,0)) && std::isnan(prediction1(0,1)) && 
300        std::isnan(prediction1(0,2)) && std::isnan(prediction1(0,3)) &&
301        suite.equal(prediction1(1,0),2.0) &&
302        suite.equal(prediction1(1,1),2.0) &&
303        suite.equal(prediction1(1,2),2.0) &&
304        suite.equal(prediction1(1,3),2.0) )) {
305    suite.err() << "Difference to expected prediction too large\n";
306    suite.add(false);
307  }
308}
309
310void test_no_features(test::Suite& suite)
311{
312  ////////////////////////////////////////////////////////////////
313  // A test of when a test sample has no variables with non-zero
314  // weights in common with training samples: should not vote
315  ////////////////////////////////////////////////////////////////
316  suite.err() << "test of predictions with nan distances (set to infinity in KNN)\n";
317  utility::MatrixWeighted xw1 = data_weighted();
318  xw1(1,0).weight()=xw1(1,1).weight()=xw1(2,0).weight()=xw1(2,1).weight()=0.0;
319  classifier::MatrixLookupWeighted mlw1(xw1);
320
321  classifier::KNN<statistics::EuclideanDistance> knn6;
322  knn6.k(3);
323  classifier::Target target1(vec_target());
324  knn6.train(mlw1,target1);
325
326  utility::MatrixWeighted xw3 = data_weighted();
327  xw3(1,3).data()=7;
328  xw3(0,0).weight()=0;
329  classifier::MatrixLookupWeighted mlw3(xw3);
330  utility::Matrix prediction1;
331  knn6.predict(mlw3,prediction1); 
332  utility::Matrix result1(2,4);
333  result1(0,0)=0;
334  result1(0,2)=result1(1,1)=result1(1,3)=1.0;
335  result1(0,1)=result1(0,3)=result1(1,0)=result1(1,2)=2.0;
336  suite.add(suite.equal_range(result1.begin(), result1.end(), 
337                              prediction1.begin(), 1));
338}
339
340std::vector<std::string> vec_target(void)
341{
342  std::vector<std::string> vec1(4, "pos");
343  vec1[0]="neg";
344  vec1[1]="neg";
345  return vec1;
346}
347
Note: See TracBrowser for help on using the repository browser.