pybind11—vlfeat SVM添加python接口

前言

由于研究內容需要晦毙,需要在python項目使用到SVM模型来惧。目前大量庫都提供了SVM模型暇赤,比如libsvm心例、opencv ml模塊等。 但是在項目中翎卓,第三方采用了Matlab的 lv_svm, 為了保持一致,只能在工程中引入vl_svm契邀。


開發(fā)測試環(huán)境

  • windows10 64bit
  • Anaconda 3, with python 3.7
  • pybind11
  • vl_feat

Aboult VLFeat

image.png
image.png
image.png

The VLFeat open source library implements popular computer vision algorithms specializing in image understanding and local features extraction and matching. Algorithms include Fisher Vector, VLAD, SIFT, MSER, k-means, hierarchical k-means, agglomerative information bottleneck, SLIC superpixels, quick shift superpixels, large scale SVM training, and many others. It is written in C for efficiency and compatibility, with interfaces in MATLAB for ease of use, and detailed documentation throughout. It supports Windows, Mac OS X, and Linux. The latest version of VLFeat is 0.9.21.

VLFeat是一個開源的庫,包含許多流行的計算視覺算法失暴,比如圖像理解識別坯门、局部特征提取、匹配等逗扒。
VLFeat采用C語言實現古戴, 目前有Matlab接口以及文檔,但是沒有python接口矩肩,沒有python接口那就自己實現O帜铡!黍檩!
由于VLFeat庫算法太多叉袍,而且工程目前只需要SVM、HOG刽酱、LBP提取算法的python接口喳逛,因此本文主要SVM的python接口。

image.png

測試SVM

vlfeat提供了比較詳細的document和API reference


image.png

除此之外棵里,還提供了一個簡單的例子润文,關于訓練SVM

C語言代碼

  • 訓練部分
    輸入: 4個2維的樣本, 以及標簽y, -1代表負樣本殿怜, +1代表正樣本
    輸出: SVM訓練的參數典蝌,權重model, 偏置bias
#include <stdio.h>
#include <vl/svm.h>
int main()
{
  vl_size const numData = 4 ;
  vl_size const dimension = 2 ;
  double x [dimension * numData] = {
    0.0, -0.5,
    0.6, -0.3,
    0.0,  0.5
    0.6,  0.0} ;
  double y [numData] = {1, 1, -1, 1} ;
  double lambda = 0.01;
  double * const model ;
  double bias ;
  VlSvm * svm = vl_svm_new(VlSvmSolverSgd,
                           x, dimension, numData,
                           y,
                           lambda) ;
  vl_svm_train(svm) ;
  model = vl_svm_get_model(svm) ;
  bias = vl_svm_get_bias(svm) ;
  printf("model w = [ %f , %f ] , bias b = %f \n",
         model[0],
         model[1],
         bias);
  vl_svm_delete(svm) ;
  return 0;
}

  • 測試部分
    測試的代碼非常簡單, 就是輸入一個樣本头谜,得出其判別輸出骏掀。
double svm_test(double* svm_w, double svm_b, double* inputData, int dims) {

    double result = 0;
    for (int i = 0; i < dims; i++)
    {
        result += svm_w[i] * inputData[i];
    }

    result += svm_b;

    return result;


}

封裝python接口

封裝接口主要把輸入、輸出數據搞定即可柱告,其他部分直接代碼重用即可截驮。

svm訓練函數:

  • 輸入數據的轉換
語言類型 描述 參數類型
C 多個輸入樣本(向量形式) double數組/指針
python 多個輸入樣本(向量形式) list
語言類型 描述 參數類型
C 多個標簽 double數組
python 多個標簽 list
  • 輸出數據的轉換
語言類型 描述 參數類型
C 一個權值向量(向量形式) ,一個偏置bias double數組/指針 + double數
python 一個權值向量(向量形式) 末荐,一個偏置bias list侧纯, 包含2項

pytho接口代碼實現

#include<vector>
#include<pybind11/pybind11.h>
#include<pybind11/stl.h>
#include<pybind11/numpy.h>
#include"svm_classifier.h"

namespace py = pybind11;


std::vector<std::vector<double>> train_svm(std::vector<std::vector<double>>& trainData, std::vector<double>& labels, double lambda) {

    std::vector<double> weights;
    double bias;

    int numData = trainData.size();
    int dims = trainData.at(0).size();

    double* x = new double[dims*numData];
    double* y = new double[numData];

    int cnt = 0;
    for (int i = 0; i < numData; i++)
    {
        for (int j = 0; j < dims; j++)
        {
            x[cnt] = trainData[i][j];
            cnt++;
        }

        y[i] = labels[i];
    }


    VlSvm* svm = vl_svm_new(VlSvmSolverSgd, x, dims, numData, y, lambda);

    vl_svm_train(svm);

    const double *  model = vl_svm_get_model(svm);
    bias = vl_svm_get_bias(svm);

    for (int i = 0; i < dims; i++)
    {
        weights.push_back(model[i]);
    }

    vl_svm_delete(svm);

    return std::vector<std::vector<double>>{weights, { bias }};
    
    
    


}


PYBIND11_MODULE(vlfeat_svm, m) {

    m.doc() = "Simple svm demo!";

    m.def("train_svm", &train_svm, py::arg("train_dataset"), py::arg("labels"), py::arg("lambda_value"));



}

編譯直接生成.pyd動態(tài)鏈接庫

python接口的測試

為了符合OO面向對象, 將代碼封裝到類中:
首先構造一個類SVM甲脏, 包含2個方法:

  • train() 訓練
  • eval() 測試
import numpy as np
import detector.svm.vlfeat_svm as svm
import random


class SVM:
    def __init__(self):
        self.name = 'svm'
        self.weights = []
        self.bias = 0.0
        self.lambda_value = 0.0
        self.optim_method = 'SGD'

    def train(self, train_datas, labels, lambda_value):
        self.lambda_value = lambda_value
        self.weights, bias_ = svm.train_svm(train_datas, labels, lambda_value)
        self.bias = bias_[0]

    def eval(self, sample):
        if len(sample) != len(self.weights):
            raise ValueError

        value = np.sum(np.array(self.weights) * np.array(sample))+self.bias
        return value

在此基礎上眶熬,在定義一個派生類 ClassifierSVM:

  • setPSamples() 設置正樣本訓練集
  • setNSamples() 設置負樣本訓練集
  • setLabel() 設置訓練標簽
  • train() 訓練妹笆, overwrite父類方法
  • eval() 測試, 繼承父類
class ClassifierSVM(SVM):

    def __init__(self):
        super(ClassifierSVM, self).__init__()
        self.nSamples = []
        self.pSamples = []
        self.pLabels = []
        self.nLables = []
        self.nLable = -1
        self.pLabel = 1

    def setPSamples(self, samples):
        self.pSamples = samples


    def setNSamples(self, samples):
        self.nSamples = samples


    def setLabel(self, pLabel, nLabel):
        self.pLabel = pLabel
        self.nLable = nLabel

    def train(self, lambda_vlue, shuffle, **kwargs):
        self.lambda_value = lambda_vlue
        self.nLables = [self.nLable for i in range(len(self.nSamples))]
        self.pLabels = [self.pLabel for i in range(len(self.pSamples))]
        train_samples = self.pSamples + self.nSamples
        train_labels = self.pLabels + self.nLables

        all_data = []
        for sample, label in zip(train_samples, train_labels):
            all_data.append({'image': sample, 'label': label})
        if shuffle:
            random.shuffle(all_data)
        train_samples = list(map(lambda x: x['image'], all_data))
        train_labels = list(map(lambda x: x['label'], all_data))

        super(ClassifierSVM, self).train(train_datas=train_samples, labels=train_labels, lambda_value=self.lambda_value)


訓練結果: 權值娜氏,偏置

image.png

完整工程

import numpy as np
import detector.svm.vlfeat_svm as svm
import random


class SVM:
    def __init__(self):
        self.name = 'svm'
        self.weights = []
        self.bias = 0.0
        self.lambda_value = 0.0
        self.optim_method = 'SGD'

    def train(self, train_datas, labels, lambda_value):
        self.lambda_value = lambda_value
        self.weights, bias_ = svm.train_svm(train_datas, labels, lambda_value)
        self.bias = bias_[0]

    def eval(self, sample):
        if len(sample) != len(self.weights):
            raise ValueError

        value = np.sum(np.array(self.weights) * np.array(sample))+self.bias
        return value


class ClassifierSVM(SVM):

    def __init__(self):
        super(ClassifierSVM, self).__init__()
        self.nSamples = []
        self.pSamples = []
        self.pLabels = []
        self.nLables = []
        self.nLable = -1
        self.pLabel = 1

    def setPSamples(self, samples):
        self.pSamples = samples


    def setNSamples(self, samples):
        self.nSamples = samples


    def setLabel(self, pLabel, nLabel):
        self.pLabel = pLabel
        self.nLable = nLabel

    def train(self, lambda_vlue, shuffle, **kwargs):
        self.lambda_value = lambda_vlue
        self.nLables = [self.nLable for i in range(len(self.nSamples))]
        self.pLabels = [self.pLabel for i in range(len(self.pSamples))]
        train_samples = self.pSamples + self.nSamples
        train_labels = self.pLabels + self.nLables

        all_data = []
        for sample, label in zip(train_samples, train_labels):
            all_data.append({'image': sample, 'label': label})

        random.shuffle(all_data)
        train_samples = list(map(lambda x: x['image'], all_data))
        train_labels = list(map(lambda x: x['label'], all_data))

        super(ClassifierSVM, self).train(train_datas=train_samples, labels=train_labels, lambda_value=self.lambda_value)


if __name__ == '__main__':

    print('*'*30)
    pSamples = [[0.0, -0.5],
                [0.6, -0.3],
                [0.6, 0.0]]
    nSamples = [[0.0, 0.5]]
    classifier = ClassifierSVM()
    classifier.setLabel(pLabel=1, nLabel=-1)
    classifier.setPSamples(samples=pSamples)
    classifier.setNSamples(samples=nSamples)
    classifier.train(lambda_vlue=0.01, shuffle=False)
    print(classifier.weights)
    print(classifier.bias)




?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末拳缠,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子贸弥,更是在濱河造成了極大的恐慌窟坐,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件绵疲,死亡現場離奇詭異哲鸳,居然都是意外死亡,警方通過查閱死者的電腦和手機盔憨,發(fā)現死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門徙菠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人郁岩,你說我怎么就攤上這事婿奔。” “怎么了问慎?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵萍摊,是天一觀的道長。 經常有香客問我如叼,道長冰木,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任薇正,我火速辦了婚禮片酝,結果婚禮上囚衔,老公的妹妹穿的比我還像新娘挖腰。我一直安慰自己,他們只是感情好练湿,可當我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布猴仑。 她就那樣靜靜地躺著,像睡著了一般肥哎。 火紅的嫁衣襯著肌膚如雪辽俗。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天篡诽,我揣著相機與錄音崖飘,去河邊找鬼。 笑死杈女,一個胖子當著我的面吹牛朱浴,可吹牛的內容都是我干的吊圾。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼翰蠢,長吁一口氣:“原來是場噩夢啊……” “哼项乒!你這毒婦竟也來了?” 一聲冷哼從身側響起梁沧,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤檀何,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后廷支,有當地人在樹林里發(fā)現了一具尸體频鉴,經...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年恋拍,在試婚紗的時候發(fā)現自己被綠了砚殿。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡芝囤,死狀恐怖似炎,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情悯姊,我是刑警寧澤羡藐,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站悯许,受9級特大地震影響仆嗦,放射性物質發(fā)生泄漏。R本人自食惡果不足惜先壕,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一瘩扼、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧垃僚,春花似錦集绰、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至改淑,卻和暖如春碍岔,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背朵夏。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工蔼啦, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人仰猖。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓捏肢,卻偏偏與公主長得像掠河,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子猛计,可洞房花燭夜當晚...
    茶點故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內容

  • AI 開發(fā)工具總結機器學習工具開發(fā)工具包:機器學習的開源工具深度學習工具github 上一些工具: 參考 機器學習...
    風火布衣閱讀 2,816評論 1 2
  • 首頁 資訊 文章 資源 小組 相親 登錄 注冊 首頁 最新文章 IT 職場 前端 后端 移動端 數據庫 運維 其他...
    Helen_Cat閱讀 3,874評論 1 10
  • Python 兵器譜 摘要: 曾經因為NLTK的緣故開始學習Python唠摹,之后漸漸成為我工作中的第一輔助腳本語言,...
    hzyido閱讀 65,405評論 0 48
  • Python 兵器譜 曾經因為NLTK的緣故開始學習Python奉瘤,之后漸漸成為我工作中的第一輔助腳本語言勾拉,雖然開發(fā)...
    hzyido閱讀 64,515評論 1 23
  • 該文章為轉載文章,作者簡介:汪劍盗温,現在在出門問問負責推薦與個性化藕赞。曾在微軟雅虎工作,從事過搜索和推薦相關工作卖局。 T...
    名字真的不重要閱讀 5,260評論 0 3