LibSVM之C# Wrapper

来源:http://www.cnblogs.com/caiyic/archive/2016/10/29/6012024.html
-Advertisement-
Play Games

【百度百科】 LIBSVM是臺灣大學林智仁(Lin Chih-Jen)教授等開發設計的一個簡單、易於使用和快速有效的SVM模式識別與回歸的軟體包,他不但提供了編譯好的可在Windows系列系統的執行文件,還提供了源代碼,方便改進、修改以及在其它操作系統上應用;該軟體對SVM所涉及的參數調節相對比較少 ...


【百度百科】

  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。

  因而,在EmguCVML滿足不了的情況下,萌生了兩個想法:

    一是修改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中學習到的精髓部分!將在下篇作介紹~


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 文檔目錄 本節內容: 簡介 安裝 安裝Nuget包 設置模塊依賴 配置你的實體 創建控制器 示例 獲取實體列表 請求 響應 獲取單個實體 請求 響應 獲取單個實體及導航屬性 請求 響應 查詢 請求 響應 創建一個新實體 請求 響應 獲取元數據 請求 響應 示例項目 安裝Nuget包 設置模塊依賴 配 ...
  • Quartz.NET是一個非常強大的作業調度框架,適用於各種定時執行的業務處理等,類似於WINDOWS自帶的任務計劃程式,其中運用Cron表達式來實現各種定時觸發條件是我認為最為驚喜的地方。 Quartz.NET主要用到下麵幾個類: IScheduler --調度器 IJobDetail --作業任 ...
  • using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ref_out { class Program { static void Method1(ref int ...
  • 文檔目錄 本節內容: 創建動態Web Api控制器 ForAll 方法 重寫 ForAll ForMethods Http 動詞 WithVerb 方法 HTTP 特性 命名約定 Api 瀏覽器 RemoteService 特性 動態Javascript代理 AJAX 參數 單獨服務腳本 Angul ...
  • 在網上收集。。。 C#的值類型,引用類型,棧,堆,ref,out C# 的類型系統可分為兩種類型,一是值類型,一是引用類型,這個每個C#程式員都瞭解。還有托管堆,棧,ref,out等等概念也是每個C#程式員都會接觸到的概念,也是C#程式員面試經常考到的知識,隨便搜搜也有無數的文章講解相關的概念,貌似 ...
  • 文檔目錄 本節內容: 簡介 AbpApiController 基類 本地化 其它 過濾 審計日誌 授權 防偽造過濾 工作單元 結果包裝和異常處理 結果緩存 驗證 模塊綁定器 本地化 其它 審計日誌 授權 防偽造過濾 工作單元 結果包裝和異常處理 結果緩存 驗證 簡介 通過Abp.Web.Api的nu ...
  • 最近在做一個項目的時候,需要增加一個日誌的功能,需要使用Log4Net記錄日誌,把數據插入到Oracle資料庫,經過好久的研究終於成功了。把方法記錄下來,以備以後查詢。 直接寫實現方法,分兩步完成: 1、使用NuGet Manager管理工具,增加對Oracle.ManagedDataAccess. ...
  • 最近準備下PostgreSQL資料庫開發的相關知識,本文把總結的PPT內容通過博客記錄分享,本隨筆的主要內容是介紹PostgreSQL資料庫的基礎信息,以及如何在我們的開發框架中使用PostgreSQL資料庫,希望大家多多提意見。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...