スパムメール判別

スパムメールフィルターの開発にチャレンジ!

賞金: 100,000 参加ユーザー数: 261 4年弱前に終了

MultinomialNBを使ったbaseline(参考)

はじめに

運営の皆様、新しいコンペの開催ありがとうございます。自然言語処理に挑戦したことが無いのでコンペに参加しながら勉強したいと思います。

上述の通り自然言語処理は初めてですが、ネット上の情報を寄せ集めてひとまず自分の中でBaselintと呼べるものができたので、今朝までの私と同じような「自然言語処理って何からすれば良いの?」という方の参考になれば幸いです。

また、付け焼刃の知識ですので、間違った知識やよろしくない処理が含まれていると思います。不慣れな方は信じすぎず、詳しい方はコメントで教えて頂けると大変幸いです。

参考にしたサイト

Baseline作成に当たり、以下のサイトを参考にしました。特にkaggleの方はまだまだ読み切っていませんが、当コンペでも役立つ情報が多そうです。

[Kaggle:SMS Spam Collection Dataset](https://www.kaggle.com/uciml/sms-spam-collection-dataset/notebooks)
[Qiita:機械学習 〜 迷惑メール分類(ナイーブベイズ分類器) 〜](https://qiita.com/fujin/items/50fe0e0227ef8457a473)

データとライブラリのインポート

import re
import string

import matplotlib.pyplot as plt
import nltk
import numpy as np
import pandas as pd
import scipy.stats as stats
import seaborn as sns
from bs4 import BeautifulSoup
from imblearn.under_sampling import RandomUnderSampler
from nltk.corpus import stopwords
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.metrics import f1_score
from sklearn.naive_bayes import MultinomialNB
from wordcloud import STOPWORDS, WordCloud
df = pd.read_csv("train_data.csv")
test = pd.read_csv("test_data.csv")
df.head()
id contents y
0 1 Subject: re : fw : willis phillips\r\ni just s... 0
1 2 Subject: re : factor loadings for primary curv... 0
2 3 Subject: re : meridian phone for kate symes\r\... 0
3 4 Subject: re : october wellhead\r\nvance ,\r\nd... 0
4 5 Subject: california 6 / 13\r\nexecutive summar... 0

前処理

stopwordsの定義更新

stopwordsとは、日本語で言えば「は」、「です」など、英語で言えば「a」、「is」などの出現頻度が高いにも関わらず特別な意味を持たない単語のことで、こういった単語を削除することで認識精度を向上させます。
nltkライブラリに基本的なものは格納されていますが、こちらは要するに単なるlistなので、自分でstopwordsを追加することもできます。
以下では、string.punctuationで取得できる記号と、rawデータを見て無駄そうだった"Subject"も加えています。

以下の処理の際、私の環境(Anaconda上のjupyter notebook)ですとpip installできていたと思ってもエラーが起きました。
Anacondaでうまく動かないときは以下もご参照ください。
https://stackoverflow.com/questions/48385829/how-to-install-stop-words-package-for-anaconda

stop = set(stopwords.words("english"))

# string.punctuation = ['!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~']
punctuation = list(string.punctuation)

# 手動で追加
org_stop = ["Subject"]

# stopwordsの定義更新
add_stop = punctuation + org_stop
stop.update(add_stop)

前処理の適用

stopwordsの削除の他、URLの削除などを行います。

# htmlの分割
def strip_html(text):
    soup = BeautifulSoup(text, "html.parser")
    return soup.get_text()

# []で囲まれた文章の削除(脚注、linkなど)
def remove_between_square_brackets(text):
    return re.sub('\[[^]]*\]', '', text)
# URLの削除
def remove_between_square_brackets(text):
    return re.sub(r'http\S+', '', text)

# stopwordsの削除
def remove_stopwords(text):
    final_text = []
    for i in text.split():
        if i.strip().lower() not in stop:
            if i.strip().isalpha():
                final_text.append(i.strip())
    return " ".join(final_text)

# ↑の関数をまとめて適用する関数
def denoise_text(text):
    text = strip_html(text)
    text = remove_between_square_brackets(text)
    text = remove_stopwords(text)
    return text

# ↑の関数の適用
df['contents']=df['contents'].apply(denoise_text)

EDA

targetの分布の確認

sns.countplot(df["y"])
<matplotlib.axes._subplots.AxesSubplot at 0x24f2f8ba4c8>

ほとんどが非スパムで、非常に不均衡なデータです

spamと非spamに分けてWordCloudを確認

WordColudは「実装したことは無いけど見たことはある」人も多いのではないでしょうか。出現頻度が高い単語ほど大きく、そうでない単語ほど小さく表示し、直感的にどういった単語が多い文章か分かる様になってます。

# 非spam
plt.figure(figsize=(20,20))
wc = WordCloud(max_words = 2000, width = 1600, height = 800, stopwords = STOPWORDS).generate(" ".join(df[df.y == 0].contents))
plt.imshow(wc, interpolation="bilinear")
<matplotlib.image.AxesImage at 0x24f2fd08508>