LibSVM之C# Wrapper

【百度百科】

  LIBSVM是台湾大学林智仁(Lin Chih-Jen)教授等开发设计的一个简单、易于使用和快速有效的SVM模式识别与回归的软件包,他不但提供了编译好的可在Windows系列系统的执行文件,还提供了源代码,方便改进、修改以及在其它操作系统上应用;该软件对SVM所涉及的参数调节相对比较少,提供了很多的默认参数,利用这些默认参数可以解决很多问题;并提供了交互检验(Cross Validation)的功能。该软件可以解决C-SVM、ν-SVM、ε-SVR和ν-SVR等问题,包括基于一对一算法的多类模式识别问题……

  如果你对libsvm还不够了解,建议先浏览下百度百科等对libsvm的介绍~

【C# Wrapper 动机】

  参与过一个项目,使用IDE是VS winform,工具包为EmguCV 2.4.10。我们知道OpenCV2中的svm部分是根据libsvm-2.6编写的,该版本的libsvm已经能够estimate预测概率了(libsvm首页的change log中有详细说明),但是OpenCV却舍弃了predictProbability。在具体的项目中,如果可以获得预测概率信息,那将对提高识别性有很大的帮助。然而,opencv2舍弃了识别概率,包括opencv3,我看源代码的svm部分也是基于libsvm-2.6修改的,也没有引进predictProbability。

  因而,在EmguCV的ML满足不了的情况下,萌生了两个想法:

    一是修改OpenCV代码,然后重新CMake得到cvextern.dll;

    二是直接找其它的svm库。

  首先尝试CMake。像OpenCV这样的大项目,CMake起来确实不容易,更何况是从零开始学CMake。在时间不允许的条件下,只得走第二条路。找到libsvmSharp后,我如获至宝。但是,很快我又再度失望了,因为实时性要求满足不了(EmguCV自带SVM可以在5ms内完成识别预测,而libsvmSharp需要500ms)。

  这是为什么?

  同样是C#对C++的wrapper,同样都是基于libsvm,同样是对C++所编译的dll的引用,效率竟相差百倍!本着一颗学习的心,我决定一探究竟……

【现有libsvm的C#/.Net版本】

  目前,LIBSVM拥有C、Java、Matlab、C#、Ruby、Python、R、Perl、Common LISP、Labview等数十种语言版本。最常使用的是C、Matlab、Java和命令行(c语言编译的工具)的版本。

  首先我们看张libsvm官网首页上的截图:

  下面,我们看看现在libsvm有哪些C#版本:

  1、SVM.NET by Matthewa Johnson

  2009年,剑桥大学的Matthewa Johnson博士将SVM.NET更新到了V2.89,也就是现在的最新版本。无奈现在不FQ竟已经找不到SVM.NET的原生版了。这份神秘感使我觉得,这个C#版本的libsvm应该是质量最高的。

  后人有在V2.89的基础上做一些修改,提出了:SVM.NET with Parallel Optimization。相关描述为:When finding parameters, C and Gamma, in Grid-search algorithm using ParameterSelection.PGrid instead of the original ParameterSelection.Grid will increase the calculation speed.

  2、NSVM by Joannes

    3年时间,却只有2下载量,何其惨淡……好吧,或许你也像我一样主观臆断了。

3、KMLib(Kernel Machine Library with GPU SVM solver in .Net) by Krzysztof Sopy?a

  Key Features

    • .Net implementation
    • Parallel kernel implementation
    • SVM CUDA acceleration – kernels and solver
    • CUDA SVM with sparse data formats: CSR, Ellpack-R, Sliced-Ellpack
    • For non commercial and academic use: Free MIT license when use please cite: Bibtex CUDA SVM CSR

  另外,还有一点需要强调的是,它是基于libsvm的java版本转换过来的。也正因如此,我感觉用起来可能会有点麻烦,故没有选择。

4、libsvmSharp by ccerhan

  选择它的理由很简单,有一定的下载量(从众心理又开始作祟了!)下载方便,用VS的Nuget package,通过命令“PM> Install-Package LibSVMsharp”即下载到本地。

5、libsvm-net by Nicolas Panel

  下载起来同样十分方便: NuGet package : PM> Install-Package libsvm.net,比起libsvmSharp有更高的人气。

【分析libsvmSharp】

  为什么libsvmSharp.dll如此低效?

  在反编译后的源代码中(稍后将介绍如何反编译C#编译出来的dll文件),我们可以看到libsvmSharp所用的数据结构有:

    1、struct:svm_node、svm_model、svm_problem、svm_parameter;

    2、calss:SVMNode、SVMModel、SVMProblem、SVMParameter。

实际上,结构体能做的事情,类完全也能做,似乎结构体没有存在的必要。

  而且,可以看到各类的实现中,有很多“结构体=>类”、“指针=>结构体”、“类=>指针”等这样的类型转换。我们知道,C#要引用C++所编译的dll,用得最多的就是IntPtr这个数据结构。而libsvmSharp低效的原因,也正在于对指针的处理策略选取不当,它只在需要传指针的时候,硬生生地用Marshal类重新在内存中开辟当前数据结构大小的区域,并返回指针,美其名曰convert到指针。这种方式,无论是在时间上还是空间上,都有太多没必要的浪费。

  这里我们用libsvm中的svm_predict作为例子来讲解。

  在libsvm.dll(该dll由C++编译得到)中,函数为:

 double svm_predict(const svm_model *model, const svm_node *x)

  在libsvmSharp.dll(该dll由C#编译得到)中,我们这样声明它:

 [DllImport("libsvm.dll", CallingConvention = CallingConvention.Cdecl)]
 public static extern double svm_predict(IntPtr model, IntPtr x);

  DllImport时,更多关于C++数据结构到C#数据结构的信息请读者查阅资料获得。由上可见,IntPtr是个很关键的数据结构,由它声明的变量实际上是一个指针值(即内存地址值)。第一个参数IntPtr model,要求传入model所在内存区域的地址,第二个参数IntPtr x,要求传入特征节点数组所在内存区域的地址。下面,我们看看libsvmSharp是怎么使用这个函数的:

 1         public static double Predict(SVMModel model, SVMNode[] x)
 2         {
 3             if (model == null)
 4             {
 5                 throw new ArgumentNullException("model");
 6             }
 7             if (x == null)
 8             {
 9                 throw new ArgumentNullException("x");
10             }
11             IntPtr intPtr = SVMModel.Allocate(model);
12             double result = SVM.Predict(intPtr, x);
13             SVMModel.Free(intPtr);
14             return result;
15         }
16
17         public static double Predict(IntPtr ptr_model, SVMNode[] x)
18         {
19             if (ptr_model == IntPtr.Zero)
20             {
21                 throw new ArgumentNullException("ptr_model");
22             }
23             if (x == null)
24             {
25                 throw new ArgumentNullException("x");
26             }
27             List<SVMNode> list = (from a in x
28             select a.Clone()).ToList<SVMNode>();
29             list.Add(new SVMNode(-1, 0.0));
30             IntPtr intPtr = SVMNode.Allocate(list.ToArray());
31             double result = libsvm.svm_predict(ptr_model, intPtr);
32             SVMNode.Free(intPtr);
33             return result;
34         }

  细心的你有没有发现什么问题?看不懂?毕竟我是断章取义。然而,请看第11行,每次调用都要重新给model分配内存哦!再如,第27、28、29、30行,在熟悉C++的人看来,that‘s what?参数传进来的可不是数组名吗,干嘛如此大费周章?内存不会被玩坏吗?

  一切都是因为C#有指针,但不是那个我们所熟悉的指针。C#没有像Java一样完全摈弃指针,但为了代码安全考虑而弱化指针。C#是面向对象的语言,里面任何一种数据结构都没有指针这一属性,除非你自己在定义数据结构时,将指针作为成员变量。我们所熟悉的EmguCV就是这么实现对OpenCV的wrapper的。

【开始libsvm的C# Wrapper之旅】

  很好,我们可以进入正题了。我将以wrapper libsvm为例,分步骤讲解整个过程。读者可以举一反三,希望本文可以帮助你加深你对跨语言编程的理解。

  1.wrapper第一步(准备)

  获取你要wrapper的dll(由C++编译得到),最好有源代码,当然有参考手册也可以,但是如果除了dll的名字,对该dll一无所知,那或许就无能为力了。

  安装C#的dll反编译工具,这里推荐ILSpy。为什么要安装?比起自己黑暗中摸索,如果有可以参考借鉴的资源,视而不见是多么可惜的一件事啊。EmguCV真的称得上wrapper中的精华。

2. wrapper第二步(DllImport)

  首先,VS新建C#工程,项目类别选择类库,这样最后生成解决方案后,便可以在bin/Debug目录下获得实用的dll文件了。我将项目命名为libsvmSharpCyc。

  其次,添加需要wrapper的C++ dll文件。右键单击解决方案资源管理器中的libsvmSharpCyc,然后添加现有项,把libsvm.dll添加进项目。

  接着,新建类,用于DllImport。我建的是LsInvoke.cs,可以像下图所示这样,把想要使用的函数方法给Import进来:

  该过程中,DllImport要如何使用,感兴趣的读者可自行学习,这里需要注意的是C++函数中的数据结构到C#中的数据结构是有映射关系的,下面附上一张dll引用常用转化表:

            C++            C#
        =====================================
        WORD              ushort
        DWORD             uint
        UCHAR             int/byte   大部分情况都可以使用int代替,而如果需要严格对齐的话则应该用bytebyte
        UCHAR*            string/IntPtr
        unsigned char*    [MarshalAs(UnmanagedType.LPArray)]byte[]/?(Intptr)
        char*             string
        LPCTSTR           string
        LPTSTR            [MarshalAs(UnmanagedType.LPTStr)] string
        long              int
        ulong             uint
        Handle            IntPtr
        HWND              IntPtr
        void*             IntPtr
        int               int
        int*              ref int
        *int              IntPtr
        unsigned int      uint
        COLORREF          uint

3、wrapper第三步(数据结构)

  这一步是最为关键的一步,在C#中新建数据结构,必须要与C++中的数据结构相一致,否则碰到无法预料的问题。

  前文已经简单地介绍过libsvm的数据结构了。这里重复一下:

 1 struct svm_node
 2 {
 3     int index;
 4     double value;
 5 };
 6
 7 struct svm_problem
 8 {
 9     int l;
10     double *y;
11     struct svm_node **x;
12 };
13
14 enum { C_SVC, NU_SVC, ONE_CLASS, EPSILON_SVR, NU_SVR };    /* svm_type */
15 enum { LINEAR, POLY, RBF, SIGMOID, PRECOMPUTED }; /* kernel_type */
16
17 struct svm_parameter
18 {
19     int svm_type;
20     int kernel_type;
21     int degree;    /* for poly */
22     double gamma;    /* for poly/rbf/sigmoid */
23     double coef0;    /* for poly/sigmoid */
24
25     /* these are for training only */
26     double cache_size; /* in MB */
27     double eps;    /* stopping criteria */
28     double C;    /* for C_SVC, EPSILON_SVR and NU_SVR */
29     int nr_weight;        /* for C_SVC */
30     int *weight_label;    /* for C_SVC */
31     double* weight;        /* for C_SVC */
32     double nu;    /* for NU_SVC, ONE_CLASS, and NU_SVR */
33     double p;    /* for EPSILON_SVR */
34     int shrinking;    /* use the shrinking heuristics */
35     int probability; /* do probability estimates */
36 };
37
38 //
39 // svm_model
40 //
41 struct svm_model
42 {
43     struct svm_parameter param;    /* parameter */
44     int nr_class;        /* number of classes, = 2 in regression/one class svm */
45     int l;            /* total #SV */
46     struct svm_node **SV;        /* SVs (SV[l]) */
47     double **sv_coef;    /* coefficients for SVs in decision functions (sv_coef[k-1][l]) */
48     double *rho;        /* constants in decision functions (rho[k*(k-1)/2]) */
49     double *probA;        /* pariwise probability information */
50     double *probB;
51     int *sv_indices;        /* sv_indices[0,...,nSV-1] are values in [1,...,num_traning_data] to indicate SVs in the training set */
52
53     /* for classification only */
54
55     int *label;        /* label of each class (label[k]) */
56     int *nSV;        /* number of SVs for each class (nSV[k]) */
57                 /* nSV[0] + nSV[1] + ... + nSV[k-1] = l */
58     /* XXX */
59     int free_sv;        /* 1 if svm_model is created by svm_load_model*/
60                 /* 0 if svm_model is created by svm_train */
61 };

  对应地,我们在C#中建立数据结构:

    public struct svm_node
    {
        /// <summary>
        /// 索引
        /// </summary>
        public int index;

        /// <summary>
        /// 值
        /// </summary>
        public double value;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="i"></param>
        /// <param name="v"></param>
        public svm_node(int i,double v)
        {
            this.index = i;
            this.value = v;
        }
        public bool Equals(svm_node x)
        {
            return this.index.Equals(x.index) && this.value.Equals(x.value);
        }
    }
    public struct svm_problem
    {
        /// <summary>
        /// 支持向量个数
        /// </summary>
        public int l;

        /// <summary>
        /// 标签值
        /// </summary>
        public IntPtr y;

        /// <summary>
        /// 节点情况
        /// </summary>
        public IntPtr x;
    }
    ……

  可能有读者会问,结构体你加构造函数和其它函数干嘛?这其实是为了日后好简化代码。否则,每次对象创建于赋值分开操作有点麻烦。

  进行到现在,我们只是完成了数据结构搭建的一小部分,下面是从EmguCV中学习到的精髓部分!将在下篇作介绍~

时间: 2024-08-02 02:49:47

LibSVM之C# Wrapper的相关文章

在python下学习libsvm

1.下载libsvm,python,gnuplot(链接网上全有,压缩包自己保留着) 2.在python上的实现(主要用截图的形式展现) (1)输入命令寻求最优参数 (2) 参数c,g输出结果 gnuplot输出图像 (3)最后输入训练数据,训练数据,通过建立模型进行预测 大概也就这样了,grid.py里面需要改下gnuplot的路径 在python下学习libsvm,布布扣,bubuko.com

多个 gradle 文件夹 \.gradle\wrapper\dists\ 设置gradle不是每次都下载

韩梦飞沙  韩亚飞  [email protected]  yue31313  han_meng_fei_sha 设置gradle不是每次都下载 \.gradle\wrapper\dists\ ======= 在你导入项目的时候,有个选项的: 你要是选了Use default gradle mapper就会下载一次,Use local gradle distribution就会用你制定的gradle了 ====== 设置gradle不是每次都下载 \.gradle\wrapper\dists\

Windows cordova build Error: Could not find gradle wrapper within Android SDK.(转)

原文:http://blog.csdn.net/kongxx/article/details/68954151 在Windows7上运行 "cordova build Android" 报错,如下: C:\test\hello> cordova build android ANDROID_HOME=C:\Users\kongxx\AppData\Local\Android\sdk JAVA_HOME=C:\Program Files\Java\jdk1.8.0_121 Error

Windows service wrapper 初探

Windows 服务包装器(Windows service wrapper),用于把.exe文件注册为windows服务.比如把Nginx.exe注册为windows服务,这样做的好处是,每次启动nginx时不用在命令行中输入命令,而且可以随windows系统启动而启动.不用担心服务器意外重启,服务挂掉. github地址:https://github.com/kohsuke/winsw 下载地址:https://github.com/kohsuke/winsw/releases 目前(2017

libsvm的数据格式及制作

1.libsvm数据格式 libsvm使用的训练数据和检验数据文件格式如下: [label] [index1]:[value1] [index2]:[value2] … [label] [index1]:[value1] [index2]:[value2] … label  目标值,就是说class(属于哪一类),就是你要分类的种类,通常是一些整数. index 是有顺序的索引,通常是连续的整数.就是指特征编号,必须按照升序排列 value 就是特征值,用来train的数据,通常是一堆实数组成.

MATLAB安装libsvm无法使用解决办法(转)

buaasuozi  这是原作者: 安装libsvm 不成功有可能是你的MATLAB版本或者是编译文件版本的问题,但是不要急着换其他版本....说不定就有别的解决办法呢 首先感谢Lin教授及其实验室提供的libsvm工具箱,原始下载地址:下载主页:http://www.csie.ntu.edu.tw/~cjlin/libsvm/       下载地址:http://www.csie.ntu.edu.tw/~cjlin/libsvm/#download 原本我使用的是libsvm-3.01版本,但

LibSvm标签问题

今天在做SVM恶意程序检测算法研究的过程中,由于libsvm输入要求的数据格式是lable index:value,训练数据标签和特征值索引和值是确定的,顺利生成模型,然而测试集数据标签是未知的,是需要得到的分类信息,此时比较迷惑,查找相关资料.得以解答:测试集的标签随意写上,它主要是用来测试模型的效果的,具体如下. 首先用svm(libsvm,lssvm.hssvm)等等进行分类预测,要进行三个步骤1.训练 2.测试 3.预测1.训练——大家都知道,就是用训练数据集,不管你采用那种寻优方式,得

Http Wrapper vs2008工程

http://files.cnblogs.com/files/kekec2/WinHttpClient_20100921.zip.gif http://www.codeproject.com/Articles/66625/A-Fully-Featured-Windows-HTTP-Wrapper-in-C Features Cookies supported Proxy supported GET, POST methods supported Request headers customiza

LIBSVM之一

libSVM简单的介绍 libSVM是台湾林智仁(Chih-Jen Lin) 教授2001年开发的一套支持向量机库,这套库运算速度挺快,可以很方便的对数据做分类或回归.由于libSVM程序小,运用灵活,输入参数少,并且是开源的,易于扩展,因此成为目前国内应用最多的SVM的库. 其中包含的文件夹的主要的作用 (1)Java文件夹  ,主要应用于java平台: (2)Python文件夹,是用来参数优选的工具,稍后介绍: (3)tools文件夹,主要包含四个python文件,用来数据集抽样(subse