pythonからC++で作成したものを使用したい場合の設定メモ。
C++で作成したオンライン線形分類器である
SCW(Soft Confidence-Weighted Learning)のpython Wrapperを作成しPythonからの使用テストを行った。
【環境】
◆pyd(dll)作成環境
Visual Studio 2012 for Windows Desktop
◆実行環境
python 2.7.9
1.pyd作成
①[構成プロパティ]-[C/C++]-[全般]の追加のインストールディレクトリ「C:\Python27\include」
②[構成プロパティ]-[リンカー]-[全般]の出力ファイルを.pyd形式に変更する

③[構成プロパティ]-[リンカー]-[入力]の追加の依存ファイルには「C:\Python27\libs\python27.lib」
モジュール定義ファイル名を追加(ここでは便宜上SCW.def)

④モジュールの定義ファイルの作成(SCW.def)
プロジェクトディレクトリに定義ファイルを作成
------------------------------------ SCW.def ------------------------------------
EXPORTS
initSCW
------------------------------------ SCW.def ------------------------------------
⑤ラッパー作成(実体は未実装)
------------------------------------ scw_wrapper.c ------------------------------------
#include "Python.h"
#include <stdio.h>
#include <string.h>
#include <windows.h>
#include <vector>
#include "SCW.h"/* 自作ヘッダー */
using namespace std;
SCW scw;
/* 初期化 */
static PyObject* init(PyObject* self, PyObject* args){
float eta, C;
PyObject* x;
if(!PyArg_ParseTuple(args, "ffO", &eta, &C, &x)) {
return NULL;
}
int len = PyList_Size(x);
scw(len, eta, C);
return Py_BuildValue("ff", eta, C);
}
/* 学習 */
static PyObject* train(PyObject* self, PyObject* args){
int y;// 教師(+1, -1)
PyObject* x;// 特徴ベクトル(Python Object)
vector<double> x_vec;// 特徴ベクトル(C++)
PyObject* WObj;// モデル(重みベクトル)
PyObject* list_item;
if(!PyArg_ParseTuple(args, "iO", &y, &x)) {
return NULL;
}
/* create array from list */
for(Py_ssize_t i = 0; i < PyList_Size(x); ++i)
{
list_item = PyList_GetItem(x, i);
x_vec.push_back(PyFloat_AS_DOUBLE(list_item));
}
/* SCW学習 */
scw.train(y, x_vec);
/* 学習モデル取得 */
vector<double> Mu = scw.getMu();
/* create list from array */
WObj = PyList_New(PyList_Size(x));
for (Py_ssize_t i = 0; i < PyList_Size(x); ++i)
{
PyList_SetItem(WObj, i, PyFloat_FromDouble(Mu[i]));
}
return Py_BuildValue("O", WObj);
}
/* 予測 */
static PyObject* predict(PyObject* self, PyObject* args){
PyObject* x;// 特徴ベクトル(Python Object)
vector<double> x_vec;// 特徴ベクトル(C++)
PyObject* list_item;
if(!PyArg_ParseTuple(args, "O", &x)) {
return NULL;
}
/* create array from list */
for(Py_ssize_t i = 0; i < PyList_Size(x); ++i)
{
list_item = PyList_GetItem(x, i);
x_vec.push_back(PyFloat_AS_DOUBLE(list_item));
}
return Py_BuildValue("i", scw.predict(x_vec));
}
/* Python関数と実態関数を結びつける */
static PyMethodDef defs[] = {
{"init", init, METH_VARARGS},
{"train", train, METH_VARARGS},
{"predict", predict, METH_VARARGS},
{NULL, NULL}
};
/* 初期化時pythonから呼ばれる関数 */
/* 定義ファイル(SCW.def)で公開設定されている */
__declspec(dllexport) void APIENTRY initSCW(void){
Py_InitModule("SCW", defs);
}
------------------------------------ scw_wrapper.c ------------------------------------
⑤ビルド後イベント追加
作成したSCW.pydファイルをpythonのDLLへコピーさせるようにした。
[構成プロパティ]-[ビルドイベント]-[ビルド後イベント]のコマンドラインに以下を追加する。
copy /Y "$(OutDir)SCW.pyd" "C:\Python27\DLLs\SCW.pyd"
2.pythonで使用
いつも通りの方法でモジュールを使用できるはず。
------------------------------------ TestSCW.py ------------------------------------
import numpy as np
import random
import SCW
def load_train_svmguide3():
train_name = r"./data/svmguide3"
fp = open(train_name, 'r')
y = []
x = []
line = fp.readline()
while line:
xtmp = []
for d in line.split(' '):
if ':' in d:
val = d[d.find(':', 0) + 1:]
xtmp.append(float(val))
else:
y.append(int(d))
if len(xtmp) == 21: break
x.append(xtmp)
line = fp.readline()
fp.close()
return y, x
# 共著者のmatlabコード結果との比較
def Debug_Evidence_SCW():
# データ読み込み
y, x = load_train_svmguide3()
# パラメータ設定
eta = 0.75
C = 0.0625
SCW.init(eta, C, x[1011])
W = SCW.train(y[1011], x[1011])
Wcheck = [0.01178605000, -0.0009365075000, 0.0001888526875, -0.01455644375, 0.00104711750, \
-1.96136562500000e-05, 0.00154455250000000, -3.94857937500000e-05, 1.91726312500000e-06, 0.0625, \
0.04889844375, 0.0502117187500000, 0.0224358625000000, 0.00625, 0.00625, \
0.04888393125, 0.00596817562500000, 0.0113636375000000, 0.0153821000000000, 0.000229309875000000, 0]
Wnp = np.array(W)
Wchecknp = np.array(W)
print 'Diff:', Wnp - Wcheck
if __name__ == '__main__':
#Debug_Evidence_SCW()
y, x = load_train_svmguide3()
ytest, xtest = load_test_svmguide3()
# パラメータ設定
eta = 0.75
C = 0.0625
# 学習用順番インデックスリスト作成(ランダムシャッフリング)
index_list = range(len(y))
random.shuffle(index_list)
# 初期化
SCW.init(eta, C, x[0])
for idx in index_list:
W = SCW.train(y[idx], x[idx])
index_list = range(len(y))
random.shuffle(index_list)
tp = 0.0
for idx in index_list:
if y[idx] == SCW.predict(x[idx]):
tp = tp + 1.0
print 100 * (tp / len(y)), "% (", int(tp), "/", len(y), ")"
------------------------------------ TestSCW.py ------------------------------------
とりあえず、SCWアルゴが共著者のmatlabコードと同じことを確認しつつ
同程度の精度を確認した。
何故か、matlabコードでは未知データに対して精度確認しておらず
自分で確認すると精度が途轍もなく悪かった。。。仕様だろうか?
データが線形分離不可能の可能性もあるが未確認
自分用のソース(Pass付)
PyModule_SCWPyModule_SCWPR
COMMENT