前言
因为刚在公司接手了一个使用crf++做对象识别的项目,该项目之前一直不能在windows上运行(crf++需要配置相应的环境,并且如
果想和JAVA整合需要用swig来封装动态链接库形成JNI接口),而且也缺乏日志,所以就想直接配置出windows的开发调试环境,在网
上搜索了很多前辈的方法,很受启发,但是也有一些语焉不详的地方让我入了不少坑,所以在此准备将Linux的Crf+java整合方法和
windows的Crf+java环境配置方法都整理出来,也为自己之后的留用,也为给有同样需求的同学提供一些小小的帮助。
配置步骤
1.windows
软件环境 win10 + crf++0.58win32 + jdk1.7.0._45(win32) + swigwin3.0.10 + swig-3.0.12 + visual studio2017(crf和swig之类的
东西会在末尾打包留种的)
Tip:关于为什么使用32位的jdk是因为crf++0.58只有linux版本和win32版本的,如果用64位的jdk的话在加载libcrfpp.dll的时候容易
出现Can't load IA
32-bit .dll on a AMD 64-bit platform的问题,当然不是说不能搞,只不过为了方便起见,博主还是直接安装了一个
32位的jdk,关于win上安装多个jdk有
时候会出现改环境变量不好使的情况,这个时候可以检查下 控制面板-》java-》java-》查看 在
里面设置运行时环境设置,如果还不好使的话你
C:\Windows\System32\目录下绝对有java.exe,javaw.exe,javaws.exe这些文件,直
接删掉就好,这个操作的详细描述请戳
由于CRF++是用c++编写的条件随机场工具,为了在JAVA为主体的程序中使用的话就应该使用JNI技术对java进行扩展,并将CRF++编译为可供java
调用的动态链接库。SWIG可以帮我们将C++编写的CRF++和JAVA系统进行联合。
首先将 XXX\CRF++-0.58\sdk(windows版) 目录下的crfpp.h,libcrfpp.lib两个文件拷贝出来放在一个单独的文件夹里,在将XXX\CRF++-0.58\swig
下的CRFPP.i和version.h拷贝出来放在同样的目录下,这个时候这个目录的截图是这样的。
打开CRFPP.i,在倒数第二行的地方将%include ../crfpp.h 改为 %include crfpp.h 因为他们现在都在同一个文件夹下了嘛。
之后我们需要使用win版的swig了,直接解压就可以用,你也可以将swig的swig.exe添加进环境变量,这样不需要每次使用都再输一大串的地址。
如果配置了swig环境变量 swig –c++ -java -package org.anon.crfpp CRFPP.i (-package 后面的包名自己随便起的啦)
没配的话就给一个全路径咯 /d/crf++Env/swig3.0.10win/swigwin-3.0.10/swig.exe -c++ -java -package com.anon.crfpp /d/crf++Env/sdkCol/CRFPP.i
(我
是在git bash里执行的,cmd的自己转下路径格式)
然后在我们的文件夹中可以看到以下文件。
*.java就是swig生成的调用文件,测试类可以去linux版的crf++0.58下的java目录下拷贝出test.java类,将这些类拷进一个项目中。
接下来就是最容易出错也是最麻烦的环节了,我们要编译一个dll动态链接库,在这里我使用的是Visual studio 2017,当然任何版本的都是可以的,
在项目配置向导这里记得要选择dll,附件选项选上空项目即可,要不然会有一堆然并卵的东西生成。
接下来我们将生成的CRFPP_wrap.cxx(和*.java一起生成的)文件拷贝入CRFPP项目中,放在源文件下
这个时候你可以试着右键项目,生成一下,会报错误如下
这是因为我们编译dll还需要JDK中jni.h的帮助,这个时候我们可以对项目进行少许配置。
右键工程点击属性 配置属性-》VC++目录中进行如下配置
将包含目录中添加你使用的jdk的include目录( xxx\java\jdk_1.7.0_45win32\include) ,和include下的32win目录(xxx\java\jdk_1.7.0_45win32
\include\win32)。
在库目录下添加win版的crf++的sdk文件夹,也就是libcrfpp.lib的目录(xxx\crf++Env\CRF++-0.58\sdk)。
网上有的博客说这样就可以生成成功了,大家可以试一下,反正我配置到这一步没有生成成功,报之下的错误
如果你到这一步能生成成功那很Nice,不能的话那就接着按之下的步骤进行配置。
依旧是右键项目,属性,链路器下的常规再配置下附加库目录,
接着还没完,在链路器下的输入里加上libcrfpp.lib,编辑里直接输就可以
OK,全部配置流程走完了。build一下试试。
果不其然的生成成功了,但是请注意这个时候是Debug模式生成的dll,这个时候我们要再调到Release模式生成一遍dll,使用release的dll去进行
JAVA整合调用。对的,是不是又报错了?那是因为Release和debug的属性是两套,再练一遍熟下手吧,我也是为你好
接下来就是和JAVA项目整合了,在项目里建一个文件夹,我比较习惯取名lib,将刚生成的dll文件和win版的CRF++0.58目录下的 libcrfpp.dll都拷入文
件夹下。
然而工作并没有结束,我们要在JRE System Library里添加入lib的配置路径,右键点击JRE System Library 然后build Path ->configure build path
将之前的lib文件夹目录配置入Native library location中,如下所示
然后运行test.java 的main方法,不过记得之前将他读取的模型路径改变一下
如果没有模型的话在附件里找就行,友情提供一个。
然后当你满怀激动的心情运行的时候,他偏偏有可能报这个错
这是因为我们的dll库并不叫CRFPP,而是叫CRFPP_demo,我们load的library应该是和这个dll库同名的,然后我们很无奈的改了loadLibrary的参数
以后,他很有可能还会报一个错。
这句话的意思是找不到依赖的dll包,感觉这个有很多种解决方案,有人说可以直接在C:\Windows\System32文件夹下加入libcrfpp.dll就好,但是我尝
试的并没有什么效果,同样尝试了感觉没什么效果的可以再加一行load库的代码如下。然后运行成功
这是在eclipse下的crfpp配置,IDEA下的其实也差不多,有人想配的话照着这个流程来就行,实在出不来了给我留言我更新下,windows占的篇幅太
多了,有一些坑没有全部列出来,如果有哪些坑以上没有涉及到可以留言,我再更新(毕竟win下坑还是蛮多的)。
2.Linux
软件环境 CentOS release 6.9 (Final),CRF++0.58,pcre-8.32.tar.gz,swig3.0.12, java version "1.8.0_111".
首先确认一点,你的jdk如果是openjdk的话你需要删除它,安装正常的jdk,就是打印java -version出现信息是如下这样的jdk
java version "1.8.0_111"
Java(TM) SE Runtime Environment (build 1.8.0_111-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.111-b14, mixed mode)
卸载方法自行百度,jdk安装方法也不多说了,都很简单。
tar -zxvf CRF++-0.58.tar.gz 解压
然后cd进去三步走起
./configure
make
make install
如果没有安装g++会报错应该,安装的命令是:yum install -y gcc-c++
如果三步进行完了我们就可以开始训练自己的模型了,正好也测试下crf++到底好不好使。
在训练模型的时候需要模板和语料,这个大家可以自己上网找一下,我在附加中也会提供一些简单的仅供测试的语料和模板,关于模板和
模型之类的知识不是本文讨论的重点,大家可以自行查阅相关文章。
训练模型 crf_learn segment_template train.data test.model -t (segment_template是模板,train.data是训练语料,test.model
是生成的模型名字, -t的意思是生成可读的模型,可用于深入理解CRF模型)
如果报以下错误
crf_learn: error while loading shared libraries: libcrfpp.so.0: cannot open shared object file: No such file or directory
则修改/etc/ld.so.conf文件,加入 include /usr/local/lib,执行/sbin/ldconfig -v,刷新LIB库,再试试就好用了。
然后用生成的模型来标注下测试语料,
crf_test -m seg_1_test.model test.data > result.txt (-m 后面选择要使用的模型,test.data是要标注的语料,result.txt是标注结
果,本文提供的测试语料是简单的BMES标注,只为熟悉流程。train.data和result.txt的内容应该是一样的,你也可以找一些其他的测试语料来测
试模型的标注效果。PS:本文语料选择中石化新闻网)。
OK,测试CRF运行无误以后我们开始用swig整合JNI接口联合JAVA。
tar -zxvf swig-3.0.12.tar.gz 使用命令解压swig,然后cd进入swig目录。
将pcre-8.32.tar.gz拷贝入swig目录,在目录下依次执行
./Tools/pcre-build.sh
./configure
make
make install
然后可以用swig -version查看版本
显示版本则证明swig安装成功,这个时候我们开始使用swig将crf++封装为JNI接口。
cd CRF++-0.58/swig/ 进入CRF下的swig目录。
make
然后再进入crf下的java目录 cd ../java/
make
这个时候报错了,找不到jni.h
打开文件CRFPP_wrap.cxx,找到这一行 : #include <jni.h> ,将这一行改为:
#include "jni.h"
然后,到java的安装目录下,将这两个文件(jni.h,jni_md.h)拷贝到 /crf0.58/java目录下。
然后make,编译成功
这个时候一般来讲运行java test是不会直接就成功的,我们还需要对test.java做一些修改。
这个时候就把这里的model路径换成之前我们生成的model路径。wq保存退出再make
很多人会发现然而这并没有什么用,该报错还是报错,那么依序执行以下命令
cp libCRFPP.so/lib
cp libCRFPP.so/lib64
cp libCRFPP.so/usr/lib
cp libCRFPP.so/usr/lib64
chmod 775 /usr/lib64/libCRFPP.so
然后执行下列命令:
cd /usr/local/lib
cp libcrfpp.so.0 /usr/lib64
最后不要忘了回到CRF++0.58的java目录下。
再试下java test。再没结果给我留言