タグ一覧
プロフィール
Feed
次の記事
JavaScriptにJavaのようなequalsメソッドを作成する
前の記事
ScalaからCライブラリを読み込んで実行する
  • ScalaとMecabで形態素解析を行いコンソールに自動Tagリストを作る
    Scala
    Mecab
    形態素解析

    08
    Feb
    2011
    Scala
    このエントリーをはてなブックマークに追加
    この記事をLivedoorクリップ!
    Evernoteにclipする

    MecabをScalaから呼び出し、あるデーターを形態素解析し、コンソール上にTag Cloudっぽい表示をしたいと思います。自動的に抽出した単語をTagっぽくみせますので、形態素解析した単語の中から「名詞」にターゲットを絞りこんでScalaで抽出します。コンソールへの出力は以前blogで書いた、「ScalaからCライブラリを読み込んで実行する」で利用した、libfont_style.soを使用し、ScalaからMecab APIを呼び出すのにJavaのAPIが提供されている、cmecab-javaを使用したいと思います。

    実装環境

    OS     :Ubuntu(kernel:2.6.32-24-generic)
    JVM    :OpenJDK 1.6.0_20
    Scala  :Scala 2.8.1.final
    charset:UTF-8
    

    解析対象データ

    2011/02/07の共同通信社の報道ニュースを使用

    米ナスダックのシステムにハッカー侵入―取引システムには被害なし


     米株式市場ナスダックを運営するナスダックOMXは5日、 同社のコンピューターシステムにハッカーが侵入していたことを認めた。 ただ、取引システムには被害は生じていないとしている。 ハッカー攻撃を受けたシステムの顧客には通知済みだという。 ハッカー攻撃にあったのは、企業役員向け情報サービス 「ディレクターズ・デスク(Directors Desk)」の システム内で、不正ファイルが見つかったという。

     ナスダックへのハッカー攻撃については、 5日付のウォール・ストリート・ジャーナルが報じており、 過去1年間に複数回、侵入されていたことを明らかにしている。 ナスダックOMXは本紙報道の後、リリースを発表し、 「当該ファイルはすぐに除去されており、現時点では、 ハッカーがディレクターズ・デスクの顧客の情報にアクセスしたり、 それらを取り出したりした形跡はない」と述べた。

     取引システムについては、「ディレクターズ・デスクなど ウェブにつながっているサービスとは独立して運営されており、 ナスダックOMXが運営また提供する取引プラットフォームが 影響を受けたことはまったくない」としている。 取引所運営大手のNYSEユーロネクストとダイレクト・エッジは、 7日もナスダックとの接続を続けるとしている。 一方、一部トレーディング会社も、ナスダックへの発注を続ける方針を示した。

    動作環境構築

    1. sconsのインストール(cmecab-javaのインストールで必要)

    2. cmecab-javaではmake形式ではなく、sconsをつかってC/C++プログラムをコンパイルします。aptパッケージにsconsがあるのでaptからインストールします

      apt-get install scons
      
    3. antのインストール(cmecab-javaのインストールで必要)

    4. 日々Javaで開発を行っている人であれば、antは既にインストールされていると思いますが、一応書いておきます。aptパッケージにantがあるのでaptからインストールします

      apt-get install ant
      
    5. protocol bufferのインストール(cmecab-javaのインストールで必要)

    6. wget http://protobuf.googlecode.com/files/protobuf-2.3.0.tar.bz2
      
      tar -xjf protobuf-2.3.0.tar.bz2
      cd protobuf-2.3.0
      
      ./configure
      make
      make install
      

      usr/local/lib に作られるので、ldconfigを実行しておきます (もしかすると)/etc/ld.so.confに"/usr/local/lib"というパスの追加が必要かもしれないです

    7. mecabのインストール

    8. wget http://sourceforge.net/projects/mecab/files/mecab/0.98/mecab-0.98.tar.gz/download -O mecab.tar.gz
      
      tar -xvf mecab.tar.gz
      cd mecab-0.98
      
      ./configure --with-charset=utf8
      make
      make install
      

      デフォルトだと/usr/local/lib配下にライブラリはインストールされます

    9. cmecab-javaのインストール

    10. wget http://cmecab-java.googlecode.com/files/cmecab-1.7.tar.gz
      
      tar -xzf cmecab-1.7.tar.gz
      
      cd cmecab-1.7/
      ant
      

      cmecab-1.7/bin/cmecab-1.7.jarが生成されます

      cd cmecab-1.7/jni/
      vi SConstruct
      
      #環境に応じてjavaの動作環境パスに適時変更します
      Line:37 
       javahome = '/usr/lib/jvm/java-6-openjdk'
      
      #環境に応じてjni.hファイルなどJavaヘッダーファイルのパスを適時追加します
      Line:39
       join(javahome, 'include', 'linux', '/usr/lib/jvm/java-6-openjdk/include')]
      
      scons libCMeCab.so
      

      cmecab-1.7/jni/libCMeCab.soが生成されます

    11. ipa辞書のインストール

    12. このままですと、単語の認識ができないお馬鹿な解析になってしまいますので、標準的な「ipa辞書」をインストールします

      wget http://sourceforge.net/projects/mecab/files/mecab-ipadic/2.7.0-20070801/mecab-ipadic-2.7.0-20070801.tar.gz/download  -O mecab-ipadic-2.7.0-20070801.tar.gz
      
      tar -xzf mecab-ipadic-2.7.0-20070801.tar.gz
      cd mecab-ipadic-2.7.0-20070801
      
      ./configure --with-charset=utf8
      make
      make install
      

      "--with-charset=utf8"をつけないと、デフォルトcharsetで"EUC-JP"になってしまいます 「ipa辞書」意外にも「wikipedia」、「はてな」から辞書を追加する方法を明記されている方がいらっしゃるので、そちらを参考にしてより語彙を高めるとよいかと思います。

    実行プログラム

    1. Scalaのソース内容(Analyzer.scala)

    2. ソースの可読性を優先するためエラー処理などは省いています。
      (def main()のwhile (node.hasNext) {xxxxx}の中の実装がいけてない感じがします・・・)

      /**
       * Analyzer.scala
       * This class Morphological analysis, occurrence frequency
       * of words does display
       * 形態素解析、単語出現リスト表示クラス
       *
       * @author fukaoi
       * @version 0.1
       */
      package blojsomscala
      
      import net.moraleboost.mecab.{ Node }
      import net.moraleboost.mecab.impl.StandardTagger
      import scala.util.matching.Regex
      import scala.io.Source
      import com.sun.jna._
      
      object Analyzer {
      
       /**
        * Main method
        * メインメソッド
        */
        def main(args: Array[String]) {
          val words = Source.fromFile(args(0)).getLines.mkString
          val tagger = new StandardTagger("UTF-8", "")
          val node = tagger.parse(words)
          val regex = """名詞""".r
      
          var mapWordLists = Map[Int, String]()
          var i = 0
      
          while (node.hasNext) {
            val feature = node.next
            regex findPrefixOf node.feature match {
              case Some(v) = mapWordLists += (i - feature)
              case None =
            }
            i = i + 1
          }
          val sortWordLists = getMostShowWord(mapWordLists)
          display(sortWordLists)
        }
      
       /**
        * Get Occurrence frequency of word in Scala List
        * 単語の出現頻度をリストにして返す
        */
        def getMostShowWord(wordList: Map[Int, String]): Seq[(String, Int)] = {
          wordList.groupBy(x = x._2).
            map(y = (y._1, y._2.size)).
            toSeq.sortWith(_._2  _._2)
        }
      
       /**
        * Setting in JNA for C Library
        * CライブラリをJava JNAにセットする
        */
        def setNativeLibrary(colorName: String) {
          val lib = NativeLibrary.getInstance("libfont_style.so")
          val func = lib.getFunction("font_color")
          func.invokeVoid(Array(colorName))
        }
      
       /**
        * Analytics data to display on console
        * コンソール結果に集計した結果を出力する
        */
        def display(list: Seq[(String, Int)]) {
          val charCode = "Shift-JIS"
          var maxLength = 0
          list.map(x = if (x._1.getBytes(charCode).length  maxLength) maxLength = x._1.getBytes(charCode).length)
          list.map(x = (setNativeLibrary(getColorNumber(x._2)),
            print(x._1),
            print(" " * (maxLength - x._1.getBytes(charCode).length)),
            print("+" * x._2),
            print("\n")))
        }
      
       /**
        * Get color code for C Library(setNativeLibrary)
        * Cライブラリ(setNativeLibrary)に渡す
        * カラーコードを取得する
        */
        def getColorNumber(count: Int): String = {
          val colors = Map(
            (1 - "blue"),
            (2 - "cyan"),
            (3 - "green"),
            (4 - "magenta"),
            (5 - "red"))
          if (count = 6)
            "yellow"
          else
            colors(count)
        }
      }
      

      自動Tagを作成する上で対象となる名詞だけを抽出するので、mecabの解析データから正規表現でマッチさせて抜き出します

      val regex = """名詞""".r
      

      Tagの文字長がばらばらなため、コンソールに出力する際の見た目の体裁を整えるために、一番長いTagに合わせて出力位置を決定します。そのために冗長ながら、Mapの中から最大文字長を調べています。charCode"Shift-JIS"と定義したのは、UTF-8ですと1文字が1~6バイトの可変長になり文字長を求めるのが難解だったため、固定長のChar Codeにしたかったからです

      val charCode = "Shift-JIS"
      
      if (x._1.getBytes(charCode).length  maxLength) maxLength = x._1.getBytes(charCode).length
      

      Analyzer.scalaのソースコードをダウンロードする(GitHub)

    3. Scalaプログラムのコンパイル

    4. cd blog/2011/
      scalac -classpath .:jna.jar:cmecab-1.7.jar blojsomscala/Analyzer.scala
      
    5. コマンド実行

    6. scala -Djava.library.path=

    7. 実行結果

    8. "+"の数が解析対象データの中でのTag(名詞単語)の出現回数になり、回数が多いTagから上位に並んでいます

      mecab

    結果考察

    抽出したTagを見てみると、上位3位の「ナスダック」、「システム」、「ハッカー」はTagとしては十分意味が通じる単語 ですが、それより下位の「日」、「こと」、「5」、「の」はTagとしては不十分な結果です。やはり辞書の追加であったり、N-gram による単語の解析が必要だと思います。今後の課題結果となりました。


    関連リンク
    MeCabの辞書にはてなキーワードを追加しよう - 不可視点
    コメントを書く
      • 投稿する
      • 確認する
    Powered by CoffeeBox、CoffeeScript、Node.js、MongoDB  Copyright (C) 深追い Fukaoi.org - Since 2007