CNTK从入门到深入研究10.docx
《CNTK从入门到深入研究10.docx》由会员分享,可在线阅读,更多相关《CNTK从入门到深入研究10.docx(12页珍藏版)》请在冰豆网上搜索。
CNTK从入门到深入研究10
CNTK从入门到深入研究(10)
前言
CNTK中网络模型的使用是通过EvalDLL进行的。
但EvalDLL目前为C++的实现,本文将仿照CNTK中EvalWrapper工程,将EvalDLL封装为Python可以调用的Python模块。
目标
根据EvalWrapper工程,具体需要封装的其实只有2个全局的方法(分别对应float和double)以及1个接口。
enumNodeGroup
{
nodeInput,//aninputnode
nodeOutput,//anoutputnode
nodeSpecified
};
//IEvaluateModel-interfaceusedbydecodersandothercomponentsthatneedjustevaluatorfunctionalityinDLLform
template
classIEvaluateModel//EvaluateModelInterface
{
public:
virtualvoidInit(conststd:
:
string&config)=0;
virtualvoidDestroy()=0;
virtualvoidCreateNetwork(conststd:
:
string&networkDescription)=0;
virtualvoidGetNodeDimensions(std:
:
map&dimensions,NodeGroupnodeGroup)=0;
virtualvoidStartEvaluateMinibatchLoop(conststd:
:
wstring&outputNodeName)=0;
virtualvoidEvaluate(std:
:
map*>&inputs,std:
:
map*>&outputs)=0;
virtualvoidEvaluate(std:
:
map*>&outputs)=0;
virtualvoidResetState()=0;
};
//GetEval-getaevaluatortypefromtheDLL
//sincewehave2evaluatortypesbasedontemplateparameters,exposes2exports
//couldbedonedirectlywiththetemplatedname,butthatrequiresmangledC++names
template
voidEVAL_APIGetEval(IEvaluateModel**peval);
extern"C"EVAL_APIvoidGetEvalF(IEvaluateModel**peval);
extern"C"EVAL_APIvoidGetEvalD(IEvaluateModel**peval);123456789101112131415161718192021222324252627282930123456789101112131415161718192021222324252627282930
上述声明中的GetEvalF以及GetEvalD方法分别对应获取float或者是double类型的IEvaluateModel。
之后通过IEvaluateModel即可调用已经训练完成的网络模型。
实现
封装的思路
通过Python调用C++的方法有很多,这里计划使用Boost.Python来封装,原因主要是因为CNTK工程中已经引入的Boost库,所以继续使用Boost.Python可以不增加负担,并且目前为Boost.Python的封装的软件也有好多,包括py++等。
使用Boost.Python对C++进行封装也是比较成熟的方法。
所以笔者目前决定使用Boost.Python来对EvalDLL进行针对Python调用的封装。
具体针对Boost.Python的介绍可以参考如下链接:
http:
//www.boost.org/doc/libs/1_60_0/libs/python/doc/html/index.html
另外介绍一点,其实Apache的thrift也是在备选范围内的一种,Thrift的本质目的就是提供一种异步架构的互操作机制。
但是如果要使用Thrift,则需要提供一个service的进程来提供服务。
相比于直接利用C++中本身Boost库提供的机制来讲,Boost.Python在封装上相比较thrift比较麻烦,但是提供给最终用户的调用很方便。
具体针对Thrift的介绍可以参考如下链接:
https:
//thrift.apache.org/
创建及配置工程
为保持CNTK的整体Project的结构风格,笔者将在CNTK工程中的Extensibility目录创建一个子工程,命名为CNTKPythonWrapper,目标目录选择CNTK\Source\Extensibility\。
之后就是配置工程,配置工程不建议用VisualStudio的界面去配置,因为目前已经存在EvalWrapper工程,并且我们的工程配置信息应该同这个类似,所以我建议复制EvalWrapper.vcxproj的内容,来修改我们的配置。
具体需要做的事情就是:
1.将EvalWrapper.vcxproj拷贝至CNTKPythonWrapper工程目录,然后改名为CNTKPythonWrapper.vcxproj覆盖原油的.vcxproj文件。
(这步骤主要是方便直接创建了x64以及Debug和Debuy_CPUonly等编译相关的配置管理)
2.删除.vcxproj中对原有.cpp文件的引用,就是删掉下面的内容,
```
```
3.去掉clr编译选项
4.添加Boost相关的Header文件以及Lib文件引用
5.Post-build时的文件拷贝(主要是boost库的一些dll)
重要的一点是Python加载的模块的拓展名为.pyd,我们修改输出文件为.pyd文件
根据上述步骤,我们即完成了工程的创建以及配置工作。
由于是该工程是用于封装,所以不必复杂,我们简单的添加一个PythonWrapper.cpp文件在工程中,所有实现都写在这个文件里即可(不用使用头文件,python调用不需要关心这些的)。
Boost.Python简单的介绍
这里简单介绍下Boost.Python的使用,首先是Header文件的引用,
#include11
对于Boost.Python,是通过BOOST_PYTHON_MODULE来创建一个Python的模块,模块名字写在其后面的括号中,具体形式如下:
BOOST_PYTHON_MODULE(TestForPython)
{
usingnamespaceboost:
:
python;
class_("hello",init())
//Addaregularmemberfunction.
.def("greet",&hello:
:
greet)
//Addinvite()asamemberofhello!
.def("invite",invite);
//Alsoaddinvite()asaregularfunctiontothemodule.
def("invite",invite);
}123456789101112123456789101112
上述代码中则实现了一个TestForPython的Python模块的封装,其中,定义了一个hello的类型,hello类型中,包括了一个传入std:
:
string的构造函数,以及greet方法。
而invite方法,被定义了两次,第一次是定义于hello类型中,默认这种定义要求的是可以接受一个hello类型作为传入参数的方法。
而后面单独的def("invite",invite);则是定义invite为一个全局方法。
def函数后面可能根据需要还需要添加针对参数以及返回值的修饰说明,例如下面情况,
def("getHello",getHello,return_value_policy());11
这里定义的getHello方法将返回值作为引用类型返回,这种情况下getHello的原型可能为:
hello&getHello()11
还有就是一些传入的数组等,Boost.Python中,提供了boost:
:
python:
:
list类型供使用。
所以一般c++同Python交互的时候使用该类型,然后再在c++这端将其解开在赋值。
封装IEvaluateModel
笔者对Boost.Python并不是很熟悉,研究了一下午,简单的封装了下IEvalueteModel方法,重点其实涉及到的主要是加载DLL以及找到函数的入口并进行调用。
首先是定义一下GetEval方法的原型,也就是之前在EvalDLL中暴露出来的C方法。
//Usedforretrievingthemodelappropriatefortheelementtype(float/double)
template
usingGetEvalProc=void(*)(IEvaluateModel**);123123
可以根据下面的方法,首先先加载evaldll.dll,然后再该dll中找到具体的获取IEvaluateModel对象的方法(GetEvalF或者是GetEvalD)。
autohModule=LoadLibraryA("evaldll.dll");
if(hModule==nullptr)
{
throwstd:
:
runtime_error("Cannotfindlibrary:
evaldll.dll");
}
autoprocAddress=GetProcAddress(hModule,funcName.c_str());
autogetEvalProc=(GetEvalProc)procAddress;
getEvalProc(&m_eval);
if(m_eval==nullptr)
{
throwstd:
:
runtime_error("CannotgetIEvaluateModel.");
}1234567891011121312345678910111213
最后就是针对于相关接口的封装,其中用到了Boost.Python的一些对象,包括boost:
:
python:
:
dict以及boost:
:
python:
:
list等。
笔者将最后的封装完成的代码粘贴如下:
//Usedforretrievingthemodelappropriatefortheelementtype(float/double)
template
usingGetEvalProc=void(*)(IEvaluateModel**);
///Managedwrapperforthenativeevaluationmodel
template
classIPythonEvaluateModel
{
typedefstd:
:
pair*>MapEntry;
public:
///Initializesanewinstanceoftheclass.
///Factoryfunctionnameforretrievingthenativemodelfromthedll.
IPythonEvaluateModel(conststd:
:
string&funcName)
{
autohModule=LoadLibraryA("evaldll.dll");
if(hModule==nullptr)
{
throwstd:
:
runtime_error("Cannotfindlibrary:
evaldll.dll");
}
autoprocAddress=GetProcAddress(hModule,funcName.c_str());
autogetEvalProc=(GetEvalProc)procAddress;
getEvalProc(&m_eval);
if(m_eval==nullptr)
{
throwstd:
:
runtime_error("CannotgetIEvaluateModel.");
}
}
///InitializesthemodelevaluationlibrarywithaCNTKconfiguration
///Modelconfigurationentries
voidInit(conststd:
:
string&config)
{
if(m_eval==nullptr)
{
throwstd:
:
runtime_error("Objecthasbeendisposed.");
}
m_eval->Init(config);
}
///Createsanetworkbasedfromthenetworkdescriptionintheconfiguration
///Theconfigurationfilecontainingthenetworkdescription
voidCreateNetwork(conststd:
:
string&networkDescription)
{
if(m_eval==nullptr)
{
throwstd:
:
runtime_error("Objecthasbeendisposed.");
}
m_eval->CreateNetwork(networkDescription);
}
///Evaluatesthemodelusingasingleforwardfeedpassandretrievestheoutputlayerdata
///
///
///Resultsforspecifiedlayer
boost:
:
python:
:
listEvaluate(conststd:
:
wstring&outputKey,intoutputSize)
{
if(m_eval==nullptr)
{
throwstd:
:
runtime_error("Objecthasbeendisposed.");
}
std:
:
map*>stdOutputs;
std:
:
shared_ptr>pOutputVector(newstd:
:
vector());
pOutputVector->resize(outputSize);
stdOutputs[outputKey]=pOutputVector.get();
m_eval->Evaluate(stdOutputs);
boost:
:
python:
:
listret;
for(std:
:
vector:
:
iteratoritr=pOutputVector->begin();itr!
=pOutputVector->end();++itr)
{
ret.append(*itr);
}
returnret;
}///Evaluatesthemodelagainstinputdataandretrievestheoutputlayerdata
///
///
///
///Resultsforspecifiedlayer
boost:
:
python:
:
listEvaluate2(constboost:
:
python:
:
dict&inputs,conststd:
:
wstring&outputKey,intoutputSize)
{
if(m_eval==nullptr)
{
throwstd:
:
runtime_error("Objecthasbeendisposed.");
}
std:
:
map*>stdInputs;
std:
:
vector>>resourceManagement;
for(inti=0;ipInputVector(newstd:
:
vector());
resourceManagement.push_back(pInputVector);
boost:
:
python:
:
listtheValues=boost:
:
python:
:
extract(inputs.values()[i]);
for(intj=0;jpush_back(boost:
:
python:
:
extract(theValues[j]));
}
std:
:
wstringkey=boost:
:
python:
:
extract(inputs.keys()[i]);
stdInputs[key]=pInputVector.get();
}
std:
:
map*>stdOutputs;
std:
:
shared_ptr>pOutputVector(newstd:
:
vector());
pOutputVector->resize(outputSize);
stdOutputs[outputKey]=pOutputVector.get();
m_eval->Evaluate(stdInputs,stdOutputs);
boost:
:
python:
:
listret;
for(std:
:
vector:
:
iteratoritr=pOutputVector->begin();itr!
=pOutputVector->end();++itr)
{
ret.append(*itr);
}
returnret;
}
std:
:
stringTEST()
{
return"TEST";
}
#if0//NotIMPLEMENT
///Evaluatesthemodelagainstinputdataandretrievestheoutputlayerdata
///
///
voidEvaluate3(constboost:
:
python:
:
dict&inputs,boost:
:
python:
:
dict&outputs)
{
if(m_eval==nullptr)
{
throwstd:
:
runtime_error("Objecthasbeendisposed.");
}
throwstd:
:
runtime_error("Notimplemented.");
}
#endif
~IPythonEvaluateModel()
{
if(m_eval!
=nullptr)
{
m_eval->Destroy();
m_eval=nullptr;
}
}
private:
//Nativemodelevaluationinstance
IEvaluateModel*m_eval;
};
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
上述封装的内容为base类,根据不同的类型(float或者是double),派生出来2个其他类型,IPythonEvaluateModelF以及IPythonEvaluateModelD,这两个类型将在python中直接使用。
classIPythonEvaluateModelF:
publicIPythonEvaluateModel
{
public:
IPythonEvaluateModelF()
:
IPythonEvaluateModel("GetEvalF")
{
}
};
classIPythonEvaluateModelD:
publicIPythonEvaluateModel
{
public:
IPythonEvaluateModelD()
:
IPythonEvaluateModel("GetEvalD")
{
}
};12345678910111213141516171234567891011121314151617
封装Python模块
上述内容只是将IEvaluateModel给暴露出来,但是并没有暴露到具体的Python模块。
下面的代码将根据Boost.Python将其封装成模块。
BOOST_PYTHON_MODULE(CNTKPythonWrapper)
{
usingnamespace