今回は文章などを形態素(単語区切り)にして解析を行えるMeCabを使って、入力された文章がどのカテゴリーに属するのかを分類し出力するような実装を行っていきたいと思います。
この記事ではMeCabの導入については触れないので、インストールから簡単な実装についてはこちら「【Python】形態素解析器のMeCabを使って自然言語処理の実装」をご参照ください。
実装のイメージとしては、予め「カテゴリー1」と「カテゴリー2」の情報を格納した「辞書」を用意し、人が入力した文章(テキスト)を単語に区切って1つ1つを各カテゴリーでチェックを行い、その文章の内容が最も多く一致したカテゴリーであれば「文章の内容はカテゴリー〇です」と出力されます。
また、分類をする上でMeCabの基本的な操作を知る必要があるので、前半は扱い方を簡単に触れて後半に分類を実装していきたいと思います。
※自然言語処理の実装でありながら、専門用語などの意味には殆ど触れないのでご了承願います。
実行環境 |
---|
Windows Subsystem for Linux |
Python 3.6.8 |
pip 9.0.1 |
使用ライブラリ | ライセンス |
---|---|
mecab-python3==0.996.2 | BSD |
MeCabを使って形態素解析を行うには、2通りの実装方法があります。
公式的には以下のようにオブジェクトを生成してその後の処理を行いますが、入力値である文章を与えるメソッドによって出力される挙動が変わります。
import MeCab
text = '入力値'
""" オブジェクトの生成 """
mecab = MeCab.Tagger()
parses = mecab.メソッド(text)
メソッドには「parse()」と「parseToNode()」が用意されているのでそれぞれ簡単に見ていきましょう。
まずは「parse()」メソッドの実装です。
適当なスクリプトファイルにて以下のようにすると、解析された結果が文字列として出力されます。
# test.py
import MeCab
text = '形態素解析をする'
mecab = MeCab.Tagger()
parses = mecab.parse(text)
print(type(parses))
print(parses)
# 実行
$ python3 test.py
<class 'str'>
形態素 名詞,一般,*,*,*,*,形態素,ケイタイソ,ケイタイソ
解析 名詞,サ変接続,*,*,*,*,解析,カイセキ,カイセキ
を 助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
する 動詞,自立,*,*,サ変・スル,基本形,する,スル,スル
EOS
例えば「名詞」の単語だけを出力したい場合には、Python組み込み関数の「split()」を使用します。
# test.py
import MeCab
text = '形態素解析をする'
mecab = MeCab.Tagger()
parses = mecab.parse(text)
parses_1 = parses.split('\n')
for parse in parses_1:
parse_1 = parse.split('\t')
for par in parse_1:
par_1 = par.split(',')[0]
if par_1 == '名詞':
print(parse.split()[0])
else:
continue
少し複雑になりましたが、「split('\n')」により文字列内の改行部分で分けてリスト形式にし、さらに「for文」内で順番に渡されたリスト内の文字列を「split('\t')」により空白部分で分け、さらに「split(',')」のカンマ区切り0番目の要素に対して文字列内の「名詞」が現れたら比較演算子によって取り出されます。
# 実行
$ python3 test.py
形態素
解析
split()後がそれぞれどのように出力されているかスライスを使わずに見てみましょう。
# test.py
import MeCab
text = '形態素解析をする'
mecab = MeCab.Tagger()
parses = mecab.parse(text)
parses_1 = parses.split('\n')
print('-----改行で分ける-----')
print(parses_1)
for parse in parses_1:
parse_1 = parse.split('\t')
print('-----空白で分ける-----')
print(parse)
for par in parse_1:
par_1 = par.split(',')
print('-----カンマで分ける-----')
print(par_1)
# 実行
$ python3 test.py
-----改行で分ける-----
['形態素\t名詞,一般,*,*,*,*,形態素,ケイタイソ,ケイタイソ', '解析\t名詞,サ変接続,*,*,*,*,解 析,カイセキ,カイセキ', 'を\t助詞,格助詞,一般,*,*,*,を,ヲ,ヲ', 'する\t動詞,自立,*,*,サ変・スル,基本形,する,スル,スル', 'EOS', '']
-----空白で分ける-----
形態素 名詞,一般,*,*,*,*,形態素,ケイタイソ,ケイタイソ
-----カンマで分ける-----
['形態素']
-----カンマで分ける-----
['名詞', '一般', '*', '*', '*', '*', '形態素', 'ケイタイソ', 'ケイタイソ']
-----空白で分ける-----
解析 名詞,サ変接続,*,*,*,*,解析,カイセキ,カイセキ
-----カンマで分ける-----
['解析']
-----カンマで分ける-----
['名詞', 'サ変接続', '*', '*', '*', '*', '解析', 'カイセキ', 'カイセキ']
-----空白で分ける-----
を 助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
-----カンマで分ける-----
['を']
-----カンマで分ける-----
['助詞', '格助詞', '一般', '*', '*', '*', 'を', 'ヲ', 'ヲ']
-----空白で分ける-----
する 動詞,自立,*,*,サ変・スル,基本形,する,スル,スル
-----カンマで分ける-----
['する']
-----カンマで分ける-----
['動詞', '自立', '*', '*', 'サ変・スル', '基本形', 'する', 'スル', 'スル']
-----空白で分ける-----
EOS
-----カンマで分ける-----
['EOS']
-----空白で分ける-----
-----カンマで分ける-----
['']
parse()メソッドでは、split()やfor文を工夫して使うことで形態素解析の各要素を取り出すことができます。
「parseToNode()」に渡された文章(テキスト)はNodeクラスに格納され、解析結果を取り出すにはクラスメソッドを呼び出す必要があります。
繰り返し処理によって明示的に呼び出す必要があるので、「while文」を使用して、各要素を出力します。
# test.py
import MeCab
text = '形態素解析をする'
mecab = MeCab.Tagger()
nodes = mecab.parseToNode(text)
print(type(nodes))
while nodes:
print(nodes.surface)
print(nodes.feature)
nodes = nodes.next
「parses.next」とすることで、次の処理を実行します。この処理を忘れると無限ループになってしまいます。
# 実行
$ python3 test.py
<class 'MeCab.Node'>
BOS/EOS,*,*,*,*,*,*,*,*
形態素
名詞,一般,*,*,*,*,形態素,ケイタイソ,ケイタイソ
解析
名詞,サ変接続,*,*,*,*,解析,カイセキ,カイセキ
を
助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
する
動詞,自立,*,*,サ変・スル,基本形,する,スル,スル
BOS/EOS,*,*,*,*,*,*,*,*
クラスメソッドの詳細は、公式で掲載されているのでこちら「各形態素の詳細情報を取得する」をご参照ください。
数ある要素の1部を取得してみます。
# test.py
import MeCab
text = '形態素解析をする'
mecab = MeCab.Tagger()
nodes = mecab.parseToNode(text)
while nodes:
print('-----形態素の文字列情報')
print(nodes.surface)
print('-----CSVで表記された素性情報')
print(nodes.feature)
print('-----形態素の長さ')
print(nodes.length)
print('-----形態素に付与されたユニークID')
print(nodes.id)
print('-----文字種情報')
print(nodes.char_type)
print('-----形態素の種類')
print(nodes.stat)
print('-----周辺確率')
print(nodes.prob)
print('-----単語生起コスト')
print(nodes.wcost)
print('-----累計コスト')
print(nodes.cost)
nodes = nodes.next
※以下では「形態素」に関する要素の情報のみ
# 実行
$ python3 test.py
......
......
-----形態素の文字列情報
形態素
-----CSVで表記された素性情報
名詞,一般,*,*,*,*,形態素,ケイタイソ,ケイタイソ
-----形態素の長さ
9
-----形態素に付与されたユニークID
7
-----文字種情報
2
-----形態素の種類
0
-----周辺確率
0.0
-----単語生起コスト
5621
-----累計コスト
5338
.......
.......
では「parseToNode()」メソッドを使用して「名詞」の単語だけを取得するプログラムを実装します。
# test.py
import MeCab
text = '形態素解析をする'
mecab = MeCab.Tagger()
nodes = mecab.parseToNode(text)
while nodes:
if nodes.feature.split(',')[0] == '名詞':
print(nodes.surface)
nodes = nodes.next
# 実行
$ python3 test.py
形態素
解析
クラスメソッドを呼び出さなければいけないですが、非常に簡単な短いコードで実装することができました。
「parse()」と「parseToNode()」のどちらを使うかはプログラムの性質や好みによって分かれると思うのでお好きな方で実装しましょう。
それではMeCabの実装に慣れたところでカテゴリー分類をしていきたいと思います。
どんなカテゴリーに分けるかは自由ですが、ここでは「自然言語処理」と「音声認識」による分類をしていきます。
入力された文章の内容がどのカテゴリーに属するかを判別するプログラムです。
流れとしては
となります。
今回はWikipediaの1部を使わせていただいて「自然言語処理」と「音声認識」の辞書を作成します。
それぞれの記事から、必要であろう文章を取得します。
'''
自然言語処理(しぜんげんごしょり、英語: natural language processing、略称:NLP)は、人間が日常的に使っている自然言語をコンピュータに処理させる一連の技術であり、人工知能と言語学の一分野である。「計算言語学」(computational linguistics)との類似もあるが、自然言語処理は工学的な視点からの言語処理をさすのに対して、計算言語学は言語学的視点を重視する手法をさす事が多い。データベース内の情報を自然言語に変換したり、自然言語の文章をより形式的な(コンピュータが理解しやすい)表現に変換するといった処理が含まれる。応用例としては予測変換、IMEなどの文字変換が挙げられる。
'''
'''
音声認識とは、人間の声などをコンピューターに認識させることである。話し言葉を文字列に変換したり音声の特徴をとらえて声を出している人を識別したりする機能を指している。話し言葉を文字列に変換する機能は、指を用いてキーボードから入力する方法に代わるものである。 文字列(文章)を入力する機能だけを呼び分ける場合は「音声入力」あるいは「ディクテーション(聞き取り)」と言う。(ちょうどキーボードから文字列やショートカットを入力してアプリケーションを操作できるように)音声認識でアプリケーションを操作することも可能である。音声でアプリケーションを操作することは「音声操作」と言う。「音声認識」に話者が誰なのか識別する機能を含めることもある。これは、あらかじめ記録しておいた音声パターンと比較して個人認証等をおこなう機能であり、この機能を特に呼び分ける場合は「話者認識」とも言う。
'''
上記の文章を変数に格納します。
※必要であればテキストファイルに保存しopen()メソッドで処理しましょう。
自然言語処理用のテキスト
nlp_text = '自然言語処理(しぜんげんごしょり、英語: natural language processing、略称:NLP)は、人間が日常的に使っている自然言語をコンピュータに処理させる一連の技術であり、人工知能と言語学の一分野である。「計算言語学」(computational linguistics)との類似もあるが、自然言語処理は工学的な視点からの言語処理をさすのに対して、計算言語学は言語学的視点を重視する手法をさす事が多い。データベース内の情報を自然言語に変換したり、自然言語の文章をより形式的な(コンピュータが理解しやすい)表現に変換するといった処理が含まれる。応用例としては予測変換、IMEなどの文字変換が挙げられる。'
音声認識用のテキスト
voice_text = '音声認識とは、人間の声などをコンピューターに認識させることである。話し言葉を文字列に変換したり音声の特徴をとらえて声を出している人を識別したりする機能を指している。話し言葉を文字列に変換する機能は、指を用いてキーボードから入力する方法に代わるものである。 文字列(文章)を入力する機能だけを呼び分ける場合は「音声入力」あるいは「ディクテーション(聞き取り)」と言う。(ちょうどキーボードから文字列やショートカットを入力してアプリケーションを操作できるように)音声認識でアプリケーションを操作することも可能である。音声でアプリケーションを操作することは「音声操作」と言う。「音声認識」に話者が誰なのか識別する機能を含めることもある。これは、あらかじめ記録しておいた音声パターンと比較して個人認証等をおこなう機能であり、この機能を特に呼び分ける場合は「話者認識」とも言う。'
それぞれ変数に格納された文章を辞書として扱えるように、MeCabを使用して変換器となる関数を定義します。
※parse()メソッドを使用して文章を解析しています。
# test.py
import MeCab
def mecab_filter(text):
dict_list = set()
mecab = MeCab.Tagger()
parses = mecab.parse(text)
parses_1 = parses.split('\n')
for parse in parses_1:
parse_1 = parse.split('\t')
for par in parse_1:
par_1 = par.split(',')[0]
if par_1 == '名詞':
dict_list.add(parse.split()[0])
else:
continue
return dict_list
parse()メソッドの実装で行った時の処理と殆ど変わらず、唯一追加されたコードはPython組み込み関数の「set()」メソッドです。
空set()をdict_list変数に格納し「dict_list.add」で与えられた順に要素を格納していますが、重複する要素があった場合は新しい要素を優先的に格納し、中身は一意の要素となります。
ではこの変換器へ先ほど変数に格納した文章を渡して辞書に変換させましょう。
# test.py
import MeCab
.......
.......
nlp_dict = mecab_filter(nlp_text)
voice_dict = mecab_filter(voice_text)
print('自然言語処理用の辞書-----')
print(nlp_dict)
print('音声認識用の辞書-----')
print(voice_dict)
# 実行
$ python3 test.py
自然言語処理用の辞書-----
{'変換', '視点', '技術', '工学', '表現', '計算', '略称', 'linguistics', '内', '例', '予測', '言語', '文字', 'げん', 'processing', '処理', '一', '情報', '英語', '自然', 'IME', ':', 'natural', 'ぜん', 'コンピュータ', '人間', '人工', '応用', 'computational', 'データベース', '文章', '知能', '学', '類似', '事', '理解', 'language', '手法', '的', 'NLP', '一連', '分野', '形式', '日常', '重視', 'の'}
音声認識用の辞書-----
{'話し言葉', '変換', 'こと', '指', 'アプリケーション', 'の', '比較', '声', '可能', 'もの', 'よう', '個人', '場合', '機能', 'これ', '識別', '音声', '等', 'パターン', 'ショートカット', '方法', '人間', 'コンピューター', '人', '話者', '認識', '特徴', '認証', '文章', '誰', 'キ ーボード', '記録', '列', 'ディクテーション', '操作', '入力', '文字'}
今回は文章から「名詞」だけを取り除いて分類に必要な辞書を作成しましたが、カテゴリーのキーとして使えそうな単語を追加したり削除することによって、高い性能で分類できるかと思われます。
最後に分類を実装していきたいと思います。
先ほどから使用しているスクリプトファイルの下方に、分類用のMeCabメソッドを定義していきます。
# test.py
import MeCab
......
......
nlp_dict = mecab_filter(nlp_text)
voice_dict = mecab_filter(voice_text)
text = '音声認識(おんせいにんしき、英: speech recognition)とは、人間の声などをコンピューターに認識させることであり、話し言葉を文字列に変換したり、あるいは音声の特徴をとらえて声を出している人を識別する機能を指す'
mecab = MeCab.Tagger()
parses = mecab.parse(text)
parses_1 = parses.split('\n')
""" 与えられた文章の単語が各辞書と一致されたらカウントする変数 """
nlp_num = 0
voice_num = 0
for parse in parses_1:
parse_1 = parse.split('\t')[0]
if parse_1 in nlp_dict:
nlp_num += 1
parse_1 = parse.split('\t')[0]
if parse_1 in voice_dict:
voice_num += 1
print('自然言語処理:{0} 音声認識:{1}'.format(nlp_num, voice_num))
if nlp_num > voice_num:
print('自然言語処理についての内容です')
else:
print('音声認識についての内容です')
与えた文章の内容はWikipediaの1部「Wikipedia:音声認識」を使わせていただきました。
分類の指標として「nlp_num」と「voice_num」のどちらかに多く辞書と一致した数によって、文章の内容が明らかになるということになります。
# 実行
$ python3 test.py
自然言語処理:6 音声認識:19
音声認識についての内容です
もちろん機能としては単純ですが逆に拡張性もあります。
開発に正解は無いということで、実用的なアプリを目指してどんどんカスタムしていきましょう。
それでは以上となります。
最後までご覧いただきありがとうございました。