ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

SVM垃圾短信识别系统:Python实现与部署全攻略

SVM垃圾短信识别系统:Python实现与部署全攻略 简介面向课程设计、毕业设计及期末大作业场景这份Python机器学习项目完整实现了基于SVM的垃圾短信识别系统。项目源码按模块组织DataProcess.py完成中文文本预处理SVM_Trainer.py负责训练分类模型Message_Classify.py用于预测单条短信类别并附带基于Apache/PHP的在线演示模块model目录保存训练好的模型Data目录提供带标签与无标签短信数据以及特征、TF-IDF向量等中间结果方便复现与二次开发。配套的项目说明和设计报告系统梳理了从数据清洗、中文分词、TF-IDF特征提取到SVM建模与评估的完整流程可帮助计算机、人工智能、大数据等专业学生快速上手文本分类项目。资源包整体约107.89MB以Python源码、数据文件、说明文档等为主压缩包结构清晰目前已吸引289人学习下载适合作为课程设计、大作业、初期项目立项演示及SVM入门进阶的参考资料。1. 当课程设计遇上垃圾短信SVM 方案为什么还能打如果你正在为课程设计或期末大作业找一个「机器学习味够浓、又能跑通演示」的项目垃圾短信识别几乎是最合适的切入点。它不像图像识别那样吃显卡也不像推荐系统那样依赖大规模数据一份几千条的带标签短信就能完成「数据预处理 → 特征工程 → SVM 训练 → 在线预测」的完整闭环正好覆盖机器学习课程的核心知识点。这套基于 Python 的 SVM 垃圾短信识别系统就是按这个思路组织的本地脚本负责训练和评估Apache PHP 负责把模型包成一个能访问的网页服务演示完直接截图写报告工作量一眼看得见。我用的时候最直接的感受是项目结构非常「课程设计友好」。model 目录放训练产物Data 目录集中管理带标签和无标签数据code 目录下三个 Python 脚本各管一段——预处理、训练、预测职责清晰到可以照着画系统架构图。适合计科、大数据、人工智能专业的本科生直接用作课程设计或毕业设计的前置验证。接下来我会按真实复现的顺序把数据格式、SVM 训练参数、网页部署步骤和几个容易翻车的坑逐一拆开讲。2. 中文短信的向量化DataProcess.py 与 TF-IDF 特征工程2.1 先搞懂数据长什么样label.txt 与 nolabel.txt整个项目的起点是 Data 目录下的两个文本文件。label.txt 是带标签数据每一行是一条短信标签和内容之间用分隔符切开nolabel.txt 是不带标签的原始短信训练完模型后用来做效果检验。从课程设计的角度这两个文件的组织方式决定了后续脚本不用大改。首次打开 label.txt 时建议先确认两件事一是标签用什么符号分隔二是正负样本比例是否失衡到会影响训练。常见格式是「标签\t短信内容」比如 1 表示垃圾短信、0 表示正常短信。项目里的 DataProcess.py 就是围绕这种格式写的如果你手里的数据是 CSV 或者标签在末尾需要先做一次格式统一。提示训练前把 label.txt 的行数记下来后面拆分训练集和测试集时比例是否合理直接看这两个数就够了。2.2 预处理脚本到底做了什么DataProcess.py 的职责是把原始短信文本转成后续 SVM 能接受的数值特征。中文文本和英文不一样词与词之间没有天然空格所以第一步永远是分词。项目采用的方案是把 jieba 分词结果按空格拼接再用 TF-IDF 向量化。来看核心代码# -*- coding: utf-8 -*- import jieba from sklearn.feature_extraction.text import TfidfVectorizer import json import numpy as np def load_labeled_data(filepath): texts, labels [], [] with open(filepath, r, encodingutf-8) as f: for line in f: line line.strip() if not line: continue # 按制表符切分前半是标签后半是短信内容 parts line.split(\t) if len(parts) 2: labels.append(int(parts[0])) texts.append(parts[1]) return texts, labels def preprocess_text(text): # 去掉多余空白字符统一小写对中文影响不大但能兼容英文短信 text .join(text.split()) # jieba 精确模式分词结果用空格连接方便后续 TF-IDF 处理 seg_list jieba.cut(text, cut_allFalse) return .join(seg_list) texts, labels load_labeled_data(Data/label.txt) # 对每条短信做分词预处理 corpus [preprocess_text(t) for t in texts] # TF-IDF 向量化ngram_range 控制特征粒度max_features 限制特征维度 vectorizer TfidfVectorizer(ngram_range(1, 2), max_features5000) X vectorizer.fit_transform(corpus) # 保存向量化结果和标签供训练脚本复用 from scipy.io import mmwrite mmwrite(Data/X.mtx, X) with open(Data/y.json, w, encodingutf-8) as f: json.dump(labels, f) # 保存特征词表方便排查特征是否合理 with open(Data/feature.json, w, encodingutf-8) as f: json.dump(vectorizer.get_feature_names_out().tolist(), f, ensure_asciiFalse)这段代码把预处理和向量化一次性完成逻辑上有几个关键点值得注意。ngram_range(1, 2)意味着同时保留单个词和相邻双词作为特征像「中奖」「恭喜您」这类短信高频双词会被单独捕获max_features5000限制了特征维度防止稀疏矩阵过大拖慢 SVM 训练。分词后的语料以空格连接是因为 TfidfVectorizer 默认按空白字符切分 token这与英文文本的处理方式一致。保存环节同样有讲究。X.mtx 用 Matrix Market 格式存储稀疏矩阵SVM_Trainer.py 读取时不需要重新分词和向量化直接进入训练环节。y.json 保存标签列表feature.json 保存特征词表后面排查模型问题时对照词表看权重才有依据。做完这一步Data 目录下就多出了四个文件它们之间的依赖关系是X.mtx y.json 决定训练输入feature.json 决定特征含义vec_tfidf 则会在训练脚本里生成。2.3 特征工程的边界什么时候该加自定义词表跑通默认流程不算难但课程设计答辩时老师大概率会问一句「你做了什么特征工程」。只靠 jieba 分词 TF-IDF 是基准方案想加分可以在 DataProcess.py 里加一个自定义词典。比如短信里常见的「代开票」「信用卡提额」这类词jieba 可能切成「代开 / 票」「信用卡 / 提额」语义就散了。# 加载自定义词典强制 jieba 按领域词切分 jieba.load_userdict(Data/userdict.txt)userdict.txt 每行一个词格式是「词 词频 词性」词频可以随便写比如「代开票 10 n」。加词典后重新跑 DataProcess.py再对比 feature.json 里是否出现这些完整词。这一步的成本很低但能体现出对中文分词和领域知识的理解属于典型的低成本高回报操作。注意TfidfVectorizer 的 max_features 如果设得太小自定义词典里的词可能因为词频低而被裁掉。改完词典后建议同时检查这个参数特征空间要给领域词留位置。3. 训练 SVM 模型核函数选择与参数调优3.1 SVM_Trainer.py 的训练流程预处理产出的 X.mtx 和 y.json 直接喂给 SVM_Trainer.py。项目使用的训练脚本核心逻辑是读取稀疏矩阵 → 划分训练集和测试集 → 用网格搜索找一组好参数 → 训练并保存模型。代码大致如下# -*- coding: utf-8 -*- from scipy.io import mmread import json from sklearn.model_selection import train_test_split, GridSearchCV from sklearn.svm import SVC from sklearn.metrics import classification_report, confusion_matrix import joblib # 读取预处理结果 X mmread(Data/X.mtx) with open(Data/y.json, r, encodingutf-8) as f: y json.load(f) # 按 8:2 划分训练集和测试集stratify 保证正负样本比例一致 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42, stratifyy ) # 候选参数线性核 一组惩罚系数 C param_grid { C: [0.1, 1, 10, 100], kernel: [linear] } # 5 折交叉验证用 F1 分数作为评选标准更贴合类别不平衡场景 grid GridSearchCV( SVC(), param_grid, cv5, scoringf1, n_jobs-1 ) grid.fit(X_train, y_train) print(best params:, grid.best_params_) print(best score:, grid.best_score_) # 用最优参数重新训练并评估 best_model grid.best_estimator_ y_pred best_model.predict(X_test) print(classification_report(y_test, y_pred)) print(confusion_matrix(y_test, y_pred)) # 模型和向量器一起保存预测脚本才能完整还原特征处理过程 joblib.dump(best_model, model/svm_model.pkl)训练脚本里有几个参数值得逐一说清。test_size0.2是常见的 8:2 划分random_state42固定随机种子保证每次运行划分结果一致——这一点在答辩时要能说出来否则助教跑你的代码两次结果不一样会很难解释。stratifyy是按标签比例分层抽样垃圾短信通常只占一小部分若不分层测试集里可能一条垃圾短信都没有评估指标会虚高。GridSearchCV的候选参数只有C和kernel两组其中核函数只选了 linear。为什么不用 RBF因为文本 TF-IDF 特征本身就是高维稀疏的线性核在这个场景下往往已经够用RBF 核反而更容易过拟合训练时间也更长。C 是误分类惩罚系数C 越大模型对训练集的拟合越强越小越倾向于宽泛的决策边界。候选范围 0.1 到 100 覆盖了从欠拟合到过拟合的典型区间5 折交叉验证选出来的 C 基本能落在合理范围里。3.2 评估指标不能只看准确率课程设计报告里最容易犯的错误是只写 accuracy。垃圾短信识别属于典型的类别不平衡问题正常短信可能占 90% 以上如果模型把所有短信都判为正常准确率也有 90%。所以脚本里特意用scoringf1做网格搜索的评选标准输出里也包含 precision、recall、F1 和混淆矩阵。这三个指标在这个场景下的含义分别是精确率高说明判成垃圾短信的里面真垃圾的比例高误伤少召回率高说明真正的垃圾短信被抓住的比例高漏放少F1 是两者的调和平均防止为了压误伤而放过大量垃圾短信。报告里建议把分类报告和混淆矩阵的截图都放进去矩阵里四个格子分别对应 TN、FP、FN、TP能直接看出模型在哪个方向上犯错更严重。注意如果训练后模型把所有样本都判成正常短信先别急着调参回去看 label.txt 里的标签分布。正负样本比例如果超过 9:1F1 分数会明显低于准确率这时候优先处理类别不平衡问题比如用 class_weightbalanced 或在预处理阶段做欠采样。3.3 模型保存与加载的完整闭环训练结束后脚本用joblib.dump保存了训练好的 sklearn 模型但只保存模型是不够的。Message_Classify.py 在预测新短信时必须先做分词和 TF-IDF 向量化而且 TfidfVectorizer 的词表必须和训练时完全一致否则特征维度对不上直接报错。所以我在写这类项目时习惯把 vectorizer 也一并保存joblib.dump(vectorizer, model/tfidf_vectorizer.pkl)这样预测阶段就是标准的「加载两个 pkl → 分词 → transform → predict」流程完整闭环。项目本身的代码结构里预测部分就是按这个思路组织的课程设计报告里画系统架构图时模型文件和向量器文件正好作为中间产物串起全流程。4. 从命令行到网页Message_Classify.py 预测与 Apache 部署4.1 单条短信预测的脚本实现训练好的模型不能只躺在 pkl 文件里Message_Classify.py 就是用来干活的。它的输入是一条短信文本输出是垃圾或正常的判定结果。这里直接给出一个兼容项目结构的预测脚本写法# -*- coding: utf-8 -*- import jieba import joblib def load_model(): # 加载训练阶段保存的模型和向量器 model joblib.load(model/svm_model.pkl) vectorizer joblib.load(model/tfidf_vectorizer.pkl) return model, vectorizer def predict_message(model, vectorizer, text): # 与训练时保持一致先清洗再分词 text .join(text.split()) seg_list jieba.cut(text, cut_allFalse) corpus [ .join(seg_list)] # transform 而不是 fit_transform保证使用训练时的词表 X_vec vectorizer.transform(corpus) pred model.predict(X_vec)[0] return 垃圾短信 if pred 1 else 正常短信 if __name__ __main__: model, vectorizer load_model() test_msgs [ 恭喜您获得某某公司抽奖资格点击链接领取奖品, 明天下午三点会议室开会请准时参加 ] for msg in test_msgs: result predict_message(model, vectorizer, msg) print(f短信内容: {msg}) print(f判定结果: {result}) print(---)预测脚本里最容易忽略的是vectorizer.transform而不是fit_transform。fit_transform会重新学习词表如果新短信里出现训练时没见过的词特征维度就变了模型直接无法 predict。使用transform则保持词表固定在训练时的状态新词会被忽略这正是文本分类的标准做法。单条预测没问题后下一步就是把它接进网页。项目提供的方案是用 PHP 做后端调 Python 脚本执行预测再把结果回显到页面。这个架构在课程设计里足够演示老师看到的是网页交互底层原理又能讲清楚。4.2 Apache 目录结构与 PHP 调用 Python 的细节项目说明里明确写了把 SPAM_CLASSIFY_online 文件夹放到 Apache 根目录index.php 放在 Apache 根目录下。这意味着整个在线模块是一个典型的 LAMP 环境——Linux Apache MySQL PHP外加 Python 2.7 作为预测引擎。目录结构示意如下/var/www/html/ ├── index.php # 前端页面负责收集输入和展示结果 └── SPAM_CLASSIFY_online/ ├── classify.py # PHP 调用的预测脚本 └── model/ # 模型文件副本PHP 调用 Python 的常见做法是使用shell_exec或exec函数把短信内容作为参数传给 Python 脚本脚本打印结果后 PHP 捕获输出。核心 PHP 代码类似这样?php if ($_SERVER[REQUEST_METHOD] POST) { $msg $_POST[message]; // 转义特殊字符防止命令行注入 $msg_escaped escapeshellarg($msg); // 调用 Python 脚本第二个参数是待预测短信 $output shell_exec(python classify.py $msg_escaped 21); // $output 格式约定为一行文本垃圾短信|正常短信 $result trim($output); } ?这里有两个细节值得注意。第一escapeshellarg必须加短信内容如果包含单引号或分号不转义可能导致命令注入这在课程设计的代码评审里是大扣分项。第二21的作用是把 Python 的 stderr 也并入 stdout这样 Python 脚本报错时 PHP 页面能直接看到错误信息排查问题不用反复翻 Apache 日志。4.3 Python 2.7 环境下的兼容性处理项目标注的运行环境是 Python 2.7这一点在复现时要格外留意。Python 2.7 和 Python 3 在 print 语法、字符串编码、json.load行为上都有差异如果直接把项目脚本丢到 Python 3 环境里跑大概率会报 SyntaxError 或编码错误。我一般建议在课程设计演示时用项目标注的环境即 Python 2.7 Apache PHP。如果本机实在装不上 Python 2.7退而求其次的方案是把脚本里的print语句改成print()函数并在文件头部加上from __future__ import print_function, unicode_literals。但要注意 sklearn 的版本兼容——Python 2.7 能用的 sklearn 最高到 0.20.x新版 sklearn 早已放弃 Python 2 支持装新版本会直接提示环境不满足。5. 避坑指南复现这个项目最容易翻车的五个地方5.1 第一次训练报错维度不匹配现象运行 SVM_Trainer.py 时sklearn 报ValueError: X has 5000 features, but SVC is expecting 5001 features before fitting。原因DataProcess.py 跑完之后又手动改过max_features参数重新生成了 X.mtx但 model 目录下残留了旧模型文件或者预测脚本里加载的 vectorizer 与当前训练用的 vectorizer 不是同一份。解决把 Data 目录下的 X.mtx、y.json、feature.json 全部删掉重新运行 DataProcess.py再跑 SVM_Trainer.py。也就是说凡是改了特征工程的配置就必须按「预处理 → 训练 → 保存」的顺序完整重跑一遍不能只重训模型。5.2 模型把所有短信都判为正常现象混淆矩阵显示模型几乎没有预测出垃圾短信但准确率却很高。原因训练集里正常短信占比过高SVM 为了最小化整体误差倾向于把所有样本判为多数类。这是类别不平衡问题的典型表现。解决在 SVC 中加class_weightbalanced让模型根据类别频率自动调整权重或在 DataProcess 阶段对多数类样本进行随机欠采样让正负样本比例接近 1:1。改完后重新训练对比 F1 分数的变化。5.3 中文乱码预处理环节的编码陷阱现象打印分词结果时显示\u6b63\u5e38之类的内容或者训练时报UnicodeDecodeError。原因数据文件不是 UTF-8 编码可能是 GBK 或 GB2312Python 2.7 默认按 ASCII 读取文件导致解码失败。解决打开 label.txt 确认编码格式用 VSCode 或 Notepad 另存为 UTF-8 without BOM。如果数据量大不方便转换在open时指定编码codecs.open(filepath, r, utf-8)Python 2.7 下codecs模块比内置open对中文更友好。5.4 sklearn 版本与 Python 版本不匹配现象安装 sklearn 时提示需要 Python 3.x或 import sklearn 直接报错。原因项目运行环境标注的是 Python 2.7但部分机器安装的 sklearn 是最新版本只支持 Python 3.7 以上。解决如果坚持使用 Python 2.7用pip install scikit-learn0.20.4锁定历史版本。如果是课程设计且老师允许直接把环境迁移到 Python 3.8 scikit-learn 1.0 以上同时修改脚本里的 print 语法这是更省事的路线。5.5 Apache 页面无法访问 Python 脚本现象浏览器打开 index.php 能显示页面但点击「识别」按钮后空白Apache 错误日志里有command not found或permission denied。原因PHP 调用python命令时Apache 的 PATH 环境变量里没有 Python 安装路径或者脚本文件没有执行权限。解决在 PHP 代码里改用 Python 的绝对路径比如shell_exec(/usr/bin/python /var/www/html/SPAM_CLASSIFY_online/classify.py $msg_escaped)。同时给脚本目录加执行权限chmod -R 755 SPAM_CLASSIFY_online。Windows 环境下注意 Python 路径通常形如C:\Python27\python.exe。6. 进阶玩法从课程设计到能写进简历的完整项目模型跑通只是第一步如果想让这个课程设计在答辩和简历上更有分量可以往三个方向做延伸。第一个方向是特征层目前只用 TF-IDF 统计特征可以加入短信长度、数字占比、URL 数量等手工特征和 TF-IDF 拼接成混合特征向量。SVM 对特征缩放敏感拼接后记得用StandardScaler做标准化from sklearn.preprocessing import StandardScaler from scipy.sparse import hstack # 手工特征向量假设从原始文本提取了 3 个数值特征 import numpy as np extra_features np.array([[len(msg), digit_ratio, url_count] for msg in texts]) # 标准化数值特征 scaler StandardScaler() extra_features_scaled scaler.fit_transform(extra_features) # 与 TF-IDF 稀疏矩阵横向拼接 X_combined hstack([X, extra_features_scaled])第二个方向是做错误案例分析。把混淆矩阵里 FP 和 FN 的样本各挑 5 条打印出来逐个分析模型为什么判错。比如某些正常短信包含「退订」「回复 TD」反而触发了垃圾短信特征某些广告短信伪装成「快递通知」绕过了模型。把这些案例整理成表格写进课程设计报告答辩时讲「我从 10 条误判样本里发现了一个规律然后针对性加了特征」效果远比「我调参调到 F1 到了 0.97」更有说服力。第三个方向是把模型评估做扎实。我刚拿到这个项目时第一反应是直接跑训练后来发现把评估逻辑理清楚更重要按 8:2 划分训练测试集后还需要把所有样本重新训练一遍——训练集里那 20% 的测试样本也是宝贵的有标签数据。最终交付给网页服务的模型应该是在全部 label.txt 上重训的版本# 网格搜索确定参数后用全部数据重训最终模型 final_model SVC(Cgrid.best_params_[C], kernellinear, class_weightbalanced) final_model.fit(X, y) joblib.dump(final_model, model/svm_final.pkl)这会让模型的泛化能力比只用训练集训练更强页面演示时的识别效果也更好。从那以后我每次做文本分类的课程设计都会在报告里留出一节专门写「测试集之外的验证」比如从 nolabel.txt 里随机抽出几十条未参与训练的真实短信跑一轮人工评估确认模型在完全陌生的数据上不会崩盘。这套流程走完这个项目就不只是「能跑的课程设计」而是一个能讲清楚特征工程、模型选择、部署链路和评估逻辑的完整案例了希望帮到你。本文还有配套的精品资源点击获取
返回列表