Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Tetragramm
GitHub Repository: Tetragramm/opencv
Path: blob/master/modules/ml/src/svm.cpp
16337 views
1
/*M///////////////////////////////////////////////////////////////////////////////////////
2
//
3
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
4
//
5
// By downloading, copying, installing or using the software you agree to this license.
6
// If you do not agree to this license, do not download, install,
7
// copy or use the software.
8
//
9
//
10
// License Agreement
11
// For Open Source Computer Vision Library
12
//
13
// Copyright (C) 2000, Intel Corporation, all rights reserved.
14
// Copyright (C) 2014, Itseez Inc, all rights reserved.
15
// Third party copyrights are property of their respective owners.
16
//
17
// Redistribution and use in source and binary forms, with or without modification,
18
// are permitted provided that the following conditions are met:
19
//
20
// * Redistribution's of source code must retain the above copyright notice,
21
// this list of conditions and the following disclaimer.
22
//
23
// * Redistribution's in binary form must reproduce the above copyright notice,
24
// this list of conditions and the following disclaimer in the documentation
25
// and/or other materials provided with the distribution.
26
//
27
// * The name of the copyright holders may not be used to endorse or promote products
28
// derived from this software without specific prior written permission.
29
//
30
// This software is provided by the copyright holders and contributors "as is" and
31
// any express or implied warranties, including, but not limited to, the implied
32
// warranties of merchantability and fitness for a particular purpose are disclaimed.
33
// In no event shall the Intel Corporation or contributors be liable for any direct,
34
// indirect, incidental, special, exemplary, or consequential damages
35
// (including, but not limited to, procurement of substitute goods or services;
36
// loss of use, data, or profits; or business interruption) however caused
37
// and on any theory of liability, whether in contract, strict liability,
38
// or tort (including negligence or otherwise) arising in any way out of
39
// the use of this software, even if advised of the possibility of such damage.
40
//
41
//M*/
42
43
#include "precomp.hpp"
44
45
#include <stdarg.h>
46
#include <ctype.h>
47
48
/****************************************************************************************\
49
COPYRIGHT NOTICE
50
----------------
51
52
The code has been derived from libsvm library (version 2.6)
53
(http://www.csie.ntu.edu.tw/~cjlin/libsvm).
54
55
Here is the original copyright:
56
------------------------------------------------------------------------------------------
57
Copyright (c) 2000-2003 Chih-Chung Chang and Chih-Jen Lin
58
All rights reserved.
59
60
Redistribution and use in source and binary forms, with or without
61
modification, are permitted provided that the following conditions
62
are met:
63
64
1. Redistributions of source code must retain the above copyright
65
notice, this list of conditions and the following disclaimer.
66
67
2. Redistributions in binary form must reproduce the above copyright
68
notice, this list of conditions and the following disclaimer in the
69
documentation and/or other materials provided with the distribution.
70
71
3. Neither name of copyright holders nor the names of its contributors
72
may be used to endorse or promote products derived from this software
73
without specific prior written permission.
74
75
76
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
77
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
78
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
79
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
80
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
81
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
82
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
83
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
84
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
85
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
86
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
87
\****************************************************************************************/
88
89
namespace cv { namespace ml {
90
91
typedef float Qfloat;
92
const int QFLOAT_TYPE = DataDepth<Qfloat>::value;
93
94
// Param Grid
95
static void checkParamGrid(const ParamGrid& pg)
96
{
97
if( pg.minVal > pg.maxVal )
98
CV_Error( CV_StsBadArg, "Lower bound of the grid must be less then the upper one" );
99
if( pg.minVal < DBL_EPSILON )
100
CV_Error( CV_StsBadArg, "Lower bound of the grid must be positive" );
101
if( pg.logStep < 1. + FLT_EPSILON )
102
CV_Error( CV_StsBadArg, "Grid step must greater then 1" );
103
}
104
105
// SVM training parameters
106
struct SvmParams
107
{
108
int svmType;
109
int kernelType;
110
double gamma;
111
double coef0;
112
double degree;
113
double C;
114
double nu;
115
double p;
116
Mat classWeights;
117
TermCriteria termCrit;
118
119
SvmParams()
120
{
121
svmType = SVM::C_SVC;
122
kernelType = SVM::RBF;
123
degree = 0;
124
gamma = 1;
125
coef0 = 0;
126
C = 1;
127
nu = 0;
128
p = 0;
129
termCrit = TermCriteria( CV_TERMCRIT_ITER+CV_TERMCRIT_EPS, 1000, FLT_EPSILON );
130
}
131
132
SvmParams( int _svmType, int _kernelType,
133
double _degree, double _gamma, double _coef0,
134
double _Con, double _nu, double _p,
135
const Mat& _classWeights, TermCriteria _termCrit )
136
{
137
svmType = _svmType;
138
kernelType = _kernelType;
139
degree = _degree;
140
gamma = _gamma;
141
coef0 = _coef0;
142
C = _Con;
143
nu = _nu;
144
p = _p;
145
classWeights = _classWeights;
146
termCrit = _termCrit;
147
}
148
149
};
150
151
/////////////////////////////////////// SVM kernel ///////////////////////////////////////
152
class SVMKernelImpl CV_FINAL : public SVM::Kernel
153
{
154
public:
155
SVMKernelImpl( const SvmParams& _params = SvmParams() )
156
{
157
params = _params;
158
}
159
160
int getType() const CV_OVERRIDE
161
{
162
return params.kernelType;
163
}
164
165
void calc_non_rbf_base( int vcount, int var_count, const float* vecs,
166
const float* another, Qfloat* results,
167
double alpha, double beta )
168
{
169
int j, k;
170
for( j = 0; j < vcount; j++ )
171
{
172
const float* sample = &vecs[j*var_count];
173
double s = 0;
174
for( k = 0; k <= var_count - 4; k += 4 )
175
s += sample[k]*another[k] + sample[k+1]*another[k+1] +
176
sample[k+2]*another[k+2] + sample[k+3]*another[k+3];
177
for( ; k < var_count; k++ )
178
s += sample[k]*another[k];
179
results[j] = (Qfloat)(s*alpha + beta);
180
}
181
}
182
183
void calc_linear( int vcount, int var_count, const float* vecs,
184
const float* another, Qfloat* results )
185
{
186
calc_non_rbf_base( vcount, var_count, vecs, another, results, 1, 0 );
187
}
188
189
void calc_poly( int vcount, int var_count, const float* vecs,
190
const float* another, Qfloat* results )
191
{
192
Mat R( 1, vcount, QFLOAT_TYPE, results );
193
calc_non_rbf_base( vcount, var_count, vecs, another, results, params.gamma, params.coef0 );
194
if( vcount > 0 )
195
pow( R, params.degree, R );
196
}
197
198
void calc_sigmoid( int vcount, int var_count, const float* vecs,
199
const float* another, Qfloat* results )
200
{
201
int j;
202
calc_non_rbf_base( vcount, var_count, vecs, another, results,
203
-2*params.gamma, -2*params.coef0 );
204
// TODO: speedup this
205
for( j = 0; j < vcount; j++ )
206
{
207
Qfloat t = results[j];
208
Qfloat e = std::exp(-std::abs(t));
209
if( t > 0 )
210
results[j] = (Qfloat)((1. - e)/(1. + e));
211
else
212
results[j] = (Qfloat)((e - 1.)/(e + 1.));
213
}
214
}
215
216
217
void calc_rbf( int vcount, int var_count, const float* vecs,
218
const float* another, Qfloat* results )
219
{
220
double gamma = -params.gamma;
221
int j, k;
222
223
for( j = 0; j < vcount; j++ )
224
{
225
const float* sample = &vecs[j*var_count];
226
double s = 0;
227
228
for( k = 0; k <= var_count - 4; k += 4 )
229
{
230
double t0 = sample[k] - another[k];
231
double t1 = sample[k+1] - another[k+1];
232
233
s += t0*t0 + t1*t1;
234
235
t0 = sample[k+2] - another[k+2];
236
t1 = sample[k+3] - another[k+3];
237
238
s += t0*t0 + t1*t1;
239
}
240
241
for( ; k < var_count; k++ )
242
{
243
double t0 = sample[k] - another[k];
244
s += t0*t0;
245
}
246
results[j] = (Qfloat)(s*gamma);
247
}
248
249
if( vcount > 0 )
250
{
251
Mat R( 1, vcount, QFLOAT_TYPE, results );
252
exp( R, R );
253
}
254
}
255
256
/// Histogram intersection kernel
257
void calc_intersec( int vcount, int var_count, const float* vecs,
258
const float* another, Qfloat* results )
259
{
260
int j, k;
261
for( j = 0; j < vcount; j++ )
262
{
263
const float* sample = &vecs[j*var_count];
264
double s = 0;
265
for( k = 0; k <= var_count - 4; k += 4 )
266
s += std::min(sample[k],another[k]) + std::min(sample[k+1],another[k+1]) +
267
std::min(sample[k+2],another[k+2]) + std::min(sample[k+3],another[k+3]);
268
for( ; k < var_count; k++ )
269
s += std::min(sample[k],another[k]);
270
results[j] = (Qfloat)(s);
271
}
272
}
273
274
/// Exponential chi2 kernel
275
void calc_chi2( int vcount, int var_count, const float* vecs,
276
const float* another, Qfloat* results )
277
{
278
Mat R( 1, vcount, QFLOAT_TYPE, results );
279
double gamma = -params.gamma;
280
int j, k;
281
for( j = 0; j < vcount; j++ )
282
{
283
const float* sample = &vecs[j*var_count];
284
double chi2 = 0;
285
for(k = 0 ; k < var_count; k++ )
286
{
287
double d = sample[k]-another[k];
288
double devisor = sample[k]+another[k];
289
/// if devisor == 0, the Chi2 distance would be zero,
290
// but calculation would rise an error because of dividing by zero
291
if (devisor != 0)
292
{
293
chi2 += d*d/devisor;
294
}
295
}
296
results[j] = (Qfloat) (gamma*chi2);
297
}
298
if( vcount > 0 )
299
exp( R, R );
300
}
301
302
void calc( int vcount, int var_count, const float* vecs,
303
const float* another, Qfloat* results ) CV_OVERRIDE
304
{
305
switch( params.kernelType )
306
{
307
case SVM::LINEAR:
308
calc_linear(vcount, var_count, vecs, another, results);
309
break;
310
case SVM::RBF:
311
calc_rbf(vcount, var_count, vecs, another, results);
312
break;
313
case SVM::POLY:
314
calc_poly(vcount, var_count, vecs, another, results);
315
break;
316
case SVM::SIGMOID:
317
calc_sigmoid(vcount, var_count, vecs, another, results);
318
break;
319
case SVM::CHI2:
320
calc_chi2(vcount, var_count, vecs, another, results);
321
break;
322
case SVM::INTER:
323
calc_intersec(vcount, var_count, vecs, another, results);
324
break;
325
default:
326
CV_Error(CV_StsBadArg, "Unknown kernel type");
327
}
328
const Qfloat max_val = (Qfloat)(FLT_MAX*1e-3);
329
for( int j = 0; j < vcount; j++ )
330
{
331
if( results[j] > max_val )
332
results[j] = max_val;
333
}
334
}
335
336
SvmParams params;
337
};
338
339
340
341
/////////////////////////////////////////////////////////////////////////
342
343
static void sortSamplesByClasses( const Mat& _samples, const Mat& _responses,
344
vector<int>& sidx_all, vector<int>& class_ranges )
345
{
346
int i, nsamples = _samples.rows;
347
CV_Assert( _responses.isContinuous() && _responses.checkVector(1, CV_32S) == nsamples );
348
349
setRangeVector(sidx_all, nsamples);
350
351
const int* rptr = _responses.ptr<int>();
352
std::sort(sidx_all.begin(), sidx_all.end(), cmp_lt_idx<int>(rptr));
353
class_ranges.clear();
354
class_ranges.push_back(0);
355
356
for( i = 0; i < nsamples; i++ )
357
{
358
if( i == nsamples-1 || rptr[sidx_all[i]] != rptr[sidx_all[i+1]] )
359
class_ranges.push_back(i+1);
360
}
361
}
362
363
//////////////////////// SVM implementation //////////////////////////////
364
365
Ptr<ParamGrid> SVM::getDefaultGridPtr( int param_id)
366
{
367
ParamGrid grid = getDefaultGrid(param_id); // this is not a nice solution..
368
return makePtr<ParamGrid>(grid.minVal, grid.maxVal, grid.logStep);
369
}
370
371
ParamGrid SVM::getDefaultGrid( int param_id )
372
{
373
ParamGrid grid;
374
if( param_id == SVM::C )
375
{
376
grid.minVal = 0.1;
377
grid.maxVal = 500;
378
grid.logStep = 5; // total iterations = 5
379
}
380
else if( param_id == SVM::GAMMA )
381
{
382
grid.minVal = 1e-5;
383
grid.maxVal = 0.6;
384
grid.logStep = 15; // total iterations = 4
385
}
386
else if( param_id == SVM::P )
387
{
388
grid.minVal = 0.01;
389
grid.maxVal = 100;
390
grid.logStep = 7; // total iterations = 4
391
}
392
else if( param_id == SVM::NU )
393
{
394
grid.minVal = 0.01;
395
grid.maxVal = 0.2;
396
grid.logStep = 3; // total iterations = 3
397
}
398
else if( param_id == SVM::COEF )
399
{
400
grid.minVal = 0.1;
401
grid.maxVal = 300;
402
grid.logStep = 14; // total iterations = 3
403
}
404
else if( param_id == SVM::DEGREE )
405
{
406
grid.minVal = 0.01;
407
grid.maxVal = 4;
408
grid.logStep = 7; // total iterations = 3
409
}
410
else
411
cvError( CV_StsBadArg, "SVM::getDefaultGrid", "Invalid type of parameter "
412
"(use one of SVM::C, SVM::GAMMA et al.)", __FILE__, __LINE__ );
413
return grid;
414
}
415
416
417
class SVMImpl CV_FINAL : public SVM
418
{
419
public:
420
struct DecisionFunc
421
{
422
DecisionFunc(double _rho, int _ofs) : rho(_rho), ofs(_ofs) {}
423
DecisionFunc() : rho(0.), ofs(0) {}
424
double rho;
425
int ofs;
426
};
427
428
// Generalized SMO+SVMlight algorithm
429
// Solves:
430
//
431
// min [0.5(\alpha^T Q \alpha) + b^T \alpha]
432
//
433
// y^T \alpha = \delta
434
// y_i = +1 or -1
435
// 0 <= alpha_i <= Cp for y_i = 1
436
// 0 <= alpha_i <= Cn for y_i = -1
437
//
438
// Given:
439
//
440
// Q, b, y, Cp, Cn, and an initial feasible point \alpha
441
// l is the size of vectors and matrices
442
// eps is the stopping criterion
443
//
444
// solution will be put in \alpha, objective value will be put in obj
445
//
446
class Solver
447
{
448
public:
449
enum { MIN_CACHE_SIZE = (40 << 20) /* 40Mb */, MAX_CACHE_SIZE = (500 << 20) /* 500Mb */ };
450
451
typedef bool (Solver::*SelectWorkingSet)( int& i, int& j );
452
typedef Qfloat* (Solver::*GetRow)( int i, Qfloat* row, Qfloat* dst, bool existed );
453
typedef void (Solver::*CalcRho)( double& rho, double& r );
454
455
struct KernelRow
456
{
457
KernelRow() { idx = -1; prev = next = 0; }
458
KernelRow(int _idx, int _prev, int _next) : idx(_idx), prev(_prev), next(_next) {}
459
int idx;
460
int prev;
461
int next;
462
};
463
464
struct SolutionInfo
465
{
466
SolutionInfo() { obj = rho = upper_bound_p = upper_bound_n = r = 0; }
467
double obj;
468
double rho;
469
double upper_bound_p;
470
double upper_bound_n;
471
double r; // for Solver_NU
472
};
473
474
void clear()
475
{
476
alpha_vec = 0;
477
select_working_set_func = 0;
478
calc_rho_func = 0;
479
get_row_func = 0;
480
lru_cache.clear();
481
}
482
483
Solver( const Mat& _samples, const vector<schar>& _y,
484
vector<double>& _alpha, const vector<double>& _b,
485
double _Cp, double _Cn,
486
const Ptr<SVM::Kernel>& _kernel, GetRow _get_row,
487
SelectWorkingSet _select_working_set, CalcRho _calc_rho,
488
TermCriteria _termCrit )
489
{
490
clear();
491
492
samples = _samples;
493
sample_count = samples.rows;
494
var_count = samples.cols;
495
496
y_vec = _y;
497
alpha_vec = &_alpha;
498
alpha_count = (int)alpha_vec->size();
499
b_vec = _b;
500
kernel = _kernel;
501
502
C[0] = _Cn;
503
C[1] = _Cp;
504
eps = _termCrit.epsilon;
505
max_iter = _termCrit.maxCount;
506
507
G_vec.resize(alpha_count);
508
alpha_status_vec.resize(alpha_count);
509
buf[0].resize(sample_count*2);
510
buf[1].resize(sample_count*2);
511
512
select_working_set_func = _select_working_set;
513
CV_Assert(select_working_set_func != 0);
514
515
calc_rho_func = _calc_rho;
516
CV_Assert(calc_rho_func != 0);
517
518
get_row_func = _get_row;
519
CV_Assert(get_row_func != 0);
520
521
// assume that for large training sets ~25% of Q matrix is used
522
int64 csize = (int64)sample_count*sample_count/4;
523
csize = std::max(csize, (int64)(MIN_CACHE_SIZE/sizeof(Qfloat)) );
524
csize = std::min(csize, (int64)(MAX_CACHE_SIZE/sizeof(Qfloat)) );
525
max_cache_size = (int)((csize + sample_count-1)/sample_count);
526
max_cache_size = std::min(std::max(max_cache_size, 1), sample_count);
527
cache_size = 0;
528
529
lru_cache.clear();
530
lru_cache.resize(sample_count+1, KernelRow(-1, 0, 0));
531
lru_first = lru_last = 0;
532
lru_cache_data.create(max_cache_size, sample_count, QFLOAT_TYPE);
533
}
534
535
Qfloat* get_row_base( int i, bool* _existed )
536
{
537
int i1 = i < sample_count ? i : i - sample_count;
538
KernelRow& kr = lru_cache[i1+1];
539
if( _existed )
540
*_existed = kr.idx >= 0;
541
if( kr.idx < 0 )
542
{
543
if( cache_size < max_cache_size )
544
{
545
kr.idx = cache_size;
546
cache_size++;
547
if (!lru_last)
548
lru_last = i1+1;
549
}
550
else
551
{
552
KernelRow& last = lru_cache[lru_last];
553
kr.idx = last.idx;
554
last.idx = -1;
555
lru_cache[last.prev].next = 0;
556
lru_last = last.prev;
557
last.prev = 0;
558
last.next = 0;
559
}
560
kernel->calc( sample_count, var_count, samples.ptr<float>(),
561
samples.ptr<float>(i1), lru_cache_data.ptr<Qfloat>(kr.idx) );
562
}
563
else
564
{
565
if( kr.next )
566
lru_cache[kr.next].prev = kr.prev;
567
else
568
lru_last = kr.prev;
569
if( kr.prev )
570
lru_cache[kr.prev].next = kr.next;
571
else
572
lru_first = kr.next;
573
}
574
if (lru_first)
575
lru_cache[lru_first].prev = i1+1;
576
kr.next = lru_first;
577
kr.prev = 0;
578
lru_first = i1+1;
579
580
return lru_cache_data.ptr<Qfloat>(kr.idx);
581
}
582
583
Qfloat* get_row_svc( int i, Qfloat* row, Qfloat*, bool existed )
584
{
585
if( !existed )
586
{
587
const schar* _y = &y_vec[0];
588
int j, len = sample_count;
589
590
if( _y[i] > 0 )
591
{
592
for( j = 0; j < len; j++ )
593
row[j] = _y[j]*row[j];
594
}
595
else
596
{
597
for( j = 0; j < len; j++ )
598
row[j] = -_y[j]*row[j];
599
}
600
}
601
return row;
602
}
603
604
Qfloat* get_row_one_class( int, Qfloat* row, Qfloat*, bool )
605
{
606
return row;
607
}
608
609
Qfloat* get_row_svr( int i, Qfloat* row, Qfloat* dst, bool )
610
{
611
int j, len = sample_count;
612
Qfloat* dst_pos = dst;
613
Qfloat* dst_neg = dst + len;
614
if( i >= len )
615
std::swap(dst_pos, dst_neg);
616
617
for( j = 0; j < len; j++ )
618
{
619
Qfloat t = row[j];
620
dst_pos[j] = t;
621
dst_neg[j] = -t;
622
}
623
return dst;
624
}
625
626
Qfloat* get_row( int i, float* dst )
627
{
628
bool existed = false;
629
float* row = get_row_base( i, &existed );
630
return (this->*get_row_func)( i, row, dst, existed );
631
}
632
633
#undef is_upper_bound
634
#define is_upper_bound(i) (alpha_status[i] > 0)
635
636
#undef is_lower_bound
637
#define is_lower_bound(i) (alpha_status[i] < 0)
638
639
#undef is_free
640
#define is_free(i) (alpha_status[i] == 0)
641
642
#undef get_C
643
#define get_C(i) (C[y[i]>0])
644
645
#undef update_alpha_status
646
#define update_alpha_status(i) \
647
alpha_status[i] = (schar)(alpha[i] >= get_C(i) ? 1 : alpha[i] <= 0 ? -1 : 0)
648
649
#undef reconstruct_gradient
650
#define reconstruct_gradient() /* empty for now */
651
652
bool solve_generic( SolutionInfo& si )
653
{
654
const schar* y = &y_vec[0];
655
double* alpha = &alpha_vec->at(0);
656
schar* alpha_status = &alpha_status_vec[0];
657
double* G = &G_vec[0];
658
double* b = &b_vec[0];
659
660
int iter = 0;
661
int i, j, k;
662
663
// 1. initialize gradient and alpha status
664
for( i = 0; i < alpha_count; i++ )
665
{
666
update_alpha_status(i);
667
G[i] = b[i];
668
if( fabs(G[i]) > 1e200 )
669
return false;
670
}
671
672
for( i = 0; i < alpha_count; i++ )
673
{
674
if( !is_lower_bound(i) )
675
{
676
const Qfloat *Q_i = get_row( i, &buf[0][0] );
677
double alpha_i = alpha[i];
678
679
for( j = 0; j < alpha_count; j++ )
680
G[j] += alpha_i*Q_i[j];
681
}
682
}
683
684
// 2. optimization loop
685
for(;;)
686
{
687
const Qfloat *Q_i, *Q_j;
688
double C_i, C_j;
689
double old_alpha_i, old_alpha_j, alpha_i, alpha_j;
690
double delta_alpha_i, delta_alpha_j;
691
692
#ifdef _DEBUG
693
for( i = 0; i < alpha_count; i++ )
694
{
695
if( fabs(G[i]) > 1e+300 )
696
return false;
697
698
if( fabs(alpha[i]) > 1e16 )
699
return false;
700
}
701
#endif
702
703
if( (this->*select_working_set_func)( i, j ) != 0 || iter++ >= max_iter )
704
break;
705
706
Q_i = get_row( i, &buf[0][0] );
707
Q_j = get_row( j, &buf[1][0] );
708
709
C_i = get_C(i);
710
C_j = get_C(j);
711
712
alpha_i = old_alpha_i = alpha[i];
713
alpha_j = old_alpha_j = alpha[j];
714
715
if( y[i] != y[j] )
716
{
717
double denom = Q_i[i]+Q_j[j]+2*Q_i[j];
718
double delta = (-G[i]-G[j])/MAX(fabs(denom),FLT_EPSILON);
719
double diff = alpha_i - alpha_j;
720
alpha_i += delta;
721
alpha_j += delta;
722
723
if( diff > 0 && alpha_j < 0 )
724
{
725
alpha_j = 0;
726
alpha_i = diff;
727
}
728
else if( diff <= 0 && alpha_i < 0 )
729
{
730
alpha_i = 0;
731
alpha_j = -diff;
732
}
733
734
if( diff > C_i - C_j && alpha_i > C_i )
735
{
736
alpha_i = C_i;
737
alpha_j = C_i - diff;
738
}
739
else if( diff <= C_i - C_j && alpha_j > C_j )
740
{
741
alpha_j = C_j;
742
alpha_i = C_j + diff;
743
}
744
}
745
else
746
{
747
double denom = Q_i[i]+Q_j[j]-2*Q_i[j];
748
double delta = (G[i]-G[j])/MAX(fabs(denom),FLT_EPSILON);
749
double sum = alpha_i + alpha_j;
750
alpha_i -= delta;
751
alpha_j += delta;
752
753
if( sum > C_i && alpha_i > C_i )
754
{
755
alpha_i = C_i;
756
alpha_j = sum - C_i;
757
}
758
else if( sum <= C_i && alpha_j < 0)
759
{
760
alpha_j = 0;
761
alpha_i = sum;
762
}
763
764
if( sum > C_j && alpha_j > C_j )
765
{
766
alpha_j = C_j;
767
alpha_i = sum - C_j;
768
}
769
else if( sum <= C_j && alpha_i < 0 )
770
{
771
alpha_i = 0;
772
alpha_j = sum;
773
}
774
}
775
776
// update alpha
777
alpha[i] = alpha_i;
778
alpha[j] = alpha_j;
779
update_alpha_status(i);
780
update_alpha_status(j);
781
782
// update G
783
delta_alpha_i = alpha_i - old_alpha_i;
784
delta_alpha_j = alpha_j - old_alpha_j;
785
786
for( k = 0; k < alpha_count; k++ )
787
G[k] += Q_i[k]*delta_alpha_i + Q_j[k]*delta_alpha_j;
788
}
789
790
// calculate rho
791
(this->*calc_rho_func)( si.rho, si.r );
792
793
// calculate objective value
794
for( i = 0, si.obj = 0; i < alpha_count; i++ )
795
si.obj += alpha[i] * (G[i] + b[i]);
796
797
si.obj *= 0.5;
798
799
si.upper_bound_p = C[1];
800
si.upper_bound_n = C[0];
801
802
return true;
803
}
804
805
// return 1 if already optimal, return 0 otherwise
806
bool select_working_set( int& out_i, int& out_j )
807
{
808
// return i,j which maximize -grad(f)^T d , under constraint
809
// if alpha_i == C, d != +1
810
// if alpha_i == 0, d != -1
811
double Gmax1 = -DBL_MAX; // max { -grad(f)_i * d | y_i*d = +1 }
812
int Gmax1_idx = -1;
813
814
double Gmax2 = -DBL_MAX; // max { -grad(f)_i * d | y_i*d = -1 }
815
int Gmax2_idx = -1;
816
817
const schar* y = &y_vec[0];
818
const schar* alpha_status = &alpha_status_vec[0];
819
const double* G = &G_vec[0];
820
821
for( int i = 0; i < alpha_count; i++ )
822
{
823
double t;
824
825
if( y[i] > 0 ) // y = +1
826
{
827
if( !is_upper_bound(i) && (t = -G[i]) > Gmax1 ) // d = +1
828
{
829
Gmax1 = t;
830
Gmax1_idx = i;
831
}
832
if( !is_lower_bound(i) && (t = G[i]) > Gmax2 ) // d = -1
833
{
834
Gmax2 = t;
835
Gmax2_idx = i;
836
}
837
}
838
else // y = -1
839
{
840
if( !is_upper_bound(i) && (t = -G[i]) > Gmax2 ) // d = +1
841
{
842
Gmax2 = t;
843
Gmax2_idx = i;
844
}
845
if( !is_lower_bound(i) && (t = G[i]) > Gmax1 ) // d = -1
846
{
847
Gmax1 = t;
848
Gmax1_idx = i;
849
}
850
}
851
}
852
853
out_i = Gmax1_idx;
854
out_j = Gmax2_idx;
855
856
return Gmax1 + Gmax2 < eps;
857
}
858
859
void calc_rho( double& rho, double& r )
860
{
861
int nr_free = 0;
862
double ub = DBL_MAX, lb = -DBL_MAX, sum_free = 0;
863
const schar* y = &y_vec[0];
864
const schar* alpha_status = &alpha_status_vec[0];
865
const double* G = &G_vec[0];
866
867
for( int i = 0; i < alpha_count; i++ )
868
{
869
double yG = y[i]*G[i];
870
871
if( is_lower_bound(i) )
872
{
873
if( y[i] > 0 )
874
ub = MIN(ub,yG);
875
else
876
lb = MAX(lb,yG);
877
}
878
else if( is_upper_bound(i) )
879
{
880
if( y[i] < 0)
881
ub = MIN(ub,yG);
882
else
883
lb = MAX(lb,yG);
884
}
885
else
886
{
887
++nr_free;
888
sum_free += yG;
889
}
890
}
891
892
rho = nr_free > 0 ? sum_free/nr_free : (ub + lb)*0.5;
893
r = 0;
894
}
895
896
bool select_working_set_nu_svm( int& out_i, int& out_j )
897
{
898
// return i,j which maximize -grad(f)^T d , under constraint
899
// if alpha_i == C, d != +1
900
// if alpha_i == 0, d != -1
901
double Gmax1 = -DBL_MAX; // max { -grad(f)_i * d | y_i = +1, d = +1 }
902
int Gmax1_idx = -1;
903
904
double Gmax2 = -DBL_MAX; // max { -grad(f)_i * d | y_i = +1, d = -1 }
905
int Gmax2_idx = -1;
906
907
double Gmax3 = -DBL_MAX; // max { -grad(f)_i * d | y_i = -1, d = +1 }
908
int Gmax3_idx = -1;
909
910
double Gmax4 = -DBL_MAX; // max { -grad(f)_i * d | y_i = -1, d = -1 }
911
int Gmax4_idx = -1;
912
913
const schar* y = &y_vec[0];
914
const schar* alpha_status = &alpha_status_vec[0];
915
const double* G = &G_vec[0];
916
917
for( int i = 0; i < alpha_count; i++ )
918
{
919
double t;
920
921
if( y[i] > 0 ) // y == +1
922
{
923
if( !is_upper_bound(i) && (t = -G[i]) > Gmax1 ) // d = +1
924
{
925
Gmax1 = t;
926
Gmax1_idx = i;
927
}
928
if( !is_lower_bound(i) && (t = G[i]) > Gmax2 ) // d = -1
929
{
930
Gmax2 = t;
931
Gmax2_idx = i;
932
}
933
}
934
else // y == -1
935
{
936
if( !is_upper_bound(i) && (t = -G[i]) > Gmax3 ) // d = +1
937
{
938
Gmax3 = t;
939
Gmax3_idx = i;
940
}
941
if( !is_lower_bound(i) && (t = G[i]) > Gmax4 ) // d = -1
942
{
943
Gmax4 = t;
944
Gmax4_idx = i;
945
}
946
}
947
}
948
949
if( MAX(Gmax1 + Gmax2, Gmax3 + Gmax4) < eps )
950
return 1;
951
952
if( Gmax1 + Gmax2 > Gmax3 + Gmax4 )
953
{
954
out_i = Gmax1_idx;
955
out_j = Gmax2_idx;
956
}
957
else
958
{
959
out_i = Gmax3_idx;
960
out_j = Gmax4_idx;
961
}
962
return 0;
963
}
964
965
void calc_rho_nu_svm( double& rho, double& r )
966
{
967
int nr_free1 = 0, nr_free2 = 0;
968
double ub1 = DBL_MAX, ub2 = DBL_MAX;
969
double lb1 = -DBL_MAX, lb2 = -DBL_MAX;
970
double sum_free1 = 0, sum_free2 = 0;
971
972
const schar* y = &y_vec[0];
973
const schar* alpha_status = &alpha_status_vec[0];
974
const double* G = &G_vec[0];
975
976
for( int i = 0; i < alpha_count; i++ )
977
{
978
double G_i = G[i];
979
if( y[i] > 0 )
980
{
981
if( is_lower_bound(i) )
982
ub1 = MIN( ub1, G_i );
983
else if( is_upper_bound(i) )
984
lb1 = MAX( lb1, G_i );
985
else
986
{
987
++nr_free1;
988
sum_free1 += G_i;
989
}
990
}
991
else
992
{
993
if( is_lower_bound(i) )
994
ub2 = MIN( ub2, G_i );
995
else if( is_upper_bound(i) )
996
lb2 = MAX( lb2, G_i );
997
else
998
{
999
++nr_free2;
1000
sum_free2 += G_i;
1001
}
1002
}
1003
}
1004
1005
double r1 = nr_free1 > 0 ? sum_free1/nr_free1 : (ub1 + lb1)*0.5;
1006
double r2 = nr_free2 > 0 ? sum_free2/nr_free2 : (ub2 + lb2)*0.5;
1007
1008
rho = (r1 - r2)*0.5;
1009
r = (r1 + r2)*0.5;
1010
}
1011
1012
/*
1013
///////////////////////// construct and solve various formulations ///////////////////////
1014
*/
1015
static bool solve_c_svc( const Mat& _samples, const vector<schar>& _y,
1016
double _Cp, double _Cn, const Ptr<SVM::Kernel>& _kernel,
1017
vector<double>& _alpha, SolutionInfo& _si, TermCriteria termCrit )
1018
{
1019
int sample_count = _samples.rows;
1020
1021
_alpha.assign(sample_count, 0.);
1022
vector<double> _b(sample_count, -1.);
1023
1024
Solver solver( _samples, _y, _alpha, _b, _Cp, _Cn, _kernel,
1025
&Solver::get_row_svc,
1026
&Solver::select_working_set,
1027
&Solver::calc_rho,
1028
termCrit );
1029
1030
if( !solver.solve_generic( _si ))
1031
return false;
1032
1033
for( int i = 0; i < sample_count; i++ )
1034
_alpha[i] *= _y[i];
1035
1036
return true;
1037
}
1038
1039
1040
static bool solve_nu_svc( const Mat& _samples, const vector<schar>& _y,
1041
double nu, const Ptr<SVM::Kernel>& _kernel,
1042
vector<double>& _alpha, SolutionInfo& _si,
1043
TermCriteria termCrit )
1044
{
1045
int sample_count = _samples.rows;
1046
1047
_alpha.resize(sample_count);
1048
vector<double> _b(sample_count, 0.);
1049
1050
double sum_pos = nu * sample_count * 0.5;
1051
double sum_neg = nu * sample_count * 0.5;
1052
1053
for( int i = 0; i < sample_count; i++ )
1054
{
1055
double a;
1056
if( _y[i] > 0 )
1057
{
1058
a = std::min(1.0, sum_pos);
1059
sum_pos -= a;
1060
}
1061
else
1062
{
1063
a = std::min(1.0, sum_neg);
1064
sum_neg -= a;
1065
}
1066
_alpha[i] = a;
1067
}
1068
1069
Solver solver( _samples, _y, _alpha, _b, 1., 1., _kernel,
1070
&Solver::get_row_svc,
1071
&Solver::select_working_set_nu_svm,
1072
&Solver::calc_rho_nu_svm,
1073
termCrit );
1074
1075
if( !solver.solve_generic( _si ))
1076
return false;
1077
1078
double inv_r = 1./_si.r;
1079
1080
for( int i = 0; i < sample_count; i++ )
1081
_alpha[i] *= _y[i]*inv_r;
1082
1083
_si.rho *= inv_r;
1084
_si.obj *= (inv_r*inv_r);
1085
_si.upper_bound_p = inv_r;
1086
_si.upper_bound_n = inv_r;
1087
1088
return true;
1089
}
1090
1091
static bool solve_one_class( const Mat& _samples, double nu,
1092
const Ptr<SVM::Kernel>& _kernel,
1093
vector<double>& _alpha, SolutionInfo& _si,
1094
TermCriteria termCrit )
1095
{
1096
int sample_count = _samples.rows;
1097
vector<schar> _y(sample_count, 1);
1098
vector<double> _b(sample_count, 0.);
1099
1100
int i, n = cvRound( nu*sample_count );
1101
1102
_alpha.resize(sample_count);
1103
for( i = 0; i < sample_count; i++ )
1104
_alpha[i] = i < n ? 1 : 0;
1105
1106
if( n < sample_count )
1107
_alpha[n] = nu * sample_count - n;
1108
else
1109
_alpha[n-1] = nu * sample_count - (n-1);
1110
1111
Solver solver( _samples, _y, _alpha, _b, 1., 1., _kernel,
1112
&Solver::get_row_one_class,
1113
&Solver::select_working_set,
1114
&Solver::calc_rho,
1115
termCrit );
1116
1117
return solver.solve_generic(_si);
1118
}
1119
1120
static bool solve_eps_svr( const Mat& _samples, const vector<float>& _yf,
1121
double p, double C, const Ptr<SVM::Kernel>& _kernel,
1122
vector<double>& _alpha, SolutionInfo& _si,
1123
TermCriteria termCrit )
1124
{
1125
int sample_count = _samples.rows;
1126
int alpha_count = sample_count*2;
1127
1128
CV_Assert( (int)_yf.size() == sample_count );
1129
1130
_alpha.assign(alpha_count, 0.);
1131
vector<schar> _y(alpha_count);
1132
vector<double> _b(alpha_count);
1133
1134
for( int i = 0; i < sample_count; i++ )
1135
{
1136
_b[i] = p - _yf[i];
1137
_y[i] = 1;
1138
1139
_b[i+sample_count] = p + _yf[i];
1140
_y[i+sample_count] = -1;
1141
}
1142
1143
Solver solver( _samples, _y, _alpha, _b, C, C, _kernel,
1144
&Solver::get_row_svr,
1145
&Solver::select_working_set,
1146
&Solver::calc_rho,
1147
termCrit );
1148
1149
if( !solver.solve_generic( _si ))
1150
return false;
1151
1152
for( int i = 0; i < sample_count; i++ )
1153
_alpha[i] -= _alpha[i+sample_count];
1154
1155
return true;
1156
}
1157
1158
1159
static bool solve_nu_svr( const Mat& _samples, const vector<float>& _yf,
1160
double nu, double C, const Ptr<SVM::Kernel>& _kernel,
1161
vector<double>& _alpha, SolutionInfo& _si,
1162
TermCriteria termCrit )
1163
{
1164
int sample_count = _samples.rows;
1165
int alpha_count = sample_count*2;
1166
double sum = C * nu * sample_count * 0.5;
1167
1168
CV_Assert( (int)_yf.size() == sample_count );
1169
1170
_alpha.resize(alpha_count);
1171
vector<schar> _y(alpha_count);
1172
vector<double> _b(alpha_count);
1173
1174
for( int i = 0; i < sample_count; i++ )
1175
{
1176
_alpha[i] = _alpha[i + sample_count] = std::min(sum, C);
1177
sum -= _alpha[i];
1178
1179
_b[i] = -_yf[i];
1180
_y[i] = 1;
1181
1182
_b[i + sample_count] = _yf[i];
1183
_y[i + sample_count] = -1;
1184
}
1185
1186
Solver solver( _samples, _y, _alpha, _b, 1., 1., _kernel,
1187
&Solver::get_row_svr,
1188
&Solver::select_working_set_nu_svm,
1189
&Solver::calc_rho_nu_svm,
1190
termCrit );
1191
1192
if( !solver.solve_generic( _si ))
1193
return false;
1194
1195
for( int i = 0; i < sample_count; i++ )
1196
_alpha[i] -= _alpha[i+sample_count];
1197
1198
return true;
1199
}
1200
1201
int sample_count;
1202
int var_count;
1203
int cache_size;
1204
int max_cache_size;
1205
Mat samples;
1206
SvmParams params;
1207
vector<KernelRow> lru_cache;
1208
int lru_first;
1209
int lru_last;
1210
Mat lru_cache_data;
1211
1212
int alpha_count;
1213
1214
vector<double> G_vec;
1215
vector<double>* alpha_vec;
1216
vector<schar> y_vec;
1217
// -1 - lower bound, 0 - free, 1 - upper bound
1218
vector<schar> alpha_status_vec;
1219
vector<double> b_vec;
1220
1221
vector<Qfloat> buf[2];
1222
double eps;
1223
int max_iter;
1224
double C[2]; // C[0] == Cn, C[1] == Cp
1225
Ptr<SVM::Kernel> kernel;
1226
1227
SelectWorkingSet select_working_set_func;
1228
CalcRho calc_rho_func;
1229
GetRow get_row_func;
1230
};
1231
1232
//////////////////////////////////////////////////////////////////////////////////////////
1233
SVMImpl()
1234
{
1235
clear();
1236
checkParams();
1237
}
1238
1239
~SVMImpl()
1240
{
1241
clear();
1242
}
1243
1244
void clear() CV_OVERRIDE
1245
{
1246
decision_func.clear();
1247
df_alpha.clear();
1248
df_index.clear();
1249
sv.release();
1250
uncompressed_sv.release();
1251
}
1252
1253
Mat getUncompressedSupportVectors() const CV_OVERRIDE
1254
{
1255
return uncompressed_sv;
1256
}
1257
1258
Mat getSupportVectors() const CV_OVERRIDE
1259
{
1260
return sv;
1261
}
1262
1263
inline int getType() const CV_OVERRIDE { return params.svmType; }
1264
inline void setType(int val) CV_OVERRIDE { params.svmType = val; }
1265
inline double getGamma() const CV_OVERRIDE { return params.gamma; }
1266
inline void setGamma(double val) CV_OVERRIDE { params.gamma = val; }
1267
inline double getCoef0() const CV_OVERRIDE { return params.coef0; }
1268
inline void setCoef0(double val) CV_OVERRIDE { params.coef0 = val; }
1269
inline double getDegree() const CV_OVERRIDE { return params.degree; }
1270
inline void setDegree(double val) CV_OVERRIDE { params.degree = val; }
1271
inline double getC() const CV_OVERRIDE { return params.C; }
1272
inline void setC(double val) CV_OVERRIDE { params.C = val; }
1273
inline double getNu() const CV_OVERRIDE { return params.nu; }
1274
inline void setNu(double val) CV_OVERRIDE { params.nu = val; }
1275
inline double getP() const CV_OVERRIDE { return params.p; }
1276
inline void setP(double val) CV_OVERRIDE { params.p = val; }
1277
inline cv::Mat getClassWeights() const CV_OVERRIDE { return params.classWeights; }
1278
inline void setClassWeights(const cv::Mat& val) CV_OVERRIDE { params.classWeights = val; }
1279
inline cv::TermCriteria getTermCriteria() const CV_OVERRIDE { return params.termCrit; }
1280
inline void setTermCriteria(const cv::TermCriteria& val) CV_OVERRIDE { params.termCrit = val; }
1281
1282
int getKernelType() const CV_OVERRIDE { return params.kernelType; }
1283
void setKernel(int kernelType) CV_OVERRIDE
1284
{
1285
params.kernelType = kernelType;
1286
if (kernelType != CUSTOM)
1287
kernel = makePtr<SVMKernelImpl>(params);
1288
}
1289
1290
void setCustomKernel(const Ptr<Kernel> &_kernel) CV_OVERRIDE
1291
{
1292
params.kernelType = CUSTOM;
1293
kernel = _kernel;
1294
}
1295
1296
void checkParams()
1297
{
1298
int kernelType = params.kernelType;
1299
if (kernelType != CUSTOM)
1300
{
1301
if( kernelType != LINEAR && kernelType != POLY &&
1302
kernelType != SIGMOID && kernelType != RBF &&
1303
kernelType != INTER && kernelType != CHI2)
1304
CV_Error( CV_StsBadArg, "Unknown/unsupported kernel type" );
1305
1306
if( kernelType == LINEAR )
1307
params.gamma = 1;
1308
else if( params.gamma <= 0 )
1309
CV_Error( CV_StsOutOfRange, "gamma parameter of the kernel must be positive" );
1310
1311
if( kernelType != SIGMOID && kernelType != POLY )
1312
params.coef0 = 0;
1313
else if( params.coef0 < 0 )
1314
CV_Error( CV_StsOutOfRange, "The kernel parameter <coef0> must be positive or zero" );
1315
1316
if( kernelType != POLY )
1317
params.degree = 0;
1318
else if( params.degree <= 0 )
1319
CV_Error( CV_StsOutOfRange, "The kernel parameter <degree> must be positive" );
1320
1321
kernel = makePtr<SVMKernelImpl>(params);
1322
}
1323
else
1324
{
1325
if (!kernel)
1326
CV_Error( CV_StsBadArg, "Custom kernel is not set" );
1327
}
1328
1329
int svmType = params.svmType;
1330
1331
if( svmType != C_SVC && svmType != NU_SVC &&
1332
svmType != ONE_CLASS && svmType != EPS_SVR &&
1333
svmType != NU_SVR )
1334
CV_Error( CV_StsBadArg, "Unknown/unsupported SVM type" );
1335
1336
if( svmType == ONE_CLASS || svmType == NU_SVC )
1337
params.C = 0;
1338
else if( params.C <= 0 )
1339
CV_Error( CV_StsOutOfRange, "The parameter C must be positive" );
1340
1341
if( svmType == C_SVC || svmType == EPS_SVR )
1342
params.nu = 0;
1343
else if( params.nu <= 0 || params.nu >= 1 )
1344
CV_Error( CV_StsOutOfRange, "The parameter nu must be between 0 and 1" );
1345
1346
if( svmType != EPS_SVR )
1347
params.p = 0;
1348
else if( params.p <= 0 )
1349
CV_Error( CV_StsOutOfRange, "The parameter p must be positive" );
1350
1351
if( svmType != C_SVC )
1352
params.classWeights.release();
1353
1354
if( !(params.termCrit.type & TermCriteria::EPS) )
1355
params.termCrit.epsilon = DBL_EPSILON;
1356
params.termCrit.epsilon = std::max(params.termCrit.epsilon, DBL_EPSILON);
1357
if( !(params.termCrit.type & TermCriteria::COUNT) )
1358
params.termCrit.maxCount = INT_MAX;
1359
params.termCrit.maxCount = std::max(params.termCrit.maxCount, 1);
1360
}
1361
1362
void setParams( const SvmParams& _params)
1363
{
1364
params = _params;
1365
checkParams();
1366
}
1367
1368
int getSVCount(int i) const
1369
{
1370
return (i < (int)(decision_func.size()-1) ? decision_func[i+1].ofs :
1371
(int)df_index.size()) - decision_func[i].ofs;
1372
}
1373
1374
bool do_train( const Mat& _samples, const Mat& _responses )
1375
{
1376
int svmType = params.svmType;
1377
int i, j, k, sample_count = _samples.rows;
1378
vector<double> _alpha;
1379
Solver::SolutionInfo sinfo;
1380
1381
CV_Assert( _samples.type() == CV_32F );
1382
var_count = _samples.cols;
1383
1384
if( svmType == ONE_CLASS || svmType == EPS_SVR || svmType == NU_SVR )
1385
{
1386
int sv_count = 0;
1387
decision_func.clear();
1388
1389
vector<float> _yf;
1390
if( !_responses.empty() )
1391
_responses.convertTo(_yf, CV_32F);
1392
1393
bool ok =
1394
svmType == ONE_CLASS ? Solver::solve_one_class( _samples, params.nu, kernel, _alpha, sinfo, params.termCrit ) :
1395
svmType == EPS_SVR ? Solver::solve_eps_svr( _samples, _yf, params.p, params.C, kernel, _alpha, sinfo, params.termCrit ) :
1396
svmType == NU_SVR ? Solver::solve_nu_svr( _samples, _yf, params.nu, params.C, kernel, _alpha, sinfo, params.termCrit ) : false;
1397
1398
if( !ok )
1399
return false;
1400
1401
for( i = 0; i < sample_count; i++ )
1402
sv_count += fabs(_alpha[i]) > 0;
1403
1404
CV_Assert(sv_count != 0);
1405
1406
sv.create(sv_count, _samples.cols, CV_32F);
1407
df_alpha.resize(sv_count);
1408
df_index.resize(sv_count);
1409
1410
for( i = k = 0; i < sample_count; i++ )
1411
{
1412
if( std::abs(_alpha[i]) > 0 )
1413
{
1414
_samples.row(i).copyTo(sv.row(k));
1415
df_alpha[k] = _alpha[i];
1416
df_index[k] = k;
1417
k++;
1418
}
1419
}
1420
1421
decision_func.push_back(DecisionFunc(sinfo.rho, 0));
1422
}
1423
else
1424
{
1425
int class_count = (int)class_labels.total();
1426
vector<int> svidx, sidx, sidx_all, sv_tab(sample_count, 0);
1427
Mat temp_samples, class_weights;
1428
vector<int> class_ranges;
1429
vector<schar> temp_y;
1430
double nu = params.nu;
1431
CV_Assert( svmType == C_SVC || svmType == NU_SVC );
1432
1433
if( svmType == C_SVC && !params.classWeights.empty() )
1434
{
1435
const Mat cw = params.classWeights;
1436
1437
if( (cw.cols != 1 && cw.rows != 1) ||
1438
(int)cw.total() != class_count ||
1439
(cw.type() != CV_32F && cw.type() != CV_64F) )
1440
CV_Error( CV_StsBadArg, "params.class_weights must be 1d floating-point vector "
1441
"containing as many elements as the number of classes" );
1442
1443
cw.convertTo(class_weights, CV_64F, params.C);
1444
//normalize(cw, class_weights, params.C, 0, NORM_L1, CV_64F);
1445
}
1446
1447
decision_func.clear();
1448
df_alpha.clear();
1449
df_index.clear();
1450
1451
sortSamplesByClasses( _samples, _responses, sidx_all, class_ranges );
1452
1453
//check that while cross-validation there were the samples from all the classes
1454
if( class_ranges[class_count] <= 0 )
1455
CV_Error( CV_StsBadArg, "While cross-validation one or more of the classes have "
1456
"been fell out of the sample. Try to reduce <Params::k_fold>" );
1457
1458
if( svmType == NU_SVC )
1459
{
1460
// check if nu is feasible
1461
for( i = 0; i < class_count; i++ )
1462
{
1463
int ci = class_ranges[i+1] - class_ranges[i];
1464
for( j = i+1; j< class_count; j++ )
1465
{
1466
int cj = class_ranges[j+1] - class_ranges[j];
1467
if( nu*(ci + cj)*0.5 > std::min( ci, cj ) )
1468
// TODO: add some diagnostic
1469
return false;
1470
}
1471
}
1472
}
1473
1474
size_t samplesize = _samples.cols*_samples.elemSize();
1475
1476
// train n*(n-1)/2 classifiers
1477
for( i = 0; i < class_count; i++ )
1478
{
1479
for( j = i+1; j < class_count; j++ )
1480
{
1481
int si = class_ranges[i], ci = class_ranges[i+1] - si;
1482
int sj = class_ranges[j], cj = class_ranges[j+1] - sj;
1483
double Cp = params.C, Cn = Cp;
1484
1485
temp_samples.create(ci + cj, _samples.cols, _samples.type());
1486
sidx.resize(ci + cj);
1487
temp_y.resize(ci + cj);
1488
1489
// form input for the binary classification problem
1490
for( k = 0; k < ci+cj; k++ )
1491
{
1492
int idx = k < ci ? si+k : sj+k-ci;
1493
memcpy(temp_samples.ptr(k), _samples.ptr(sidx_all[idx]), samplesize);
1494
sidx[k] = sidx_all[idx];
1495
temp_y[k] = k < ci ? 1 : -1;
1496
}
1497
1498
if( !class_weights.empty() )
1499
{
1500
Cp = class_weights.at<double>(i);
1501
Cn = class_weights.at<double>(j);
1502
}
1503
1504
DecisionFunc df;
1505
bool ok = params.svmType == C_SVC ?
1506
Solver::solve_c_svc( temp_samples, temp_y, Cp, Cn,
1507
kernel, _alpha, sinfo, params.termCrit ) :
1508
params.svmType == NU_SVC ?
1509
Solver::solve_nu_svc( temp_samples, temp_y, params.nu,
1510
kernel, _alpha, sinfo, params.termCrit ) :
1511
false;
1512
if( !ok )
1513
return false;
1514
df.rho = sinfo.rho;
1515
df.ofs = (int)df_index.size();
1516
decision_func.push_back(df);
1517
1518
for( k = 0; k < ci + cj; k++ )
1519
{
1520
if( std::abs(_alpha[k]) > 0 )
1521
{
1522
int idx = k < ci ? si+k : sj+k-ci;
1523
sv_tab[sidx_all[idx]] = 1;
1524
df_index.push_back(sidx_all[idx]);
1525
df_alpha.push_back(_alpha[k]);
1526
}
1527
}
1528
}
1529
}
1530
1531
// allocate support vectors and initialize sv_tab
1532
for( i = 0, k = 0; i < sample_count; i++ )
1533
{
1534
if( sv_tab[i] )
1535
sv_tab[i] = ++k;
1536
}
1537
1538
int sv_total = k;
1539
sv.create(sv_total, _samples.cols, _samples.type());
1540
1541
for( i = 0; i < sample_count; i++ )
1542
{
1543
if( !sv_tab[i] )
1544
continue;
1545
memcpy(sv.ptr(sv_tab[i]-1), _samples.ptr(i), samplesize);
1546
}
1547
1548
// set sv pointers
1549
int n = (int)df_index.size();
1550
for( i = 0; i < n; i++ )
1551
{
1552
CV_Assert( sv_tab[df_index[i]] > 0 );
1553
df_index[i] = sv_tab[df_index[i]] - 1;
1554
}
1555
}
1556
1557
optimize_linear_svm();
1558
1559
return true;
1560
}
1561
1562
void optimize_linear_svm()
1563
{
1564
// we optimize only linear SVM: compress all the support vectors into one.
1565
if( params.kernelType != LINEAR )
1566
return;
1567
1568
int i, df_count = (int)decision_func.size();
1569
1570
for( i = 0; i < df_count; i++ )
1571
{
1572
if( getSVCount(i) != 1 )
1573
break;
1574
}
1575
1576
// if every decision functions uses a single support vector;
1577
// it's already compressed. skip it then.
1578
if( i == df_count )
1579
return;
1580
1581
AutoBuffer<double> vbuf(var_count);
1582
double* v = vbuf.data();
1583
Mat new_sv(df_count, var_count, CV_32F);
1584
1585
vector<DecisionFunc> new_df;
1586
1587
for( i = 0; i < df_count; i++ )
1588
{
1589
float* dst = new_sv.ptr<float>(i);
1590
memset(v, 0, var_count*sizeof(v[0]));
1591
int j, k, sv_count = getSVCount(i);
1592
const DecisionFunc& df = decision_func[i];
1593
const int* sv_index = &df_index[df.ofs];
1594
const double* sv_alpha = &df_alpha[df.ofs];
1595
for( j = 0; j < sv_count; j++ )
1596
{
1597
const float* src = sv.ptr<float>(sv_index[j]);
1598
double a = sv_alpha[j];
1599
for( k = 0; k < var_count; k++ )
1600
v[k] += src[k]*a;
1601
}
1602
for( k = 0; k < var_count; k++ )
1603
dst[k] = (float)v[k];
1604
new_df.push_back(DecisionFunc(df.rho, i));
1605
}
1606
1607
setRangeVector(df_index, df_count);
1608
df_alpha.assign(df_count, 1.);
1609
sv.copyTo(uncompressed_sv);
1610
std::swap(sv, new_sv);
1611
std::swap(decision_func, new_df);
1612
}
1613
1614
bool train( const Ptr<TrainData>& data, int ) CV_OVERRIDE
1615
{
1616
clear();
1617
1618
checkParams();
1619
1620
int svmType = params.svmType;
1621
Mat samples = data->getTrainSamples();
1622
Mat responses;
1623
1624
if( svmType == C_SVC || svmType == NU_SVC )
1625
{
1626
responses = data->getTrainNormCatResponses();
1627
if( responses.empty() )
1628
CV_Error(CV_StsBadArg, "in the case of classification problem the responses must be categorical; "
1629
"either specify varType when creating TrainData, or pass integer responses");
1630
class_labels = data->getClassLabels();
1631
}
1632
else
1633
responses = data->getTrainResponses();
1634
1635
if( !do_train( samples, responses ))
1636
{
1637
clear();
1638
return false;
1639
}
1640
1641
return true;
1642
}
1643
1644
class TrainAutoBody : public ParallelLoopBody
1645
{
1646
public:
1647
TrainAutoBody(const vector<SvmParams>& _parameters,
1648
const cv::Mat& _samples,
1649
const cv::Mat& _responses,
1650
const cv::Mat& _labels,
1651
const vector<int>& _sidx,
1652
bool _is_classification,
1653
int _k_fold,
1654
std::vector<double>& _result) :
1655
parameters(_parameters), samples(_samples), responses(_responses), labels(_labels),
1656
sidx(_sidx), is_classification(_is_classification), k_fold(_k_fold), result(_result)
1657
{}
1658
1659
void operator()( const cv::Range& range ) const CV_OVERRIDE
1660
{
1661
int sample_count = samples.rows;
1662
int var_count_ = samples.cols;
1663
size_t sample_size = var_count_*samples.elemSize();
1664
1665
int test_sample_count = (sample_count + k_fold/2)/k_fold;
1666
int train_sample_count = sample_count - test_sample_count;
1667
1668
// Use a local instance
1669
cv::Ptr<SVMImpl> svm = makePtr<SVMImpl>();
1670
svm->class_labels = labels;
1671
1672
int rtype = responses.type();
1673
1674
Mat temp_train_samples(train_sample_count, var_count_, CV_32F);
1675
Mat temp_test_samples(test_sample_count, var_count_, CV_32F);
1676
Mat temp_train_responses(train_sample_count, 1, rtype);
1677
Mat temp_test_responses;
1678
1679
for( int p = range.start; p < range.end; p++ )
1680
{
1681
svm->setParams(parameters[p]);
1682
1683
double error = 0;
1684
for( int k = 0; k < k_fold; k++ )
1685
{
1686
int start = (k*sample_count + k_fold/2)/k_fold;
1687
for( int i = 0; i < train_sample_count; i++ )
1688
{
1689
int j = sidx[(i+start)%sample_count];
1690
memcpy(temp_train_samples.ptr(i), samples.ptr(j), sample_size);
1691
if( is_classification )
1692
temp_train_responses.at<int>(i) = responses.at<int>(j);
1693
else if( !responses.empty() )
1694
temp_train_responses.at<float>(i) = responses.at<float>(j);
1695
}
1696
1697
// Train SVM on <train_size> samples
1698
if( !svm->do_train( temp_train_samples, temp_train_responses ))
1699
continue;
1700
1701
for( int i = 0; i < test_sample_count; i++ )
1702
{
1703
int j = sidx[(i+start+train_sample_count) % sample_count];
1704
memcpy(temp_test_samples.ptr(i), samples.ptr(j), sample_size);
1705
}
1706
1707
svm->predict(temp_test_samples, temp_test_responses, 0);
1708
for( int i = 0; i < test_sample_count; i++ )
1709
{
1710
float val = temp_test_responses.at<float>(i);
1711
int j = sidx[(i+start+train_sample_count) % sample_count];
1712
if( is_classification )
1713
error += (float)(val != responses.at<int>(j));
1714
else
1715
{
1716
val -= responses.at<float>(j);
1717
error += val*val;
1718
}
1719
}
1720
}
1721
1722
result[p] = error;
1723
}
1724
}
1725
1726
private:
1727
const vector<SvmParams>& parameters;
1728
const cv::Mat& samples;
1729
const cv::Mat& responses;
1730
const cv::Mat& labels;
1731
const vector<int>& sidx;
1732
bool is_classification;
1733
int k_fold;
1734
std::vector<double>& result;
1735
};
1736
1737
bool trainAuto( const Ptr<TrainData>& data, int k_fold,
1738
ParamGrid C_grid, ParamGrid gamma_grid, ParamGrid p_grid,
1739
ParamGrid nu_grid, ParamGrid coef_grid, ParamGrid degree_grid,
1740
bool balanced ) CV_OVERRIDE
1741
{
1742
checkParams();
1743
1744
int svmType = params.svmType;
1745
RNG rng((uint64)-1);
1746
1747
if( svmType == ONE_CLASS )
1748
// current implementation of "auto" svm does not support the 1-class case.
1749
return train( data, 0 );
1750
1751
clear();
1752
1753
CV_Assert( k_fold >= 2 );
1754
1755
// All the parameters except, possibly, <coef0> are positive.
1756
// <coef0> is nonnegative
1757
#define CHECK_GRID(grid, param) \
1758
if( grid.logStep <= 1 ) \
1759
{ \
1760
grid.minVal = grid.maxVal = params.param; \
1761
grid.logStep = 10; \
1762
} \
1763
else \
1764
checkParamGrid(grid)
1765
1766
CHECK_GRID(C_grid, C);
1767
CHECK_GRID(gamma_grid, gamma);
1768
CHECK_GRID(p_grid, p);
1769
CHECK_GRID(nu_grid, nu);
1770
CHECK_GRID(coef_grid, coef0);
1771
CHECK_GRID(degree_grid, degree);
1772
1773
// these parameters are not used:
1774
if( params.kernelType != POLY )
1775
degree_grid.minVal = degree_grid.maxVal = params.degree;
1776
if( params.kernelType == LINEAR )
1777
gamma_grid.minVal = gamma_grid.maxVal = params.gamma;
1778
if( params.kernelType != POLY && params.kernelType != SIGMOID )
1779
coef_grid.minVal = coef_grid.maxVal = params.coef0;
1780
if( svmType == NU_SVC || svmType == ONE_CLASS )
1781
C_grid.minVal = C_grid.maxVal = params.C;
1782
if( svmType == C_SVC || svmType == EPS_SVR )
1783
nu_grid.minVal = nu_grid.maxVal = params.nu;
1784
if( svmType != EPS_SVR )
1785
p_grid.minVal = p_grid.maxVal = params.p;
1786
1787
Mat samples = data->getTrainSamples();
1788
Mat responses;
1789
bool is_classification = false;
1790
Mat class_labels0;
1791
int class_count = (int)class_labels.total();
1792
1793
if( svmType == C_SVC || svmType == NU_SVC )
1794
{
1795
responses = data->getTrainNormCatResponses();
1796
class_labels = data->getClassLabels();
1797
class_count = (int)class_labels.total();
1798
is_classification = true;
1799
1800
vector<int> temp_class_labels;
1801
setRangeVector(temp_class_labels, class_count);
1802
1803
// temporarily replace class labels with 0, 1, ..., NCLASSES-1
1804
class_labels0 = class_labels;
1805
class_labels = Mat(temp_class_labels).clone();
1806
}
1807
else
1808
responses = data->getTrainResponses();
1809
1810
CV_Assert(samples.type() == CV_32F);
1811
1812
int sample_count = samples.rows;
1813
var_count = samples.cols;
1814
1815
vector<int> sidx;
1816
setRangeVector(sidx, sample_count);
1817
1818
// randomly permute training samples
1819
for( int i = 0; i < sample_count; i++ )
1820
{
1821
int i1 = rng.uniform(0, sample_count);
1822
int i2 = rng.uniform(0, sample_count);
1823
std::swap(sidx[i1], sidx[i2]);
1824
}
1825
1826
if( is_classification && class_count == 2 && balanced )
1827
{
1828
// reshuffle the training set in such a way that
1829
// instances of each class are divided more or less evenly
1830
// between the k_fold parts.
1831
vector<int> sidx0, sidx1;
1832
1833
for( int i = 0; i < sample_count; i++ )
1834
{
1835
if( responses.at<int>(sidx[i]) == 0 )
1836
sidx0.push_back(sidx[i]);
1837
else
1838
sidx1.push_back(sidx[i]);
1839
}
1840
1841
int n0 = (int)sidx0.size(), n1 = (int)sidx1.size();
1842
int a0 = 0, a1 = 0;
1843
sidx.clear();
1844
for( int k = 0; k < k_fold; k++ )
1845
{
1846
int b0 = ((k+1)*n0 + k_fold/2)/k_fold, b1 = ((k+1)*n1 + k_fold/2)/k_fold;
1847
int a = (int)sidx.size(), b = a + (b0 - a0) + (b1 - a1);
1848
for( int i = a0; i < b0; i++ )
1849
sidx.push_back(sidx0[i]);
1850
for( int i = a1; i < b1; i++ )
1851
sidx.push_back(sidx1[i]);
1852
for( int i = 0; i < (b - a); i++ )
1853
{
1854
int i1 = rng.uniform(a, b);
1855
int i2 = rng.uniform(a, b);
1856
std::swap(sidx[i1], sidx[i2]);
1857
}
1858
a0 = b0; a1 = b1;
1859
}
1860
}
1861
1862
// If grid.minVal == grid.maxVal, this will allow one and only one pass through the loop with params.var = grid.minVal.
1863
#define FOR_IN_GRID(var, grid) \
1864
for( params.var = grid.minVal; params.var == grid.minVal || params.var < grid.maxVal; params.var = (grid.minVal == grid.maxVal) ? grid.maxVal + 1 : params.var * grid.logStep )
1865
1866
// Create the list of parameters to test
1867
std::vector<SvmParams> parameters;
1868
FOR_IN_GRID(C, C_grid)
1869
FOR_IN_GRID(gamma, gamma_grid)
1870
FOR_IN_GRID(p, p_grid)
1871
FOR_IN_GRID(nu, nu_grid)
1872
FOR_IN_GRID(coef0, coef_grid)
1873
FOR_IN_GRID(degree, degree_grid)
1874
{
1875
parameters.push_back(params);
1876
}
1877
1878
std::vector<double> result(parameters.size());
1879
TrainAutoBody invoker(parameters, samples, responses, class_labels, sidx,
1880
is_classification, k_fold, result);
1881
parallel_for_(cv::Range(0,(int)parameters.size()), invoker);
1882
1883
// Extract the best parameters
1884
SvmParams best_params = params;
1885
double min_error = FLT_MAX;
1886
for( int i = 0; i < (int)result.size(); i++ )
1887
{
1888
if( result[i] < min_error )
1889
{
1890
min_error = result[i];
1891
best_params = parameters[i];
1892
}
1893
}
1894
1895
class_labels = class_labels0;
1896
setParams(best_params);
1897
return do_train( samples, responses );
1898
}
1899
1900
struct PredictBody : ParallelLoopBody
1901
{
1902
PredictBody( const SVMImpl* _svm, const Mat& _samples, Mat& _results, bool _returnDFVal )
1903
{
1904
svm = _svm;
1905
results = &_results;
1906
samples = &_samples;
1907
returnDFVal = _returnDFVal;
1908
}
1909
1910
void operator()(const Range& range) const CV_OVERRIDE
1911
{
1912
int svmType = svm->params.svmType;
1913
int sv_total = svm->sv.rows;
1914
int class_count = !svm->class_labels.empty() ? (int)svm->class_labels.total() : svmType == ONE_CLASS ? 1 : 0;
1915
1916
AutoBuffer<float> _buffer(sv_total + (class_count+1)*2);
1917
float* buffer = _buffer.data();
1918
1919
int i, j, dfi, k, si;
1920
1921
if( svmType == EPS_SVR || svmType == NU_SVR || svmType == ONE_CLASS )
1922
{
1923
for( si = range.start; si < range.end; si++ )
1924
{
1925
const float* row_sample = samples->ptr<float>(si);
1926
svm->kernel->calc( sv_total, svm->var_count, svm->sv.ptr<float>(), row_sample, buffer );
1927
1928
const SVMImpl::DecisionFunc* df = &svm->decision_func[0];
1929
double sum = -df->rho;
1930
for( i = 0; i < sv_total; i++ )
1931
sum += buffer[i]*svm->df_alpha[i];
1932
float result = svm->params.svmType == ONE_CLASS && !returnDFVal ? (float)(sum > 0) : (float)sum;
1933
results->at<float>(si) = result;
1934
}
1935
}
1936
else if( svmType == C_SVC || svmType == NU_SVC )
1937
{
1938
int* vote = (int*)(buffer + sv_total);
1939
1940
for( si = range.start; si < range.end; si++ )
1941
{
1942
svm->kernel->calc( sv_total, svm->var_count, svm->sv.ptr<float>(),
1943
samples->ptr<float>(si), buffer );
1944
double sum = 0.;
1945
1946
memset( vote, 0, class_count*sizeof(vote[0]));
1947
1948
for( i = dfi = 0; i < class_count; i++ )
1949
{
1950
for( j = i+1; j < class_count; j++, dfi++ )
1951
{
1952
const DecisionFunc& df = svm->decision_func[dfi];
1953
sum = -df.rho;
1954
int sv_count = svm->getSVCount(dfi);
1955
const double* alpha = &svm->df_alpha[df.ofs];
1956
const int* sv_index = &svm->df_index[df.ofs];
1957
for( k = 0; k < sv_count; k++ )
1958
sum += alpha[k]*buffer[sv_index[k]];
1959
1960
vote[sum > 0 ? i : j]++;
1961
}
1962
}
1963
1964
for( i = 1, k = 0; i < class_count; i++ )
1965
{
1966
if( vote[i] > vote[k] )
1967
k = i;
1968
}
1969
float result = returnDFVal && class_count == 2 ?
1970
(float)sum : (float)(svm->class_labels.at<int>(k));
1971
results->at<float>(si) = result;
1972
}
1973
}
1974
else
1975
CV_Error( CV_StsBadArg, "INTERNAL ERROR: Unknown SVM type, "
1976
"the SVM structure is probably corrupted" );
1977
}
1978
1979
const SVMImpl* svm;
1980
const Mat* samples;
1981
Mat* results;
1982
bool returnDFVal;
1983
};
1984
1985
bool trainAuto(InputArray samples, int layout,
1986
InputArray responses, int kfold, Ptr<ParamGrid> Cgrid,
1987
Ptr<ParamGrid> gammaGrid, Ptr<ParamGrid> pGrid, Ptr<ParamGrid> nuGrid,
1988
Ptr<ParamGrid> coeffGrid, Ptr<ParamGrid> degreeGrid, bool balanced) CV_OVERRIDE
1989
{
1990
Ptr<TrainData> data = TrainData::create(samples, layout, responses);
1991
return this->trainAuto(
1992
data, kfold,
1993
*Cgrid.get(),
1994
*gammaGrid.get(),
1995
*pGrid.get(),
1996
*nuGrid.get(),
1997
*coeffGrid.get(),
1998
*degreeGrid.get(),
1999
balanced);
2000
}
2001
2002
2003
float predict( InputArray _samples, OutputArray _results, int flags ) const CV_OVERRIDE
2004
{
2005
float result = 0;
2006
Mat samples = _samples.getMat(), results;
2007
int nsamples = samples.rows;
2008
bool returnDFVal = (flags & RAW_OUTPUT) != 0;
2009
2010
CV_Assert( samples.cols == var_count && samples.type() == CV_32F );
2011
2012
if( _results.needed() )
2013
{
2014
_results.create( nsamples, 1, samples.type() );
2015
results = _results.getMat();
2016
}
2017
else
2018
{
2019
CV_Assert( nsamples == 1 );
2020
results = Mat(1, 1, CV_32F, &result);
2021
}
2022
2023
PredictBody invoker(this, samples, results, returnDFVal);
2024
if( nsamples < 10 )
2025
invoker(Range(0, nsamples));
2026
else
2027
parallel_for_(Range(0, nsamples), invoker);
2028
return result;
2029
}
2030
2031
double getDecisionFunction(int i, OutputArray _alpha, OutputArray _svidx ) const CV_OVERRIDE
2032
{
2033
CV_Assert( 0 <= i && i < (int)decision_func.size());
2034
const DecisionFunc& df = decision_func[i];
2035
int count = getSVCount(i);
2036
Mat(1, count, CV_64F, (double*)&df_alpha[df.ofs]).copyTo(_alpha);
2037
Mat(1, count, CV_32S, (int*)&df_index[df.ofs]).copyTo(_svidx);
2038
return df.rho;
2039
}
2040
2041
void write_params( FileStorage& fs ) const
2042
{
2043
int svmType = params.svmType;
2044
int kernelType = params.kernelType;
2045
2046
String svm_type_str =
2047
svmType == C_SVC ? "C_SVC" :
2048
svmType == NU_SVC ? "NU_SVC" :
2049
svmType == ONE_CLASS ? "ONE_CLASS" :
2050
svmType == EPS_SVR ? "EPS_SVR" :
2051
svmType == NU_SVR ? "NU_SVR" : format("Unknown_%d", svmType);
2052
String kernel_type_str =
2053
kernelType == LINEAR ? "LINEAR" :
2054
kernelType == POLY ? "POLY" :
2055
kernelType == RBF ? "RBF" :
2056
kernelType == SIGMOID ? "SIGMOID" :
2057
kernelType == CHI2 ? "CHI2" :
2058
kernelType == INTER ? "INTER" : format("Unknown_%d", kernelType);
2059
2060
fs << "svmType" << svm_type_str;
2061
2062
// save kernel
2063
fs << "kernel" << "{" << "type" << kernel_type_str;
2064
2065
if( kernelType == POLY )
2066
fs << "degree" << params.degree;
2067
2068
if( kernelType != LINEAR )
2069
fs << "gamma" << params.gamma;
2070
2071
if( kernelType == POLY || kernelType == SIGMOID )
2072
fs << "coef0" << params.coef0;
2073
2074
fs << "}";
2075
2076
if( svmType == C_SVC || svmType == EPS_SVR || svmType == NU_SVR )
2077
fs << "C" << params.C;
2078
2079
if( svmType == NU_SVC || svmType == ONE_CLASS || svmType == NU_SVR )
2080
fs << "nu" << params.nu;
2081
2082
if( svmType == EPS_SVR )
2083
fs << "p" << params.p;
2084
2085
fs << "term_criteria" << "{:";
2086
if( params.termCrit.type & TermCriteria::EPS )
2087
fs << "epsilon" << params.termCrit.epsilon;
2088
if( params.termCrit.type & TermCriteria::COUNT )
2089
fs << "iterations" << params.termCrit.maxCount;
2090
fs << "}";
2091
}
2092
2093
bool isTrained() const CV_OVERRIDE
2094
{
2095
return !sv.empty();
2096
}
2097
2098
bool isClassifier() const CV_OVERRIDE
2099
{
2100
return params.svmType == C_SVC || params.svmType == NU_SVC || params.svmType == ONE_CLASS;
2101
}
2102
2103
int getVarCount() const CV_OVERRIDE
2104
{
2105
return var_count;
2106
}
2107
2108
String getDefaultName() const CV_OVERRIDE
2109
{
2110
return "opencv_ml_svm";
2111
}
2112
2113
void write( FileStorage& fs ) const CV_OVERRIDE
2114
{
2115
int class_count = !class_labels.empty() ? (int)class_labels.total() :
2116
params.svmType == ONE_CLASS ? 1 : 0;
2117
if( !isTrained() )
2118
CV_Error( CV_StsParseError, "SVM model data is invalid, check sv_count, var_* and class_count tags" );
2119
2120
writeFormat(fs);
2121
write_params( fs );
2122
2123
fs << "var_count" << var_count;
2124
2125
if( class_count > 0 )
2126
{
2127
fs << "class_count" << class_count;
2128
2129
if( !class_labels.empty() )
2130
fs << "class_labels" << class_labels;
2131
2132
if( !params.classWeights.empty() )
2133
fs << "class_weights" << params.classWeights;
2134
}
2135
2136
// write the joint collection of support vectors
2137
int i, sv_total = sv.rows;
2138
fs << "sv_total" << sv_total;
2139
fs << "support_vectors" << "[";
2140
for( i = 0; i < sv_total; i++ )
2141
{
2142
fs << "[:";
2143
fs.writeRaw("f", sv.ptr(i), sv.cols*sv.elemSize());
2144
fs << "]";
2145
}
2146
fs << "]";
2147
2148
if ( !uncompressed_sv.empty() )
2149
{
2150
// write the joint collection of uncompressed support vectors
2151
int uncompressed_sv_total = uncompressed_sv.rows;
2152
fs << "uncompressed_sv_total" << uncompressed_sv_total;
2153
fs << "uncompressed_support_vectors" << "[";
2154
for( i = 0; i < uncompressed_sv_total; i++ )
2155
{
2156
fs << "[:";
2157
fs.writeRaw("f", uncompressed_sv.ptr(i), uncompressed_sv.cols*uncompressed_sv.elemSize());
2158
fs << "]";
2159
}
2160
fs << "]";
2161
}
2162
2163
// write decision functions
2164
int df_count = (int)decision_func.size();
2165
2166
fs << "decision_functions" << "[";
2167
for( i = 0; i < df_count; i++ )
2168
{
2169
const DecisionFunc& df = decision_func[i];
2170
int sv_count = getSVCount(i);
2171
fs << "{" << "sv_count" << sv_count
2172
<< "rho" << df.rho
2173
<< "alpha" << "[:";
2174
fs.writeRaw("d", (const uchar*)&df_alpha[df.ofs], sv_count*sizeof(df_alpha[0]));
2175
fs << "]";
2176
if( class_count >= 2 )
2177
{
2178
fs << "index" << "[:";
2179
fs.writeRaw("i", (const uchar*)&df_index[df.ofs], sv_count*sizeof(df_index[0]));
2180
fs << "]";
2181
}
2182
else
2183
CV_Assert( sv_count == sv_total );
2184
fs << "}";
2185
}
2186
fs << "]";
2187
}
2188
2189
void read_params( const FileNode& fn )
2190
{
2191
SvmParams _params;
2192
2193
// check for old naming
2194
String svm_type_str = (String)(fn["svm_type"].empty() ? fn["svmType"] : fn["svm_type"]);
2195
int svmType =
2196
svm_type_str == "C_SVC" ? C_SVC :
2197
svm_type_str == "NU_SVC" ? NU_SVC :
2198
svm_type_str == "ONE_CLASS" ? ONE_CLASS :
2199
svm_type_str == "EPS_SVR" ? EPS_SVR :
2200
svm_type_str == "NU_SVR" ? NU_SVR : -1;
2201
2202
if( svmType < 0 )
2203
CV_Error( CV_StsParseError, "Missing or invalid SVM type" );
2204
2205
FileNode kernel_node = fn["kernel"];
2206
if( kernel_node.empty() )
2207
CV_Error( CV_StsParseError, "SVM kernel tag is not found" );
2208
2209
String kernel_type_str = (String)kernel_node["type"];
2210
int kernelType =
2211
kernel_type_str == "LINEAR" ? LINEAR :
2212
kernel_type_str == "POLY" ? POLY :
2213
kernel_type_str == "RBF" ? RBF :
2214
kernel_type_str == "SIGMOID" ? SIGMOID :
2215
kernel_type_str == "CHI2" ? CHI2 :
2216
kernel_type_str == "INTER" ? INTER : CUSTOM;
2217
2218
if( kernelType == CUSTOM )
2219
CV_Error( CV_StsParseError, "Invalid SVM kernel type (or custom kernel)" );
2220
2221
_params.svmType = svmType;
2222
_params.kernelType = kernelType;
2223
_params.degree = (double)kernel_node["degree"];
2224
_params.gamma = (double)kernel_node["gamma"];
2225
_params.coef0 = (double)kernel_node["coef0"];
2226
2227
_params.C = (double)fn["C"];
2228
_params.nu = (double)fn["nu"];
2229
_params.p = (double)fn["p"];
2230
_params.classWeights = Mat();
2231
2232
FileNode tcnode = fn["term_criteria"];
2233
if( !tcnode.empty() )
2234
{
2235
_params.termCrit.epsilon = (double)tcnode["epsilon"];
2236
_params.termCrit.maxCount = (int)tcnode["iterations"];
2237
_params.termCrit.type = (_params.termCrit.epsilon > 0 ? TermCriteria::EPS : 0) +
2238
(_params.termCrit.maxCount > 0 ? TermCriteria::COUNT : 0);
2239
}
2240
else
2241
_params.termCrit = TermCriteria( TermCriteria::EPS + TermCriteria::COUNT, 1000, FLT_EPSILON );
2242
2243
setParams( _params );
2244
}
2245
2246
void read( const FileNode& fn ) CV_OVERRIDE
2247
{
2248
clear();
2249
2250
// read SVM parameters
2251
read_params( fn );
2252
2253
// and top-level data
2254
int i, sv_total = (int)fn["sv_total"];
2255
var_count = (int)fn["var_count"];
2256
int class_count = (int)fn["class_count"];
2257
2258
if( sv_total <= 0 || var_count <= 0 )
2259
CV_Error( CV_StsParseError, "SVM model data is invalid, check sv_count, var_* and class_count tags" );
2260
2261
FileNode m = fn["class_labels"];
2262
if( !m.empty() )
2263
m >> class_labels;
2264
m = fn["class_weights"];
2265
if( !m.empty() )
2266
m >> params.classWeights;
2267
2268
if( class_count > 1 && (class_labels.empty() || (int)class_labels.total() != class_count))
2269
CV_Error( CV_StsParseError, "Array of class labels is missing or invalid" );
2270
2271
// read support vectors
2272
FileNode sv_node = fn["support_vectors"];
2273
2274
CV_Assert((int)sv_node.size() == sv_total);
2275
2276
sv.create(sv_total, var_count, CV_32F);
2277
FileNodeIterator sv_it = sv_node.begin();
2278
for( i = 0; i < sv_total; i++, ++sv_it )
2279
{
2280
(*sv_it).readRaw("f", sv.ptr(i), var_count*sv.elemSize());
2281
}
2282
2283
int uncompressed_sv_total = (int)fn["uncompressed_sv_total"];
2284
2285
if( uncompressed_sv_total > 0 )
2286
{
2287
// read uncompressed support vectors
2288
FileNode uncompressed_sv_node = fn["uncompressed_support_vectors"];
2289
2290
CV_Assert((int)uncompressed_sv_node.size() == uncompressed_sv_total);
2291
uncompressed_sv.create(uncompressed_sv_total, var_count, CV_32F);
2292
2293
FileNodeIterator uncompressed_sv_it = uncompressed_sv_node.begin();
2294
for( i = 0; i < uncompressed_sv_total; i++, ++uncompressed_sv_it )
2295
{
2296
(*uncompressed_sv_it).readRaw("f", uncompressed_sv.ptr(i), var_count*uncompressed_sv.elemSize());
2297
}
2298
}
2299
2300
// read decision functions
2301
int df_count = class_count > 1 ? class_count*(class_count-1)/2 : 1;
2302
FileNode df_node = fn["decision_functions"];
2303
2304
CV_Assert((int)df_node.size() == df_count);
2305
2306
FileNodeIterator df_it = df_node.begin();
2307
for( i = 0; i < df_count; i++, ++df_it )
2308
{
2309
FileNode dfi = *df_it;
2310
DecisionFunc df;
2311
int sv_count = (int)dfi["sv_count"];
2312
int ofs = (int)df_index.size();
2313
df.rho = (double)dfi["rho"];
2314
df.ofs = ofs;
2315
df_index.resize(ofs + sv_count);
2316
df_alpha.resize(ofs + sv_count);
2317
dfi["alpha"].readRaw("d", (uchar*)&df_alpha[ofs], sv_count*sizeof(df_alpha[0]));
2318
if( class_count >= 2 )
2319
dfi["index"].readRaw("i", (uchar*)&df_index[ofs], sv_count*sizeof(df_index[0]));
2320
decision_func.push_back(df);
2321
}
2322
if( class_count < 2 )
2323
setRangeVector(df_index, sv_total);
2324
if( (int)fn["optimize_linear"] != 0 )
2325
optimize_linear_svm();
2326
}
2327
2328
SvmParams params;
2329
Mat class_labels;
2330
int var_count;
2331
Mat sv, uncompressed_sv;
2332
vector<DecisionFunc> decision_func;
2333
vector<double> df_alpha;
2334
vector<int> df_index;
2335
2336
Ptr<Kernel> kernel;
2337
};
2338
2339
2340
Ptr<SVM> SVM::create()
2341
{
2342
return makePtr<SVMImpl>();
2343
}
2344
2345
Ptr<SVM> SVM::load(const String& filepath)
2346
{
2347
FileStorage fs;
2348
fs.open(filepath, FileStorage::READ);
2349
2350
Ptr<SVM> svm = makePtr<SVMImpl>();
2351
2352
((SVMImpl*)svm.get())->read(fs.getFirstTopLevelNode());
2353
return svm;
2354
}
2355
2356
2357
}
2358
}
2359
2360
/* End of file. */
2361
2362