Y

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')

スパムと非スパムが判定されてますね。メールの文面をアレコレ変更して試したくなりました。

まとめ

やまかつさんの発表をなぞっただけの記事になっていますが... ライブラリを使えばスパムフィルタを作ることができました。内部の仕組みなどは少しずつ調べるとして、まずはライブラリを使って他のパターンも試してみます。

参考

scikit-learn

CR10 Pythonとscikit-learnではじめる機械学習 (ja)

scikit-learn から学ぶ機械学習の手法の概要

機械学習で未来を予測する - scikit-learn の決定木で未来の株価を予測

SMS Spam Collection Data Set

Not understood behavior of Numpy genfromtxt

交差検証 wikipedia

TipMe

TipMe with IndieSquare