# 3.句子数
df['sen']=df['message'].apply(lambda x: len(nltk.sent_tokenize(x)))
df.head()
label | message | char | words | sen | |
---|---|---|---|---|---|
0 | 0 | Go until jurong point, crazy… Available only … | 111 | 24 | 2 |
1 | 0 | Ok lar… Joking wif u oni… | 29 | 8 | 2 |
2 | 1 | Free entry in 2 a wkly comp to win FA Cup fina… | 155 | 37 | 2 |
3 | 0 | U dun say so early hor… U c already then say… | 49 | 13 | 1 |
4 | 0 | Nah I don’t think he goes to usf, he lives aro… | 61 | 15 | 1 |
描述性统计
# 描述性统计
df.describe()
index | label | char | words | sen |
---|---|---|---|---|
count | 5572.0 | 5572.0 | 5572.0 | 5572.0 |
mean | 0.13406317300789664 | 80.11880832735105 | 18.69562096195262 | 1.9707465900933239 |
std | 0.34075075489776974 | 59.6908407765033 | 13.742586801744975 | 1.4177777134026657 |
min | 0.0 | 2.0 | 1.0 | 1.0 |
25% | 0.0 | 36.0 | 9.0 | 1.0 |
50% | 0.0 | 61.0 | 15.0 | 1.0 |
75% | 0.0 | 121.0 | 27.0 | 2.0 |
max | 1.0 | 910.0 | 220.0 | 28.0 |
下面我们通过可视化比较一下不同短信在这些数字特征上的分布情况
# 字符数比较
plt.figure(figsize=(12,6))
sns.histplot(df[df['label']==0]['char'],color='red')#正常短信
sns.histplot(df[df['label']==1]['char'],color = 'blue')#垃圾短信
<matplotlib.axes._subplots.AxesSubplot at 0x7fce63763dd0>
# 比较
plt.figure(figsize=(12,6))
sns.histplot(df[df['label']==0]['words'],color='red')#正常短信
sns.histplot(df[df['label']==1]['words'],color = 'blue')#垃圾短信
<matplotlib.axes._subplots.AxesSubplot at 0x7fce63f4bed0>
sns.pairplot(df,hue='label')
#删除数据集中存在的一些异常值
i=df[df['char']>500].index
df.drop(i,axis=0,inplace=True)
df=df.reset_index()
df.drop("index",inplace=True,axis=1)
#相关系数矩阵
sns.heatmap(df.corr(),annot=True)
<matplotlib.axes._subplots.AxesSubplot at 0x7fce606d0250>
我们这里看到存在多重共线性,因此,我们不使用所有的列,在这里选择与label相关性最强的char
3.数据预处理
对于英文文本数据,我们常用的数据预处理方式如下
- 去除标点符号
- 去除停用词
- 去除专有名词
- 变换成小写
- 分词处理
- 词根、词缀处理
下面我们来看看如何实现这些步骤
nltk.download('stopwords')
[nltk_data] Downloading package stopwords to /root/nltk_data...
[nltk_data] Unzipping corpora/stopwords.zip.
True
# 首先导入需要使用到的包
from nltk.corpus import stopwords
from nltk.stem import PorterStemmer
from wordcloud import WordCloud
import string,time
# 标点符号
string.punctuation
'!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'
# 停用词
stopwords.words('english')
3.1清洗文本数据
- 去除web链接
- 去除邮件
- 取掉数字
下面使用正则表达式来处理这些数据。
def remove\_website\_links(text):
no_website_links = text.replace(r"http\S+", "")#去除网络连接
return no_website_links
def remove\_numbers(text):
removed_numbers = text.replace(r'\d+','')#去除数字
return removed_numbers
def remove\_emails(text):
no_emails = text.replace(r"\S\*@\S\*\s?",'')#去除邮件
return no_emails
df['message'] = df['message'].apply(remove_website_links)
df['message'] = df['message'].apply(remove_numbers)
df['message'] = df['message'].apply(remove_emails)
df.head()
label | message | char | words | sen | |
---|---|---|---|---|---|
0 | 0 | Go until jurong point, crazy… Available only … | 111 | 24 | 2 |
1 | 0 | Ok lar… Joking wif u oni… | 29 | 8 | 2 |
2 | 1 | Free entry in 2 a wkly comp to win FA Cup fina… | 155 | 37 | 2 |
3 | 0 | U dun say so early hor… U c already then say… | 49 | 13 | 1 |
4 | 0 | Nah I don’t think he goes to usf, he lives aro… | 61 | 15 | 1 |
3.2 文本特征转换
def message\_transform(text):
text = text.lower()#转换为小写
text = nltk.word_tokenize(text)#分词处理
# 去除停用词和标点
y = []#创建一个空列表
for word in text:
stopwords_punc = stopwords.words('english')+list(string.punctuation)#存放停用词和标点
if word.isalnum()==True and word not in stopwords_punc:
y.append(word)
# 词根变换
message=y[:]
y.clear()
for i in message:
ps=PorterStemmer()
y.append(ps.stem(i))
return " ".join(y)#返回字符串形式
df['message'] = df['message'].apply(message_transform)
df['num\_words\_transform']=df['message'].apply(lambda x: len(str(x).split()))
df.head()
label | message | char | words | sen | |
---|---|---|---|---|---|
0 | 0 | Go until jurong point, crazy… Available only … | 111 | 24 | 2 |
1 | 0 | Ok lar… Joking wif u oni… | 29 | 8 | 2 |
2 | 1 | Free entry in 2 a wkly comp to win FA Cup fina… | 155 | 37 | 2 |
3 | 0 | U dun say so early hor… U c already then say… | 49 | 13 | 1 |
4 | 0 | Nah I don’t think he goes to usf, he lives aro… | 61 | 15 | 1 |
4.词频统计
4.1绘制词云
#绘制信息中出现最多的词的词云
from wordcloud import WordCloud
#首先,创建一个object
wc=WordCloud(width=500,height=500,min_font_size=10,background_color='white')
# 垃圾信息的词云
spam_wc=wc.generate(df[df['label']==1]['message'].str.cat(sep=""))
plt.figure(figsize=(18,12))
plt.imshow(spam_wc)
<matplotlib.image.AxesImage at 0x7fce5d938710>
可以看出,这些垃圾邮件出现频次最多的单词是:free、call等这种具有诱导性的信息
# 正常信息的词云
ham_wc = wc.generate(df[df['label']==0]['message'].str.cat(sep=''))
plt.figure(figsize=(18,12))
plt.imshow(ham_wc)
<matplotlib.image.AxesImage at 0x7fce607af190>
可以看出正常信息出现频次较多的单词为u、go、got、want等一些传达信息的单词
为了简化词云图的信息,我们现在分别统计垃圾短信和正常短信频次top30的单词
4.2找出词数top30的单词
垃圾短信:
# 统计词频
spam_corpus=[]
for i in df[df['label']==1]['message'].tolist():
for word in i.split():
spam_corpus.append(word)
from collections import Counter
Counter(spam_corpus)#记数
Counter(spam_corpus).most_common(30)#取最多的30个单词
plt.figure(figsize=(10,7))
sns.barplot(y=pd.DataFrame(Counter(spam_corpus).most_common(30))[0],x=pd.DataFrame(Counter(spam_corpus).most_common(30))[1])
plt.xticks()
plt.xlabel("Frequnecy")
plt.ylabel("Spam Words")
plt.show()
正常短信
ham_corpus=[]
for i in df[df['label']==0]['message'].tolist():
for word in i.split():
ham_corpus.append(word)
from collections import Counter
plt.figure(figsize=(10,7))
sns.barplot(y=pd.DataFrame(Counter(ham_corpus).most_common(30))[0],x=pd.DataFrame(Counter(ham_corpus).most_common(30))[1])
plt.xticks()
plt.xlabel("Frequnecy")
plt.ylabel("Ham Words")
plt.show()
下面进一步分析垃圾短信和非垃圾短信的单词和字符数分布情况
# 字符数
fig,(ax1,ax2)=plt.subplots(1,2,figsize=(15,6))
text_len=df[df['label']==1]['text'].str.len()
ax1.hist(text_len,color='green')
ax1.set_title('Original text')
text_len=df[df['label']==0]['text'].str.len()
ax2.hist(text_len,color='red')
ax2.set_title('Fake text')
fig.suptitle('Characters in texts')
plt.show()
#单词数
fig,(ax1,ax2)=plt.subplots(1,2,figsize=(15,6))
text_len=df[df['label']==1]['num\_words\_transform']
ax1.hist(text_len,color='red')
ax1.set_title('Original text')
text_len=df[df['label']==0]['num\_words\_transform']
ax2.hist(text_len,color='green')
ax2.set_title('Fake text')
fig.suptitle('Words in texts')
plt.show()
总结
经过上面分析,我们可以得出结论,垃圾短信文本与非垃圾短信文本相比具有更多的单词和字符。
- 垃圾短信中包含的平均字符数约为 90 个字符
- 垃圾短信中包含的平均字数约为 15 个字
5.模型构建
根据历史经验,在文本数据上朴素贝叶斯算法效果很好,因此我们将使用它,但在此过程中还将它与不同的算法进行比较。
在统计学中,朴素贝叶斯分类器是一系列简单的“概率分类器”,它们基于应用贝叶斯定理和特征之间的(朴素)条件独立假设。它们是最简单的贝叶斯网络模型之一,但与核密度估计相结合,它们可以达到更高的准确度水平。
首先,我们这里的输入数据是文本数据,不能够直接建立模型。因此,我们必须将这些文本数据进行特征提取。比较常用的几种方法:
- 词袋模型(Bag of words) 存在稀疏性问题
- TF-IDF
- Word2vec
因为是实战训练,在这里不具体展开的几种方法的原理,在这里我选择TF-IDF。
我也试了一下Word embedding,结合一些深度学习的方法,精度能够有所提高,感兴趣的小伙伴可以自己尝试一下,基本步骤类似。下面我们首先建立贝叶斯模型。
5.1 构建贝叶斯模型
from sklearn.feature_extraction.text import TfidfVectorizer
tfidf = TfidfVectorizer(max_features=3000)
X = tfidf.fit_transform(df['message']).toarray()
y = df['label'].values
array([0, 0, 1, ..., 0, 0, 0])
from sklearn.model_selection import train_test_split
X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.2,random_state=2)#训练集测试集划分
from sklearn.naive_bayes import GaussianNB,MultinomialNB,BernoulliNB
from sklearn.metrics import accuracy_score,confusion_matrix,precision_score
这里我们比较三种不同的贝叶斯模型的各个评估指标结果
#GaussianNB
gnb = GaussianNB()
gnb.fit(X_train,y_train)
y_pred1 = gnb.predict(X_test)
print("Accuracy Score -",accuracy_score(y_test,y_pred1))
print("Precision Score -",precision_score(y_test,y_pred1))
print(confusion_matrix(y_test,y_pred1))
Accuracy Score - 0.8456014362657092
Precision Score - 0.47038327526132406
[[807 152]
[ 20 135]]
#MultinomialNB
mnb = MultinomialNB()
mnb.fit(X_train,y_train)
y_pred2 = mnb.predict(X_test)
print("Accuracy Score -",accuracy_score(y_test,y_pred2))
print("Precision Score -",precision_score(y_test,y_pred2))
print(confusion_matrix(y_test,y_pred2))
Accuracy Score - 0.9793536804308797
Precision Score - 0.9925373134328358
[[958 1]
[ 22 133]]
#Bernuli
bnb = BernoulliNB()
bnb.fit(X_train,y_train)
y_pred3 = bnb.predict(X_test)
print("Accuracy Score -",accuracy_score(y_test,y_pred3))
print("Precision Score -",precision_score(y_test,y_pred3))
print(confusion_matrix(y_test,y_pred3))
Accuracy Score - 0.9829443447037702
Precision Score - 1.0
[[959 0]
[ 19 136]]
从上述结果来看,我选择了BNB来建模
5.2 模型比较
下面我们继续比较其他几种常见的分类模型的效果
#导入基本库
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC
from sklearn.naive_bayes import MultinomialNB
from sklearn.tree import DecisionTreeClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.ensemble import AdaBoostClassifier
from sklearn.ensemble import BaggingClassifier
from sklearn.ensemble import ExtraTreesClassifier
from sklearn.ensemble import GradientBoostingClassifier
from xgboost import XGBClassifier
from sklearn.metrics import precision_score, recall_score, plot_confusion_matrix, classification_report, accuracy_score, f1_score
from sklearn.model_selection import cross_val_score
# 构建多个分类器
classifiers={"svc":SVC(kernel='sigmoid', gamma=1.0),
"knc": KNeighborsClassifier(),
"bnb" : BernoulliNB(),
![img](https://img-blog.csdnimg.cn/img_convert/80e5791da4b9b943dadb1e191e203ec9.png)
![img](https://img-blog.csdnimg.cn/img_convert/91b91efeca00bdd874b086c216d55023.png)
![img](https://img-blog.csdnimg.cn/img_convert/fe3e62d70fda0968f04248ef4024e5c2.png)
**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!**
**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**
**[需要这份系统化资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618545628)**
precision_score, recall_score, plot_confusion_matrix, classification_report, accuracy_score, f1_score
from sklearn.model_selection import cross_val_score
# 构建多个分类器
classifiers={"svc":SVC(kernel='sigmoid', gamma=1.0),
"knc": KNeighborsClassifier(),
"bnb" : BernoulliNB(),
[外链图片转存中...(img-B5yY6tss-1714447081351)]
[外链图片转存中...(img-4NLoRv7J-1714447081351)]
[外链图片转存中...(img-GBtQJvuy-1714447081351)]
**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!**
**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**
**[需要这份系统化资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618545628)**