天天看點

Day14:使用斯坦福 NER 軟體包實作你自己的命名實體識别器(Named Entity Recognition,NER)

編者注:我們發現了有趣的一系列文章 《30天學習30種新技術》

,正在翻譯中,一天一篇更新,年終禮包。下面是第 14 天的内容。

我并不是一個機器學習(Machine Learning)、自然語言處理(Natural Text Processing,NLP)等的狂熱者,但我總會想到一些需要用到它們的主意。我們今天在這篇博文中要實作的目标是:利用 Twitter 資料建立一個實時的職位搜尋。每個單獨的搜尋結果要包括提供職位的公司名稱、工作的地點、去公司應聘時聯系的人。這需要我們從 個人(Person)、地點(Location)、組織(Organisation)三方面去分析每一條推(tweet)。這類問題被歸為

命名實體識别(Named Entity Recognition,NER)

問題。

根據維基百科的資料,命名實體識别是資訊提取(Information Extraction)的一個子任務,它把文字的原子元素(Atomic Element)定位和分類好,然後輸出為固定格式的目錄,例如: 人名、組織、位置、時間的表示、數量、貨币值、百分比等。

為了說的更明白,我們來舉個例子。假設我們有下面這條推:

Day14:使用斯坦福 NER 軟體包實作你自己的命名實體識别器(Named Entity Recognition,NER)

一個普通人可以輕易地分辨出一個名為 PSI Pax 的組織在 Baltimore 有個空缺的職位。但是我們怎麼用程式設計的方式來完成這個識别呢? 最簡單的辦法是維護一個包含所有組織名稱、地點的清單,然後對這個清單進行搜尋。然而,這種做法的可擴充性太差了。

今天,在這篇博文中,我會描述如何用斯坦福 NER(Stanford NER) 軟體包去設定我們自己的 NER 伺服器。

什麼是 斯坦福 NER?

斯坦福 NER 命名實體識别(Named Entity Recognizer,NER)的 Java 實作。 NER 辨別一段文字中的一系列名詞,例如人名、公司名,又或者基因名、蛋白質名。

前期準備

  1. 一些基本的 Java 知識是需要的。在你的作業系統上安裝最新版本的 JDK,你可以安裝 OpenJDK 或者 Oracle JDK 7 。OpenShift 支援 OpenJDK 6 和 7.
  2. 從官網中下載下傳 斯坦福 NER 軟體包
  3. 注冊一個 OpenShift 賬戶。這是完全免費的,而且紅帽(Red Hat)會給每個使用者三個免費的 Gears,在 Gears 上你可以運作你的程式。在這篇文章寫的時候,OpenShift 會為每個使用者配置設定 1.5GB 的記憶體和 3GB 的硬碟空間。
  4. 在本機上,安裝 rhc 用戶端工具 。rhc 是一個 ruby gem,是以你需要機子上安裝好 ruby 1.8.7 及以上的 ruby。要安裝 rhc,輸入:

sudo gem install rhc

更新 rhc 到最新版本,執行:

sudo gem updatge rhc

如果需要閱讀額外的安裝 rhc 指令行工具時的幫助檔案,可以浏覽:

https://openshift.redhat.com/community/developers/rhc-client-tools-install

5.使用 rhc setup 指令設定好 OpenShift 賬戶,這個指令會為你建立一個命名空間,然後上傳你的 ssh keys 到 OpenShift 伺服器上。

第一步:建立一個 JBoss EAP 應用

我們現在開始建立這個示範應用。這個應用的名稱是 nerdemo

rhc create-app nerdemo jbosseap

如果你可以通路媒介齒輪(Medium Gears),你可以使用下面的指令:

$ rhc create-app nerdemo jbosseap -g medium

它會為我們建立一個應用容器,叫做 Gear,會自動設定好需要的 SELinux/cgroup 配置。OpenShift 也會為我們建立一個私密的 git 倉庫,然後可克隆這個倉庫到本地系統上。最後,OpenShift 還會部署一個連接配接外面的 DNS。部署的應用可以通過連結:

http://linkbin-domain-name.rhcloud.com/

來通路。把領域換成自己的 OpenShit 領域(有時候叫 指令空間)

第二步:增加 Maven 依賴

在 pom.xml 檔案中,增加一下依賴:

<dependency>

    <groupId>edu.stanford.nlpgroupId>

    <artifactId>stanford-corenlpartifactId>

    <version>3.2.0version>

dependency>

然後,通過更新 pom.xml 檔案中的一些屬性把 Maven 項目更新到 Java 7

compiler.source>1.7compiler.source>

compiler.target>1.7compiler.target>

現在,通過

右擊 > Maven > 更新項目

更新 Maven

第三步:開啟 CDI

我們會使用 CDI 來進行依賴項注入(Dependency Injection)。CDI(Context and Dependency Injection)是 Java EE 6 的一個特性,它允許在 Java EE 6 項目中的依賴項注入。CDI 為 Java EE 定義一個類型安全(type-safe) 的 依賴項注入機制。幾乎任何 POJO 可以作為 CDI 豆(bean)那樣被注入。

在 src/main/webapp/WEB-INF 目錄下,建立一個名為 beans.xml 的 xml 檔案。用下面的内容代替 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>

第四步:應用程式作用域分類器的豆(Application Scoped Classifier Bean)

現在,我們建立一個應用程式作用域的豆(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;

}

從下載下傳的斯坦福 NER 軟體包中複制 english.all.3class.distsim.crf.ser.gz 分類器到 src/main/resources/classifiers 目錄下。

第五步:開啟 AX-RS

為了開啟 AX-RS,建立一個擴充 javax.ws.rs.core.Application 的類,然後用下面 javax.ws.rs.ApplicationPath 的标記法标記應用程式的路徑:

import javax.ws.rs.ApplicationPath;

import javax.ws.rs.core.Application;

@ApplicationPath("/api/v1")

public class JaxrsInitializer extends Application{

第六步:建立 ClassifyRestResource 類

現在我們要建立 ClassifyRestResource 類,它傳回一個 NER 結果。建立一個新的 ClassifyRestResource 類,然後用下面代碼代替它:

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.ling.CoreAnnotations;

@Path("/classify")

public class ClassifierRestResource {

    @Inject

    private CRFClassifier classifier;

    @GET

    @Path(value = "/{text}")

    @Produces(value = MediaType.APPLICATION_JSON)

    public List findNer(@PathParam("text") String text) {

        List> classify = classifier.classify(text);

        List results = new ArrayList<>();

        for (List 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

最後,部署所做的改變到 OpenShift:

$ git add .

$ git commit -am "NER demo app"

$ git push

當代碼成功部署之後,我們可以通過通路

http://nerdemo-

{domain-name}.rhcloud.com 看到應用運作。我的示例應用運作在:

http://nerdemo-t20.rhcloud.com

現在,發送一個請求:

http://nerdemo-t20.rhcloud.com/api/v1/classify/Microsoft%20SCCM%20Windows%20Server%202012%20Web%20Development%20Expert%20(SME3)%20at%20PSI%20Pax%20(Baltimore,%20MD)

然後,你就會得到一個 JSON 格式的結果:

[

{"word":"Microsoft","answer":"ORGANIZATION"},

{"word":"PSI","answer":"ORGANIZATION"},

{"word":"Pax","answer":"ORGANIZATION"},

{"word":"Baltimore","answer":"LOCATION"}

]

這就是今天的内容了,保持回報!

接下來