図1

初心者がベイズ推定に挑戦してみた~自然言語の分類器を作ってみた~

概要

機械学習を何も知らない初心者がベイズ推定に挑戦
天気に関する文章、料理に関する文書、どちらなのか分類するスクリプトを作ってみた。

2019年8月14日追記
精度を向上させました →こちら

環境

この環境で実施しました。
※WindowsパソコンにPython環境を用意しただけ
Surface Pro 4 ( Windows 10 )
Python 3.7.1

参考

参考したサイトを紹介します。
たった数行でベイズ推定を試すサンプルあります。

日本語の文章をベイズ推定するための下準備について、こちらを参考にさせていただきました。

ソースを紹介

「ソースがわかればいいよ」という方に向けて、まずはソースを紹介します。以下の2つの文章について天気に関する文章なのか、料理に関する文章なのか予測するスクリプトです。

・雪が降り寒いので、家から出ない。明日は晴れるといいな。 
・豆腐料理を食べたい、麻婆豆腐はありますか?
# test.py
## (1)
from janome.tokenizer import Tokenizer
from janome.analyzer import Analyzer
from janome.charfilter import UnicodeNormalizeCharFilter, RegexReplaceCharFilter
from janome.tokenfilter import POSStopFilter
from collections import Counter

## (2)
def wakati_filter(text: str,
                  char_reg_filter=[("[,\.\(\)\{\}\[\]]"," ")],
                  ignore_filter=['接続詞', '接頭辞', '接尾辞', '記号', '助詞', '助動詞']):
    char_filters = [UnicodeNormalizeCharFilter()]
    for reg in char_reg_filter:
        char_filters.append(RegexReplaceCharFilter(*reg))

    tokenizer = Tokenizer()
    token_filters = [POSStopFilter(ignore_filter)]
    analyzer = Analyzer(char_filters, tokenizer, token_filters)
    return [token.surface for token in analyzer.analyze(text)]

## (3)
cat01=[
    "11日は低気圧が日本海と本州の南を東へ進むでしょう。"
    ,"沖縄は雲が多く、所々で雨が降る見込みです。"
    ,"九州と四国は朝まで雨や雪の所がありますが、日中は次第に晴れるでしょう。"
    ,"中国、四国、近畿も午前を中心に雨や雪が降りやすく、東海と関東甲信は昼過ぎにかけて湿った雪や雨の降る所がある見込みです。"
    ,"北陸と東北、北海道は朝晩を中心に広く雪で、局地的にふぶくでしょう。"
    ,"関東は朝から雪が降ります。雪に注意が必要です。関東地方は雪です。"
]

cat02=[
    "四川式麻婆豆腐用にきのこを加えてボリューム満点☆副菜はレンジでラクチン"
    ,"「ピュアセレクト マヨネーズ」でコクのあるマヨパン粉焼きを♪ごま生姜和えととろろみそ汁を添えて"
    ,"「味の素KKコンソメ」でキャベツのおいしさ引き立つ☆スープは塩分カットでおいしさそのまま"
    ,"メインは旬の白菜のクリーム煮を☆スープは「丸鶏がらスープ」であっさりなのにコクも"
    ,"ガリバタ鶏用で牡蠣料理にアレンジ♪汁物は豆板醤でピリ辛に"
    ,"たった10分で完成☆電子レンジを使えば簡単に唐揚げ風チキンが楽しめる♪副菜に無限長ねぎを"
    ,"鶏肉のから揚げがカンタンにできる"
    ,"レンジだけで、鶏肉のから揚げを作る"
    ,"5分で、鶏肉のから揚げを作る方法"
]

## (4)
X = []
Y = []

## (5-1)
cat01SentenceList = []
for text in cat01:
    curSentenceList = []
    curSentenceList = wakati_filter(text)
    cat01SentenceList += curSentenceList

## (6-1)
cat01WordList = []
cat01WordList = Counter(cat01SentenceList)
cat01ValueList, cat01WordCntList = zip(*cat01WordList.most_common())

## (5-2)
cat02SentenceList = []
for text in cat02:
    curSentenceList = []
    curSentenceList = wakati_filter(text)
    cat02SentenceList += curSentenceList

## (6-2)
cat02WordList = []
cat02WordList = Counter(cat02SentenceList)
cat02ValueList, cat02WordCntList = zip(*cat02WordList.most_common())

## (7)
dictionaryListOrg = cat01SentenceList + cat02SentenceList
dictionaryListOrg = Counter(dictionaryListOrg)
dictionaryValueList, dictionaryWordCntList = zip(*dictionaryListOrg.most_common())

## (8)
def makeXArray(argTargetSentence):
    priTargetWordFlgArr = []
    for wrd in dictionaryValueList:
        src_set = set([ wrd ])
        tag_set = set( argTargetSentence )
        matched_list = []
        matched_list = list(src_set & tag_set)
        if(matched_list):
            priTargetWordFlgArr.append(1)
        else:
            priTargetWordFlgArr.append(0)
    return priTargetWordFlgArr

## (9-1)
targetSentence = cat01ValueList
targetWordFlgArr = makeXArray(targetSentence)
X.append( targetWordFlgArr )
Y.append( 0 )

## (9-2)
targetSentence = cat02ValueList
targetWordFlgArr = makeXArray(targetSentence)
X.append( targetWordFlgArr )
Y.append( 1 )

## (10)
from sklearn import linear_model
reg = linear_model.BayesianRidge()
reg.fit(X, Y)

## (11)
def makeTargetArr(argTargetSentence):
    priTargetWordFlgArr = []
    for wrd in dictionaryValueList:
        src_set = set([ wrd ])
        tag_set = set( wakati_filter(argTargetSentence) ) 
        matched_list = []
        matched_list = list(src_set & tag_set)
        if(matched_list):
            priTargetWordFlgArr.append(1)
        else:
            priTargetWordFlgArr.append(0)
        #print("WORD = ", wrd, "\t\tresult = ", matched_list, "\tFlg = ", priTargetWordFlgArr[-1])
    return priTargetWordFlgArr

## (12)
def exePredict(argTargetSentence):
    targetWordFlgArr = makeTargetArr(argTargetSentence)
    return reg.predict ([targetWordFlgArr])

## (13)
test=[
    "雪が降り寒いので、家から出ない。明日は晴れるといいな。"
    ,"豆腐料理を食べたい、麻婆豆腐はありますか?"
]

## (14)
for text in test:
    print(text, "exePredict : ", exePredict(text) )

実行結果

C:\test\>python test.py
雪が降り寒いので、家から出ない。明日は晴れるといいな。 exePredict :  [0.34645731]
豆腐料理を食べたい、麻婆豆腐はありますか? exePredict :  [0.39370121]

C:\test\>

実行結果について

[0.3xxxx] が予測結果です。0.0 に近いほど天気に関連しているという判定です。1.0 に近いほど、料理に関連しているという判定です。どちらも 0.3xxx と似たような結果になってしまいました。精度はいまいちですね。
ですが、1つ目より2つ目の文章の方が1.0に近いので、2つ目の方が料理に近い文章と判断してくれたようです。

課題:予測精度を高くするには?

今回は、スクリプトができたところで終わりですが、今後は精度を高くしていきたい思っています。精度が高くするため、課題を確認しておきます。おそらく以下の3つが課題です。

①教師データが少ない
②単語の選別をしてない
③単語の頻度を考慮してない

①教師データが少ない

ソースのはじめの方にある日本語の文章が、教師データになります。その文章が少なかったが精度が悪い原因の1つと思います。「じゃあ増やせばいいのでは?」と思いますが、おそらくそれでは処理時間が長くなるだけで、精度は上がりません。

②単語の選別をしてない

今回は、文章内の単語を抜き出して分析対象にしています。その単語の選別が足りてないと思います。分析対象を有効な単語だけに絞るように

③単語の頻度を考慮してない

今回は、単語の発生頻度は考慮していません。1回でも複数回でも同じ扱いです。単語別に発生頻度を考慮すると精度が上がるかもしれません。

※①の対策の教師データを増やしつつ、②と③の課題を対策できれば精度が上がりそうです。精度を上げたver2 が完成したら紹介しますね。

さいごに

ソースを紹介したのに、ソース内で何をしているのか説明ができませんでした。長くなったので、ここら辺は、別で書こうと思います。

【追記】2019年8月14日

精度が向上しました。


こんな弱小ブログでもサポートしてくれる人がいることに感謝です。