介绍

在上一篇博客:数据挖掘入门系列教程(八点五)之SVM介绍以及从零开始公式推导中,详细的讲述了SVM的原理,并进行了详细的数学推导。在这篇博客中,主要是应用SVM,使用SVM进行数据分类,不会涉及到SVM的解释,so,如果对svm并不是特别了解的话,非常建议先去看我的上一篇博客(or其他博主的博客),然后再来看这一篇博客。因为在这篇并不是自己实现SVM而是基于sklearn中的svm包来进行应用。因此,我们可能使用几行代码可能就可以对数据集进行训练了。

我们不仅要知其然,更要知其所以然。

在这一章博客中,我们会使用SVM做两个任务:

  • 基于SVM对MNIST数据集进行分类。
  • 基于SVM对垃圾邮件进行判断

基于SVM对MNIST数据集进行分类

在前面神经网络的博客中,我们基于pybrain使用神经网络对MNIST手写数据集进行分类,但是最后结果并不是很好(可以说得上是比较差了),只有:

这次我们使用SVM来进行继续操作。数据集在前面的博客中已经进行说明,就不再赘述。

直接看代码吧:

使用SVM

下面的代码没有什么好说的,就是加载下载处理好的数据集,然后在将数据集分割成训练集和测试集(在Github中有这两个数据集,先解压再使用【其中dataset是压缩包,需要解压】):

import numpy as np
X = np.load("./Data/dataset.npy")
y = np.load("./Data/class.npy")
from sklearn.model_selection import train_test_split
x_train,x_test,y_train,y_test = train_test_split(X,y,random_state=14 )

然后我们就可以使用SVM进行训练了:

from sklearn import svm
predictor = svm.SVC(gamma='scale', C=1.0, decision_function_shape='ovr', kernel='rbf')
# 进行训练
predictor.fit(x_train, y_train)

同样关于SVM的官网介绍在这里。关于svm包具体的使用可以看官方文档,官方文档写的还是蛮详细的。

这里来解释一下代码:

svm有很多类型的Estimators如下:

在这里我们选择SVC,也就是说使用SVM进行分类。具体的使用在这里。关于代码中参数的介绍:

  1. C = 1.0

    在上一章博客中,我们提到的软间隔支持向量机中提到了以下公式:

    \[\begin{equation}\begin{aligned}
    \mathcal{L}(\boldsymbol{w}, b, \boldsymbol{\xi}, \boldsymbol{\alpha}, \boldsymbol{\beta}):=& \frac{1}{2} \boldsymbol{w}^{\top} \boldsymbol{w}+C \sum_{i=1}^{m} \xi_{i} \\
    &+\sum_{i=1}^{m} \alpha_{i}\left(1-\xi_{i}-y_{i}\left(\boldsymbol{w}^{\top} \boldsymbol{\phi}\left(\boldsymbol{x}_{i}\right)+b\right)\right) \\
    &+\sum_{i=1}^{m} \beta_{i}\left(-\xi_{i}\right)
    \end{aligned}\end{equation} \tag{18}
    \]

    其中C就是用来平衡结构风险和经验风险的。

  2. kernel='rbf'

    kernel代表的是核函数,“rbf”代表的就是(高斯径向基函数核(英语:Radial basis function kernel)表达式如下:

    \[\begin{equation}K\left(\mathbf{x}, \mathbf{x}^{\prime}\right)=\exp \left(-\gamma\left\|\mathbf{x}-\mathbf{x}^{\prime}\right\|_{2}^{2}\right)\end{equation}
    \]

  3. gamma='scale'

    在rbf核函数中,我们有一个变量\(\gamma\),gamma='scale'代表\(\gamma = \frac{1}{(n\_features * X.var()) }\)

  4. decision_function_shape='ovr'

    在SVM的介绍中,我们详细的推导过SVM二分类的问题,但是如果类别是多个(大于3),那么怎么办呢?(比如说MNIST数据集中一共有0~9一共十个类别),此时我们可以使用一对一(one vs one),一对多(one vs rest)的方法来解决。

    • ovo

       其做法就是在任意两个类样本之间设计一个SVM,因此\(k\)个类别的样本就需要设计\(\frac{k(k-1)}{2}\)个SVM。最后进行预测分类的时候,哪一个类别划分的次数最多,则就判定为该类别。

    • ovr

       训练时依次把某个类别的样本归为一类,其他剩余的样本归为另一类,这样k个类别的样本就构造出了k个SVM。分类时将未知样本分类为离超平面最远的那类。

fit()就是表示对数据集进行训练。

再然后,我们进行预测并使用F1进行评估:

# 预测结果
result = predictor.predict(x_test)
# 进行评估
from sklearn.metrics import f1_score
print("F-score: {0:.2f}".format(f1_score(result,y_test,average='micro')))

最后结果如下

\(97 \%\),这个结果还是不错的。

本来呢,这篇博客到这里就ok了,怎么使用也介绍完了,但是我觉得这篇博客也太少了点,因此又决定再水一点内容。

SVM分析垃圾邮件

简单点来说,我们就是想通过一封邮件是不是垃圾邮件。一共有1w+1条数据(50001垃圾邮件数据,5k正常邮件的数据)。数据集下载地址:GitHub

部分数据展示如下:

其中每一行数据就是一封邮件。

加载数据集

加载数据集还是挺简单的,代码如下(数据集在我的GitHub中):

# 垃圾邮件文件地址
spam_data_path = "./Data/resource/spam_5000.utf8"
# 正常邮件文件地址
ham_data_path = "./Data/resource/ham_5000.utf8" with open(spam_data_path,encoding='utf-8') as f:
spam_txt_list = f.readlines()
with open(ham_data_path,encoding="utf-8") as f:
ham_txt_list= f.readlines()

这里还需要介绍一个概念——停止词(百度百科介绍):

人类语言包含很多功能词。与其他词相比,功能词没有什么实际含义。最普遍的功能词是限定词(“the”、“a”、“an”、“that”、和“those”),这些词帮助在文本中描述名词和表达概念,如地点或数量。介词如:“over”,“under”,“above” 等表示两个词的相对位置。

这些功能词的两个特征促使在搜索引擎的文本处理过程中对其特殊对待。第一,这些功能词极其普遍。记录这些词在每一个文档中的数量需要很大的磁盘空间。第二,由于它们的普遍性和功能,这些词很少单独表达文档相关程度的信息。如果在检索过程中考虑每一个词而不是短语,这些功能词基本没有什么帮助。

在信息检索中,这些功能词的另一个名称是:停用词(stopword)。称它们为停用词是因为在文本处理过程中如果遇到它们,则立即停止处理,将其扔掉。将这些词扔掉减少了索引量,增加了检索效率,并且通常都会提高检索的效果。停用词主要包括英文字符、数字、数学字符、标点符号及使用频率特高的单汉字等。

这里我们使用的是百度的停用词表,数据是来自GitHub,当然在我的GitHub上面,已经将这个词表上传上去了。

stop_word_path = "./Data/resource/stopword.txt"
with open(stop_word_path,encoding='utf-8') as f:
# 去除空格以及换行符
stop_words = f.read().strip()

分词

什么是分词呢?对于中文来说,分词就是将连续的一串子序列(句子)分成一个一个的词。比如说”我喜欢你“可以分成”我“,”喜欢“,”你“。实际上在计算机中对中文进行分词还是满困难的。因为有很多歧义,新词等等。这里我们使用jieba库进行分词,举例使用如下:

import jieba

a = "请不要把陌生人的些许善意,视为珍稀的瑰宝,却把身边亲近人的全部付出,当做天经地义的事情,对其视而不见"
cut_a = jieba.cut(a)
print(list(cut_a))

结果如下:

使用jieba对垃圾邮件和正常邮件进行分词代码如下(去除分词中的停词以及部分词):

import jieba

spam_words = []
# 垃圾邮件
for spam_txt in spam_txt_list:
words = []
cut_txts = jieba.cut(spam_txt)
for cut_txt in cut_txts:
# 判断分词是否是字母表组成的,是否是换行符,并且是否在停词表中
if(cut_txt.isalpha() and cut_txt!="\n" and cut_txt not in stop_words):
words.append(cut_txt)
# 将词组成句子
sentence = " ".join(words)
spam_words.append(sentence)

spam_words部分数据结果如下(数据类型为list):

也就是说,我们将每一封垃圾邮件数据分成了一个一个的词(在词的中间使用空格分开),然后组成这一封邮件的词的特征。

同理我们处理正常邮件数据:

import jieba

ham_words = []
for ham_txt in ham_txt_list:
words = []
cut_txts = jieba.cut(ham_txt)
for cut_txt in cut_txts:
if(cut_txt.isalpha() and cut_txt!="\n" and cut_txt not in stop_words):
words.append(cut_txt)
sentence = " ”.join(words)
ham_words.append(sentence)

构建词云

这个没什么好说的,就是使用WordCloud构建词云。text是一个字符串,WordCloud会自动使用空格或者逗号对text进行分割。

%pylab inline
from wordcloud import WordCloud
import matplotlib.pyplot as plt
# 词云展示
def showWordCloud(text):
wc = WordCloud(
background_color = "white",
max_words = 200,
# 为了显示中文,使用字体
font_path = "./Data/resource/simhei.ttf",
min_font_size = 15,
max_font_size = 50,
width = 600
)
wordcloud = wc.generate(text)
plt.imshow(wordcloud, interpolation="bilinear")
plt.axis("off")

展示垃圾邮件词云我们将数据list变成使用空格连接的字符串。

showWordCloud(" ".join(spam_words))

展示正常邮件数据

showWordCloud(" ".join(ham_words))

构建数据集

通过前面的步骤我们已经得到了邮件进行分词之后的结果。在SVM中我们知道,每一条数据的特征的个数是一样多的(也就是他们拥有相同的特征,但是可能特征值不同),但是很明显对于文本数据,每一封邮件的特征词明显是不一样的。这里我们可以想一想在数据挖掘入门系列教程(七)之朴素贝叶斯进行文本分类中,我们使用了DictVectorizer转换器将特征字典转换成了一个矩阵,这里的数据是list数据,因此我们选择CountVectorizer将list数据转换成举证。

from sklearn.feature_extraction.text import CountVectorizer
import pandas as pd
import numpy as np
from collections import defaultdict
data = []
data.extend(ham_words)
data.extend(spam_words)
# binary默认为False,一个关键词在一篇文档中可能出现n次,如果binary=True,非零的n将全部置为1
# max_features 对所有关键词的出现的频率进行降序排序,只取前max_features个作为关键词集
vectorizer = CountVectorizer(binary=False,max_features=1500)
result = vectorizer.fit_transform(data)

然后我们在加上列对应的名字(非必须,不影响训练):

# 词汇表,为字典类型,key为词汇,value为索引
vocabulary = vectorizer.vocabulary_
result = pd.DataFrame(result.toarray())
# 对索引进行从小到大的排序
colnames = sorted(vocabulary.items(),key = lambda item:item[1])
colname = []
for i in colnames:
colname.append(i[0])
result.columns = colname

最后部分的result的数据如下所示(0代表此词在邮件中没有出现,非0则代表出现):

其中index中\([0,5000)\)是正常的邮件数据,\([5000,10001]\)是垃圾邮寄数据。

进行训练

from sklearn.model_selection import train_test_split
from sklearn.svm import SVC # 使用0,1划分垃圾邮件和正常邮件
labels = []
labels.extend(np.ones(5000))
labels.extend(np.zeros(5001)) # 划分训练集和测试集
train,test,trainlabel,testlabel = train_test_split(result,labels,random_state=14)
predictor = SVC(gamma='scale', C=1.0, decision_function_shape='ovr', kernel='rbf')
predictor.fit(train,trainlabel)
predict_lable = predictor.predict(test)

最后进行评估结果为:

print("the accurancy is :",np.mean(predict_lable == testlabel))

交叉验证

当然我们还可以进行交叉验证来进行评估:

from sklearn.model_selection import cross_val_score
predictor = SVC(gamma='scale', C=1.0, decision_function_shape='ovr', kernel='rbf')
scores = cross_val_score(predictor,result,labels,scoring='f1')
print("Score: {}".format(np.mean(scores)))

这个准确度可以说是杠杠的:

炼丹术

继续水一波,来试一下选择不同数量的features,然后再观察不同数量的features对精度的影响。

from sklearn.model_selection import train_test_split
from sklearn.svm import SVC
from sklearn.feature_extraction.text import CountVectorizer
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt labels = []
labels.extend(np.ones(5000))
labels.extend(np.zeros(5001)) # 画图的两个轴
scores = []
indexs = [] data = []
data.extend(ham_words)
data.extend(spam_words) for i in range(1,3000,50):
# 转换器
vectorizer = CountVectorizer(binary=False,max_features=i)
result = vectorizer.fit_transform(data) train,test,trainlabel,testlabel = train_test_split(result,labels,random_state=14)
# 划分训练集和测试集
predictor = SVC(gamma='scale', C=1.0, decision_function_shape='ovr', kernel='rbf')
predictor.fit(train,trainlabel)
score = predictor.score(test,testlabel)
scores.append(score)
indexs.append(i)

然后,画图即可:


plt.plot(indexs,scores)
plt.show()

总的来说,结果还不错咯,500个以上的features就可以达到越\(98\%\)以上的精度。

总结

啊,终于水完了!!

这一篇博客主要是介绍基于sklearn的SVM的使用。可以很明显的看到,基本上只需要几行代码就ok了。因此我们更应该去关注svm的原理而不是简单的学会调用包然后就表示:“啊!我会了!SVM真简单”。实际上,如果我们不了解原理的话,我们甚至连调包都不知道里面的参数代表的含义是什么。

项目地址:Github

参考

数据挖掘入门系列教程(九)之基于sklearn的SVM使用的更多相关文章

  1. 数据挖掘入门系列教程(八点五)之SVM介绍以及从零开始推导公式

    目录 SVM介绍 线性分类 间隔 最大间隔分类器 拉格朗日乘子法(Lagrange multipliers) 拉格朗日乘子法推导 KKT条件(Karush-Kuhn-Tucker Conditions ...

  2. 数据挖掘入门系列教程(四)之基于scikit-lean实现决策树

    目录 数据挖掘入门系列教程(四)之基于scikit-lean决策树处理Iris 加载数据集 数据特征 训练 随机森林 调参工程师 结尾 数据挖掘入门系列教程(四)之基于scikit-lean决策树处理 ...

  3. 数据挖掘入门系列教程(八)之使用神经网络(基于pybrain)识别数字手写集MNIST

    目录 数据挖掘入门系列教程(八)之使用神经网络(基于pybrain)识别数字手写集MNIST 下载数据集 加载数据集 构建神经网络 反向传播(BP)算法 进行预测 F1验证 总结 参考 数据挖掘入门系 ...

  4. 数据挖掘入门系列教程(二)之分类问题OneR算法

    数据挖掘入门系列教程(二)之分类问题OneR算法 数据挖掘入门系列博客:https://www.cnblogs.com/xiaohuiduan/category/1661541.html 项目地址:G ...

  5. 数据挖掘入门系列教程(三)之scikit-learn框架基本使用(以K近邻算法为例)

    数据挖掘入门系列教程(三)之scikit-learn框架基本使用(以K近邻算法为例) 简介 scikit-learn 估计器 加载数据集 进行fit训练 设置参数 预处理 流水线 结尾 数据挖掘入门系 ...

  6. 数据挖掘入门系列教程(十点五)之DNN介绍及公式推导

    深度神经网络(DNN,Deep Neural Networks)简介 首先让我们先回想起在之前博客(数据挖掘入门系列教程(七点五)之神经网络介绍)中介绍的神经网络:为了解决M-P模型中无法处理XOR等 ...

  7. 数据挖掘入门系列教程(四点五)之Apriori算法

    目录 数据挖掘入门系列教程(四点五)之Apriori算法 频繁(项集)数据的评判标准 Apriori 算法流程 结尾 数据挖掘入门系列教程(四点五)之Apriori算法 Apriori(先验)算法关联 ...

  8. 数据挖掘入门系列教程(五)之Apriori算法Python实现

    数据挖掘入门系列教程(五)之Apriori算法Python实现 加载数据集 获得训练集 频繁项的生成 生成规则 获得support 获得confidence 获得Lift 进行验证 总结 参考 数据挖 ...

  9. 数据挖掘入门系列教程(十一)之keras入门使用以及构建DNN网络识别MNIST

    简介 在上一篇博客:数据挖掘入门系列教程(十点五)之DNN介绍及公式推导中,详细的介绍了DNN,并对其进行了公式推导.本来这篇博客是准备直接介绍CNN的,但是想了一下,觉得还是使用keras构建一个D ...

随机推荐

  1. JS三个事件绑定方法

    1. JS三个事件绑定方法 1.1. 使用html进行事件绑定 1.直接在html标签上写入事件类型和事件处理方法. <button onclick = "alert('hello w ...

  2. 常见排序算法总结分析之选择排序与归并排序-C#实现

    本篇文章对选择排序中的简单选择排序与堆排序,以及常用的归并排序做一个总结分析. 常见排序算法总结分析之交换排序与插入排序-C#实现是排序算法总结系列的首篇文章,包含了一些概念的介绍以及交换排序(冒泡与 ...

  3. html-css:浮动_清除浮动

    1.浮动 清除浮动之前我们首先需要了解为什么要清除浮动 1. 假设我们有一个父盒子,不设置高度,其高度有内部子盒子的大小自动撑开,这样是完全可行的,因为有时候我们并不想直接固定死父盒子的大小,而是根据 ...

  4. Windows程序卡顿、无响应问题定位

    当windows程序出现异常.界面卡顿.无响应情况时,在有工程和源码的情况下,程序员通常是打开IDE,在DEBUG模式下进行调试.但如果是一个RELEASE程序,且无调试环境,该如何来定位呢. 这里介 ...

  5. 三、【Docker笔记】Docker镜像

    镜像是Docker的三大核心概念之一.Docker在运行容器之前,本地需要存有镜像,若不存在则Docker会首先尝试从默认的镜像仓库中去下载,当然我们也可以去配置自己的仓库,如此就会从我们配置的仓库中 ...

  6. 原生js判断手机端页面滚动停止

    var topValue = 0,// 上次滚动条到顶部的距离 interval = null;// 定时器 contactsList = document.getElementById(" ...

  7. NKOJ 【NOIP2015 Day2】运输计划

    时间限制 : 20000 MS   空间限制 : 262144 KB 评测说明 : 2s,256m 问题描述 公元 2044 年,人类进入了宇宙纪元. L 国有 n 个星球,还有 n−1 条双向航道, ...

  8. iOS - scrollView与headerView的视差滚动实现思路

    假设场景:viewController里面有一个scrollView,该scrollView有一个headerView.现在需要将scrollView的滚动contentOffset与headerVi ...

  9. 万字长文带你入门Zookeeper!!!

    导读 文章首发于微信公众号[码猿技术专栏],原创不易,谢谢支持. Zookeeper 相信大家都听说过,最典型的使用就是作为服务注册中心.今天陈某带大家从零基础入门 Zookeeper,看了本文,你将 ...

  10. const不同位置带来的区别

    const不同位置带来的区别 今天同学问我数据结构时,我对以下代码懵了一下: template <class T> class Link{ public: T data; Link< ...