
Partager:
Aboze Brain John est Technology Business Analyst chez Axa Mansard. Il a de l'expérience dans les domaines de la science des données et de l'analyse, de la recherche sur les produits et de la rédaction technique. Brain a participé à des projets d'analyse de données de bout en bout, allant de la collecte de données à l'exploration, la transformation/élaboration, la modélisation et la dérivation d'informations commerciales exploitables, et il assure le leadership en matière de connaissances.
Détection de spam par SMS avec l'apprentissage automatique en Python
Cet article a été mis à jour en avril 2025
Dans ce tutoriel, vous allez construire une application web de détection de spam par SMS. Cette application sera construite avec Python en utilisant le framework Flask et inclura un modèle d'apprentissage automatique que vous entraînerez à détecter les spams SMS. Nous travaillerons avec l Vonage SMS API de Vonage afin que vous puissiez classer les SMS envoyés au numéro de téléphone que vous avez enregistré dans votre Account Vonage.
Conditions préalables
Vonage API Account
To complete this tutorial, you will need a Vonage API account. If you don’t have one already, you can sign up today and start building with free credit. Once you have an account, you can find your API Key and API Secret at the top of the Vonage API Dashboard.
Pour acheter un numéro de téléphone virtuel, rendez-vous sur votre tableau de bord API et suivez les étapes ci-dessous.
Purchase a phone number
Accédez à votre tableau de bord API
Naviguez vers CONSTRUIRE & GERER > Numbers > Acheter des Numbers.
Choisissez les attributs nécessaires et cliquez sur Rechercher
Cliquez sur le bouton Acheter à côté du numéro désiré et validez votre achat
Pour confirmer que vous avez acheté le numéro virtuel, allez dans le menu de navigation de gauche, sous CONSTRUIRE & GÉRER, cliquez sur Numéros, puis sur Vos Numéros
Python installé. Le projet Anaconda comprend un certain nombre de bibliothèques utiles pour la science des données.
Une connaissance de base de FlaskHTML et CSS.
Structure du fichier
Un aperçu du répertoire de fichiers de ce projet est présenté ci-dessous :
├── README.md
├── dataset
│ └── spam.csv
├── env
│ ├── bin
│ ├── etc
│ ├── include
│ ├── lib
│ ├── pyvenv.cfg
│ └── share
├── model
│ ├── spam_model.pkl
│ └── tfidf_model.pkl
├── notebook
│ └── project_notebook.ipynb
├── requirements.txt
├── script
└── web_app
├── app.py
├── static
└── templatesNous allons créer tous les fichiers dans l'arborescence ci-dessus au fil des étapes de ce tutoriel.
Mise en place d'un environnement virtuel Python
Nous devons créer un environnement isolé pour les différentes dépendances Python propres à ce projet.
Tout d'abord, créez un nouveau dossier de développement. Dans votre terminal, exécutez :
Ensuite, créez un nouvel environnement virtuel Python. Si vous utilisez Anacondavous pouvez exécuter la commande suivante :
Vous pouvez ensuite activer l'environnement à l'aide de :
Si vous utilisez une distribution standard de Python, créez un nouvel environnement virtuel en exécutant la commande ci-dessous :
Pour activer le nouvel environnement sur un ordinateur Mac ou Linux, exécutez :
Si vous utilisez un ordinateur Windows, activez l'environnement comme suit :
Quelle que soit la méthode utilisée pour créer et activer l'environnement virtuel, votre invite doit être modifiée pour ressembler à ce qui suit :
Installer les paquets requis
Ensuite, vous allez installer tous les paquets nécessaires pour ce tutoriel. Dans votre nouvel environnement, installez les paquets suivants (qui incluent les bibliothèques et les dépendances) :
Remarque : pour créer un projet de science des données reproductible, il convient de s'en tenir aux versions que j'ai incluses ici. Il s'agit des versions les plus récentes au moment de la rédaction de cet article.
Voici quelques détails sur ces forfaits :
jupyterlab est destiné à la construction de modèles et à l'exploration des données.
flask sert à créer le serveur d'application et les pages.
lightgbm est l'algorithme d'apprentissage automatique pour construire notre modèle
nexmo est une bibliothèque Python pour interagir avec votre Account Vonage
matplotlib, plotly, plotly-express sont destinés à la visualisation de données
python-dotenv est un paquetage permettant de gérer les variables d'environnement telles que les clés API et d'autres valeurs de configuration.
nltk pour les opérations en langage naturel
numpy permet de calculer des tableaux
pandas permet de manipuler des données structurées.
regex permet d'effectuer des opérations sur les expressions régulières
scikit-learn est une boîte à outils d'apprentissage automatique
nuage de mots est utilisé pour créer des images de nuages de mots à partir de textes
Après l'installation, démarrez votre laboratoire Jupyter en exécutant :
Cela ouvre l'interface populaire du laboratoire Jupyter dans votre navigateur web, où vous allez effectuer une exploration interactive des données et construire des modèles.
L'interface du laboratoire Jupyter est présentée ici Jupyterlab.
Construction et entraînement du modèle de détection des SMS
Maintenant que votre environnement est prêt, vous allez télécharger les données d'entraînement SMS et construire un modèle d'apprentissage automatique simple pour classer les SMS. L'ensemble de données de spam pour ce projet peut être téléchargé ici. L'ensemble de données contient 5574 messages avec les étiquettes respectives de spam et de ham (légitime). Pour en savoir plus sur l'ensemble de données ici. Avec ces données, nous allons former un modèle d'apprentissage automatique capable de classer correctement les SMS comme ham ou spam. Ces procédures seront effectuées dans un carnet Jupyter, qui, à partir de notre répertoire de fichiers, s'appelle "project_notebok
Analyse exploratoire des données (AED)
Nous appliquerons ici diverses techniques pour analyser les données et mieux les comprendre.
Importer des bibliothèques et des données
Les bibliothèques nécessaires à ce projet peuvent être importées dans project_notebook.ipynb comme suit :
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import plotly_express as px
import wordcloud
import nltk
import warnings
warnings.filterwarnings('ignore')Le jeu de données spam situé dans le répertoire dataset nommé spam.csv peut être importé comme suit :
df = pd.read_csv("../dataset/spam.csv", encoding='latin-1')Note : Le codage des caractères de ce jeu de données est latin-1 (ISO/IEC 8859-1).
Ensuite, nous avons une vue d'ensemble de l'ensemble de données :
df.head()
Dataset overview
L'ensemble de données contient 5 colonnes. La colonne v1 est l'étiquette de l'ensemble de données ("ham" ou "spam") et la colonne v2 contient le texte du SMS. Les colonnes "Unnamed : 2", "Unnamed : 3" et "Unnamed : 4" contiennent "NaN" (not a number) signifiant des valeurs manquantes. Elles ne sont pas nécessaires et peuvent donc être supprimées car elles ne seront pas utiles à la construction du modèle. L'extrait de code suivant supprimera et renommera les colonnes afin d'améliorer la compréhension de l'ensemble de données :
df.drop(columns=['Unnamed: 2', 'Unnamed: 3', 'Unnamed: 4'], inplace=True)
df.rename(columns = {'v1':'class_label','v2':'message'},inplace=True)
df.head()
Rename columns
Examinons la répartition des étiquettes :
fig = px.histogram(df, x="class_label", color="class_label", color_discrete_sequence=["#871fff","#ffa78c"])
fig.show()
Distributions of labels
Nous disposons d'un ensemble de données déséquilibré, avec 747 messages de spam et 4825 messages de ham.
fig = px.pie(df.class_label.value_counts(),labels='index', values='class_label', color="class_label", color_discrete_sequence=["#871fff","#ffa78c"] )
fig.show()
Labels pie chart
Le spam représente 13,4 % de l'ensemble de données, tandis que le ham en constitue 86,6 %.
Ensuite, nous nous pencherons sur un peu d'ingénierie des fonctionnalités. La longueur des messages pourrait nous éclairer. Jetons un coup d'œil :
df['length'] = df['message'].apply(len)
df.head()
Message length
fig = px.histogram(df, x="length", color="class_label", color_discrete_sequence=["#871fff","#ffa78c"] )
fig.show()
length distribution - ham
Length distribution - spam
On constate que les messages ham sont plus courts que les messages spam, puisque la distribution des longueurs des messages ham et spam est centrée autour de 30-40 et 155-160 caractères, respectivement.
Avoir une vue des mots les plus courants utilisés dans les spams et les hams nous aidera à mieux comprendre l'ensemble des données. Un nuage de mots peut vous donner une idée du type de mots dominants dans chaque classe.
Pour créer un nuage de mots, séparez d'abord les classes en deux cadres de données pandas et ajoutez une fonction simple de nuage de mots, comme indiqué ci-dessous :
data_ham = df[df['class_label'] == "ham"].copy()
data_spam = df[df['class_label'] == "spam"].copy()
def show_wordcloud(df, title):
text = ' '.join(df['message'].astype(str).tolist())
stopwords = set(wordcloud.STOPWORDS)
fig_wordcloud = wordcloud.WordCloud(stopwords=stopwords, background_color="#ffa78c",
width = 3000, height = 2000).generate(text)
plt.figure(figsize=(15,15), frameon=True)
plt.imshow(fig_wordcloud)
plt.axis('off')
plt.title(title, fontsize=20)
plt.show()Vous trouverez ci-dessous le code qui permet d'afficher un nuage de mots pour les SMS spam :
show_wordcloud(data_spam, "Spam messages")
word cloud spam
Vous pouvez également afficher le nuage de mots pour les SMS sur le jambon :
show_wordcloud(data_ham, "ham messages")
word cloud ham
Prétraitement des données
Le processus de conversion des données en quelque chose qu'un ordinateur peut comprendre est appelé prétraitement. Dans le contexte de cet article, il s'agit de processus et de techniques visant à préparer nos données textuelles pour notre algorithme d'apprentissage automatique.
Tout d'abord, nous allons convertir l'étiquette en format numérique. Cette étape est essentielle avant l'entraînement du modèle, car les modèles d'apprentissage profond ont besoin de données sous forme numérique.
df['class_label'] = df['class_label'].map( {'spam': 1, 'ham': 0})Ensuite, nous traiterons le contenu du message à l'aide d'expressions régulières (Regex) afin d'uniformiser les adresses électroniques et web, les numéros de téléphone et les nombres, d'encoder les symboles, de supprimer la ponctuation et les espaces blancs, et enfin de convertir tout le texte en minuscules :
# Replace email address with 'emailaddress'
df['message'] = df['message'].str.replace(r'^.+@[^\.].*\.[a-z]{2,}$', 'emailaddress')
# Replace urls with 'webaddress'
df['message'] = df['message'].str.replace(r'^http\://[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(/\S*)?$', 'webaddress')
# Replace money symbol with 'money-symbol'
df['message'] = df['message'].str.replace(r'£|\$', 'money-symbol')
# Replace 10 digit phone number with 'phone-number'
df['message'] = df['message'].str.replace(r'^\(?[\d]{3}\)?[\s-]?[\d]{3}[\s-]?[\d]{4}$', 'phone-number')
# Replace normal number with 'number'
df['message'] = df['message'].str.replace(r'\d+(\.\d+)?', 'number')
# remove punctuation
df['message'] = df['message'].str.replace(r'[^\w\d\s]', ' ')
# remove whitespace between terms with single space
df['message'] = df['message'].str.replace(r'\s+', ' ')
# remove leading and trailing whitespace
df['message'] = df['message'].str.replace(r'^\s+|\s*?$', ' ')
# change words to lower case
df['message'] = df['message'].str.lower()À l'avenir, nous supprimerons les mots vides du contenu du message. Les mots vides sont des mots que les moteurs de recherche ont été programmés pour ignorer, à la fois lors de l'indexation des entrées pour la recherche et lors de leur récupération en tant que résultat d'une requête de recherche, tels que "the", "a", "an", "in", "but", "because", etc.
from nltk.corpus import stopwords
stop_words = set(stopwords.words('english'))
df['message'] = df['message'].apply(lambda x: ' '.join(term for term in x.split() if term not in stop_words))Ensuite, nous extrairons la forme de base des mots en supprimant les affixes. C'est ce qu'on appelle le stemming, que l'on peut illustrer comme le fait de couper les branches d'un arbre pour en faire des tiges. Il existe de nombreux algorithmes de troncature, tels que :
Algorithme de Porter
Lovins Stemmer
Dawson Stemmer
Krovetz Stemmer
Xerox Stemmer
Brouilleur de N-Grammes
Boule de neige
Lancaster Stemmer
Certains de ces algorithmes d'extraction sont agressifs et dynamiques. Certains s'appliquent à des langues autres que l'anglais et la taille des données textuelles influe sur l'efficacité. Pour cet article, le Stemmer Snowball a été utilisé en raison de sa vitesse de calcul.
Remarque : lors de l'utilisation de ces algorithmes de troncature, veillez à ne pas sur- ou sous-titrer.
ss = nltk.SnowballStemmer("english")
df['message'] = df['message'].apply(lambda x: ' '.join(ss.stem(term) for term in x.split()))Les algorithmes d'apprentissage automatique ne peuvent pas travailler directement avec du texte brut. Le texte doit être converti en nombres - plus précisément, en vecteurs de nombres. Divisons les messages (données textuelles dans les phrases) en mots. Il s'agit d'une exigence dans les tâches de traitement du langage naturel, où chaque mot doit être capturé et soumis à une analyse plus poussée. Tout d'abord, nous créons un modèle de sac de mots (BOW) pour extraire les caractéristiques du texte :
sms_df = df['message']
from nltk.tokenize import word_tokenize
# creating a bag-of-words model
all_words = []
for sms in sms_df:
words = word_tokenize(sms)
for w in words:
all_words.append(w)
all_words = nltk.FreqDist(all_words)Examinons le nombre total de mots :
print('Number of words: {}'.format(len(all_words)))
Number of words 6526
Tracez maintenant les 10 mots les plus courants dans les données textuelles :
all_words.plot(10, title='Top 10 Most Common Words in Corpus');
Most common words
Ensuite, nous mettrons en œuvre une technique NLP (fréquence des termes - fréquence inverse des documents) pour évaluer l'importance des mots dans les données textuelles. En bref, cette technique définit simplement ce qu'est un "mot pertinent". Le modèle tfidf_model créé à partir de cette technique NLP sera enregistré (sérialisé) sur le disque local afin de transformer ultérieurement les données de test pour notre application web :
from sklearn.feature_extraction.text import TfidfVectorizer
tfidf_model = TfidfVectorizer()
tfidf_vec=tfidf_model.fit_transform(sms_df)
import pickle
#serializing our model to a file called model.pkl
pickle.dump(tfidf_model, open("../model/tfidf_model.pkl","wb"))
tfidf_data=pd.DataFrame(tfidf_vec.toarray())
tfidf_data.head()
tfidf
La forme du cadre de données résultant est 5572 par 6506. Afin de former et de valider les performances de notre modèle d'apprentissage automatique, nous devons diviser les données en un ensemble de données de formation et un ensemble de données de test. L'ensemble d'entraînement doit ensuite être divisé en un ensemble de formation et un ensemble de validation.
### Separating Columns
df_train = tfidf_data.iloc[:4457]
df_test = tfidf_data.iloc[4457:]
target = df['class_label']
df_train['class_label'] = target
Y = df_train['class_label']
X = df_train.drop('class_label',axis=1)
# splitting training data into train and validation using sklearn
from sklearn import model_selection
X_train,X_test,y_train,y_test = model_selection.train_test_split(X,Y,test_size=.2, random_state=42)Le ratio de division pour l'ensemble de validation est de 20 % des données de formation.
Construction de modèles
Nous utiliserons un algorithme d'apprentissage automatique connu sous le nom de LightGBM. Il s'agit d'un cadre de renforcement du gradient qui utilise des algorithmes d'apprentissage basés sur des arbres. Il présente les avantages suivants :
Une vitesse d'entraînement plus rapide et une efficacité accrue
Réduction de l'utilisation de la mémoire
Une meilleure précision
Prise en charge de l'apprentissage parallèle et de l'apprentissage par le GPU
Capacité à traiter des données à grande échelle
La mesure de performance pour ce projet est le score F1. Cette mesure prend en compte à la fois la précision et le rappel pour calculer le score. Le score F1 atteint sa meilleure valeur à 1 et sa pire valeur à 0.
import lightgbm as lgb
from sklearn.metrics import f1_score
def train_and_test(model, model_name):
model.fit(X_train, y_train)
pred = model.predict(X_test)
print(f'F1 score is: {f1_score(pred, y_test)}')
for depth in [1,2,3,4,5,6,7,8,9,10]:
lgbmodel = lgb.LGBMClassifier(max_depth=depth, n_estimators=200, num_leaves=40)
print(f"Max Depth {depth}")
print(" ")
print(" ")
train_and_test(lgbmodel, "Light GBM")
F1 score
Cette itération montre que la profondeur maximale de six (6) obtient le score F1 le plus élevé (0,9285714285714285). Nous allons ensuite effectuer une recherche aléatoire sur la grille afin de trouver les meilleurs paramètres pour le modèle :
from sklearn.model_selection import RandomizedSearchCV
lgbmodel_bst = lgb.LGBMClassifier(max_depth=6, n_estimators=200, num_leaves=40)
param_grid = {
'num_leaves': list(range(8, 92, 4)),
'min_data_in_leaf': [10, 20, 40, 60, 100],
'max_depth': [3, 4, 5, 6, 8, 12, 16, -1],
'learning_rate': [0.1, 0.05, 0.01, 0.005],
'bagging_freq': [3, 4, 5, 6, 7],
'bagging_fraction': np.linspace(0.6, 0.95, 10),
'reg_alpha': np.linspace(0.1, 0.95, 10),
'reg_lambda': np.linspace(0.1, 0.95, 10),
"min_split_gain": [0.0, 0.1, 0.01],
"min_child_weight": [0.001, 0.01, 0.1, 0.001],
"min_child_samples": [20, 30, 25],
"subsample": [1.0, 0.5, 0.8],
}
model = RandomizedSearchCV(lgbmodel_bst, param_grid, random_state=1)
search = model.fit(X_train, y_train)
search.best_params_
best parameters search
Nous utiliserons les meilleurs paramètres pour entraîner le modèle :
best_model = lgb.LGBMClassifier(subsample=0.5,
reg_lambda= 0.47777777777777775,
reg_alpha= 0.5722222222222222,
num_leaves= 88,
min_split_gain= 0.01,
min_data_in_leaf= 10,
min_child_weight= 0.01,
min_child_samples= 30,
max_depth= 3,
learning_rate= 0.1,
bagging_freq= 3,
bagging_fraction= 0.6,
random_state=1)
best_model.fit(X_train,y_train)
Trained model
Vérifions la performance du modèle par sa prédiction :
prediction = best_model.predict(X_test)
print(f'F1 score is: {f1_score(prediction, y_test)}')
Model prediction
Dans un dernier temps, nous effectuerons un entraînement complet sur l'ensemble des données afin que notre application web puisse faire des prédictions sur des données qu'elle n'a pas encore vues. Nous enregistrerons le modèle sur notre machine locale :
best_model.fit(tfidf_data, target)
pickle.dump(best_model, open("../model/spam_model.pkl","wb")) Intégrer le modèle dans une application Flask
Maintenant que vous disposez du modèle entraîné, nous allons créer une application Flask qui lira les messages envoyés et reçus via l'API SMS de Vonage et les classera en spam ou ham. Le résultat final sera affiché dans un tableau de bord SMS que vous allez également définir dans cette section.
Le répertoire web_app est composé de :
├── app.py
├── static
│ ├── Author.png
│ ├── style.css
│ ├── style2.css
│ └── vonage_logo.svg
└── templates
├── inbox.html
├── index.html
└── predict.htmlDans votre éditeur de code, ouvrez un nouveau fichier nommé .env (notez le point de départ) et ajoutez les informations d'identification suivantes :
API_KEY=<Your API key>
API_SECRET=<Your API secret>
Ceci est important pour des raisons de sécurité, car vous ne devez jamais coder en dur vos secrets dans votre application.
Ensuite, nous créons un fichier nommé app.py dans le répertoire web_app dans le répertoire Nous allons importer des bibliothèques pour réussir à construire l'application web. Ensuite, nous chargerons les informations d'identification de l'API Vonage à partir du fichier .env et lancer l'application Flask. Nous importerons également les modèles sauvegardés de notre notebook.
import os
import warnings
import nexmo
from flask import Flask, render_template, url_for, request, session
import pickle
import pandas as pd
import nltk
from nltk.corpus import stopwords
from sklearn.feature_extraction.text import TfidfVectorizer
from dotenv import load_dotenv
load_dotenv()
API_KEY = os.getenv("API_KEY")
API_SECRET = os.getenv("API_SECRET")
client = nexmo.Client(key=API_KEY, secret=API_SECRET)
warnings.filterwarnings("ignore")
app = Flask(__name__)
# secret key is needed for session
app.secret_key = os.getenv('SECRET_KEY')Après l'instance de l'application Flask, nous utilisons les sessions Flask pour faciliter la conservation des données à divers intervalles de journalisation du serveur. Ces sessions nécessitent une clé secrète - vous pouvez enregistrer la valeur de la clé secrète dans le fichier .env et la charger comme nous l'avons fait pour les informations d'identification de l'API.
Ensuite, nous définissons trois fonctions relatives aux itinéraires : home(), inbox(), et predict(). Les modèles respectifs de ces itinéraires sont index.html, inbox.html, et predict.html, stylisés à l'aide de deux feuilles de style, stlye.css et stlye2.css.
La route home/index fournit l'interface pour envoyer le message :
@app.route('/', methods=['GET', 'POST'])
def home():
return render_template('index.html')L'interface est présentée ci-dessous :
Home interface
Le dossier justificatif index.html dans le répertoire templates devrait ressembler à ceci :
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Spam detection Project</title>
<link rel="stylesheet" href="../static/style.css">
</head>
<body>
<nav>
<img src="{{url_for('static', filename='vonage_logo.svg')}}">
</nav>
<section>
<div class="side">
<img src="{{url_for('static', filename='Author.png')}}" width="350px" height="350px">
<h4>Author: Aboze Brain John Jnr</h4>
<h2>Project: SMS spam detection system</h2>
<h2>using Machine Learning, Python, Flask and</h2>
<h2>Vonage API</h2>
</div>
<div class="vl"></div>
<div class="main">
<h1>Machine Learning App with Flask</h1>
<p>Send an SMS</p>
<form action="/inbox" method="POST">
<input id="to_number" class="form-control" name="to_number" type="tel" placeholder="Phone Number"/>
<br>
<br>
<textarea id="message" class="form-control" name="message" placeholder="Your text message goes here" rows="10" cols="50"></textarea>
<br>
<br>
<input type="submit" class="btn-info" value="Send SMS">
</form>
</div>
</section>
</body>
</html>
La route suivante est la route de la boîte de réception, où sont stockés les messages envoyés et le numéro de téléphone de l'expéditeur à partir de l'index. L'API SMS de Vonage est utilisée ici pour lancer l'objet client et envoyer le message :
@app.route('/inbox', methods=['GET', 'POST'])
def inbox():
""" A POST endpoint that sends an SMS. """
# Extract the form values:
to_number = request.form['to_number']
message = request.form['message']
session['to_number'] = to_number
session['message'] = message
# Send the SMS message:
result = client.send_message({
'from': 'Vonage APIs',
'to': to_number,
'text': message,
})
return render_template('inbox.html', number=to_number, msg=message)L'interface est présentée ci-dessous :
Inbox interface
Le dossier justificatif inbox.html dans le répertoire templates ressemble à ceci :
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Prediction</title>
<link rel="stylesheet" href="../static/style2.css">
</head>
<body>
<nav>
<img src="{{url_for('static', filename='vonage_logo.svg')}}">
</nav>
</nav>
<section>
<div class="side">
<img src="{{url_for('static', filename='Author.png')}}" width="350px" height="350px">
<h4>Author: Aboze Brain John Jnr</h4>
<h2>Project: SMS spam detection system</h2>
<h2>using Machine Learning, Python, Flask and</h2>
<h2>Vonage API</h2>
</div>
<div class="vl"></div>
<div class="main">
<h1>Inbox</h1>
<br>
<table class="table" >
<tr>
<th scope="col">From</th>
<th scope="col">Body</th>
</tr>
<tr scope='row'>
<td>{{number}}</td>
<td>{{msg}}</td>
</tr>
</table>
<br>
<br>
<form action="/predict" method="POST">
<input type="submit" class="btn-info" value="Predict">
</form>
<!-- <input type="submit" class="btn-info" value="Predict" formaction="/predict" method="POST"> -->
</div>
</section>
</body>
</html>
Le dernier itinéraire est celui de notre prédiction. Elle applique toutes les techniques de prétraitement précédentes utilisées pour former le modèle d'apprentissage automatique aux nouvelles données sous la forme de messages de la boîte de réception :
@app.route('/predict', methods=['POST'])
def predict():
model = pickle.load(open("../model/spam_model.pkl", "rb"))
tfidf_model = pickle.load(open("../model/tfidf_model.pkl", "rb"))
if request.method == "POST":
message = session.get('message')
message = [message]
dataset = {'message': message}
data = pd.DataFrame(dataset)
data["message"] = data["message"].str.replace(
r'^.+@[^\.].*\.[a-z]{2,}$', 'emailaddress')
data["message"] = data["message"].str.replace(
r'^http\://[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(/\S*)?$', 'webaddress')
data["message"] = data["message"].str.replace(r'£|\$', 'money-symbol')
data["message"] = data["message"].str.replace(
r'^\(?[\d]{3}\)?[\s-]?[\d]{3}[\s-]?[\d]{4}$', 'phone-number')
data["message"] = data["message"].str.replace(r'\d+(\.\d+)?', 'number')
data["message"] = data["message"].str.replace(r'[^\w\d\s]', ' ')
data["message"] = data["message"].str.replace(r'\s+', ' ')
data["message"] = data["message"].str.replace(r'^\s+|\s*?$', ' ')
data["message"] = data["message"].str.lower()
stop_words = set(stopwords.words('english'))
data["message"] = data["message"].apply(lambda x: ' '.join(
term for term in x.split() if term not in stop_words))
ss = nltk.SnowballStemmer("english")
data["message"] = data["message"].apply(lambda x: ' '.join(ss.stem(term)
for term in x.split()))
# tfidf_model = TfidfVectorizer()
tfidf_vec = tfidf_model.transform(data["message"])
tfidf_data = pd.DataFrame(tfidf_vec.toarray())
my_prediction = model.predict(tfidf_data)
return render_template('predict.html', prediction=my_prediction)L'interface est présentée ci-dessous :
Prediction interface
Le dossier justificatif predict.html dans le répertoire templates ressemble à ceci :
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Prediction</title>
<link rel="stylesheet" href="../static/style2.css">
</head>
<body>
<nav>
<img src="{{url_for('static', filename='vonage_logo.svg')}}">
</nav>
<section>
<div class="side">
<img src="{{url_for('static', filename='Author.png')}}" width="350px" height="350px">
<h4>Author: Aboze Brain John Jnr</h4>
<h2>Project: SMS spam detection system</h2>
<h2>using Machine Learning, Python, Flask and</h2>
<h2>Vonage API</h2>
</div>
<div class="vl"></div>
<div class="main results">
<h1>Machine Learning Prediction</h1>
{% if prediction == 1%}
<h2 style="color:red; font-size: x-large;">This looks like a Spam</h2>
<span style='font-size:100px;'>😡</span>
{% elif prediction == 0%}
<h2 style="color:green; font-size: x-large;">This looks like a Ham</h2>
<span style='font-size:100px;'>😀</span>
{% endif %}
</div>
</section>
</body>
</html>
À la fin du fichier Python, ajoutez ce code pour démarrer un serveur local :
if __name__ == '__main__':
app.run(debug=True)Les feuilles de style minifiées des deux feuilles de style utilisées dans le cadre de ce projet sont fournies ci-dessous.
Pour style.css:
*{box-sizing:border-box;padding:0;margin:0}body{color:#131415;font-family:spezia,sans-serif}nav{position:sticky;padding-top:10px}section{display:flex;flex-wrap:nowrap;padding:50px 10px}section h4{font-size:12px;font-weight:400;padding-top:5px}section h2{font-size:17px;font-weight:700;padding-top:15px}.vl{border-left:2px solid #310069;margin-left:50px;height:100vh}.main{margin-left:100px}.main h1{font-size:40px;padding-bottom:15px}.main p{font-size:24px;padding-bottom:15px}.btn-info{color:#310069;height:50px;width:100px;border-radius:8px}Pour style2.css:
*{box-sizing:border-box;padding:0;margin:0}body{color:#131415;font-family:spezia,sans-serif}nav{position:sticky;padding-top:10px}section{display:flex;flex-wrap:nowrap;padding:50px 10px}section h4{font-size:12px;font-weight:400;padding-top:5px}section h2{font-size:17px;font-weight:700;padding-top:15px}.vl{border-left:2px solid #310069;margin-left:50px;height:100vh}.main{margin-left:100px}td,th{border:3px solid #ddd;text-align:left;padding:20px}tr:nth-child(even){background-color:#ddd}input{width:80px;height:40px;border-radius:8px;color:#310069}button{width:80px;height:40px;border-radius:8px;color:#310069}Vous pouvez maintenant tester votre application ! Pour démarrer votre serveur, ouvrez votre dossier racine dans un terminal, puis exécutez ce qui suit dans le répertoire web_app et exécutez la commande suivante dans le répertoire
Si vous avez suivi toutes les étapes ci-dessus, vous devriez voir votre serveur fonctionner comme indiqué ci-dessous :
Server output
Saisissez http://localhost:5000/ dans la barre d'adresse pour se connecter à l'application.
Conclusion
Nous arrivons ainsi à la fin de ce tutoriel. Vous pouvez essayer d'autres exemples de SMS pour voir le résultat. Je suis sûr que vous pouvez déjà penser à toutes les possibilités étonnantes et aux cas d'utilisation de ces nouvelles connaissances. Vous pouvez intégrer ce filtrage des spams dans les logiciels RH, les chatbots, le service client et toute autre application basée sur les messages.
Vous avez une question ou souhaitez partager ce que vous construisez ?
Rejoignez la conversation sur le Communauté Vonage Slack
S'abonner à la Bulletin d'information du développeur
Suivez-nous sur X (anciennement Twitter) pour les mises à jour
Regardez les tutoriels sur notre chaîne YouTube
Connectez-vous avec nous sur la page Vonage Developer sur LinkedIn
Restez connecté et tenez-vous au courant des dernières nouvelles, astuces et événements concernant les développeurs.
Partager:
Aboze Brain John est Technology Business Analyst chez Axa Mansard. Il a de l'expérience dans les domaines de la science des données et de l'analyse, de la recherche sur les produits et de la rédaction technique. Brain a participé à des projets d'analyse de données de bout en bout, allant de la collecte de données à l'exploration, la transformation/élaboration, la modélisation et la dérivation d'informations commerciales exploitables, et il assure le leadership en matière de connaissances.
