2016-01-22 2 views
2

Я пытаюсь создать классификатор спам-почты, и я собрал несколько наборов данных из-за Интернета (например, SpamAssassin Database для спама/сообщений ham) и построил это:Как увеличить скорость для классификатора SVM с помощью Sk-learn

import os 
import numpy 
from pandas import DataFrame 
from sklearn.feature_extraction.text import CountVectorizer 
from sklearn.pipeline import Pipeline 
from sklearn.cross_validation import KFold 
from sklearn.metrics import confusion_matrix, f1_score 
from sklearn import svm 

NEWLINE = '\n' 

HAM = 'ham' 
SPAM = 'spam' 

SOURCES = [ 
    ('C:/data/spam', SPAM), 
    ('C:/data/easy_ham', HAM), 
    # ('C:/data/hard_ham', HAM), Commented out, since they take too long 
    # ('C:/data/beck-s', HAM), 
    # ('C:/data/farmer-d', HAM), 
    # ('C:/data/kaminski-v', HAM), 
    # ('C:/data/kitchen-l', HAM), 
    # ('C:/data/lokay-m', HAM), 
    # ('C:/data/williams-w3', HAM), 
    # ('C:/data/BG', SPAM), 
    # ('C:/data/GP', SPAM), 
    # ('C:/data/SH', SPAM) 
] 

SKIP_FILES = {'cmds'} 


def read_files(path): 
    for root, dir_names, file_names in os.walk(path): 
     for path in dir_names: 
      read_files(os.path.join(root, path)) 
     for file_name in file_names: 
      if file_name not in SKIP_FILES: 
       file_path = os.path.join(root, file_name) 
       if os.path.isfile(file_path): 
        past_header, lines = False, [] 
        f = open(file_path, encoding="latin-1") 
        for line in f: 
         if past_header: 
          lines.append(line) 
         elif line == NEWLINE: 
          past_header = True 
        f.close() 
        content = NEWLINE.join(lines) 
        yield file_path, content 


def build_data_frame(path, classification): 
    rows = [] 
    index = [] 
    for file_name, text in read_files(path): 
     rows.append({'text': text, 'class': classification}) 
     index.append(file_name) 

    data_frame = DataFrame(rows, index=index) 
    return data_frame 


data = DataFrame({'text': [], 'class': []}) 
for path, classification in SOURCES: 
    data = data.append(build_data_frame(path, classification)) 

data = data.reindex(numpy.random.permutation(data.index)) 

pipeline = Pipeline([ 
    ('count_vectorizer', CountVectorizer(ngram_range=(1, 2))), 
    ('classifier', svm.SVC(gamma=0.001, C=100)) 
]) 

k_fold = KFold(n=len(data), n_folds=6) 
scores = [] 
confusion = numpy.array([[0, 0], [0, 0]]) 
for train_indices, test_indices in k_fold: 
    train_text = data.iloc[train_indices]['text'].values 
    train_y = data.iloc[train_indices]['class'].values.astype(str) 

    test_text = data.iloc[test_indices]['text'].values 
    test_y = data.iloc[test_indices]['class'].values.astype(str) 

    pipeline.fit(train_text, train_y) 
    predictions = pipeline.predict(test_text) 

    confusion += confusion_matrix(test_y, predictions) 
    score = f1_score(test_y, predictions, pos_label=SPAM) 
    scores.append(score) 

print('Total emails classified:', len(data)) 
print('Support Vector Machine Output : ') 
print('Score:' + str((sum(scores)/len(scores))*100) + '%') 
print('Confusion matrix:') 
print(confusion) 

линии, которые я закомментированы являются сбор писем, даже если я закомментировать большинство наборов данных и выберите один с наименьшим количеством писем, он по-прежнему работает очень медленно (~ 15 минут) и дают точность около 91%. Как повысить скорость и точность?

+0

Сколько документов? Похоже, что количество документов достаточно велико для ядра. Считаете ли вы использование линейного SVM? –

+0

@DavidMaust всего около 30 тыс. Документов (включая прокомментированные документы). Количество незарегистрированных документов составляет около 3 тыс. Я попробую LinearSVM сейчас ... –

+0

Да. это много для ядра SVM. Линейный SVM, вероятно, будет намного лучше. 'svc.SVM (kernel = 'linear')' –

ответ

2

Вы используете ядро ​​SVM. Есть две проблемы с этим.

Сложность времени ядра SVM: Первым шагом в выполнении ядра SVM является построение матрицы подобия, которая становится набором функций. С 30 000 документов количество элементов в матрице подобия становится 90 000 000. Это быстро растет, так как ваш корпус растет с тех пор, как матрица увеличивает квадрат количества документов в вашем корпусе. Эта проблема может быть решена с использованием RBFSampler в scikit-learn, но вы, вероятно, не захотите использовать это по следующей причине.

Dimensionality: Вы используете термины и числа bigram как свой набор функций. Это чрезвычайно высокий размерный набор данных. Используя ядро ​​RBF в высокоразмерных пространствах, даже небольшие различия (шум) могут оказать большое влияние на результаты сходства. См. curse of dimensionality. Вероятно, поэтому ваше ядро ​​RBF дает худшие результаты, чем линейное ядро.

Стохастический градиентный спуск: SGD может использоваться вместо стандартного SVM, и с хорошей настройкой параметров он может давать похожие или, возможно, даже лучшие результаты. Недостатком является то, что SGD имеет больше параметров для настройки скорости обучения и расписания обучения. Кроме того, за несколько проходов SGD не идеален. В этом случае другие алгоритмы, такие как Follow The Regularized Leader (FTRL), будут лучше. Однако Scikit-learn не реализует FTRL. Использование SGDClassifier с loss="modified_huber" часто работает хорошо.

Теперь, когда у нас есть проблемы из пути, есть несколько способов, вы можете улучшить производительность:

Tf-IDF весов: Использование tf-idf, более общие слова взвешиваются меньше. Это позволяет классификатору лучше представлять редкие слова, которые более значимы. Это может быть реализовано путем переключения CountVectorizer на TfidfVectorizer

настройки параметров: с линейным SVM, нет gamma параметра, но параметр C может быть использован, чтобы значительно улучшить результаты. В случае SGDClassifier параметры альфа и скорости обучения также могут быть настроены.

ансамбль: Запуск модели на нескольких подвыборках и усреднение результата часто приводит к созданию надежной модели, чем одиночный прогон. Это можно сделать в scikit-learn, используя BaggingClassifier. Также сочетание разных подходов может дать значительно лучшие результаты. Если используются существенно разные подходы, рассмотрите возможность использования сложной модели с древовидной моделью (RandomForestClassifier или GradientBoostingClassifier) ​​в качестве последнего этапа.

+0

с использованием tf-idf, а LinearSVC увеличил скорость и демонстрирует потрясающую точность в 99,5% :) –

+0

Отлично. Это перекрестная проверенная точность? –

+0

Да, спасибо за ваши усилия –