Spam — классификация текста (TF-IDF)

Классификация SMS: спам (1) или нет (0). Baseline — TF-IDF + sklearn; для сравнения с нейросетями — практикум тональности.

Зависимости: pip install pandas scikit-learn


1. Загрузка

import pandas as pd

url = "https://raw.githubusercontent.com/justmarkham/pycon-2016-tutorial/master/data/sms.tsv"
df = pd.read_csv(url, sep="\t", header=None, names=["label", "message"])
df["target"] = (df["label"] == "spam").astype(int)
print(df["target"].value_counts())
print(df.head(2))

Аналог задачи на Kaggle — spam or not spam.


2. Train / test

from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(
    df["message"], df["target"], test_size=0.2, random_state=42, stratify=df["target"]
)

3. TF-IDF + логистическая регрессия

from sklearn.pipeline import Pipeline
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report, confusion_matrix

pipe = Pipeline([
    ("tfidf", TfidfVectorizer(max_features=5000, ngram_range=(1, 2))),
    ("clf", LogisticRegression(max_iter=1000)),
])
pipe.fit(X_train, y_train)
y_pred = pipe.predict(X_test)

print(classification_report(y_test, y_pred))
print(confusion_matrix(y_test, y_pred))

ngram_range=(1, 2) добавляет словосочетания («free entry», «click here»).


4. Сравнение с CountVectorizer (мешок слов)

from sklearn.feature_extraction.text import CountVectorizer

bow_pipe = Pipeline([
    ("bow", CountVectorizer(max_features=3000)),
    ("clf", LogisticRegression(max_iter=1000)),
])
bow_pipe.fit(X_train, y_train)
print("BoW accuracy:", bow_pipe.score(X_test, y_test))
print("TF-IDF accuracy:", pipe.score(X_test, y_test))

TF-IDF обычно лучше подавляет частые «пустые» слова.


5. Дерево решений на тех же признаках

from sklearn.tree import DecisionTreeClassifier

tree_pipe = Pipeline([
    ("tfidf", TfidfVectorizer(max_features=2000)),
    ("clf", DecisionTreeClassifier(max_depth=20, random_state=42)),
])
tree_pipe.fit(X_train, y_train)
print("Tree accuracy:", tree_pipe.score(X_test, y_test))

На разреженных текстовых матрицах чаще выигрывает логистическая регрессия или линейный SVM.


6. Проверка на своём тексте

samples = [
    "Win a free iPhone now!!! click here",
    "See you at the meeting tomorrow",
]
for text, pred in zip(samples, pipe.predict(samples)):
    print("spam" if pred else "ham", "→", text[:50])

Самопроверка: обучите модель на датасете эмоций (6 классов): сначала LabelEncoder для y, затем TfidfVectorizer + LogisticRegression(multinomial).


Дальше