Edited at

Kuromoji を使ってブラウザ上で形態素解析を行う (Reactあり/なし)

目的: ブラウザで入力された文章を形態素解析する。

:thinking: React 勉強がてら自然言語処理のプロトタイプ画面を作っているところに形態素解析が必要となった。形態素解析 API をもったサーバを別に立てなきゃいけないかなー (自分のサイトの形態素解析デモでは API 化していたし)。

:bulb: 意外にもクライアントサイドの JavaScript のみ行けました。

形態素解析器には Kuromoji 0.1.1 を使用しています。

この記事ではReact で使うケースと、React を使わないで kuromoji.js を直接使うケースを紹介します。


React で使う方法

npm install kuromojiKuromoji をインストールした時にバンドルされている辞書 node_modules/kuromoji/dict/ (多分 IPADIC かな?) をブラウザからアクセスできる公開ディレクトリにコピーします。

$ npm install kuromoji --save

$ cp -a node_modules/kuromoji/dict public/

公開した辞書の URI を指定して kuromoji で形態素解析します。

import kuromoji from "kuromoji"

// ...

const text = "親譲りの無鉄砲で小供の時から損ばかりしている"
kuromoji.builder({ dicPath: "/dict" }).build((err, tokenizer) => {
if(err){
console.log(err)
} else {
const tokens = tokenizer.tokenize(text)
console.log(tokens)
}
})

build() のコールバックは非同期で行われる点に注意。1,067 文字の文章に対するパフォーマンスは:


  1. build() のコールバックまで 1,197ms。

  2. tokenize() の処理が 26ms。

形態素解析だけであれば意外に速い。

build() のたびに辞書の読み込みを行っているようで体感速度のボトルネックになっている。Tokenizer.js のソースを読む限り tokenizer は状態が変更されないようなので、一度構築した後の使い回しは可能と思われる (確証はないが)。

2度目以降の辞書のアクセスは 304 Not Modified レスポンスなので辞書のダウンロードではなく読み込みと解析に時間がかかっている。このため ServiceWorker で辞書をローカルにキャッシュさせてもあまり効果はないと推測する。

tokenize() が成功した場合、以下のような形態素情報の配列を返してくる。

[

{
"word_id": 564230,
"word_type": "KNOWN",
"word_position": 1,
"surface_form": "親譲り",
"pos": "名詞",
"pos_detail_1": "一般",
"pos_detail_2": "*",
"pos_detail_3": "*",
"conjugated_type": "*",
"conjugated_form": "*",
"basic_form": "親譲り",
"reading": "オヤユズリ",
"pronunciation": "オヤユズリ"
},
...
]


React を使わない方法

React を使用しなくても通常の JavaScript と同じように Kuromoji を利用することができます。必要なのは React のケースでコピーした辞書に加えて node_modules/kuromoji/build/kuromoji.js 。これらをブラウザからアクセス可能な公開ディレクトリにコピーします。

Kuromoji を利用する HTML 側で <script> 要素を記述します。これでグローバルスコープで kuromoji オブジェクトが利用可能になるので React の場合と同じように記述できます。

<script src="/js/kuromoji.js"></script>

<script>
var text = "親譲りの無鉄砲で小供の時から損ばかりしている"
kuromoji.builder({ dicPath: "/dict" }).build(function(err, tokenizer){
if(err){
console.log(err);
} else {
var tokens = tokenizer.tokenize(text);
console.log(tokens);
}
});
</script>