ARTICLE DETAIL

资讯详情

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

基于NLP的中文垃圾短信识别实战:从分词、TF-IDF到BERT模型全流程

基于NLP的中文垃圾短信识别实战:从分词、TF-IDF到BERT模型全流程 简介这份资源面向计算机相关专业的本科生与NLP入门学习者提供一套完整的中文文本分类实战方案以垃圾短信识别为具体场景帮助读者理解从数据到模型落地的全流程。压缩包共8个文件约38.02MB包含3个txt数据与停用词文件、2个pkl持久化模型、1个Python训练脚本、1份Markdown说明文档及1张流程图覆盖数据、模型与代码三类核心内容。数据集采用「标签\t文本」格式正样本为垃圾短信、负样本为正常短信便于直接加载训练。技术栈基于Python3.6、jieba与Scikit-learn默认使用SVM支持向量机完成分类并预留了替换其他分类模型的接口运行train.py即可启动训练。目前已有4450人学习下载适合作为毕业设计参考或NLP分类任务的练手项目帮助读者快速掌握中文分词、特征提取与模型评估的完整链路。1. 垃圾短信识别从一条“中奖通知”说起你手机里一定躺着这样一条短信“尊敬的客户您已获得XX平台幸运用户资格点击链接领取万元奖金。”这条短信背后是一个经典的 NLP 中文文本分类任务——垃圾短信识别。本科毕业设计选这个题目好处是数据好找、任务定义清晰、模型效果肉眼可见难处在于真正动手时你会发现中文分词、样本不均衡、特征工程、模型选型每一步都有坑。这个方向适合有 Python 基础、想用一个小而完整的项目把自然语言处理全流程跑通的人。它不需要 GPU 集群一台普通笔记本就能完成从数据清洗到模型部署的全部环节。接下来我会按实际做项目的顺序把中文文本分类在垃圾短信识别上的落地路径拆开讲清楚包括每一步的参数怎么设、代码怎么写、翻车了怎么救。2. 数据准备与中文文本预处理把原始短信变成模型能吃的格式2.1 垃圾短信数据集长什么样去哪找公开的垃圾短信数据集常见的有 SMS Spam Collection但它以英文为主。中文垃圾短信数据一般来自两个渠道一是 GitHub 上一些毕设项目整理好的 CSV通常包含 label 和 text 两列二是自己从公开短信语料中筛选。我一般会先用一份 5000 到 10000 条规模的中文短信数据集起步正负样本比例控制在 1:3 到 1:5 之间比较接近真实场景。拿到数据后第一件事不是急着建模而是先看三样东西类别分布、文本长度分布、有没有乱码或空值。用 pandas 几行代码就能摸清底细import pandas as pd df pd.read_csv(spam_sms.csv, encodingutf-8) print(df[label].value_counts()) # 类别分布 print(df[text].str.len().describe()) # 文本长度统计 print(df.isnull().sum()) # 空值检查 print(df.sample(5)) # 随机看几条逻辑说明value_counts让你知道正负样本差多少如果垃圾短信只占 5%后面必须做重采样或调权重。str.len().describe()给出长度分布中文短信大多在 20 到 70 字之间超过 200 字的极少这决定了后面截断长度设多少。空值检查不用多说有缺失就填或删。参数说明encoding一定要显式指定中文 CSV 常见 utf-8 和 gbk 两种读进来乱码就换一个试。sample(5)是血泪经验不看原始数据直接跑模型后面发现标签和文本错位了都不知道。2.2 中文分词与停用词处理jieba 的三种模式和自定义词典中文和英文不一样英文按空格切就行中文必须先分词。垃圾短信识别里分词质量直接影响特征质量。jieba 是绕不开的工具它有三种模式精确模式、全模式、搜索引擎模式。垃圾短信里经常出现“免费领取”“点击链接”“中奖通知”这类短语用精确模式容易把它们切碎我一般用搜索引擎模式再配合自定义词典。import jieba # 加载自定义词典把垃圾短信高频词加进去 jieba.load_userdict(spam_dict.txt) # 每行一个词如免费领取、点击链接、中奖通知 def cut_text(text): # 搜索引擎模式适合短文本 words jieba.lcut_for_search(text) # 过滤停用词和单字 stopwords set(open(stopwords.txt, encodingutf-8).read().split()) words [w for w in words if w not in stopwords and len(w) 1] return .join(words) df[cut_text] df[text].apply(cut_text)逻辑说明lcut_for_search会对长词再切一次召回率更高。自定义词典是关键垃圾短信有很多固定搭配不加词典会被切得七零八落。停用词表网上有很多版本但建议自己根据数据补充比如“尊敬的”“您好”这类在正常短信和垃圾短信里都出现的词留着反而干扰。参数说明len(w) 1过滤单字因为单字在短文本里区分度太低。停用词文件一行一个词注意编码统一。自定义词典的格式是“词语 词频 词性”词频和词性可以省略只写词语也行。2.3 文本向量化TF-IDF 和词袋模型怎么选分词之后要把文本变成数值向量。垃圾短信识别最常用的两种方式是词袋模型和 TF-IDF。词袋模型只看词出现没出现TF-IDF 还考虑词在整个语料里的重要性。对于垃圾短信这种短文本TF-IDF 通常比词袋好一点因为它能压低“的”“了”这种高频但无区分度的词。from sklearn.feature_extraction.text import TfidfVectorizer vectorizer TfidfVectorizer( max_features5000, # 保留最高频的5000个词 ngram_range(1, 2), # 考虑单字和双字组合 min_df3, # 至少出现在3条短信里才保留 max_df0.8 # 出现在超过80%短信里的词丢掉 ) X vectorizer.fit_transform(df[cut_text]) print(X.shape) # (样本数, 特征数)逻辑说明max_features5000是经验值中文短信词汇量不大5000 个特征足够覆盖。ngram_range(1,2)让模型能捕捉“免费 领取”这样的双词组合对垃圾短信识别很有用。min_df3过滤掉只出现一两次的稀有词减少噪声。max_df0.8把“你好”“谢谢”这种几乎所有短信都有的词压掉。参数说明如果数据量小于 3000 条max_features可以降到 3000min_df改成 2。ngram_range上到 (1,3) 会显著增加特征数训练变慢效果不一定提升建议先试 (1,2)。3. 模型选型与训练从朴素贝叶斯到 BERT 的实战对比3.1 朴素贝叶斯为什么是垃圾短信识别的基线首选朴素贝叶斯在文本分类里是出了名的“简单但能打”。它假设特征之间相互独立虽然这个假设在中文里明显不成立但在垃圾短信这种短文本、高维稀疏的场景下效果出奇地稳。训练速度快不需要调太多参数非常适合做第一个基线模型。from sklearn.naive_bayes import MultinomialNB from sklearn.model_selection import train_test_split from sklearn.metrics import classification_report X_train, X_test, y_train, y_test train_test_split( X, df[label], test_size0.2, random_state42, stratifydf[label] ) nb MultinomialNB(alpha1.0) # alpha是平滑参数 nb.fit(X_train, y_train) y_pred nb.predict(X_test) print(classification_report(y_test, y_pred))逻辑说明stratifydf[label]保证训练集和测试集的类别比例一致垃圾短信样本少的时候尤其重要。alpha1.0是拉普拉斯平滑防止某个词在训练集里没出现导致概率为零。classification_report给出精确率、召回率、F1垃圾短信识别里召回率比精确率更值得关注因为漏掉一条垃圾短信比误判一条正常短信代价更大。参数说明alpha可以调一般 0.1 到 2.0 之间试。如果垃圾短信召回率低可以适当降低 alpha让模型更激进。test_size0.2是常规做法数据少于 2000 条时用 0.3。3.2 用 SVM 和随机森林做效果对比参数怎么调朴素贝叶斯跑通后下一步一般是上 SVM 和随机森林做对比。SVM 在文本分类里表现通常比朴素贝叶斯好尤其是线性核。随机森林能给出特征重要性方便分析哪些词最能区分垃圾短信。from sklearn.svm import LinearSVC from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import f1_score # 线性SVM svm LinearSVC(C1.0, class_weightbalanced, max_iter5000) svm.fit(X_train, y_train) svm_pred svm.predict(X_test) print(SVM F1:, f1_score(y_test, svm_pred, pos_label1)) # 随机森林 rf RandomForestClassifier( n_estimators200, max_depth20, class_weightbalanced, random_state42 ) rf.fit(X_train, y_train) rf_pred rf.predict(X_test) print(RF F1:, f1_score(y_test, rf_pred, pos_label1))逻辑说明class_weightbalanced让模型自动按类别比例调整权重垃圾短信少的时候必须加否则模型会倾向于全预测成正常短信。LinearSVC的C控制正则化强度C 越大越容易过拟合。max_iter5000是防止不收敛的后悔药默认 1000 在文本数据上经常不够。参数说明SVM 的C建议在 0.1、1、10 三个值里试。随机森林的n_estimators200 到 500 都行max_depth20 左右太深会过拟合。两个模型都跑完后用 F1 对比通常 SVM 会比朴素贝叶斯高 2 到 5 个百分点。3.3 BERT 微调什么时候值得上预训练模型如果前面几个传统模型 F1 已经到 0.95 以上BERT 的提升空间就不大了。但如果数据里有很多隐晦的垃圾短信比如用谐音、拼音、符号绕过的传统模型很容易翻车这时候 BERT 的优势就出来了。BERT 能理解上下文对“薇信”“扣扣”这类变体识别更好。from transformers import BertTokenizer, BertForSequenceClassification import torch tokenizer BertTokenizer.from_pretrained(bert-base-chinese) model BertForSequenceClassification.from_pretrained(bert-base-chinese, num_labels2) def encode(texts, labels): enc tokenizer(texts, paddingTrue, truncationTrue, max_length64, return_tensorspt) return enc, torch.tensor(labels) # 训练循环省略核心是max_length64逻辑说明中文短信短max_length64足够覆盖 99% 的样本设太大浪费显存。bert-base-chinese是常用中文预训练模型毕设场景够用。训练时学习率设 2e-5 到 5e-5batch size 16 或 32epoch 3 到 5 轮就收敛。参数说明BERT 需要 GPU没有的话用 Colab 免费版也能跑。如果数据少于 3000 条BERT 容易过拟合建议只微调最后两层或者用更小的模型如bert-tiny。传统模型 F1 已经很高时不建议为了涨一个点去上 BERT投入产出比不划算。4. 避坑与排查垃圾短信识别里最容易翻车的五个地方4.1 样本不均衡导致模型全预测成正常短信现象训练完模型准确率 95%但垃圾短信召回率只有 10%。原因垃圾短信只占 5%模型发现全预测成正常短信就能拿 95% 准确率直接摆烂。解决用class_weightbalanced或者对垃圾短信过采样或者用 SMOTE 生成合成样本。最推荐的是调权重简单有效。4.2 分词把关键信息切碎导致特征失效现象模型效果怎么调都上不去看特征重要性发现全是“的”“了”这种词。原因分词模式不对或者没加自定义词典“免费领取”被切成“免费”和“领取”模型学不到完整语义。解决换搜索引擎模式加自定义词典把垃圾短信高频短语加进去。词典不用多50 到 100 个词就能明显改善。4.3 测试集里混入训练集导致指标虚高现象交叉验证 F1 0.98上线一测只有 0.7。原因数据预处理时先做了 TF-IDF 再划分训练测试集或者划分时没设随机种子导致同一条短信同时出现在训练和测试里。解决先划分训练测试集再在训练集上 fit 向量化器测试集只用 transform。train_test_split的random_state固定住。4.4 停用词表把关键否定词删掉了现象正常短信“我没有中奖”被预测成垃圾短信。原因停用词表里有“没有”“不”这类否定词被过滤后模型只看到“中奖”。解决检查停用词表把否定词、程度副词保留。垃圾短信识别里“不”“没”“未”这些词对判断语义方向很重要不能一刀切。4.5 模型上线后遇到新变体垃圾短信失效现象训练时 F1 0.96上线一个月后用户投诉漏判变多。原因垃圾短信发送者在进化开始用拼音、谐音、特殊符号绕过关键词。解决定期用新数据重新训练或者在预处理阶段做归一化把拼音转汉字、全角转半角、去掉特殊符号。更彻底的做法是上 BERT它对变体的鲁棒性比传统模型强很多。5. 把模型用起来从离线评估到在线预测的最后一公里模型训练完不是终点毕设里能演示一个完整的预测流程会加分很多。我一般会写一个简单的预测函数把训练好的向量化器和模型串起来输入一条原始短信输出分类结果和置信度。import numpy as np def predict_sms(text, vectorizer, model): # 复用训练时的分词逻辑 words cut_text(text) # 注意这里用transform不是fit_transform vec vectorizer.transform([words]) # 预测类别和概率 pred model.predict(vec)[0] # 朴素贝叶斯和SVM的概率获取方式不同 if hasattr(model, predict_proba): prob model.predict_proba(vec)[0] confidence prob[pred] else: confidence None label 垃圾短信 if pred 1 else 正常短信 return label, confidence # 测试几条 test_msgs [ 恭喜您获得一等奖点击链接领取, 明天下午三点开会别迟到, 您的快递已到请凭取件码领取 ] for msg in test_msgs: label, conf predict_sms(msg, vectorizer, nb) print(f{msg[:20]}... - {label}, 置信度: {conf})逻辑说明transform和fit_transform的区别是新手最容易犯的错测试时必须用transform否则向量空间和训练时不一致。predict_proba不是所有模型都有SVM 需要用decision_function再转概率或者直接用LinearSVC的predict结果。置信度可以帮助判断哪些短信需要人工复核。参数说明cut_text函数必须和训练时完全一致包括分词模式、停用词表、自定义词典。任何一处不一致都会导致特征错位。置信度低于 0.7 的可以标记为“待人工确认”在实际系统里很有用。验证方法上除了看 F1我习惯再做一个混淆矩阵重点看假阴性和假阳性。假阴性是垃圾短信被放过了假阳性是正常短信被误拦了。毕设答辩时老师通常会问“你这个模型误判了怎么办”提前准备好混淆矩阵和几个典型错误案例比空谈指标有说服力。最后说一个我自己的习惯每次跑完模型我都会把预测错误的样本单独拉出来看一遍。十有八九能发现数据标注错误、分词问题或者停用词表误伤。这个动作花不了十分钟但比调参涨的那零点几个点有用得多。希望帮到你。本文还有配套的精品资源点击获取
返回列表