仕事中の問題と解決メモ。

最近はPythonとGoogle Cloud Platformがメイン。株式会社ビズオーシャンで企画と開発運用、データ活用とか。 http://mstdn.bizocean.co.jp/@uyamazak https://github.com/uyamazak/

scikit-learnを使ってナイーブベイズでお問合せ分類するまでの流れ

bizoceanのお問合せを自動で分類して、定型文で済むやつは送信前に出して、サポートコストの削減とともにユーザーがすぐ問題を解決できるようにしようと思い、まずはナイーブベイズを試してみる。

環境は、Dockerで動かしたJupyterと、Python3.6とsklearn等を使う。

最近は小さなものから大きなものまで動かす力だDockerコンテナ。

ベイズを使ったフィルタリングは個人的に尊敬しているプログラマPaul Grahamも昔提唱しており、自分の中で定番化している。

ベイジアンフィルタは2002年にポール・グレアムが発表した論文“A Plan for Spam”が元になって開発された[2][3][1]。さらに改良されたアルゴリズムは“Better Bayesian Filtering”に発表されている[4][5]。

ベイジアンフィルタ - Wikipedia

まずは全体的なデータの流れを整理する。

生のソースコードは本物の問い合わせ内容が入ってしまっていて、再現性のあるコードの用意が大変なのでまた今度。

1 訓練データ(テキストと分類ラベルのペア)を準備する。

CSVでやるのが普通だと思う。CSV化にあたって個人情報やテストデータなどを消してしまう。もちろんCSVなのでテキストに改行が入ってしまっている場合は消しておく。

句読点とか括弧とか意味のないものも手で消してしまってもいいけど、そういうのは後でもライブラリ側で消せるのでしない方がいい。自動できるものはしておかないと、新しいデータが入った時に手間が増える。

ラベル付けはまず手作業で行う必要がある。

scikit-learnのチートシートではデータ数が50以下だったら「get more data」となっている。

分類数にもよるけど、ある程度作ったら、実際に動かしながら追加していった方が無駄にならないかも。

Choosing the right estimator — scikit-learn 0.19.0 documentation


このチートシートでナイーブベイズはサンプル数が100K以下の場合となっているのに気付き、今回は多くても5kしか用意できないからちょうど良さげ。100K以上のときはSGD Classifierとやらの出番らしいことを知る。

実際のCSVはこんな感じ。雑多な奴は全部「その他」をつけてしまった。

書式の使い方を教えてください,書式について
登録できません,登録について
ログインできません,ログインについて
パスワードはあっているのに入れません,ログインについて

ラベルは、数字などのIDでもいいけど、日本語のままでも問題ない。

2 日本語は分かち書きする

自然言語処理では単語ごとにデータが分かれている必要がある。

英語などもともとスペースで区切られたアルファベット系言語等でなければ分かち書きが必要になる。アラビア語もよく知らんけど画像を見る感じスペースあるな。スペースがないのは漢字圏の特徴だろうか。

今まではMecabを使うことが多かったけど、先週GoogleのNatural Language APIを使った記事を書いたように今回もそっちを使う。

データ量、目的、コストによってどっちがいいかは変わってくる。

個人的にはデータ量が多く速度、コスト、オリジナル辞書を重視するならMecab、構築スピードと運用の楽さであればGoogleを使う。

uyamazak.hatenablog.com

3 データをベクタライズする

分かち書きされた単語そのままでは計算ができないため、単語と出現頻度の数字の配列にする必要がある。

ただ単にカウントするもので十分な場合もあれば、TFIDFを計算した方がよいこともある。

tf-idf - Wikipedia

もちろん自分で書く必要はなく、sklearnに用意されているので、詳細までわからなくてもできてしまう。

Working With Text Data — scikit-learn 0.19.0 documentation

4 分類器を作成する

3で出来上がったデータを教師として作成する。

scikit-learnには、GaussianNB, BernoulliNB, MultinomialNBの3種類が用意されており、簡単に比較することができる。

TFIDF版のテストはMultinomialNBだけで行った。

multinomial_clf = MultinomialNB()
multinomial_clf.fit(X_train_v, Y_train)
print("multinomial_clf\n{}\n".format(multinomial_clf.score(X_train_v, Y_train)))

# IFIDF
multinomial_tfidf_clf = MultinomialNB()
multinomial_tfidf_clf.fit(X_train_tfidf, Y_train)
print("multinomial_tfidf_clf\n{}\n".format(multinomial_tfidf_clf.score(X_train_tfidf, Y_train)))

bernoulli_clf = BernoulliNB()
bernoulli_clf.fit(X_train_v, Y_train)
print("bernoulli_clf\n{}\n".format(bernoulli_clf.score(X_train_v, Y_train)))

gaussian_clf = GaussianNB()
gaussian_clf.fit(X_train_v.toarray(), Y_train)
print("gaussian_clf\n{}\n".format(gaussian_clf.score(X_train_v.toarray(), Y_train)))

multinomial_clf
0.907537688442211

multinomial_tfidf_clf
0.8979899497487437

bernoulli_clf
0.8748743718592965

gaussian_clf
0.8020100502512563

bizoceanの問い合わせデータだとMultinomialNB + CountVectorizerが無難そう。

5 未知のデータでテストしてみる

教師に使っていないテストデータを用意して判定させる。

テストの文章についても、分類器を作る際と同じように分かち書きベクター化が必要になるので、前処理は関数化しておいた方がいい。

今やってるものだと、10個出して正解が8,9個だった。

ベクター化しているので、scikit-learnの他の分類アルゴリズムも使うことができそうなので、試してみる予定。


ナイーブベイズは単純ベイズと訳されるようにシンプルで軽いところがいい。


Pythonではじめる機械学習 ―scikit-learnで学ぶ特徴量エンジニアリングと機械学習の基礎

Pythonではじめる機械学習 ―scikit-learnで学ぶ特徴量エンジニアリングと機械学習の基礎

入門 自然言語処理

入門 自然言語処理