形態素解析
Kuromoji with IPADIC+NEologd
概要
自然言語処理ではよく単語を文章を構成する「意味を持つ情報」の最小単位として扱っている。ラテン語圏の言語は単語の区切りに空白を使用しているためプログラムでの抽出は比較的容易だが、日本語の場合は文の中から単語を識別し適切に分割する実装が必要となる。このような単語分割処理は対象とする言語圏ごとに実装する必要がある。
単語分割 (分かち書き) は形態素解析の基本的な操作である。一般的な形態素解析の実装は単語分割と同時に品詞の付与や係り受けの解析などを行っている。
プログラミング
JavaVM 言語での形態素解析は Kuromoji がよく利用されている。
使用できる辞書は MeCab と互換のある IPADIC のほかに UniDic も利用できる。また新語に対応した IPADIC の拡張版 NEologd を使用することもできる (Kuromoji 配布版は辞書の入れ替えが必要なため以下の説明では erasticsearch のライブラリを利用している)。
sbt を使用している場合 build.sbt
に以下のリポジトリとライブラリを追加する。バージョンは Maven Central Repository で確認して都度最新のものを指定すればよい。
resolvers += "CodeLibs Repository" at "http://maven.codelibs.org/"
libraryDependencies += "org.codelibs" % "elasticsearch-analysis-kuromoji-neologd" % "5.6.+"
elasticsearch-analysis-kuromoji-neologd
は CodeLibs リポジトリ上の lucene-analyzers-kuromoji-ipadic-neologd
に依存しているが、Codeibs のサーバ不調でダウンロードに失敗しビルドできないことがしばしばある。このとき、もしコンパイルの成功するマシンがあるなら ~/.ivy2/cache/org.codelibs/lucene-analyzers-kuromoji-ipadic-neologd をコピーするとうまく行く。
import java.io.StringReader
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute
import org.codelibs.neologd.ipadic.lucene.analysis.ja.JapaneseTokenizer
import org.codelibs.neologd.ipadic.lucene.analysis.ja.tokenattributes.{BaseFormAttribute, InflectionAttribute, PartOfSpeechAttribute, ReadingAttribute}
import scala.collection.mutable
case class Morph(surface:String, pos1:String, pos2:String, pos3:String, pos4:String,
conjugationType:String, conjugationForm:String, baseForm:String,
reading:String, pronunciation:String)
def tokenize(text:String):Seq[Morph] = {
val tokenizer = new JapaneseTokenizer(null, false, JapaneseTokenizer.Mode.NORMAL)
val term = tokenizer.getAttribute(classOf[CharTermAttribute])
val pos = tokenizer.getAttribute(classOf[PartOfSpeechAttribute])
val baseForm = tokenizer.getAttribute(classOf[BaseFormAttribute])
val inflection = tokenizer.getAttribute(classOf[InflectionAttribute])
val reading = tokenizer.getAttribute(classOf[ReadingAttribute])
tokenizer.setReader(new StringReader(text))
tokenizer.reset()
// 形態素に分解
val buffer = mutable.Buffer[Morph]()
while(tokenizer.incrementToken()) {
val Array(pos1, pos2, pos3, pos4) = pos.getPartOfSpeech.split("-").padTo(4, "")
val token = Morph(
surface = term.toString,
pos1 = pos1, pos2 = pos2, pos3 = pos3, pos4 = pos4,
baseForm = Option(baseForm.getBaseForm).getOrElse(""),
conjugationForm = Option(inflection.getInflectionForm).getOrElse(""),
conjugationType = Option(inflection.getInflectionType).getOrElse(""),
reading = Option(reading.getReading).getOrElse(""),
pronunciation = Option(reading.getPronunciation).getOrElse(""))
buffer.append(token)
}
tokenizer.close()
buffer.toSeq
}
NEologd は正しい名称に変換する。口語や丁寧語は「ありません」を名詞と認識すると「有馬線」と変換することがありマイナスに働くことがある。
sbt
で上記のコードを実行し形態素解析を試すことができる。
$ sbt
sbt:test> set resolvers += "CodeLibs Repository" at "http://maven.codelibs.org/"
sbt:test> set libraryDependencies += "org.codelibs" % "elasticsearch-analysis-kuromoji-neologd" % "5.5.+"
sbt:test> console
[info] Starting scala interpreter...
Welcome to Scala 2.12.3 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_131).
Type in expressions for evaluation. Or try :help.
scala> import java.io.StringReader
... (上記のコードを実行)
scala> tokenize("アメリカ政府は12日、世界遺産の登録などで知られるユネスコ(国連教育科学文化機関)から脱退すると表明した。").mkString("\n")
res9: String =
Morph(アメリカ政府,名詞,固有名詞,一般,,,,,アメリカセイフ,アメリカセイフ)
Morph(は,助詞,係助詞,,,,,,ハ,ワ)
Morph(12日,名詞,固有名詞,一般,,,,,ジュウニニチ,ジュウニニチ)
Morph(世界遺産,名詞,固有名詞,一般,,,,,セカイイサン,セカイイサン)
Morph(の,助詞,連体化,,,,,,ノ,ノ)
Morph(登録,名詞,サ変接続,,,,,,トウロク,トーロク)
Morph(など,助詞,副助詞,,,,,,ナド,ナド)
Morph(で,助詞,格助詞,一般,,,,,デ,デ)
Morph(知ら,動詞,自立,,,五段・ラ行,未然形,知る,シラ,シラ)
Morph(れる,動詞,接尾,,,一段,基本形,,レル,レル)
Morph(ユネスコ,名詞,固有名詞,組織,,,,,ユネスコ,ユネスコ)
Morph(国連教育科学文化機関,名詞,固有名詞,一般,,,,国際連合教育科学文化機関,コクレンキョウイクカガクブンカキカン,コクレン キョーイクカガクブンカキカン)
Morph(から,助詞,格助詞,一般,,,,,カラ,カラ)
Morph(脱退,名詞,サ変接続,,,,,,ダッタイ,ダッタイ)
Morph(する,動詞,自立,,,サ変・スル,基本形,,スル,スル)
Morph(と,助詞,格助詞,引用,,,,,ト,ト)
Morph(表明,名詞,サ変接続,,,,,,ヒョウメイ,ヒョーメイ)
Morph(し,動詞,自立,,,サ変・スル,連用形,する,シ,シ)
Morph(た,助動詞,,,,特殊・タ,基本形,,タ,タ)
scala> :quit