Mllib机器学习工具包在Hadoop上的使用
Hadoop是基于Java的数据计算平台,在我们公司得到了广泛应用。同样mllib也是我们算法组同学经常使用的一个机器学习工具包。但目前mllib工具包只提供了供C++程序调用的so链接库,没有可供java程序调用的jar包。由于这个需求有一定的普遍性,笔者将mllib做了进一步封装,并通过jni的方式把其封装成了可供java程序调用的接口。
1 结构示意图
2 对mllib的二次封装
原始的mllib开发包对模型的处理(训练,加载,预测)都是per model的,对于每一个模型都需要写一些重复类似的代码。实际应用中我们的模型也一般都是多个的(分行业,或分类目来做),不太会只是用一个大而泛的模型。mllib没有提供一个类似于Manager的类来管理多个模型。所以笔者对mllib进行了二次封装,能够让大家比较方便的通过配置来管理多个模型,也让大家从需要了解mlllib最底层的接口中解放出来。
封装的Manager类:
class MlrManager { public: MlrManager(); virtual ~MlrManager(); //提供的两个接口 //初始化函数,输入为配置文件,以配置文件为驱动来加载模型 bool Init(const char* pConf); //预测函数,输入为特征数组和模型编号,输出为预测分数 feature_t CalMlrScore(std::vector<feature_t>&amp;amp; vecFeatures,int nModel); private: feature_t GetMlrScore(std::vector<feature_t>&amp;amp; vecFeatures,mllib::MlModel* pModel,ENUM_MODEL_TYPE enumModelType); private: std::vector<mllib::MlModel*> m_vecModels; int m_nTotalModelNum; int m_nDefaultModelNum; ENUM_MODEL_TYPE m_enumModelType; mllib::MlModelManager m_modelManager; };
MlrManager类被封装到了libMlrManagerJni.so链接库中,可供其它C++程序使用。
配置文件说明:
[PATH] //设置root目录,后面的模型文件只需要指定相对这个root路径的相对路径即可 BASE_PATH = ./mlr_jni.tar/conf/ [MODEL] //模型数目,指定了模型数目后,模型名就可以确定了:MODEL1 , MODEL2 .... MODEL${ TOTAL_MODEL_NUM } TOTAL_MODEL_NUM = 4 //模型的类型(回归还是分类),不同的模型类型,预测时实现的方法是不一样的,一定要正确设置 #MODEL_TYPE = classification MODEL_TYPE = regression //指定具体某个模型使用的模型文件名称,不同的模型可以使用相同的模型文件。 MODEL1 = 1.model MODEL2 = 2.model MODEL3 = 3.model MODEL4 = 3.model
MlrManager调用伪代码示例:
#include "MlrManager.h" Main() { MlrManager mlrMgr; mlrMgr.Init(confFile); vector<float> vecFeature(nFeatureSize); float fMlrScore = mlrMgr.CalMlrScore(vecFeature,1); }
3 Jni封装成可供java调用的接口
3.1
封装后的java接口如下:
public class MlrManagerJni{ static { //load链接库libMlrManagerJni.so,如果找不到libMlrManagerJni.so,该类将初始化失败 System.loadLibrary("MlrManagerJni"); } //接口初始化,输入为配置文件 public native int mlr_init(String conf); //预测分数的接口,输入为特征字符串,特征分隔符,模型编号,输出为预测值 public native float mlr_calc(String features,String spliter , int nModel); //释放接口 public native int mlr_destroy(); }
具体如何封装的这里就不细说了,jni封装技术可以参考 ( http://www.searchtb.com/2010/12/how-to-run-jni-in-hadoop.html 如何在Hadoop集群运行jni程序),使用者也不用关心这个。下面介绍一下如何使用这个封装的接口。
3.2 开发阶段
把mlr_jni.tar包拷贝到开发机上,解压出来,解压后的目录结构如下:
conf/ conf/mlr.conf conf/1.model conf/2.model …… conf/N.model lib/ lib/ libMlrManagerJni.so lib/ mlrmgrjni.jar
conf目录包含了mlr.conf配置文件,和一系列模型文件*.model。
lib目录包含了libMlrManagerJni.so,mlrmgrjni.jar两个文件,libMlrManagerJni.so是封装了原始mllib的C++接口,mlrmgrjni.jar是封装了libMlrManagerJni.so的java接口。
在Eclipse项目配置选项里把mlrmgrjni.jar加到classpath中。
MlrManagerJni调用伪代码示例:
import mlr.MlrManagerJni; Mapper() { private MlrManagerJni m_mlrJni = new MlrManagerJni(); public void configure(JobConf job) { //初始化接口 m_mlrJni.mlr_init("./mlr_jni.tar/conf/mlr.conf") } public void map(){ //接口调用 fMlrScore = m_mlrJni.mlr_calc(strFeatures, MlrManager.FEATURE_SPLITER , nModel ); ) public void close() { //释放接口 if (m_mlrJni != null) { m_mlrJni.mlr_destroy(); m_mlrJni = null; } } } Run() { //设置 lava.library.path路径,通过这个路径可以找到libMlrManagerJni.so链接库,MlrManagerJni接口初始化的时候需要加载这个链接库 conf.set("mapred.child.java.opts","-Djava.library.path=./mlr_jni.tar/lib"); //也可以通过如下这个方式来设置 //conf.set("java.library.path","./mlr_jni.tar/lib"); }
3.3 运行阶段
在运行环境中,把mlr_jni.jar拷贝到运行环境,解压出来。使用者把自己的模型文件拷贝到conf目录下,在mlr.conf文件中修改好相关配置,然后把conf目录和lib目录重新打包成mlr_jni.tar包。(注意这里的包名mlr_jni.tar是可以变的,但必须和java程序中有两处第8行和第25行指定的包名是一致的)。
调用:
hadoop jar -libjars ./lib/mlrmgrjni.jar mainClass -archives mlr_jni.tar …
-libjars mlrmgrjni.jar选项会把mlrmgrjni.jar这个jar包添加到Mapreduce程序的java classpath中。
-archives mlr_jni.tar选项,Hadoop Distributed Cache的机制将会把这个jar包拷贝到每一个task node上,供MapReduce程序使用。这个选项等同于在hadoop程序中显示调用
DistributedCache.addCacheArchive(“hdfs:///group/tbsc-dev/yuanhan/mlr_jni.tar#mlr_jni.tar”,conf);
3.4 常见错误
1) Caused by: java.lang.NoClassDefFoundError: mlr/MlrManagerJni
java.lang.ClassNotFoundException: mlr.MlrManagerJni
原因:hadoop 命令行调用的时候没有加-libjars ./lib/mlrmgrjni.jar选项
2) Caused by: java.lang.UnsatisfiedLinkError: no MlrManagerJni in java.library.path
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1709)
原因:MlrManagerJni 接口初始化失败,不能加载libMlrManagerJni.so。
hadoop 命令行调用的时候没有加-archives mlr_jni.tar选项,可以在hadoop WebUI上看下job的xml配置文件,mapred.cache.archives选项是否包含mlr_jni.tar包,如下是一个包含了mlr_jni.tar包的例子:
mapred.cache.archives=hdfs://hdpnn:9000/home/hadoop/cluster-data/mapred/system/job_201012162127_130619/libjars/mlrmgrjni.jar,/home/hadoop/cluster-data/mapred/system/job_201012162127_130619/archives/mlr_jni.tar#mlr_jni.tar
还有可能是打mlr_jni.tar包的时候忘记把libMlrManagerJni.so文件放进lib目录中了。
BTW:相关工具包下载联系元涵本人~