Menggunakan Stanford NER dengan Spring MVC

Pada tulisan kali ini saya ingin berbagi mengenai bagaimana membuat aplikasi sederhana memanfaatkan Stanford NER dengan framework Spring MVC (versi 3). Oke, pertama-tama apa itu Stanford NER? Sederhananya, NER, sebagaimana kepanjangannya Name Entity Recognizer, berguna untuk melabeli kata-kata dalam suatu teks menurut entitasnya, antara lain seperti nama orang, organisasi, atau lokasi. Sedangkan yang disebut dengan Stanford NER itu sendiri adalah implementasi NER dalam Java yang dibuat oleh The Stanford Natural Language Processing Group.

Di tulisan ini saya tidak membahas mengenai bagaimana implementasi NER tersebut dilakukan atau model yang mana yang digunakan dalam Stanford NER itu. Semua pembahasan mengenai itu sudah tersedia di webnya → http://nlp.stanford.edu/software/CRF-NER.shtml. Bahkan ada pranala juga menuju paper terkait dengan model yang digunakan. Di web tersebut juga, tepatnya di bagian menu Download, kita bisa mengunduh file JAR Stanford NER yang akan digunakan.

Masih belum beranjak dari situs Stanford NER itu, di sana kita juga bisa mencoba secara live Stanford NER itu melalui pranala berikut → http://nlp.stanford.edu:8080/ner/process. Di form aplikasi Stanford NER tersebut kita bisa memilih classifier yang akan kita gunakan, kemudian memasukkan input berupa teks. Dalam contoh saya, saya menggunakan sebuah kalimat yang saya kutip dari artikel situs BBC. Dari input kalimat yang saya berikan, saya mendapatkan output 4 labeled words (baik berupa kata tunggal ataupun majemuk) dengan entitasnya masing-masing.

Contoh input & output pada Stanford NER

Contoh input & output pada Stanford NER

Dari contoh di atas setidaknya terlihat dengan jelaslah ya apa sih kegunaan NER ini, terutama bagi rekan-rekan yang mungkin baru mendengar apa itu NER. NER ini akan sangat berguna untuk aplikasi-aplikasi yang menyediakan fitur auto-tagging. Yah … semacam fitur recommendation tag-nya Wordpress lah.

Nah, selanjutnya adalah bagaimana cara memanfaatkan Stanford NER ini untuk suatu aplikasi (web) yang kita bangun. Kalau aplikasi web kita juga berbasiskan Java, tentu akan lebih mudah meng-include-kan Stanford NER ini. Bila perlu, kita gabungkan saja ke dalam sistem aplikasi kita.

Namun, bagaimana bila aplikasi kita dibangun menggunakan bahasa pemrograman lain macam PHP, Python, Ruby, dsb? Solusinya Stanford NER ini kita manfaatkan dalam aplikasi tersendiri sebagai sebuah web service.

Dalam contoh saya, saya membangun aplikasi untuk pemrosesan NER itu dengan menggunakan Spring MVC. Struktur projek akhir yang saya buat kurang lebih sebagai berikut (saya menggunakan NetBeans):

Struktur projek

Struktur projek

Yang saya kotaki merah adalah file-file terkait dengan pemrosesan Stanford NER. Tentu saja tidak semua file classifier itu akan digunakan. Oh ya, selain itu, yang tidak boleh dilupakan adalah file stanford-ner.jar yang kita unduh bersama dengan classifier-classifier itu juga harus ditambahkan ke dalam Libraries projek kita.

SiteController pada projek ini adalah controller default alias halaman index ketika aplikasi diakses. Kurang lebih baris kodenya sebagai berikut:

package com.stanfordner.controllers;

import org.springframework.stereotype.Controller;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import edu.stanford.nlp.ie.AbstractSequenceClassifier;
import edu.stanford.nlp.ie.crf.*;
import edu.stanford.nlp.ling.CoreLabel;
import edu.stanford.nlp.ling.CoreAnnotations;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;

import java.util.List;
import org.json.simple.JSONObject;

@Controller
@RequestMapping(value = "/")
public class SiteController {

@RequestMapping(value = "", method = RequestMethod.GET)
public @ResponseBody
String NERtagging(String keyword, HttpServletRequest request) throws UnsupportedEncodingException {

String prevWord = "", prevWordClass = "O";

String defaultS = "";

// json to return
JSONObject obj = new JSONObject();

// output tags
Map tags = new HashMap();
ArrayList<String> person = new ArrayList<String>(); // PERSON
ArrayList<String> location = new ArrayList<String>(); // LOCATION
ArrayList<String> time = new ArrayList<String>(); // TIME
ArrayList<String> organization = new ArrayList<String>(); // ORGANIZATION
ArrayList<String> money = new ArrayList<String>(); // MONEY
ArrayList<String> percent = new ArrayList<String>(); // PERCENT
ArrayList<String> date = new ArrayList<String>(); // DATE

// assign the arrays with the corresponding tag names
tags.put("PERSON", person);
tags.put("LOCATION", location);
tags.put("TIME", time);
tags.put("ORGANIZATION", organization);
tags.put("MONEY", money);
tags.put("PERCENT", percent);
tags.put("DATE", date);

// cacth the input
String s = request.getParameter("s") == null ? defaultS : URLDecoder.decode(request.getParameter("s"), "UTF-8");

String serializedClassifier = request.getSession().getServletContext().getRealPath("/WEB-INF/resources/classifiers/english.muc.7class.distsim.crf.ser.gz");

AbstractSequenceClassifier<CoreLabel> classifier = CRFClassifier.getClassifierNoExceptions(serializedClassifier);

List<List<CoreLabel>> out = classifier.classify(s);
<span style="font-style: inherit; line-height: 1.6em;">for (List<CoreLabel> sentence : out) {</span>

System.out.println(sentence.toString());

for (CoreLabel word : sentence) {
String wordClass = word.get(CoreAnnotations.AnswerAnnotation.class);

System.out.print(word.word() + '/' + wordClass + ' ');

if (!prevWordClass.equalsIgnoreCase("O")) {
if (!prevWordClass.equalsIgnoreCase(wordClass)) {
((ArrayList) tags.get(prevWordClass)).add(prevWord);
} else {
prevWord += " " + word.word();
}
} else {
prevWord = word.word();
}

prevWordClass = wordClass;
}

System.out.println();

prevWord = "";
prevWordClass = "O";

}

obj.put("person", person);
obj.put("location", location);
obj.put("time", time);
obj.put("organization", organization);
obj.put("money", money);
obj.put("percent", percent);
obj.put("date", date);

return obj.toJSONString();
}
}

Nah, untuk menggunakannya, suatu teks input dilewatkan sebagai GET parameter URL fungsi tersebut. Misalkan cuplikan teks input sebagai berikut: In the Men’s Singles showdown, Kenichi Tago (Japan, 6) proved a worthy opponent for the great Lee Chong Wei (right; at left). Teks tersebut akan dilewatkan pada link sebagai value untuk parameter ‘s’. Dan URL tersebut akan mengembalikan output berupa json dengan nama-nama entitas sebagai key-nya.

Passing kalimat sebagai parameter value

Passing kalimat lewat URL

Sedangkan salah satu contoh cara pemanfaatannya dengan PHP adalah sebagai berikut:

<?php
 $text = 'In the Men’s Singles showdown, Kenichi Tago (Japan, 6) proved a worthy opponent for the great Lee Chong Wei (right; at left)';
 $input = urlencode($text);
 $output = file_get_contents("http://localhost:8080/stanford-ner-spring-mvc?s={$input}");

 $outputArr = json_decode($output);

 foreach ($outputArr as $key => $value) {
   echo $key . ' : ';
   foreach ($value as $word) {
     echo $word . '; ';
   }
   echo '<br/>';
 }

?>

Kode tersebut akan mengeluarkan output sebagai berikut:

organization :
time :
person : Kenichi Tago; Lee Chong Wei;
percent :
location : Japan;
money :
date :

Kita juga bisa memanggilnya melalui ajax. Kalau menggunakan jQuery, kita tinggal memanggil method $.get() atau $.ajax(). Tapi perhatikan persoalan cross domain yang akan muncul jika kita meletakkan aplikasi Stanford NER Spring MVC ini pada domain yang berbeda dengan aplikasi kita. Untuk menyiasati itu, akan diperlukan beberapa modifikasi di kode karena harus menggunakan jsonp.

Anyway, hasil pelabelan Stanford NER ini bukan berarti akan selalu akurat 100%. Kita bisa membuat training model sendiri kalau mau. Tapi tak ada jaminan bakal lebih baik dari training model yang sudah ada, hehe. Selamat mencoba!

One thought on “Menggunakan Stanford NER dengan Spring MVC

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s