ナード戦隊データマン

データサイエンスを用いて悪と戦うぞ

incremental learningでスパム検知の精度を向上させる

ベイズ系のモデルは、すでに学習済みのモデルに対して、追加で学習させることができるものがあります。ここでは、sklearnを用いてスパム検知の精度を追加の学習データで向上させられることを見ていきます。

データのダウンロード

In[1]:

import os
import re
import io
import requests
import numpy as np
import matplotlib.pyplot as plt
from zipfile import ZipFile

# Download or open data
data_dir = 'temp'
data_file = 'text_data.txt'
if not os.path.exists(data_dir):
    os.makedirs(data_dir)

if not os.path.isfile(os.path.join(data_dir, data_file)):
    zip_url = 'http://archive.ics.uci.edu/ml/machine-learning-databases/00228/smsspamcollection.zip'
    r = requests.get(zip_url)
    z = ZipFile(io.BytesIO(r.content))
    file = z.read('SMSSpamCollection')
    # Format Data
    text_data = file.decode()
    text_data = text_data.encode('ascii',errors='ignore')
    text_data = text_data.decode().split('\n')

    # Save data to text file
    with open(os.path.join(data_dir, data_file), 'w') as file_conn:
        for text in text_data:
            file_conn.write("{}\n".format(text))
else:
    # Open data from text file
    text_data = []
    with open(os.path.join(data_dir, data_file), 'r') as file_conn:
        for row in file_conn:
            text_data.append(row)
    text_data = text_data[:-1]

text_data = [x.split('\t') for x in text_data if len(x)>=1]
[y, X] = [list(x) for x in zip(*text_data)]

y = np.array([True if x=='spam' else False for x in y])

モデリング

In[2]:

from sklearn.feature_extraction.text import CountVectorizer
from sklearn.naive_bayes import MultinomialNB, BernoulliNB
from sklearn.model_selection import GridSearchCV
from sklearn.pipeline import Pipeline
from sklearn.model_selection import cross_val_score

pipe = Pipeline([("vectorizer", CountVectorizer(ngram_range=(1, 2), min_df=1)), 
                 ("clf", MultinomialNB())])
grid = GridSearchCV(pipe, param_grid={
    "clf":[MultinomialNB(), BernoulliNB()],
    "clf__alpha":[0.001,0.01,0.1,1,10]
}, cv=5)

cross_val_score(grid, X, y, cv=5)

Out[2]:

array([ 0.9874552 ,  0.98835125,  0.98294434,  0.98384201,  0.98833034])

In[3]:

from sklearn.model_selection import train_test_split

pipe = grid.best_estimator_
X_1, X_tmp, y_1, y_tmp = train_test_split(X, y, test_size=.66)
X_2, X_3, y_2, y_3 = train_test_split(X_tmp, y_tmp, test_size=.5)

pipe.fit(X_1, y_1)
#grid.partial_fit(X_2, y_2)
pipe.score(X_3, y_3)

Out[3]:

0.98641304347826086

In[4]:

vectorizer = pipe.named_steps["vectorizer"]
clf = pipe.named_steps["clf"]
X_2 = vectorizer.transform(X_2)
clf.partial_fit(X_2, y_2)
clf.score(vectorizer.transform(X_3), y_3)

Out[4]:

0.98858695652173911

このように、追加データをpartial_fitで学習すると精度が上がります。

考察

スパム検知の場合、新しいスパムに対応させるためにparfial_fitを用いることができるかもしれませんが、vectorizerでone-hot化された語を増やすことはできません。

つまり、vectorizerは、追加データに対してもtransformを使うしかないので、初期の学習で多くの語を学習させる必要があります。

継続的に学習させる必要がある場合や巨大なデータを学習させる場合は、このpartial_fitメソッドを用意しているモデルを用いれば実現できます。

参考

6. Strategies to scale computationally: bigger data — scikit-learn 0.19.0 documentation