Skip to content

ml spam detector

ghdrako edited this page Aug 28, 2026 · 11 revisions

EDA

import pandas as pd
# Load spam dataset
emails = pd.read_csv("spam.csv")
# Display first records
print(emails.head())
# Display dataset dimensions
print(emails.shape)
# Display dataset information
print(emails.info())
# Check missing values
print(emails.isnull().sum())
# Display label counts
print(
    emails["label"].value_counts()
)
# Display percentages
print(
    emails["label"]
    .value_counts(normalize=True) * 100
)
import matplotlib.pyplot as plt
emails["label"].value_counts().plot(
    kind="bar"
)
plt.xlabel("Email Category")
plt.ylabel("Number of Emails")
plt.title("Spam and Ham Distribution")
plt.show()

Preprocesing

import re
def clean_text(text):
    text = text.lower()  # Converting Text to Lowercase
    text = re.sub(
        r"http\S+",
        "",
        text
    )
    text = re.sub(
        r"[^a-z\s]",
        "",
        text
    )
    # Removing Extra Spaces
    text = re.sub(
        r"\s+",
        " ",
        text
    ).strip()
    return text

emails["clean_message"] = (
    emails["message"]
    .apply(clean_text)
)
print(
    emails[
        ["message", "clean_message"]
    ].head()
)

Zbyt agresywne usuwanie cech spamu w preprocessingu:

  • Wyrażenie re.sub(r"http\S+", "", text) całkowicie usuwa linki. W wykrywaniu spamu sama obecność linku jest jedną z najsilniejszych cech. Zamiast go kasować, lepiej zamienić go na token, np. re.sub(r"http\S+", " httplink ", text).
  • Podobnie wyrażenie re.sub(r"[^a-z\s]", "", text) usuwa znaki walut ($, €, £) oraz wykrzykniki (!), które są typowymi wyznacznikami wiadomości phishingowych/spamowych.

Split

from sklearn.model_selection import train_test_split
X = emails["clean_message"]
y = emails["label"]
X_train, X_test, y_train, y_test = train_test_split(
    X,
    y,
    test_size=0.20,
    random_state=42,
    stratify=y
)

Feature extraction

from sklearn.feature_extraction.text import TfidfVectorizer
vectorizer = TfidfVectorizer(
    stop_words="english",
    max_features=5000
)
X_train_tfidf = vectorizer.fit_transform(
    X_train
)
X_test_tfidf = vectorizer.transform(
    X_test)

Treain

from sklearn.naive_bayes import MultinomialNB
model = MultinomialNB()
model.fit(
    X_train_tfidf,
    y_train
)
print("Training completed.")

Prediction

predictions = model.predict(
    X_test_tfidf
)
print(predictions[:10])

Evaluation

from sklearn.metrics import (
    accuracy_score,
    precision_score,
    recall_score,
    f1_score
)
print(
    "Accuracy:",
    accuracy_score(
        y_test,
        predictions
    )
)
print(
    "Precision:",
    precision_score(
        y_test,
        predictions,
        pos_label="spam"
    )
)
print(
    "Recall:",
    recall_score(
        y_test,
        predictions,
        pos_label="spam"
    )
)
print(
    "F1 Score:",
    f1_score(
        y_test,
        predictions,
        pos_label="spam"
    )
)


from sklearn.metrics import confusion_matrix
matrix = confusion_matrix(
    y_test,
    predictions,
    labels=[
        "ham",
        "spam"
    ]
)
print(matrix)


from sklearn.metrics import classification_report
print(
    classification_report(
        y_test,
        predictions
    )
)

# Although TF-IDF does not directly indicate feature importance, examining the vocabulary provides insight into the terms learned during training.
feature_names = (
    vectorizer.get_feature_names_out()
)
print(feature_names[:50])

Predicting New Emails

new_email = [
    "Congratulations! You have won a free vacation. Click here to claim your prize."
]
new_email_clean = [
    clean_text(text)
    for text in new_email
]
new_email_vector = vectorizer.transform(
    new_email_clean
)
prediction = model.predict(
    new_email_vector
)
probability = model.predict_proba(
    new_email_vector
)
print("Prediction:", prediction[0])
print(
    "Confidence:",
    probability.max()
)

Storing model and vectorizer to later use

import joblib
joblib.dump(
    model,
    "spam_model.pkl"
)
joblib.dump(
    vectorizer,
    "tfidf_vectorizer.pkl"
)

Building a Complete Pipeline

from sklearn.pipeline import Pipeline
pipeline = Pipeline([
    (
        "tfidf",
        TfidfVectorizer(
            preprocessor=clean_text,
            stop_words="english",
            max_features=5000
        )
    ),
    (
        "classifier",
        MultinomialNB()
    )
])
pipeline.fit(
    X_train,
    y_train
)
pipeline_prediction = pipeline.predict(
    new_email
)
print(pipeline_prediction)

Aplication

import joblib
import pandas as pd
model = joblib.load(
    "spam_model.pkl"
)
vectorizer = joblib.load(
    "tfidf_vectorizer.pkl"
)
st.title(
    "Spam Analyzer"
)
st.write(
    "Classifing email using machine learning."
)
review = st.text_area(
    "Enter a email text"
)
import re
def clean_input(text):
    text = text.lower()
    text = re.sub(
        r"http\S+",
        "",
        text
    )
    text = re.sub(
        r"[^a-z\s]",
        "",
        text
    )
    text = re.sub(
        r"\s+",
        " ",
        text
    ).strip()
    return text
if st.button("Analyze"):
    cleaned_review = clean_input(
        review
    )
    review_vector = vectorizer.transform(
        [cleaned_review]
    )
    prediction = model.predict(
        review_vector
    )
    probability = model.predict_proba(
        review_vector
    )
    st.write(
        "Predicted Spam:",
        prediction[0]
    )
    st.write(
        f"Confidence: {probability.max():.2%}"
    )
st.subheader(
    "Dataset Overview"
)
st.write(
    reviews["Sentiment"]
    .value_counts()
)
st.subheader(
    "Sentiment Distribution"
)
st.bar_chart(
    reviews["Sentiment"]
    .value_counts()
)
st.subheader(
    "Sample Reviews"
)
st.dataframe(
    reviews[
        ["Review", "Sentiment"]
    ].head(10)
)

lunch app

streamlit run app.py

Test

Clone this wiki locally