Upload 7 files
Browse files- README.md +110 -13
- app.py +35 -0
- model_spam_v1.pkl +3 -0
- model_spam_v2.pkl +3 -0
- model_spam_v3.pkl +3 -0
- requirements.txt +3 -0
- train_spam_model.py +31 -0
README.md
CHANGED
@@ -1,13 +1,110 @@
|
|
1 |
-
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# 📩 SMS Spam Classifier with MLflow Model Versioning
|
2 |
+
|
3 |
+
Đây là một demo triển khai mô hình phân loại tin nhắn rác (spam) sử dụng **Gradio** và **MLflow Model Registry**, với khả năng chọn phiên bản mô hình để phục vụ.
|
4 |
+
|
5 |
+
---
|
6 |
+
|
7 |
+
## 🚀 Mục tiêu
|
8 |
+
|
9 |
+
* Áp dụng kiến thức về **MLflow Model Versioning**
|
10 |
+
* So sánh các mô hình huấn luyện khác nhau (nhiều version)
|
11 |
+
* Triển khai online miễn phí bằng Hugging Face Spaces
|
12 |
+
|
13 |
+
---
|
14 |
+
|
15 |
+
## 📦 Dataset
|
16 |
+
|
17 |
+
Dữ liệu được sử dụng là **SMS Spam Collection Dataset**, bao gồm hơn 5.000 tin nhắn đã được phân loại sẵn là `ham` (hợp lệ) hoặc `spam`.
|
18 |
+
|
19 |
+
---
|
20 |
+
|
21 |
+
## 🧐 Mô hình
|
22 |
+
|
23 |
+
Các phiên bản được huấn luyện với pipeline gồm:
|
24 |
+
|
25 |
+
* `TfidfVectorizer`: để chuyển văn bản thành vector đặc trưng
|
26 |
+
* `Multinomial Naive Bayes`: mô hình phân loại đơn giản nhưng hiệu quả
|
27 |
+
|
28 |
+
Các version chỉ khác nhau về giá trị `alpha` (tham số làm trơn trong Naive Bayes):
|
29 |
+
|
30 |
+
* `model_spam_v1.pkl`: alpha = 1.0
|
31 |
+
* `model_spam_v2.pkl`: alpha = 0.5
|
32 |
+
* `model_spam_v3.pkl`: alpha = 0.3
|
33 |
+
|
34 |
+
---
|
35 |
+
|
36 |
+
## 🧪 Hướng dẫn sử dụng
|
37 |
+
|
38 |
+
1. Nhập nội dung tin nhắn cần phân loại
|
39 |
+
2. Chọn phiên bản mô hình (v1, v2, hoặc v3)
|
40 |
+
3. Nhấn **Dự đoán**
|
41 |
+
4. Ứng dụng sẽ trả về kết quả: `Spam ❌` hoặc `Ham ✅` và độ tin cậy
|
42 |
+
|
43 |
+
---
|
44 |
+
|
45 |
+
## 📌 Công nghệ sử dụng
|
46 |
+
|
47 |
+
* 🦍 Python
|
48 |
+
* 📊 Scikit-learn
|
49 |
+
* 📦 MLflow (log model & versioning)
|
50 |
+
* 🎨 Gradio (UI)
|
51 |
+
* ☁️ Hugging Face Spaces (hosting)
|
52 |
+
|
53 |
+
---
|
54 |
+
|
55 |
+
## 💻 Chạy offline trên máy local
|
56 |
+
|
57 |
+
Nếu bạn muốn chạy ứng dụng trên máy cá nhân, làm theo các bước sau:
|
58 |
+
|
59 |
+
### 🔧 1. Tạo môi trường ảo (virtual environment)
|
60 |
+
|
61 |
+
**Windows:**
|
62 |
+
|
63 |
+
```bash
|
64 |
+
python -m venv .venv
|
65 |
+
.venv\Scripts\activate
|
66 |
+
```
|
67 |
+
|
68 |
+
**macOS/Linux:**
|
69 |
+
|
70 |
+
```bash
|
71 |
+
python3 -m venv .venv
|
72 |
+
source .venv/bin/activate
|
73 |
+
```
|
74 |
+
|
75 |
+
---
|
76 |
+
|
77 |
+
### 📦 2. Cài đặt thư viện
|
78 |
+
|
79 |
+
```bash
|
80 |
+
pip install -r requirements.txt
|
81 |
+
```
|
82 |
+
|
83 |
+
---
|
84 |
+
|
85 |
+
### ▶️ 3. Chạy ứng dụng Gradio
|
86 |
+
|
87 |
+
```bash
|
88 |
+
python app.py
|
89 |
+
```
|
90 |
+
|
91 |
+
Ứng dụng sẽ chạy tại địa chỉ: [http://localhost:7860](http://localhost:7860)
|
92 |
+
|
93 |
+
---
|
94 |
+
|
95 |
+
### 📁 Cấu trúc thư mục mẫu
|
96 |
+
|
97 |
+
```
|
98 |
+
mlflow-spam-classifier/
|
99 |
+
├── app.py
|
100 |
+
├── requirements.txt
|
101 |
+
├── model_spam_v1.pkl
|
102 |
+
├── model_spam_v2.pkl
|
103 |
+
├── model_spam_v3.pkl
|
104 |
+
├── README.md
|
105 |
+
└── train_spam_model.py (tuỳ chọn)
|
106 |
+
```
|
107 |
+
|
108 |
+
---
|
109 |
+
|
110 |
+
✅ Sau khi chạy xong, bạn có thể nhập nội dung tin nhắn và chọn phiên bản mô hình để dự đoán trực tiếp mà không cần kết nối Internet.
|
app.py
ADDED
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
import joblib
|
3 |
+
|
4 |
+
# Load các mô hình đã export từ MLflow
|
5 |
+
models = {
|
6 |
+
"1": joblib.load("model_spam_v1.pkl"),
|
7 |
+
"2": joblib.load("model_spam_v2.pkl"),
|
8 |
+
"3": joblib.load("model_spam_v3.pkl"),
|
9 |
+
}
|
10 |
+
|
11 |
+
# Hàm dự đoán
|
12 |
+
def predict_spam(text, version):
|
13 |
+
model = models[version]
|
14 |
+
pred = model.predict([text])[0]
|
15 |
+
prob = model.predict_proba([text])[0][pred]
|
16 |
+
result = "Spam ❌" if pred == 1 else "Ham ✅"
|
17 |
+
return f"{result} (Độ tin cậy: {prob:.2%})"
|
18 |
+
|
19 |
+
# Giao diện Gradio
|
20 |
+
with gr.Blocks(title="SMS Spam Classifier - MLflow Versioning Demo") as demo:
|
21 |
+
gr.Markdown("## 📩 SMS Spam Classifier")
|
22 |
+
gr.Markdown("🔢 **Chọn phiên bản mô hình (MLflow Registry)** để phân loại tin nhắn.")
|
23 |
+
|
24 |
+
with gr.Row():
|
25 |
+
with gr.Column():
|
26 |
+
message_input = gr.Textbox(label="✉️ Nội dung tin nhắn", placeholder="Nhập tin nhắn cần kiểm tra...")
|
27 |
+
version_input = gr.Radio(choices=["1", "2", "3"], label="📦 Chọn version mô hình", value="1")
|
28 |
+
submit_btn = gr.Button("📤 Dự đoán")
|
29 |
+
|
30 |
+
with gr.Column():
|
31 |
+
result_output = gr.Textbox(label="📌 Kết quả", interactive=False)
|
32 |
+
|
33 |
+
submit_btn.click(fn=predict_spam, inputs=[message_input, version_input], outputs=result_output)
|
34 |
+
|
35 |
+
demo.launch()
|
model_spam_v1.pkl
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:b021280edfe769c9ca4b3dfc9ee9bab226feb1a2d61e125dcb8720e50e1960d3
|
3 |
+
size 466574
|
model_spam_v2.pkl
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:ed5feee17e3179500cb928abb1d185338eed53607c1c794afc954d1671b9ba28
|
3 |
+
size 466574
|
model_spam_v3.pkl
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:0c98f998afd9f3b4f6b4c8ec7f668b16f074752b700fe379dbd036b185bf3d3b
|
3 |
+
size 466574
|
requirements.txt
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
gradio
|
2 |
+
scikit-learn
|
3 |
+
joblib
|
train_spam_model.py
ADDED
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import pandas as pd
|
2 |
+
import mlflow
|
3 |
+
import mlflow.sklearn
|
4 |
+
from sklearn.feature_extraction.text import TfidfVectorizer
|
5 |
+
from sklearn.naive_bayes import MultinomialNB
|
6 |
+
from sklearn.pipeline import Pipeline
|
7 |
+
from sklearn.model_selection import train_test_split
|
8 |
+
from sklearn.metrics import accuracy_score
|
9 |
+
|
10 |
+
# Load dữ liệu
|
11 |
+
df = pd.read_csv("https://www.kaggle.com/datasets/uciml/sms-spam-collection-dataset/spam.csv", encoding='latin-1')[['v1', 'v2']]
|
12 |
+
df.columns = ['label', 'text']
|
13 |
+
df['label'] = df['label'].map({'ham': 0, 'spam': 1})
|
14 |
+
|
15 |
+
X_train, X_test, y_train, y_test = train_test_split(df['text'], df['label'], test_size=0.2, random_state=42)
|
16 |
+
|
17 |
+
# Pipeline gồm TF-IDF + Naive Bayes
|
18 |
+
pipeline = Pipeline([
|
19 |
+
('tfidf', TfidfVectorizer()),
|
20 |
+
('clf', MultinomialNB(alpha=1.0)) # bạn có thể thay đổi alpha để tạo version mới
|
21 |
+
])
|
22 |
+
|
23 |
+
pipeline.fit(X_train, y_train)
|
24 |
+
y_pred = pipeline.predict(X_test)
|
25 |
+
acc = accuracy_score(y_test, y_pred)
|
26 |
+
|
27 |
+
with mlflow.start_run():
|
28 |
+
mlflow.log_param("alpha", 1.0)
|
29 |
+
mlflow.log_metric("accuracy", acc)
|
30 |
+
mlflow.sklearn.log_model(pipeline, "model", registered_model_name="SpamClassifier")
|
31 |
+
print(f"Logged model with acc={acc}")
|