我没有特别热衷机器学习或者自然文字处理(NLP),不过也总能想到办法用他们。这篇博客我想探讨一下利用twitter数据构建实时搜索引擎。例如提供工作,Tweets会包含公司名字,公司地址,公司联系人,这就需要我们从Tweet上分析人,位置,公司信息。这类问题就落到了Named Entity Recognition(NER)身上。
维基解释
命名实体识别(NER)是一个信息提取子任务,用于探索位置和把文字中的分类原子元素归到预先定义好的如人名,机构,地理位置,时间表述,质量,货币价值,百分比等类别中。
为了更清晰的表述,来看一个示例,假如我们有一下一段信息。
常人可以很容易的指出公司PSI Pax开在Baltmore. 但是我们怎样让程序来识别呢?最简单的方法是维护一个列表,收集所有的公司和地址,通过这个列表来搜索,但是,这个工作量无法衡量。
今天,我们来讨论怎样用Standford NER包来安装我们自己的NER服务。
Stanford NER是用Java编写的命名识别实体,它给文中的文字针对名字贴上标签,像人名,公司名,基因,蛋白质名等。
sudo gem install rhc
如果已经安装了,确保是最新的,要更新rhc,输入
sudo gem update rhc
想了解rhc command-line 工具,更多帮助参考https://openshift.redhat.com/community/developers/rhc-client-tools-install。
我们从建一个demo开始,程序叫nerdemo.
$ rhc create-app nerdemo jbosseap
如果你能访问普通gears, 可以用以下命令。
$ rhc create-app nerdemo jbosseap -g medium
这会创建一个程序容器,叫gear,安装所需的SELinux策略和cgroup配置。OpenShift也会为你安装一个私有的git仓库,并克隆到本地。最后,OpenShift会把DNS 扩散到网络中。程序可访问http://linkbin-domain-name.rhcloud.com/. 替换你自己唯一的OpenShift域名(有时也叫命名空间)。
在pom.xml文件里添加以下依赖:
<dependency> <groupId>edu.stanford.nlp</groupId> <artifactId>stanford-corenlp</artifactId> <version>3.2.0</version> </dependency>
同时通过更新pom.xml里的几个属性把maven项目更新到Java 7.
<maven.compiler.source>1.7</maven.compiler.source> <maven.compiler.target>1.7</maven.compiler.target>
现在更新Maven项目右击> Maven >Update Project.
我们用CDI添加依赖,CDI或者内容和依赖注入用来在Java EE 6项目中激活依赖注入。CDI给Java EE定义了安全类型的依赖注入机制,基本上所有POJO可以作为CDI bean注入。
在src/main/webapp/WEB-INF文件下新建beans.xml文件,用以下内容替代:
<beans xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd"> </beans>
现在我们创建一个ApplicationScoped bean用来创建CRFClassifier实例,这个分类器用来从文字中检测名字,位置和机构。
package com.nerdemo; import javax.annotation.PostConstruct; import javax.enterprise.context.ApplicationScoped; import javax.enterprise.inject.Produces; import javax.inject.Named; import edu.stanford.nlp.ie.crf.CRFClassifier; import edu.stanford.nlp.ling.CoreLabel; @ApplicationScoped public class ClassifierConfig { private String serializedClassifier = "classifiers/english.all.3class.distsim.crf.ser.gz"; private CRFClassifier<CoreLabel> classifier; @PostConstruct public void postConstruct() { CRFClassifier<CoreLabel> classifier = CRFClassifier.getClassifierNoExceptions(serializedClassifier); this.classifier = classifier; } @Produces @Named public CRFClassifier<CoreLabel> classifier() { return classifier; } }
从下载的Stanford NER包中复制english.all.3class.distsim.crf.ser.gz分类器到文件夹src/main/resources/classifiers.
要激活JAX-RS,创建一个类继承javax.ws.rs.core.Application, 指定程序路径用javax.ws.rs.ApplicationPath注释。
package com.nerdemo; import javax.ws.rs.ApplicationPath; import javax.ws.rs.core.Application; @ApplicationPath("/api/v1") public class JaxrsInitializer extends Application{ }
现在来创建ClassifyRestResource用来返回NER结果,创建一个新类ClassifyRestResource,用以下内容替代代码。
package com.nerdemo; import java.util.ArrayList; import java.util.List; import javax.inject.Inject; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import edu.stanford.nlp.ie.crf.CRFClassifier; import edu.stanford.nlp.ling.CoreAnnotations; import edu.stanford.nlp.ling.CoreLabel; @Path("/classify") public class ClassifierRestResource { @Inject private CRFClassifier<CoreLabel> classifier; @GET @Path(value = "/{text}") @Produces(value = MediaType.APPLICATION_JSON) public List<Result> findNer(@PathParam("text") String text) { List<List<CoreLabel>> classify = classifier.classify(text); List<Result> results = new ArrayList<>(); for (List<CoreLabel> coreLabels : classify) { for (CoreLabel coreLabel : coreLabels) { String word = coreLabel.word(); String answer = coreLabel.get(CoreAnnotations.AnswerAnnotation.class); if(!"O".equals(answer)){ results.add(new Result(word, answer)); } } } return results; } }
最后,把更新发布上OpenShift.
$ git add . $ git commit -am "NER demo app" $ git push
代码和war成功推送和发布后,我们可以看到程序在http://nerdemo-{domain-name}.rhcloud.com,我的示例在http://nerdemo-t20.rhcloud.com.
你可以得到一个JSON响应。
[ {"word":"Microsoft","answer":"ORGANIZATION"}, {"word":"PSI","answer":"ORGANIZATION"}, {"word":"Pax","answer":"ORGANIZATION"}, {"word":"Baltimore","answer":"LOCATION"} ]
这是今天的内容,继续给反馈吧。