这节的目标是实现垃圾邮件分类,俗话说巧妇难为无米之炊,要实现垃圾邮件分类,首先要有数据,这里我使用kaggle提供的spam.csv
加载数据集
这里我把数据集放在dataset文件夹下,使用pandas加载数据集
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
df = pd.read_csv("dataset/spam.csv", encoding="latin-1")
df.head()
得到的输出
v1 | v2 | Unnamed:2 | Unnamed:3 | Unnamed:4 | |
---|---|---|---|---|---|
0 | ham | Go until jurong point, crazy.. Available only ... | NaN | NaN | NaN |
1 | ham | Ok lar... Joking wif u oni... | NaN | NaN | NaN |
2 | spam | Free entry in 2 a wkly comp to win FA Cup fina.. | NaN | NaN | NaN |
3 | ham | U dun say so early hor... U c already then say. | NaN | NaN | NaN |
4 | ham | Nah I don't think he goes to usf, he lives aro... | NaN | NaN | NaN |
其中我们只需要第一列(类别)和第二列(邮件内容),然后我们使用describe函数来查看数据集的大致描述
df = df.drop(["Unnamed: 2", "Unnamed: 3", "Unnamed: 4"], axis=1)
df.describe()
输出为
-- | label | sms |
---|---|---|
count | 5572 | 5572 |
unique | 2 | 5169 |
top | ham | Sorry, I'll call later |
freq | 4825 | 30 |
通过上面的描述我们得知,该数据集有5572条数据,其中label代表该数据集的类别,总共有两类(ham|spam),在这5572条数据在有4825条为正常邮件,有5169条内容唯一的邮件,其中内容为Sorry, I'll call later
的邮件出现了30次
数据预处理
我们的数据中含有大量的文本数据,但是一般原始数据无法直接丢给算法,这些原始数据是一组符号,因为大多数算法期望的输入是固定长度的数值特征向量而不是不同长度的文本文件。为了解决这个问题,scikit-learn提供了一些实用工具可以用最常见的方式从文本内容中抽取数值特征,比如说:
- 标记(tokenizing)文本以及为每一个可能的标记(token)分配的一个整型ID ,例如用白空格和标点符号作为标记的分割符(中文的话涉及到分词的问题)
- 计数(counting)标记在每个文本中的出现频率
- 正态化(nomalizating) 降低在大多数样本/文档中都出现的标记的权重
在这个方案中,特征和样本的定义如下:
-
将每个标记出现的频率(无论是否正态化)作为特征。
-
给定文件中所有标记的出现频率所构成的向量作为多元样本。
因此,语料文件可以用一个词文档矩阵代表,每行是一个文档,每列是一个标记(即词)。
将文档文件转化为数值特征的一般过程被称为向量化。这个特殊的策略(标记,计数和正态化)被称为词袋或者Bag of n-grams表征。用词频描述文档,但是完全忽略词在文档中出现的相对位置信息。
假设我们有4个样本如下:
[
'Hello, how are you!',
'Win money, win from home.',
'Call me now',
'Hello, Call you tomorrow?'
]
我们的目标是将这组文本转换为频率分布矩阵,如下所示
词袋词袋的sklearn实现
词频统计可以用scikit-learn的CountVectorizer实现:
from sklearn.feature_extraction.text import CountVectorizer
vectorizer = CountVectorizer()
data_train_cnt = vectorizer.fit_transform(data_train)
data_test_cnt = vectorizer.transform(data_test)
CountVectorizer类会把文本全部转换成小写,然后将文本词块化(tokenize)。文本词块化是把句子分割成词块(token)或有意义的字母序列的过程。词块大多是单词,但它们也可能是一些短语,如标点符号和词缀。CountVectorizer类通过正则表达式用空格分割句子,然后抽取长度大于等于2的字母序列
贝叶斯分类
这里我们使用sklearn中的MultinomialNB对数据集进行分类,分类代码如下
from sklearn.naive_bayes import MultinomialNB
clf = MultinomialNB()
clf.fit(data_train_cnt, label_train)
score = clf.score(data_test_cnt,label_test)
print(score)
得到的评分为:
0.987443946
接下来我们使用交叉验证来测试模型的性能
from sklearn.model_selection import cross_val_score
accuracy = cross_val_score(clf, data_train_cnt, label_train, cv=20, scoring='accuracy')
print(accuracy.mean())
打印出来的平均得分为:
0.982495042
网友评论