上一次主要讲了怎么在visual C++中编辑svmlin。有时候我们还想对svmlin进行一些改过,来满足我们自己的需要。
为了不耽误高手的时间,我先把我找的两个小bug列出来,第一个svmlin.cpp中的Read函数中参数名inputs_file_name,在函数中用的是input_file_name,因为作者用的是全局变量才没出错,改造时要小心,另一个是作者好几个地方没有关闭文件(fclose),如果中间要删除文件,重命名文件就会出错(我用到了,所以才发现了)。
这一次介绍一下svmlin是如何读取文件中的数据的,至于它是怎样分类的,首先我不会,会可能也懒的讲。
要改造它,第一步是克服自己的恐惧,其实也没什么,看一下文件大小就知道。
首先看一下数据结构:
struct data
{
int m; /* number of examples */
int l; /* number of labeled examples */
int u; /* number of unlabeled examples l+u = m */
int n; /* number of features */
int nz; /* number of non-zeros */
double *val; /* data values (nz elements) [CRS format] */
int *rowptr; /* n+1 vector [CRS format] */
int *colind; /* nz elements [CRS format] */
double *Y; /* labels */
double *C; /* cost associated with each example */
};
保存数据集的数据结构,有注释,就不解释了,CRS是用于保存稀疏矩阵的一种方式,这里http://netlib2.cs.utk.edu/linalg/html_templates/node91.html有一个比较清楚的介绍,我简单介绍一下,val是保存非0元素值的一个数组指针,rowptr是指有多少行,colind是非0元素的列坐标。
struct vector_double /* defines a vector of doubles */
{
int d; /* number of elements */
double *vec; /* ptr to vector elements*/
};
struct vector_int /* defines a vector of ints for index subsets */
{
int d; /* number of elements */
int *vec; /* ptr to vector elements */
};
这两个没什么好讲的,一个数组指针,一个是数组的长度,在程序中用于保存类别,权重。
现在看svmlin.cpp文件
Main函数中第一个调用的函数parse_command_line(argc, argv);获得参数。
if(train) /* train */
{
ReadTrainingData();
ssl_train(Data,Options,Weights,Outputs);
cout << endl << "Writing Weights to file: " << weights_file_name << endl;
Write(weights_file_name,Weights);
Clear(Data);
}
如果是训练,读取训练数据,训练,输出权值,输出weights_file_name(也就是学习的结果)。
ReadTrainingData函数中定义了Subset变量,它的作用是记录哪一个样本是标记的,哪一个不是。在监督算法中Subset只记录标记样本的行坐标,而半监督算法中就直接用initialize函数全部记录下来。
ReadTrainingData通过Read函数读取样本,Read函数中的goto的写法还有点意思,作者第一次读文件获得有多少样本,然后分配内存,再读数据。
ReadTrainingData调用Read(inputs_file_name,Data,Subset)函数取读数据集特征数据,这个函数也没有什么难度,还是先读有多少个非0元素,再读特征index和特征值fscanf(fpin,"%d:%lf",&C[j],&VAL[j]);
最后再讲一下output文件中的内容,大于0的就是正例,小于0的就是负例(我猜的)。