soy-curd's blog

へぼプログラマーです [https://twitter.com/soycurd1]

scalaでテキスト分類してみた

Scala勉強会第143回 SPECIAL DAY ハッカソン in 歌舞伎座に遊びに行くことにしたので、その前にscalaの練習がてらnakを使ってみた。

nakとは?

ScalaNLPの仲間で、機械学習ライブラリ。 sbtで、

libraryDependencies ++= Seq(
  "org.scalanlp" % "breeze_2.10" % "0.7",
  "org.scalanlp" % "breeze-natives_2.10" % "0.7",
  "org.scalanlp" % "nak" % "1.2.1"
)

すればbreezeと一緒に入る。

テキスト分類

ここのExample通りにやれば、テキストを分類できる。 今回はこのExampleを使って、"なでしこ"と"ひまわり"のソースコードを分類してみた。 なお、今回用いた"なでしこ"と"ひまわり"はどちらも日本語プログラミング言語*1で、wikiを見ると、

なでしこは、クジラ飛行机(くじらひこうづくえ)が制作したインタプリタ方式のスクリプトプログラミング言語である。

"ひまわり"は、

ひまわりはスクリプトプログラミング言語の一つ。動作可能なOSは、Microsoft Windows 98/Me/2000/XP。

とある。 Hello Worldはそれぞれ、

「Welcome to Nadesiko.」と表示。
「ひまわりへようこそ」と、表示。

というかんじらしい。python2系と3系みたいなかんじだろうか。

結果

分類した結果は以下。

--------------------------------------------------------------------------------
Confusion matrix.
Columns give predicted counts. Rows give gold counts.
--------------------------------------------------------------------------------
5   1   |   6   himawari
2   4   |   6   nadeshiko
----------------
7   5
himawari    nadeshiko

--------------------------------------------------------------------------------
        75.00   Overall accuracy
--------------------------------------------------------------------------------
P   R   F
71.43   83.33   76.92   himawari
80.00   66.67   72.73   nadeshiko
...................................
75.71   75.00   74.83   Average

サンプル少ないけど、F値を見ると判定できてる感がある。とりあえず、日本語が入っていても分類できそうなかんじなので、いろいろ応用が効きそう。

ソース

なお、ソースはこんなかんじ。(stopwordsの使い方わからん...)

package nak.example

import nak.data._
import nak.NakContext._
import nak.NakContext.trainClassifier
import nak.liblinear.LiblinearConfig
import nak.util.ConfusionMatrix

import java.io.File

object analyze {
  def main(args: Array[String]) {
    classifyFile("/xxxx/")
  }

  def classifyFile(path:String) = {
    val groupsDir = new File(path)
    implicit val isoCodec = scala.io.Codec("ISO-8859-1")
    val stopwords = Set("は","の","から","に","を","まで","へ","が")

    val trainDir = new File(groupsDir, "train")
    val trainingExamples = fromLabeledDirs(trainDir).toList

    val config = LiblinearConfig(cost=5.0)
    val featurizer = new BowFeaturizer(stopwords)

    // 分類器の作成
    val classifier = trainClassifier(config, featurizer, trainingExamples)

    val evalDir = new File(groupsDir, "test")
    val maxLabelNews = maxLabel(classifier.labels) _

    // テストデータと比較
    val comparisons = for (ex <- fromLabeledDirs(evalDir).toList) yield
      (ex.label, maxLabelNews(classifier.evalRaw(ex.features)), ex.features)
    val (goldLabels, predictions, inputs) = comparisons.unzip3

    // 整形して出力
    println(ConfusionMatrix(goldLabels, predictions, inputs))
  }

面白そうなので、もう少しいじってみようと思う。

*1:なんか畳語っぽい