自然言語処理1 形態素解析


Aidemy 2020/10/29

はじめに

 こんにちは、んがょぺです!バリバリの文系ですが、AIの可能性に興味を持ったのがきっかけで、AI特化型スクール「Aidemy」に通い、勉強しています。ここで得られた知識を皆さんと共有したいと思い、Qiitaでまとめています。以前のまとめ記事も多くの方に読んでいただけてとても嬉しいです。ありがとうございます!
 今回は、自然言語処理の一つ目の投稿になります。どうぞよろしくお願いします。

*本記事は「Aidemy」での学習内容を「自分の言葉で」まとめたものになります。表現の間違いや勘違いを含む可能性があります。ご了承ください。

今回学ぶこと
・自然言語処理とは
・テキストコーパスについて
・形態素解析について

自然言語処理について

「自然言語」とは、人間が普段使う、話し言葉/書き言葉のことである。これをコンピュータに処理させることを「自然言語処理」という。
・人間が使う自然言語には曖昧な表現が含まれることがあるが、コンピュータはこれを「解釈」することはできない
・コンピュータが自然言語を処理するには、自然言語を数値に変換する必要がある。
・自然言語処理は、機械翻訳や音声認識、情報検索などに利用される。

コーパス

コーパスとは、自然言語の文書をまとめたデータである。多くの言語のサポートしており、日本語版も存在する。
・今回は「雑談対話コーパス」というものを使用する。
・データは100セットの雑談データ「init100」ディレクトリと1046セットの雑談データ「rest1046」ディレクトリに分かれている。今回は「init100」
の方を使う。
・ファイル構造については「JSON形式」で提供されている。「質問データ(人の発話)」と「回答データ(システムの発話)」に分けられる。
・これらのデータはファイル内の「turns」キーに格納されている。このうち「utterance」が発話データ、「speaker」「U」なら人、「S」ならシステムの発話になる。
・また、システムの発話データには「breakdown」というフラグが設定されている。これは、システムの発話が自然かを判定するものになっている。「O」は自然、「T」は不自然、「X」は極めて不自然(破綻)を表す。このフラグは一つの回答に対して複数付与される。
・コーパスの内部

コーパスの読み込み/データの抽出

・コーパスの読み込みは普通のファイルの読み込みと同様、「open()」で開く。読み込みについては、ファイルがJSON型式なので、「json.load()」で読み込む。
・読み込んだファイルに対し、取得したいデータのキーを指定することで、データを抽出することができる。

・会話IDを取得

#話者と発話内容を抽出し、表示
for turn in json_data['turns']:
    print("{}:{}".format(turn['speaker'],turn['utterance']))

分析データの抽出

・ここからは「自然な会話」を分析していく。すなわちbreakdownを使用していくので、まずは「人の発話内容」と「システムの発話のフラグ」を取得する
・このとき、データを取得すると重複したデータが発生してしまうため、drop_duplicates()を使用して重複したデータを削除する。このとき渡せるのはDataframeのデータであるため、取得したものはdfに変換する必要がある。

・コード

・上記コードでは、まず前項と同じ方法で発話データが格納されている「turns」から「発話ターンNo」「話者ID」「発話内容」を取得し、さらに「発話内容(utterance)」から、「人の発話内容」と「システムの発話のフラグ」を取得し、それをlabel_listというリストに入れている。最後にそれをDataFrameに変形し、重複するデータを削除している。

形態素解析

形態素解析とは

形態素解析とは、自然言語処理の手法の一つで、文章を単語(形態素)で分割し、品詞を分類する手法である。
・例えば「こんにちは、んがょぺです!」なら「こんにちは / 、 / 私 / は / んがょぺ / です / !」となる。
・形態素解析の実行ツールには、MeCabJanomeといったものがある。

MeCab

・形態素解析をMeCabで行う。使用方法は以下の通り。
k = MeCab.Tagger('出力モードの指定')としたkに対し、
k.parse('形態素解析を行う文字列')として実行する。具体的には、以下の通り。

・また、Tagger()に設定するモードについて、何も指定しなければ上記のように出力されるが、「'-Owakati'」とすると、単語(形態素)ごとに空白で区切るだけの「分かち書き」で出力される。
・他にも、読みだけが出力される「'-Oyomi'」などのモードも存在する。

Janome

・Janomeで形態素解析を行う時は、t = Tokenizer()でオブジェクトを作ってから、t.tokenize('形態素解析を行う文字列')で実行できる。
・また分かち書きの時は、この第二引数に「wakati=True」とすれば良い。

・その他の機能として、品詞でフィルターをかけることができる。
 ・特定の品詞のみ取得したい時はPOSKeepFilter(['品詞'])
 ・特定の品詞を除外したい時はPOSStopFilter(['品詞'])

Analyzer()を使えば、ここまでの処理と、形態素解析を行う文章の前処理を同時に行うことができる。
・渡す引数は、(前処理,Tokenizerオブジェクト(t),フィルター)である。
・前処理の部分は、Unicode文字列の表記揺れを正規化するUnicodeNormalizeCharFilter()などがある。ちなみにこれは全角のアルファベットやカタカナを半角に統一するなどの正規化を行う。
・また、ときに前処理を行わない場合であっても第一引数は省略できないので、そのような時は「[]」とだけ記述する。
・残り二つの引数には、先述したオブジェクトとフィルターを設定すれば良い。

・Analyzer()の実行は以下のように行う。

テキストの正規化

・形態素解析は、使用する辞書に依存するため、辞書に載っていない単語が出てくると解析が不自然になる恐れがある。
・このようなときの対策法は二種類ある。一つ目がユーザー辞書を用意する方法である。(ただしここでは説明しない)
・もう一つの方法が「テキストの正規化」である。これは、前処理として、テキストの不要な記号を削除したり、表記を統一するというものである。

・例えば、文章に「,」と「、」が混在しているときにどちらかに統一したり、「りんご」と「林檎」の表記もどちらかに統一したり、といった具合である。
・この正規化を行う文字列の指定は、「正規表現」を使って行う。
・具体的には、re.sub("取り除く文字列","変換後の文字列","取り除くテキスト")で行い、ここで指定する部分を正規表現で記述する。
・正規表現についてはここでは詳しく扱わない。(Qiitaにいろいろな記事があるので、そちらを参照)

・コード(「私は商品Aを10個買います。」のうち、英数字を除外する)

まとめ

・自然言語処理では、コンピュータに自然言語を数値として処理させることで行うことができる。
・コーパスは、自然言語の文書をまとめたデータである。
形態素解析は、自然言語処理の手法の一つで、文章を単語(形態素)で分割し、品詞を分類する手法である。
・形態素解析は「MeCab」や「Janome」で行うことができる。
・形態素解析は使う辞書に依存するので、辞書が判断できるよう、正規表現などを使って前処理することが重要である。

今回は以上です。最後まで読んでいただき、ありがとうございました。