SHOGUN  3.2.1
 全部  命名空间 文件 函数 变量 类型定义 枚举 枚举值 友元 宏定义  
Libshogun使用指南和开发者指南

Shogun分成两个部分:libshogun,它包含了已实现的所有机器学习算法;libshogunui, 它是一个接口库,提供各种静态和模块化接口,静态接口包括python、octave、matlab和r, 模块化接口包括python_modular、octave_modular和r_modular(各种接口可以在src/目录 下找到相应的子目录)。关于如何安装shogun请看src/INSTALL。

如果要想扩展shogun,最好方法就是开始使用它提供的库。通过查看examples/libshogun 的例子你可以很容易学习使用它。

最简单的基于libshogun的程序如下所示:

它可以通过g++ -lshogun minimal.cpp -o minimal来编译,很显然它什么事情都没有做 (除了在内部初始化了一组全局变量并销毁它们)。

如果想重定向shogun的输出函数SG_DEBUG()、SG_INFO()、SG_WARN()、SG_ERROR()、SG_PRINT() 等,你需要像下面这样把相应参数传给init_shogun()

void print_message(FILE* target, const char* str)
{
    fprintf(target, "%s", str);
}

void print_warning(FILE* target, const char* str)
{
    fprintf(target, "%s", str);
}

void print_error(FILE* target, const char* str)
{
    fprintf(target, "%s", str);
}

init_shogun(&print_message, &print_warning,
            &print_error);

如果要使用某些特性,你需要包含相应的头文件。例如,在下面的例子中我们创建了 一些feature和一个高斯核函数:

你很可能感到奇怪,为什么这个例子不会有内存泄漏。首先,向指向数组的指针申请内存 后新建的shogun对象将使用这些内存,并会在shogun对象销毁的同时释放这此内存。每一个 新建的shogun对象在内部都有一个引用计数器。每当一个shogun对象被返回或者作为参数 传给某个函数时,它的引用计数就会增加,比如在上面的例子中

CLibSVM* svm = new CLibSVM(10, kernel, labels);

kernel和labels的引用计数将会增加。析构对象的时候引用计数就会减少并且在引用计数 <=0时释放所占用的内存。

如果你有全局句柄指向某些对象,这些对象你在后面还要使用,那么你要保证那些对象不会 被销毁。在上面的例子中,如果在调用SG_UNREF(svm)后再访问labels会造成段错误,因为在 调用SVM的析构函数时Label对象也被销毁了。你可以通过调用SG_UNREF(obj)来保证obj指向的 对象不被销毁。相反,调用SG_UNREF(obj)可以使obj的引用计数减一,并且在引用计数<= 0 时销毁obj指向的对象并设置obj=NULL。

一般地,所有的shogun的C++对象都以C为前缀,例如CSVM,它是继承于CSGObject。因为所有 基类的成员变量都要在创建对象时被初始化,所以创建对象时会调用基类的构造函数,如CSVM 调用CKernelMachine,CKernelMachine调用CClassifier,最后CClassifier调用CSGObject。

如果你想实现一个自己的SVM(名为MySVM),你可以在MySVM的构造函数中这样做

class MySVM : public CSVM
{
    MySVM( ) : CSVM()
    {

    }

    virtual ~MySVM()
    {

    }
};

同时要保证你的析构函数是虚函数。

现在我们开始定义一个自己的核函数,一个类线性的核函数,它定义在double精度的浮点向量上。 定义如下:

\(k({\bf x}, {\bf x'}) = \sum_{i=1}^D x_i \cdot x'_{D-i+1}\)

其中D是数据的维数。 要实现这个核函数需要新建一个类CReverseLinearKernel,该类继承于CSimpleKernel<float64_t> (如果输入是字符串,基类应该是CStringKernel,如果是稀疏feature向量,基类应该是CSparseKernel)

实质上我们只需要用自己的实现来重载函数CKernel::compute()。其它都使用缺省的就可以了。 compute()的实现可以像这样

virtual float64_t compute(int32_t idx_a, int32_t idx_b)
{
    int32_t alen, blen;
    bool afree, bfree;

    float64_t* avec=
        ((CSimpleFeatures<float64_t>*) lhs)->get_feature_vector(idx_a, alen, afree);
    float64_t* bvec=
        ((CSimpleFeatures<float64_t>*) rhs)->get_feature_vector(idx_b, blen, bfree);

    ASSERT(alen==blen);

    float64_t result=0;
    for (int32_t i=0; i<alen; i++)
        result+=avec[i]*bvec[alen-i-1];

    ((CSimpleFeatures<float64_t>*) lhs)->free_feature_vector(avec, idx_a, afree);
    ((CSimpleFeatures<float64_t>*) rhs)->free_feature_vector(bvec, idx_b, bfree);

    return result;
}

对于两个索引idx_a和idx_b,我们可以得到它们相应的feature向量,然后使用它们进行计算 (中间的for循环),最后释放这两个feature向量。需要注意的是在大多数情况下,获取feature向量 实际上就是一个访问内存操作(free_feature_vector一般是一个空操作)。然而,当preprocessor 对象被捆绑在某个feature对象时,feature对象会做一些预处理。

一个完整的,可以工作的例子如下所示

你可以看到,还有其它一部分函数用于返回对象名,对象id,以及可以加载或保存核函数初始化 数据。如果你新建了一个SVM(从CSVM或CLinearClassifier继承而来)或者新建了一些feature对象 (从CFeatures,CSimpleFeatures,CStringFeatures,CSparseFeatures继承而来),这些函数 依然可以使用。对于SVM,你只需要重载CSVM::train(),参数设置如epsilon,C以及SVM评价函数 可以使用基类CSVM中的。

如果你想整合你的类实现到shogun的模块化接口中,你所需要做的就是将该类放在一个头文件中并 在相应的.i文件中包含这个头文件(上面实现了一个Kernel类,所以相应的.i文件是 src/modular/Kernel.i)。你可以很容易找到一个类似的对象然后像它那样修改其中的三行: 首先是修改%{ %}语句块(这个会被swig忽略,swig用于产生python/octave的模块化接口)

%{
#include <shogun/kernel/ReverseLinearKernel.h>
%}

然后去掉类名的C前缀(如果有的话)

%rename(ReverseLinearKernel) CReverseLinearKernel;

最后告诉swig封装所在包包含在头文件中的函数

%include <shogun/kernel/ReverseLinearKernel.h>

如果你发现自己设计的对象工作良好,而且符合shogun约定的代码规范,我们很乐意将它整合到shogun中。 代码规范的细节可见于 devel(其中包括格式、宏、类型、函数、命名等方面的约定)。请注意, 如果你修改了某些API那将破坏ABI兼容性,那样你需要增加libshogun soname(见soname)的主号。


SHOGUN 机器学习工具包 - 项目文档