DebianユーザーがNLTKで自然言語処理の初歩の初歩を試してみた
この投稿は自然言語処理の中で一番初歩的なテキスト分析(字句解析)についての事始め。
ダウンロードとインストール
Python 3のdebパッケージとPython3用のNLTK(Natural Language Toolkit)のdebパッケージを公式のリポジトリからダウンロードしてインストールした。
$ su # apt install python3 python3-nltk
python3-nltkというdebパッケージは、Debianのバージョンのbusterにもbullseyeにもbookwormにも存在する。
NLTKは、そのホームページの説明等によれば、自然言語処理に必要な道具類をまとめたプラットフォームなのだそうで、しかもオープンソースの企画らしい。
ダウンロードとインストールがうまくいったのでPython3を早速起動し、NLTKのホームページに掲載されている簡単なコードを試してみた。
まずはPython3をターミナル・エミュレーター上で起動。
$ python3 Python 3.7.3 (省略)
次にNLTKをインポート。
>>> import nltk
適当に選んだ文字列をsentenceに代入した。sentenceは任意の識別子。
>>> sentence = """A similar argument applies ... to any other a priori judgement.""" >>> print(sentence) A similar argument applies to any other a priori judgement.
Python 3ではヒアドキュメントを3つの二重引用符号(""")で囲む。こうした場合、/nなどによって改行を明示せずとも複数行に渡って文字列を入力できる。ヒアドキュメントとはHTMLのpre要素のようなもの。
文字列として選んだのはバートランド・ラッセル著の『哲学の問題』からの一節。
- Russell, Bertrand. The Problems of Philosophy. p.140
epub形式のファイルはLinuxではCalibreなどで読むことができる。
そしてNLTKのword_tokenize()に引数としてその文字列を渡してトークンを抽出。
>>> tokens = nltk.word_tokenize(sentence) Traceback (most recent call last): (中略) LookupError: ********************************************************************** Resource punkt not found. Please use the NLTK Downloader to obtain the resource: >>> import nltk >>> nltk.download('punkt') Attempted to load tokenizers/punkt/PY3/english.pickle (以下省略)
ここでいきなりエラーが生じた。リソースのpunktが見つからないと言われた。エラーのメッセージにあるとおり、NLTKのdownload()によってpunktをダウンロードしたみた。
>>> nltk.download('punkt') [nltk_data] Downloading package punkt to(中略) [nltk_data] Unzipping tokenizers/punkt.zip. True
こうしたリソースはNLTK Dataと呼ばれていてnltk.download()
によってダウンロードすることができる。
もう一度やり直し。キーボードの上矢印キーを押すと前に入力したコードを順番に出力してくれるので、その機能を利用すると便利。
>>> tokens = nltk.word_tokenize(sentence) >>> print(tokens) ['A', 'similar', 'argument', 'applies', 'to', 'any', 'other', 'a', 'priori', 'judgement', '.']
うまくいった。個々の単語や符号に分割してくれているのが見て取れる。この処理はトークン化と呼ばれているらしい。英語ではtokenization。
英文の場合はそのほとんどが分ち書きされるので、このメソッドの内部コードは簡単そうだ。日本語や中国語やタイ語だと複雑になりそう。
トークン化で生成したtokensをNLTKのpos_tag()に渡して単語に品詞タグを付ける処理を行うコードが次。ただしこの処理にはaveraged_perceptron_taggerというリソースが必要になるので先ほどと同じようにダウンロードした。
>>> nltk.download('averaged_perceptron_tagger') [nltk_data] Downloading package averaged_perceptron_tagger to(中略) [nltk_data] Unzipping taggers/averaged_perceptron_tagger.zip. True >>> tagged = nltk.pos_tag(tokens) >>> print(tagged) [('A', 'DT'), ('similar', 'JJ'), ('argument', 'NN'), ('applies', 'NNS'), ('to', 'TO'), ('any', 'DT'), ('other', 'JJ'), ('a', 'DT'), ('priori', 'JJ'), ('judgement', 'NN'), ('.', '.')]
この処理は品詞タグ付けと呼ばれているらしい。英語ではpart-of-speech tagging、略してPOS tagging。
DTは決定詞、JJは形容詞、NNは名詞の単数形、NNSは名詞の複数形、TOはtoを直接指す。
タグがどの品詞を意味しているかについて知りたい場合は「part-of-speech tagging tagset NLTK」などの文字列でインターネット上を検索してみれば見つかる。英文だけど。
よく見ると、appliesを名詞の複数形(NNS)と判断している。これは誤りだと思う。appliesは動詞applyの三人称単数現在(VBZ)が正しいはず。
次に行う処理はチャンキングと呼ばれていること。英語ではchunking。「分割すること」というような意味。この処理にはmaxent_ne_chunker とwordsというリソースが必要になるので予めダウンロードする。
>>> nltk.download('maxent_ne_chunker') [nltk_data] Downloading package maxent_ne_chunker to(中略) [nltk_data] Unzipping chunkers/maxent_ne_chunker.zip. True >>> nltk.download('words') [nltk_data] Downloading package words to (中略) [nltk_data] Unzipping corpora/words.zip. True
品詞タグ付けで生成したtaggedを与えてNLTKのchunk.ne_chunk()を実行する。
>>> entities = nltk.chunk.ne_chunk(tagged) >>> print(entities) (S A/DT similar/JJ argument/NN applies/NNS to/TO any/DT other/JJ a/DT priori/JJ judgement/NN ./.)
チャンキングの処理結果を見ると、ディレクトリ状にまとめられ、その先頭には'S'という文字があるのが見て取れる。前者をセグメント化と呼び、後者をラベル付けと呼ぶらしい。
名詞句のルールを追加
名詞句(NP)を分類するために更に文法ルールを適用する。NPは名詞句を表すタグ名。波括弧内では正規表現を用いて名詞句の構造を定義している。
>>> grammar = "NP: {<DT>?<JJ>*<NN>}
波括弧内の正規表現が意味するのは、DT(決定詞)かJJ(形容詞)で始まり、NN(名詞)で終わるもの。?がorを表し、*が任意の文字列を表している。
これをRegexpParser()に引数として渡してチャンキングのルールを追加する。
>>> cp = nltk.RegexpParser(grammar)
追加したこのルールに基づいて構文解析を行う。
>>> result = cp.parse(entities) >>> print(result) (S (NP A/DT similar/JJ argument/NN) applies/NNS to/TO any/DT other/JJ (NP a/DT priori/JJ judgement/NN) ./.)
決定詞と形容詞と名詞から成る2つの名詞句が分類されているのが見て取れる。
これを構文木として視覚化するにはdraw()を実行する。
>>> result.draw()
別ウィンドウが開いてその中に構文木が表示された。
正規表現は本当ならば"NP: {<DT>?<JJ>?<JJR>?<JJS>*<NN>?<NNS>?<NNP>?<NNPS>}"
としたかったが、appliesを名詞の複数形と認識してしまっているので誤った結果になってしまう。この修正ができるかどうかについてはまたの機会に。
コメント
コメントを投稿