scikit-learnを使ってスパムメールを判定した
PyCon2014の動画を2016年に見た
CR10 Pythonとscikit-learnではじめる機械学習 (ja)
やまかつさんの動画を見てscikit-learnを知り、発表内容をコードを実際に動かしてみました。
学習フェーズ
1.データダウンロード
SMS Spam Collection Data SetからSMSSpamCollectionをダウンロードします。
SMSSpamCollection
ham Nah I don't think he goes to usf, he lives around here though spam FreeMsg Hey there darling it's been 3 week's now and no word back! I'd like some fun you up for it still? Tb ok! XxX std chgs to send, £1.50 to rcv
先頭のspamはスパム、hamは非スパムのメッセージを表します。ちょっと面倒ですが...SMSのデータと教師データの2ファイルに分割します。僕はAtomエディタを使っているので以下のような正規表現を使って加工しました。
spam(.*) -> spam ham(.*) -> ham
分割したデータは以下の2ファイルです。
- SMSSpamCollectionData.txt
Go until jurong point, crazy.. Available only in bugis n great world la e buffet... Cine there got amore wat... Ok lar... Joking wif u oni... Free entry in 2 a wkly comp to win FA Cup final tkts 21st May 2005. Text FA to 87121 to receive entry question(std txt rate)T&C's apply 08452810075over18's
- SMSSpamCollectionLabel.txt
ham ham spam
2.ファイル読み込み
$ ipython In : import numpy as np In : train_data = np.genfromtxt('SMSSpamCollectionData.txt', delimiter='\t', dtype='S') In : train_label = np.genfromtxt('SMSSpamCollectionLabel.txt', delimiter='\t', dtype='S') In : train_data[0] Out: b'Go until jurong point, crazy.. Available only in bugis n great world la e buffet... Cine there got amore wat...' In : train_label[0] Out: b'ham' In : train_data.shape Out: (5574,) In : train_label.shape Out: (5574,)
先頭にb'
がありますがとりあえず無視して先に進みます。
3.学習データを単語に分割
- 日本語の場合は形態素解析を行う。今回はしない
- ホワイトスペースで分割&単語ごとに数えあげる(ベクトル化)
In : from sklearn.feature_extraction.text import CountVectorizer In : count_vect = CountVectorizer() In : train_counts = count_vect.fit_transform(train_data) In : train_counts.shape Out: (5574, 8527)
4.モデル作成
- ドキュメント解析の場合は
Multinomial Naive Bayes
を使う。(確率分布が違う...?)
from sklearn.naive_bayes import MultinomialNB mnb = MultinomialNB().fit(train_counts, train_label)
5.クロスバリデーションでモデルの性能評価
- 平均98.13%、最小値94.05%の正解率。十分な性能.. らしいです。
from sklearn import cross_validation k_fold = cross_validation.KFold(n=train_data.shape[0], n_folds=int(train_data.shape[0] / 100)) cv_result = cross_validation.cross_val_score(MultinomialNB(), train_counts, train_label, cv=k_fold) In : cv_result.shape Out: (55,) In : np.min(cv_result) Out: 0.94059405940594054 In : np.max(cv_result) Out: 1.0 In : np.mean(cv_result) Out: 0.98133460404863992 In : np.median(cv_result) # 中央値 Out: 0.98039215686274506
予測フェーズ
1.スパムメール、非スパムメールを用意
以下スパムメールと非スパムメールをpredict-data.txtに保存します。
I offer you to become a agent of our company in your city. This job does not require any specific qualification, but offers gret opportunities for responsible people the compensation we offer stats from 2,500,000 Yen/month.
Python JP 2014 please bring the printed-out ticket with you. Finaly, this weekend the PyCon JP 2014 Conference will start! When coming to the Conference, at the reception please bring the printed-out ticket with you. You can see your ticket the below link.
2.メールを読み込む
predict_data = np.genfromtxt('predict-data.txt', delimiter='\t', dtype='S')
3.未知のデータを単語に分割する
predict_counts = count_vect.transform(predict_data)
4.モデルを使って分類する
In : mnb.predict(predict_counts[0]) Out: array([b'spam'], dtype='|S7') In : mnb.predict(predict_counts[1]) Out: array([b'ham'], dtype='|S7')
スパムと非スパムが判定されてますね。メールの文面をアレコレ変更して試したくなりました。
まとめ
やまかつさんの発表をなぞっただけの記事になっていますが... ライブラリを使えばスパムフィルタを作ることができました。内部の仕組みなどは少しずつ調べるとして、まずはライブラリを使って他のパターンも試してみます。
参考
CR10 Pythonとscikit-learnではじめる機械学習 (ja)
機械学習で未来を予測する - scikit-learn の決定木で未来の株価を予測
Not understood behavior of Numpy genfromtxt