本文翻译自OpenCV 2.4.9官方文档《opencv2refman.pdf》。
前言
Originally, support vector machines (SVM) was a technique for building an optimal binary (2-class) classifier. Later the technique was extended to regression and clustering problems. SVM is a partial case of kernel-based methods. It maps feature vectors into a higher-dimensional space using a kernel function and builds an optimal linear discriminating function in this space or an optimal hyper-plane that fits into the training data. In case of SVM, the kernel is not defined explicitly. Instead, a distance between any 2 points in the hyper-space needs to be defined.
The solution is optimal, which means that the margin between the separating hyper-plane and the nearest feature vectors from both classes (in case of 2-class classifier) is maximal. The feature vectors that are the closest to the hyper-plane are called support vectors, which means that the position of other vectors does not affect the hyper-plane (the decision function).
SVM implementation in OpenCV is based on LibSVM.
通常来说,支持向量机(SVM)是一种用来构建一个最优二进制分类器(只分为两类)。后来,这项技术被延伸到回归与集群问题。SVM是以核函数方法为基础的众多方法之一,它通过核函数将特征向量映射到高维空间,并在这个空间创造一个最优线性分类函数,或者创造一个适合所有训练数据的最优超平面。在SVM中,核函数定义的并不明确,除此之外,在超平面上任意两点之间的距离都需要被定义。
解决方法是最优的,意味着分割超平面与两个分类(即二类分类器)上距离最近的特征向量之间的距离是最大的。距离超平面最近的特征向量被称为支持向量,就是说其它向量的位置都不会影响超平面(即决策函数)。
SVM在OpenCV中的实现是基于LibSVM的。
CvParamGrid
CvParamGrid
struct CvParamGrid
该结构体代表了统计模型参数的对数网格范围,它通过更新模型参数来优化统计模型准确度,准确度通过交叉验证的计算进行估计。
- double CvParamGrid::min_val
- double CvParamGrid::max_val
- double CvParamGrid::step
网格决定了统计模型参数值的迭代序列,如下所示:
(min_val,min_val×step,min_val×step2,...,min_val×stepn)
其中
n 是一个最大索引号,满足:
min_val×stepn<max_val
网格已经经过对数化,所以
step一定大于1。
CvParamGrid::CvParamGrid
CvParamGrid的构造函数。
- C++: CvParamGrid::CvParamGrid()
- C++: CvParamGrid::CvParamGrid( double min_val, double max_val, double log_step )
整个构造函数初始化了对应的数据成员,默认的构造函数创造的虚拟网格如下:
CvParamGrid::CvParamGrid()
{
min_val = max_val = step = 0;
}
CvParamGrid::check
检测网格的有效性。
- C++: bool CvParamGrid::check()
如果网格有效的,则返回true;如;如果无效,则返回false。当且仅当下列情况时,网格是有效的:
- 网格的下边缘边界小于上边缘边界;
- 网格的下边缘边界是正值;
- 网格步长大于1;
CvSVMParams
CvSVMParams
struct CvSVMParams
SVM训练参数。结构体将被初始化,并传递给CvSVM的训练函数。
CvSVMParams::CvSVMParams
CvSVMParams的构造函数。
- C++: CvSVMParams::CvSVMParams()
- C++: CvSVMParams::CvSVMParams( int svm_type, int kernel_type, double degree, double gamma, double coef0, double Cvalue, double nu, double p, CvMat* class_weights,CvTermCriteria term_crit )
CvSVMParams()的参数
svm_type
SVM公式类型。可能取值如下:
- CvSVM::C_SVC: C 支持向量分类器。可以分为n类 (n ≥ 2),并允许在带有松弛变量 (outliers) 的惩罚乘子C的情况下的不完善分类;
- CvSVM::NU_SVC: ν 支持向量分类器。可以分为n类 (n ≥ 2),而且可能存在不完善的分类。参数 ν 替代参数 C 使用,且 ν 值范围在[0, 1]之内,该值越大,决策边缘越光滑;
- CvSVM::ONE_CLASS:分布估计(单类SVM)。所有的训练数据都是同一类的,SVM构建了边界,将该类与特征空间的其他部分分离;
- CvSVM::EPS_SVR: ϵ 支持向量回归。训练集特征向量与拟合超平面间的距离一定小于 p 。该分类器的松弛变量 (outliers) 使用的惩罚因子为 C ;
- CvSVM::NU_SVR: ν 支持向量回归。 ν 被用来代替 p ;
其他具体解释见LibSVM。
kernel_type
SVM核函数类型。可能取值如下:
- CvSVM::LINEAR:线性核。无映射,在原始特征空间中进行线性分类(或回归)。这是速度最快的选项。核函数为: K(xi,xj)=xTixj 。
- CvSVM::POLY:多项式核。核函数为: K(xi,xj)=(γxTixj+coef0)degree,γ>0 ;
- CvSVM::RBF:以半径为基准的函数 (Radial basis function, RBF),在大多情况下的优秀选择。核函数: K(xi,xj)=e−γ∥xi−xj∥2,γ>0 ;
- CvSVM::SIGMOID:sigmoid函数。核函数为: K(xi,xj)=tanh(γxTixj+coef0) ;
degree
核函数的参数 degree ,用于多项式核。
gamma
核函数的参数 γ ,用于多项式核 / RBF核 / Sigmoid核。
coef0
核函数的参数 coef0 ,用于多项式核 / Sigmoid核。
Cvalue
SVM最优问题的参数 C ,用于 C_SVC / EPS_SVR / NU_SVR 分类器。
nu
SVM最优问题的参数 ν ,用于 NU_SVC / ONE_CLASS / NU_SVR 分类器。
p
SVM最优问题的参数 ϵ ,用于 EPS_SVR 分类器。
class_weights
C_SVC问题中的最优权重,它被分配给特定的分类。这些权重与因子 C 做乘运算,所以第 i 个分类的的参数 C 值应该为: class_weightsi×C 。因此,这些权重对其他分类的误分类惩罚有一定影响,权重越大,对应分类的数据误分类的惩罚越大。
term_crit
SVM训练迭代的迭代终止标准,用于解决带有约束条件的二次最优规划问题。你也能明确公差与 / 或迭代最大次数。
CvSVMParams()的函数
默认的构造函数使用下列取值初始化该结构体:
CvSVMParams::CvSVMParams() :
svm_type(CvSVM::C_SVC), kernel_type(CvSVM::RBF), degree(0),
gamma(1), coef0(0), C(1), nu(0), p(0), class_weights(0)
{
term_crit = cvTermCriteria( CV_TERMCRIT_ITER+CV_TERMCRIT_EPS, 1000, FLT_EPSILON );
}
CvSVM
class CvSVM : public CvStatModel
支持向量机。
注:
· (Python) 使用SVM的数字识别例程可以在路径 opencv_source/samples/python2/digits.py 下找到;
· (Python) 使用SVM的网格寻找数字识别可以在路径 opencv_source/samples/python2/digits_adjust.py 下找到;
· (Python) 使用SVM的视频数字识别例程可以在路径 opencv_source/samples/python2/digits_video.py 下找到;
CvSVM::CvSVM
默认构造函数与训练构造函数。
- C++: CvSVM::CvSVM()
- C++: CvSVM::CvSVM( const Mat& trainData, const Mat& responses, const Mat& varIdx=Mat(), const Mat& sampleIdx=Mat(), CvSVMParams params=CvSVMParams() )
- C++: CvSVM::CvSVM( const CvMat* trainData, const CvMat* responses, const CvMat* varIdx=0, const CvMat* sampleIdx=0, CvSVMParams params=CvSVMParams() )
- Python: cv2.SVM( [ trainData, responses [ , varIdx [ , sampleIdx [ , params ]]]] ) →
构造函数与CvStatModel::CvStatModel()
有相同的形式。可以在本文档的CvStatModel::train()
寻找参数的具体含义。
本文注:
下面从CvStatModel::train()
中找到参数列表:
- C++: bool CvStatModel::train( const Mat& train_data, [int tflag,] …, const Mat& responses, …, [const Mat& var_idx,] …, [const Mat& sample_idx,] … [const Mat& var_type,] …, [const Mat& missing_mask,] … ) = 0
对于其中参数,解释如下:
CvSVM()的参数
通过使用一组输入特征向量并输出相应值(或响应)的方法,训练函数训练了统计模型。输入 / 输出向量(或输入 / 输出值)都以矩阵形式传递。默认情况下,输入特征向量被存入train_data的列中,所有训练向量的组成(即特征)被连续存储。然而当全部输入集的各特定特征值(特征 / 输入变量)的所有值都是连续存储的情况下,一些算法可以处理转置表达式。如果两种布局都支持,训练方法包含的tflag参数起作用,该参数用来明确数据存储方向,具体如下:
- tflag=CV_ROW_SAMPLE:特征向量按行存储;
- tflag=CV_COL_SAMPLE:特征向量按列存储;
训练数据train_data必须使用CV_32FC1格式(32位浮点数,单通道)。返回数据responses通常以一维向量(一行或一列)的形式存储,向量中的数据格式为CV_32SC1(仅在分类问题中)或CV_32FC1,返回数据的每个值与训练数据的每个向量一一对应。相反的,某些类似于各种类型的神经网络,返回数据的类型都为向量形式。
对于分类问题,返回值是离散的分类标签;对于回归问题,返回值是被估计函数的值。一些算法只能处理分类问题,一些算法只能处理回归问题,也有一些算法可以处理两种问题。对于后者,输出参数的类型可以通过两种方式传递:一种是单独的参数,另一种是向量var_type的最后一个元素:
- CV_VAR_CATEGORICAL:输出值为离散类别标签;
- CV_VAR_ORDERED( =CV_VAR_NUMERICAL):输出值为顺序排列的,即两个不同的值可以以数字形式拿来对比,而且这是一个回归问题;
输入变量的类型可以通过输入参数var_type指定。大多数算法仅仅可以处理连续输入变量。
很多 ML (机器学习)模型可以用一个指定的特征子集与 / 或指定的训练集的样本子集进行训练。为了使其对我们更加简单,训练方法train函数通常包含参数var_idx和sample_idx,前者(即var_idx)用来指定该兴趣的变量(特征),后者(即sample_idx)指定感兴趣的样本。两个向量可以使用整数 (CV_32SC1) 向量(基于0的索引列表),也可以使用8位 (CV_8UC1)的活动变量 / 样本。我们也可以传递NULL空指针来代替众多参数,这样的话所有的变量 / 样本都被用来训练。
此外,当已知训练样本的已知特征存在未知值时,一些算法可以处理丢失的量(例如,我们星期一忘记测量病人A的体温)。参数missing_mask是一个与train_data有相同尺寸的8位矩阵,它被用来标记丢失的值(即用非零值进行标记)。
通常,先前的模型统计在运行训练函数之前都被CvStatModel::clear()清除了,然而一些算法可以选择使用新训练数据更新模型统计,而不是重置它。
CvSVM::train
训练一个SVM。
- C++: bool CvSVM::train( const Mat& trainData, const Mat& responses, const Mat& varIdx=Mat(), const Mat& sampleIdx=Mat(), CvSVMParams params=CvSVMParams() )
- C++: bool CvSVM::train( const CvMat* trainData, const CvMat* responses, const CvMat* varIdx=0, const CvMat* sampleIdx=0, CvSVMParams params=CvSVMParams() )
- Python: cv2.SVM.train( trainData, responses [ , varIdx [ , sampleIdx [ , params ]]] ) → retval
该函数用来训练一个 SVM 模型,它继承了CvStatModel::train()函数的参数列表,但对下列进行了限制:
- 数据布局仅仅支持CV_ROW_SAMPLE;
- 输入变量全部为连续值;
- 输出变量可以是离散的 (param.svm = CvSVM::C_SVC 或 param.svm = CvSVM::NU_SVC),也可以是连续的 (param.svm = CvSVM::EPS_SVR 或 param.svm = CvSVM::NU_SVR),也可以不指定 (param.svm = CvSVM::ONE_CLASS)。
- 不支持丢失测量值;
所有其他参数都被收集进入CvSVMParams结构体。
CvSVM::train_auto
用最优参数训练SVM。
- C++: bool CvSVM::train_auto( const Mat& trainData, const Mat& responses, const Mat& varIdx, const Mat& sampleIdx, CvSVMParams params, int k_fold = 10,
CvParamGrid Cgrid = CvSVM::get_default_grid(CvSVM::C),
CvParamGrid gammaGrid = CvSVM::get_default_grid(CvSVM::GAMMA),
CvParamGrid pGrid=CvSVM::get_default_grid(CvSVM::P),
CvParamGrid nuGrid=CvSVM::get_default_grid(CvSVM::NU),
CvParamGrid coeffGrid=CvSVM::get_default_grid(CvSVM::COEF),
CvParamGrid degreeGrid=CvSVM::get_default_grid(CvSVM::DEGREE),
bool balanced=false )
- C++: bool CvSVM::train_auto( const CvMat* trainData, const CvMat* responses, const CvMat* varIdx, const CvMat* sampleIdx, CvSVMParams params, int kfold=10,
CvParamGrid Cgrid=get_default_grid(CvSVM::C),
CvParamGrid gammaGrid=get_default_grid(CvSVM::GAMMA),
CvParamGrid pGrid=get_default_grid(CvSVM::P),
CvParamGrid nuGrid=get_default_grid(CvSVM::NU),
CvParamGrid coeffGrid=get_default_grid(CvSVM::COEF),
CvParamGrid degreeGrid=get_default_grid(CvSVM::DEGREE),
bool balanced=false )
- Python: cv2.SVM.train_auto( trainData, responses, varIdx, sampleIdx, params [ , k_fold [ , Cgrid [ , gammaGrid [ , pGrid [ , nuGrid [ , coeffGrid [ , degreeGrid [ , balanced ]]]]]]]] ) → retval
train_auto()参数
- k_fold:交叉验证参数。训练集被分成k_fold子集。一个子集被用于测试模型,其余子集组成训练集。所以 SVM 算法总共被执行 k_fold 次;
- Grid:相关SVM参数的迭代网格;
- balanced:如果为true,而且问题为二类分类器,那么该函数就创造更平衡的交叉验证子集,子集各分类之间的比例接近在整个训练数据集中比例;
train_auto()函数说明
train_auto()函数通过从CvSVMParams中选择最优参数C, gamma, p, nu, coef0, degree,自动训练 SVM 模型。当测试集误差的交叉验证估计值达到最小值时,参数被认为是最优的。
如果不需要优化某参数,相应的网格步长应该被设置为任意小于等于1的值。例如为了避免gamma选取最优值,则设置 gamma_grid.step = 0,并令gamma_grid.min_val, gamma_grid.max_val设置为任意值。这种情况下,params.gamma的值将被输入参数gamma赋值。
最后,如果需要优化某参数,但相应网格是位置的,我们可以调用函数CvSVM::get_default_grid()。如果要生成一个网格,以gamma为例,则调用函数:CvSVM::get_default_grid(CVSVM::GAMMA)。
train_auto()函数可以被用来处理分类问题 (param.svm = CvSVM::C_SVC 或 param.svm = CvSVM::NU_SVC),也可以处理回归问题 (param.svm = CvSVM::EPS_SVR 或 param.svm = CvSVM::NU_SVR)。如果有param.svm_type = CvSVM::ONE_CLASS,则不会生成最优值,且普通带有确定参数的 SVM 将被执行。
CvSVM::predict
预测输入样本的返回值。
- C++: float CvSVM::predict( const Mat& sample, bool returnDFVal=false ) const
- C++: float CvSVM::predict( const CvMat* sample, bool returnDFVal=false ) const
- C++: float CvSVM::predict( const CvMat* samples, CvMat* results ) const
- Python: cv2.SVM.predict( sample [ , returnDFVal ] ) → retval
- Python: cv2.SVM.predict_all( samples [ , results ] ) → results
predict()参数
- sample:预测的单个输入样本;
- samples:预测的多输入样本;
- returnDFVal:指定一个返回值的类型。如果为true,且问题为二类分类器,那么该函数返回决策函数值是有符号的间隔距离;如果为false,那么函数返回一个类标签(分类问题),或者返回估计函数值(回归问题);
- results:输出对应样本的预测返回值;
如果我们通过了一个样本,那么预测结果就会返回。如果我们想要得到几个样本的返回值,那么我们应该用矩阵results来保存预测结果。
该函数与 TBB 库并行运行。
CvSVM::get_default_grid
生成一个 SVM 参数的网格。
- C++: CvParamGrid CvSVM::get_default_grid( int param_id )
param_id
SVM 参数的 ID,必须从下列表中取值:
- CvSVM::C
- CvSVM::GAMMA
- CvSVM::P
- CvSVM::NU
- CvSVM::COEF
- CvSVM::DEGREE
网格将根据该 ID 参数生成。
该函数根据 SVM 算法的指定参数而生成一个网格,该网格会传递给函数CvSVM::train_auto()。
CvSVM::get_params
返回当前 SVM 参数。
- C++: CvSVMParams CvSVM::get_params() const
在使用函数CvSVM::train_auto()自动训练时,该函数被用来获取最优参数。
CvSVM::get_support_vector
取得若干支持向量与特定的向量。
- C++: int CvSVM::get_support_vector_count() const
- C++: const float* CvSVM::get_support_vector( int i ) const
- Python: cv2.SVM.get_support_vector_count() → retval
i:特定的支持向量的序列;
该方法用来取得一组支持向量。
CvSVM::get_var_count
返回已使用特征的数量(即变量数量);
- C++: int CvSVM::get_var_count() const
- Python: cv2.SVM.get_var_count() → retval