以前「python から kuromoji を実行」という記事を書きました。
この記事の方法で kuromoji である程度の量の文書を解析すると、実行時間がかなり遅いことに気づきました。
以前の方法では、以下のように token 毎に getSurfaceForm() などの kuromoji のメソッドを呼び出していました。
この方法だと、java サーバとの通信が 1 token につき形態素解析結果の項目分の通信が発生しているのではないかと考えました。
そこで、java サーバは形態素解析結果を json 文字列を返却するようにすることで実行速度を改善してみました。
java サーバ側では以下のように json 文字列を返すメソッドを用意します。
json ライブラリとして jackson を使用しています。
上記のプログラムを以下のようにコンパイルして実行します。
python では、前回と同様 java のメソッドを呼び出す方法と、上記の json を返すメソッドを使う方法とで
100文ずつ解析を行い、実行速度を比較します。
実行結果は以下の通りです。
java のメソッド呼び出し回数を削減した分がそのまま実行速度に反映されているようです。
この記事の方法で kuromoji である程度の量の文書を解析すると、実行時間がかなり遅いことに気づきました。
以前の方法では、以下のように token 毎に getSurfaceForm() などの kuromoji のメソッドを呼び出していました。
gw = JavaGateway() tokenizer = gw.jvm.org.atilika.kuromoji.Tokenizer.builder().build() jtkns = tokenizer.tokenize('日本語の文字列を解析します。') tkns = [] for i in range(len(jtkns)): jtkn = jtkns[i] tkn = { 'form': jtkn.getSurfaceForm(), 'base': jtkn.getBaseForm(), 'pos': jtkn.getBaseForm(), } tkns.append(tkn)
この方法だと、java サーバとの通信が 1 token につき形態素解析結果の項目分の通信が発生しているのではないかと考えました。
そこで、java サーバは形態素解析結果を json 文字列を返却するようにすることで実行速度を改善してみました。
java サーバ側では以下のように json 文字列を返すメソッドを用意します。
json ライブラリとして jackson を使用しています。
import py4j.GatewayServer; import java.util.List; import org.atilika.kuromoji.Tokenizer; import org.atilika.kuromoji.Token; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.ArrayNode; public class KuromojiJsonGateway { public String tokenize(Tokenizer tknzr, String str) { List<Token> tkns = tknzr.tokenize(str); ObjectMapper mapper = new ObjectMapper(); ArrayNode arr = mapper.createArrayNode(); for (int i = 0; i < tkns.size(); i++) { Token tkn = (Token)(tkns.get(i)); ObjectNode obj = mapper.createObjectNode(); obj.put("form", tkn.getSurfaceForm()); obj.put("base", tkn.getBaseForm()); obj.put("read", tkn.getReading()); obj.put("pos", tkn.getAllFeatures()); arr.add(obj); } try { String json = mapper.writeValueAsString(arr); return json; } catch (Exception e) { return ""; } } public static void main(String[] args) { KuromojiJsonGateway app = new KuromojiJsonGateway(); GatewayServer server = new GatewayServer(app); server.start(); } }
上記のプログラムを以下のようにコンパイルして実行します。
javac -classpath py4j0.10.9.2.jar:kuromoji-0.7.7.jar:jackson-core-2.13.0.jar:jackson-databind-2.13.0.jar:jackson-annotations-2.13.0.jar KuromojiJsonGateway.java java -classpath py4j0.10.9.2.jar:kuromoji-0.7.7.jar:jackson-core-2.13.0.jar:jackson-databind-2.13.0.jar:jackson-annotations-2.13.0.jar KuromojiJsonGateway
python では、前回と同様 java のメソッドを呼び出す方法と、上記の json を返すメソッドを使う方法とで
100文ずつ解析を行い、実行速度を比較します。
import sys import time import json from py4j.java_gateway import JavaGateway def convert(jtkns): tkns = [] for i in range(len(jtkns)): jtkn = jtkns[i] tkn = { 'form': jtkn.getSurfaceForm(), 'base': jtkn.getBaseForm(), 'read': jtkn.getReading(), 'pos': jtkn.getAllFeatures(), } return tkns def tokenize1(gw, str, num): tknzr = gw.jvm.org.atilika.kuromoji.Tokenizer.builder().build() from_time = time.time() for i in range(num): jtkns = tknzr.tokenize(str) tkns = convert(jtkns) to_time = time.time() print("tokenize1(): %s" % (to_time - from_time)) def tokenize2(gw, str, num): app = gw.entry_point tknzr = gw.jvm.org.atilika.kuromoji.Tokenizer.builder().build() from_time = time.time() for i in range(num): tkns_str = app.tokenize(tknzr, str) tkns = json.loads(tkns_str) to_time = time.time() print("tokenize2(): %s" % (to_time - from_time)) def main(): gw = JavaGateway() num = 100 str = '形態素解析を実行します' tokenize1(gw, str, num) tokenize2(gw, str, num) return 0 if __name__ == '__main__': res = main() exit(res)
実行結果は以下の通りです。
tokenize1(): 0.7767603397369385 tokenize2(): 0.13006877899169922
java のメソッド呼び出し回数を削減した分がそのまま実行速度に反映されているようです。