フツーって言うなぁ!

フツーなサラリーマンのフツーな嘆き.

言語処理100本ノックを(第5章まで)やってみた

久しぶりに技術関係のネタ書きます.

「言語処理100本ノック」という,自然言語処理関係の問題集があることを知ったので取り組んでみました.
これは,東北大学乾・岡崎研究室でのプログラミング勉強会にて使われている教材だそうです.
「100本ノック」の言葉通り,100問の問題からなる問題集をこなすことで,自然言語処理に関する基礎力と,プログラミング言語運用能力が同時に培えるようになっています. こういうものが公開されるとは,「いい時代になったなー」と純粋に思います.

www.cl.ecei.tohoku.ac.jp

内容は,自然言語処理だけでなく,データベース,機械学習など,今の言語処理関係の研究に必要なスキルがこれ1つで身につくように設計されています.
対象プログラミング言語Pythonのようですが,基本的に他の言語でも問題なく進められるようにはなっていると思います(言語処理に強いプログラミング言語でないと難易度跳ね上がるとは思いますが).

一応,半分(問題49)まで終わらせたので,軽く感想を述べておきたいと思います.*1

Pythonによる実装を

github.com

↑に載せています.

第1章: 準備運動

プログラミング言語を用いた文字列操作の経験があれば,この辺はラクラク進められるのではないかと.

個人的に気になるのは,問題04で,元素記号のディクショナリを作るのに,

{'Be': 4, 'C': 6, 'B': 5, 'Ca': 20, 'F': 9, 'S': 16, 'H': 1, 'K': 19, 'Al': 13, 'Mg': 12, 'Ne': 10, 'O': 8, 'Li': 3, 'P': 15, 'Si': 14, 'Ar': 18, 'Na': 11, 'N': 7, 'Cl': 17, 'He': 2}

ではなく,

{'Be': 4, 'C': 6, 'B': 5, 'Ca': 20, 'F': 9, 'S': 16, 'H': 1, 'K': 19, 'Al': 13, 'Mi': 12, 'Ne': 10, 'O': 8, 'Li': 3, 'P': 15, 'Si': 14, 'Ar': 18, 'Na': 11, 'N': 7, 'Cl': 17, 'He': 2}

になってるところ(12番目が"Mi"になってる).

あと,nltk.tokenizeは英文をいい感じにTokenizeしてくれるめちゃくちゃ便利なライブラリなのでみなさん使いましょう…*2

第2章: UNIXコマンドの基礎

言語処理というよりはUnixコマンドを用いたTSVファイルの扱いの練習.
「わざわざスクリプト書かなくてもここまでできるんだー」というのが個人的な学びだった.

sortとuniq -cのコンボとか,cut辺りはググりながら進めたので要復習ですかね.*3

第3章: 正規表現

ここからが本番.
難易度もかなり高くなる.

まず,入力である,イギリスについてのWikipedia記事のソースコードがそこそこ汚いため,MediaWikiの文法通り書いても正規表現にマッチしたりしなかったりするのが苦痛だった.
問題27,28辺りは,期待される出力を出すのが精一杯で,他のWikipedia記事にこのプログラムを適用してもうまくいかないような気がする.

正規表現を使う際は,

RegExr: Learn, Build, & Test RegEx

のように,視覚的にマッチされる文字列が見えるサービスを使って出力を確認しながらやるのが精神的にいいと思う.*4

正規表現のグループ化と最短マッチを初めて使った.
なかなか使いこなせていない部分もあったが,勉強になった.
でも二度とやりたくない.

第4章: 形態素解析

第3章で削られたSAN値を回復するのにちょうどいい箸休めだった.
MeCabはこれまで何度か使った経験があったし,問題も比較的簡単(もちろん頻出な操作だが)だったように思う.

問題37,38,39では,matplotlibを使用した.
Pythonを使ったデータ分析でよく用いられる可視化用のライブラリで,今回初めて利用したが,あまりデザインにこだわらなければすぐにグラフを出力できるような気がした.
これからもデータ分析をやっていきたいと考えているので,ぜひとも習得したい.

あと,今回の実装では,cPickleモジュールを用いて形態素オブジェクトの永続化を行っている.
これは,Python専用のデータ構造で,Pythonオブジェクトであれば簡単に内容の保存,再利用が可能になる.
自分はよくキャッシュの用途でcPickleを用いることが多いですね.

第5章: 係り受け解析

CaboChaを用いて係り受け解析を行った結果をさらに分析していく.
最初,ダウンロードしてきたテキストを

$ curl http://www.cl.ecei.tohoku.ac.jp/nlp100/data/neko.txt | cabocha > neko.txt.cabocha

てな感じでCaboChaにかけたが,

EOS
EOS
         ---D
      吾輩は-D
    猫である。
EOS
名前は---D
    まだ-D
    無い。
EOS
EOS
 どこで-D
  生れたか-----D
      とんと---D
        見当が-D
        つかぬ。
EOS
        何でも-D
          薄暗い---D
      じめじめした-D
                所で---D
          ニャーニャー-D
                  泣いて---D
              いた事だけは-D
              記憶している。

のように,構文木だけの表示になってしまい(簡易Tree表示というらしい),「ここからどうやって構文解析したらいいんだ」ってなった.

正しくは

$ curl http://www.cl.ecei.tohoku.ac.jp/nlp100/data/neko.txt | cabocha -f1 > neko.txt.cabocha

こう.

これで,

* 0 -1D 0/0 0.000000
一      名詞,数,*,*,*,*,一,イチ,イチ
EOS
EOS
* 0 2D 0/0 -0.764522
       記号,空白,*,*,*,*, , ,
* 1 2D 0/1 -0.764522
吾輩    名詞,代名詞,一般,*,*,*,吾輩,ワガハイ,ワガハイ
は      助詞,係助詞,*,*,*,*,は,ハ,ワ
* 2 -1D 0/2 0.000000
猫      名詞,一般,*,*,*,*,猫,ネコ,ネコ
で      助動詞,*,*,*,特殊・ダ,連用形,だ,デ,デ
ある    助動詞,*,*,*,五段・ラ行アル,基本形,ある,アル,アル
。      記号,句点,*,*,*,*,。,。,。
EOS
* 0 2D 0/1 -1.911675
名前    名詞,一般,*,*,*,*,名前,ナマエ,ナマエ
は      助詞,係助詞,*,*,*,*,は,ハ,ワ
* 1 2D 0/0 -1.911675
まだ    副詞,助詞類接続,*,*,*,*,まだ,マダ,マダ
* 2 -1D 0/0 0.000000
無い    形容詞,自立,*,*,形容詞・アウオ段,基本形,無い,ナイ,ナイ
。      記号,句点,*,*,*,*,。,。,。
EOS
EOS

上記のような,「lattice形式」と呼ばれる,計算機で扱いやすい形で解析結果が表示されるようだ.

その後は,わりとサクサク進められたほうだと思うが,問題49だけが,問題の意味を読み解くのに時間を要した.

問題49では,コーパスの各文について,

  1. 名詞句(名詞の連続)を含む文節を2つ探す(これらの名詞句が含まれる文節の番号をそれぞれi,jとする.i < j).

  2. 文節iが含む名詞句を"X",文節jが含む名詞句を"Y"に,それぞれ置き換える.

  3. 文節iから述語(構文木の根)までのパス,文節jから述語までのパスを求め,

    • 文節iからのパスが文節jからのパスを完全に包含していれば,文節iから文節jのパスを表示
    • そうでない場合は,文節iのみに含まれるパスの要素,文節jのみに含まれるパスの要素,両方のパスが合流してから述語までのパスのそれぞれを,"|"で連結して表示.

という操作を行うのだと思う.

でも,これだと,問題49の例の,

Xは | Yで -> 始めて -> 人間という -> ものを | 見た
Xは | Yという -> ものを | 見た
Xは | Yを | 見た
Xで -> 始めて -> Y
Xで -> 始めて -> 人間という -> Y
Xという -> Y

の最後の3パターンに合わない(Yの後に助詞がつくべき)ので,もし間違ってたら指摘欲しいです…


もちろん,教科書を読むことも重要だと思いますが,こういう風に実装してみることで,より理解が深まる部分もあると思います.
また,実際に研究を進める際にも,プログラミング言語の運用能力はあるに越したことはないと思います.
教材として,重々しい課題というよりは,面白さを重視した設計になっているとは感じたので,一度取り組んでみてはいかがでしょうか?

*1:本当は4月から解き始めていたのですが,「さすがに10問解いたぐらいで記事書いてそのまま放置,だとカッコ悪いな」ということで,50問解くまでは記事を書かないようにしてました.3ヶ月ちまちまやり続けてやっとここまで来れたので,個人的には一段落かなと

*2:NLTKのインストールが必要

*3:この前grepが使えなくて上司に笑われたのを思い出す

*4:正規表現は言語ごとに少しづつ文法違うのが厄介だが…