垃圾短信分类
使用说明:
-
需要准备 stopwords.txt 停用词表和 sms_dict.txt 自定义词表
-
原始数据文件需为竖线分隔的文本格式
-
首次运行可能需要安装依赖库:
pip install pandas jieba scikit-learn matplotlib seaborn wordcloud
或者
python -m pip install --upgrade pandas jieba scikit-learn matplotlib seaborn wordcloud
-
输出包含模型准确率、分类报告、混淆矩阵以及可视化图表
关键注释说明
-
数据加载阶段特别指定手机号为字符串类型,避免解析错误
-
清洗过程中通过 lambda 表达式生成垃圾短信标签,规则可根据需要调整
-
脱敏处理采用部分隐藏的方式保护隐私,同时处理异常情况
-
分词过程包含 URL 替换和停用词过滤,提高文本表示质量
-
模型训练使用 TF-IDF + 多项式贝叶斯组合,适合短文本分类
-
可视化部分展示了数据分布和关键词云,帮助理解数据特征
引入依赖库
python
import pandas as pd # 导入数据处理库
import jieba # 导入中文分词库
import re # 导入正则表达式库
from sklearn.feature_extraction.text import TfidfVectorizer # 导入TF-IDF向量化工具
from sklearn.model_selection import train_test_split # 导入数据集划分工具
from sklearn.naive_bayes import MultinomialNB # 导入多项式朴素贝叶斯分类器
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix # 导入模型评估指标
import matplotlib.pyplot as plt # 导入基础可视化库
import seaborn as sns # 导入高级可视化库
from wordcloud import WordCloud # 导入词云生成库
数据加载
python
# 1. 数据加载函数
def load_data(file_path):
# 使用pandas读取文本文件,指定分隔符为竖线,设置列名
# dtype参数确保手机号保持字符串类型,避免转换为整数
df = pd.read_csv(file_path, sep='|', header=None, names=['phone_number', 'datetime', 'content'], dtype={'phone_number': str})
return df # 返回加载后的数据框
数据清洗
python
# 2. 数据清洗函数
def clean_data(df):
# 去除重复行
df = df.drop_duplicates()
# 尝试将时间列转换为datetime格式,错误转换的条目设为NaN
df['datetime'] = pd.to_datetime(df['datetime'], errors='coerce')
# 删除时间列为空的无效行
df = df.dropna(subset=['datetime'])
# 生成垃圾短信标签:内容中同时包含http视为垃圾短信(1表示垃圾短信,0表示正常)
df['label'] = df['content'].apply(lambda x: 1 if 'http' in str(x) else 0)
# 过滤掉内容为空或过短的条目(保留至少5个字符的内容)
df = df[df['content'].apply(lambda x: isinstance(x, str) and len(x)>=5)]
return df # 返回清洗后的数据框
数据脱敏函数
python
# 3. 数据脱敏函数
def desensitize_data(df):
# 手机号脱敏:保留前3位和后4位,中间用****填充
# 检查是否为字符串且长度>=11,否则返回默认值
df['phone_number'] = df['phone_number'].apply(lambda x: x[:3] + '****' + x[7:] if (isinstance(x, str) and len(x)>=11) else '*******')
# 时间脱敏:仅保留日期部分(去除时间)
df['datetime'] = df['datetime'].dt.date
return df # 返回脱敏后的数据框
文本分词函数
python
# 4. 文本分词函数
def tokenize_text(text):
# 替换URL为统一标识
text = re.sub(r'http\S+', 'URL', text)
# 去除所有非字母数字字符
text = re.sub(r'[^\w\s]', '', text)
# 加载自定义词典
jieba.load_userdict('./data/sms_dict.txt')
# 使用jieba进行中文分词
tokens = jieba.lcut(text)
# 加载停用词表(需提前准备stopwords.txt文件)
with open('./data/stopwords.txt', 'r', encoding='utf-8') as f:
stopwords = set(f.read().splitlines())
# 过滤停用词并返回空格分隔的字符串
tokens = [word for word in tokens if word not in stopwords]
return ' '.join(tokens)
模型训练函数
python
# 5. 模型训练函数
def train_model(df):
# 初始化TF-IDF向量化器,保留前5000个最重要的特征
vectorizer = TfidfVectorizer(max_features=5000)
# 将文本转换为TF-IDF特征矩阵
X = vectorizer.fit_transform(df['clean_content'])
# 提取标签列
y = df['label']
# 划分训练集和测试集(80%训练,20%测试)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 初始化多项式朴素贝叶斯分类器
model = MultinomialNB()
# 模型训练
model.fit(X_train, y_train)
# 对测试集进行预测
y_pred = model.predict(X_test)
# 输出模型评估结果
print("准确率:", accuracy_score(y_test, y_pred))
print(classification_report(y_test, y_pred))
print("混淆矩阵:\n", confusion_matrix(y_test, y_pred))
return model, vectorizer # 返回训练好的模型和向量化器
数据可视化函数
python
# 6. 数据可视化函数
def visualize_data(df):
plt.rcParams['font.sans-serif'] = ['SimHei'] # 指定字体为 SimHei
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
# 绘制类别分布柱状图
plt.figure(figsize=(12, 6)) # 设置画布大小
sns.countplot(x='label', data=df) # 绘制计数图
plt.title('垃圾短信类别分布') # 设置标题
plt.xlabel('标签(1=垃圾短信)') # 设置x轴标签
plt.ylabel('数量') # 设置y轴标签
plt.savefig('./img/sms_bar.png', dpi=1080)
plt.show() # 显示图表
# 绘制类别分布饼图显示百分比
counts = df['label'].value_counts()
labels = ['正常短信', '垃圾短信']
colors = ['#4CAF50', '#FF5722']
plt.figure(figsize=(12, 6))
plt.pie(counts, labels=labels, colors=colors, autopct='%1.1f%%', pctdistance=0.85, startangle=90)
plt.title('短信类型百分比分布', fontsize=14)
plt.axis('equal')
# 添加数据标签
plt.text(-1.2, 0.5, f'正常短信\n{counts[0]}条', ha='center', va='center', fontsize=12)
plt.text(1.2, 0.5, f'垃圾短信\n{counts[1]}条', ha='center', va='center', fontsize=12)
plt.legend(title='类别', bbox_to_anchor=(1, 1), loc='upper left')
plt.savefig('./img/sms_pie.png', dpi=1080)
plt.show()
# 生成垃圾短信关键词云
all_text = ' '.join(df[df['label']==1]['clean_content']) # 提取所有垃圾短信内容
# 设置字体路径,这里需要根据你系统中字体的实际路径来设置
font_path = 'C:/Windows/Fonts/simkai.ttf'
wordcloud = WordCloud(font_path=font_path, width=800, height=400, background_color='white').generate(all_text) # 生成词云
plt.figure(figsize=(12, 6)) # 设置画布大小
plt.imshow(wordcloud) # 显示词云图像
plt.axis("off") # 关闭坐标轴
plt.title('垃圾短信关键词云') # 设置标题
plt.savefig('./img/sms_word_could.png', dpi=1080)
plt.show() # 显示图表
主程序流程
python
# 主程序流程
if __name__ == "__main__":
# 1. 加载原始数据
df = load_data('./data/sms_data.txt')
# 2. 数据清洗
df_clean = clean_data(df)
# 3. 数据脱敏处理
df_desensitized = desensitize_data(df_clean)
# 4. 文本预处理(分词和停用词过滤)3
df_desensitized['clean_content'] = df_desensitized['content'].apply(tokenize_text)
# 5. 模型训练与评估
model, vectorizer = train_model(df_desensitized)
# 6. 可视化分析结果
visualize_data(df_desensitized)
Building prefix dict from the default dictionary ...
Loading model from cache C:\Users\64626\AppData\Local\Temp\jieba.cache
Loading model cost 0.454 seconds.
Prefix dict has been built successfully.
准确率: 1.0
precision recall f1-score support
0 1.00 1.00 1.00 44983
1 1.00 1.00 1.00 15017
accuracy 1.00 60000
macro avg 1.00 1.00 1.00 60000
weighted avg 1.00 1.00 1.00 60000
混淆矩阵:
[[44983 0]
[ 0 15017]]