【机器学习实验】使用朴素贝叶斯进行文本的分类

引言

朴素贝叶斯由贝叶斯定理延伸而来的简单而强大的概率模型,它根据每个特征的概率确定一个对象属于某一类别的概率。该方法基于一个假设,所有特征需要相互独立,即任一特征的值和其他特征的值没有关联关系。

虽然这种条件独立的假设在许多应用领域未必能很好满足,甚至是不成立的。但这种简化的贝叶斯分类器在许多实际应用中还是得到了较好的分类精度。训练模型的过程可以看作是对相关条件概率的计算,它可以用统计对应某一类别的特征的频率来估计。

朴素贝叶斯最成功的一个应用是自然语言处理领域,自然语言处理的的数据可以看做是在文本文档中标注数据,这些数据可以作为训练数据集来使用机器学习算法进行训练。

本小节中,主要介绍使用朴素贝叶斯方法来进行文本的分类,我们将要使用一组标记类别的文本文档来训练朴素贝叶斯分类器,进而对未知的数据实例进行类别的预测。这个方法可以用作垃圾邮件的过滤。

数据集

该实验的数据可以通过scikit-learn获取一组新闻信息。

数据集由19,000个新闻信息组成,其中包含了20个不同的主题,包含政治、体育、科学等内容。

该数据集可以分成训练和测试两部分,训练和测试数据的划分基于某个特定日期。

数据的加载有两种方式:

  1. sklearn.datasets.fetch_20newsgroups,该函数返回一个原数据列表,可以将它作为文本特征提取的接口(sklearn.feature_extraction.text.CountVectorizer)的输入
  2. sklearn.datasets.fetch_20newsgroups_vectorized,该接口直接返回直接可以使用的特征,可以不再使用特征提取了
from sklearn.datasets import fetch_20newsgroups
news = fetch_20newsgroups(subset=‘all‘)
print news.keys()
print type(news.data), type(news.target), type(news.target_names)
print news.target_names
print len(news.data)
print len(news.target)

打印信息:

[‘DESCR’, ‘data’, ‘target’, ‘target_names’, ‘filenames’]

print news.data[0]
print news.target[0], news.target_names[news.target[0]]

打印的新闻内容略去,类别为10,类别名为rec.sport.hockey。

数据的预处理

机器学习算法只能作用在数值数据上,算法期望使用定长的数值特征而不是不定长的原始文本文件,我们下一步的工作是将文本数据集转换成数值数据集。

现在,我们只有一种特征:新闻消息的文本内容,我们需要一个函数将一段文本转换成一组有意义的数值特征。

直觉上,可以尝试着眼于每种文本类别的独立字符串(更准确说是标记,token),然后将每种类别对应的标记词的频率分布特性描述出来。sklearn.feature_extraction.text模块具有一些用文本文档来构建数值特征向量的有用的工具。

划分训练与测试数据

在进行转换工作之前,我们需要将数据划分为训练和测试数据集。由于载入的数据是随机顺序出现的,我们可以将数据划分为两部分,75%作为训练数据,25%作为测试数据:

SPLIT_PERC = 0.75
split_size = int(len(news.data)*SPLIT_PERC)
X_train = news.data[:split_size]
X_test = news.data[split_size:]
Y_train = news.target[:split_size]
Y_test = news.target[split_size:]

因为sklearn.datasets.fetch_20newsgroups本身可以根据subset参数来选择训练数据和测试数据,这里训练数据有11,314条,占总数据集的60%,测试数据集占40%。可以通过如下方式得到:

news_train = fetch_20newsgroups(subset=‘train‘)
news_test = fetch_20newsgroups(subset=‘test‘)
X_train = news_train.data
X_test = news_test.data
Y_train = news_train.target
Y_test = news_test.target

词袋(Bag of Words)表征

词袋模型是在自然语言处理和信息检索中的一种简单假设。在这种模型中,文本(段落或者文档)被看作是无序的词汇集合,忽略语法甚至是单词的顺序。

词袋模型被用在文本分类的一些方法当中。当传统的贝叶斯分类被应用到文本当中时,贝叶斯中的条件独立性假设导致词袋模型。

scikit-learn提供了一些实用工具可以用最常见的方式从文本内容中抽取数值特征,比如说:

  • 标记(tokenizing)文本以及为每一个可能的标记(token)分配的一个整型ID,例如用空格和标点符号作为标记的分割符(中文的话涉及到分词的问题)
  • 计数(counting)标记(token)在每个文本中的出现频率
  • 在大多数样本/文档中都出现的标记的重要性递减过程中,进行标准化(normalizing)和加权(weighting)

    我们将上面这个从一堆文本文件转化成数值特征向量的过程的策略称为词袋

在这种策略下,特征和样本定义如下:

将每个独立的标记(token)的出现频率(不管是否标准化)看做是特征

给定一个文档的所有标记的频率构成向量看做是一个多变量的样本

这样一个文本的语料库就可以表征为一个矩阵,其中每一行代表了一个文档,而每一列代表了在该语料库中出现的一个标记词。

文本可以用词语的出现频率表征,这样可以完全忽略词在文本中的相对位置信息,这一点应该就保证了贝叶斯的条件独立性。

稀疏性

大多数文档通常只会使用语料库中所有词的一个子集,因而产生的矩阵将有许多特征值是0(通常99%以上都是0)。

例如,一组10,000个短文本(比如email)会使用100,000的词汇总量,而每个文档会使用100到1,000个唯一的词。

为了能够在内存中存储这个矩阵,同时也提供矩阵/向量代数运算的速度,通常会使用稀疏表征例如在scipy.sparse包中提供的表征。

文本特征提取的接口

sklearn.feature_extraction.text提供了以下构建特征向量的工具:

  • feature_extraction.text.CountVectorizer([…]) Convert a collection of text documents to a matrix of token counts
  • feature_extraction.text.HashingVectorizer([…]) Convert a collection of text documents to a matrix of token occurrences
  • feature_extraction.text.TfidfTransformer([…]) Transform a count matrix to a normalized tf or tf-idf representation
  • feature_extraction.text.TfidfVectorizer([…]) Convert a collection of raw documents to a matrix of TF-IDF features.

解释:

  • CountVectorizer方法构建单词的字典,每个单词实例被转换为特征向量的一个数值特征,每个元素是特定单词在文本中出现的次数
  • HashingVectorizer方法实现了一个哈希函数,将标记映射为特征的索引,其特征的计算同CountVectorizer方法
  • TfidfVectorizer使用了一个高级的计算方法,称为Term Frequency Inverse Document

    Frequency (TF-IDF)。这是一个衡量一个词在文本或语料中重要性的统计方法。直觉上讲,该方法通过比较在整个语料库的词的频率,寻求在当前文档中频率较高的词。这是一种将结果进行标准化的方法,可以避免因为有些词出现太过频繁而对一个实例的特征化作用不大的情况(我猜测比如a和and在英语中出现的频率比较高,但是它们对于表征一个文本的作用没有什么作用)

构建朴素贝叶斯分类器

由于我们使用词的出现次数作为特征,可以用多项分布来描述这一特征。在sklearn中使用sklearn.naive_bayes模块的MultinomialNB类来构建分类器。

我们使用Pipeline这个类来构建包含量化器(vectorizers)和分类器的复合分类器(compound classifer)。

from sklearn.naive_bayes import MultinomialNB
from sklearn.pipeline import Pipeline
from sklearn.feature_extraction.text import TfidfVectorizer, HashingVectorizer, CountVectorizer

#nbc means naive bayes classifier
nbc_1 = Pipeline([
    (‘vect‘, CountVectorizer()),
    (‘clf‘, MultinomialNB()),
])
nbc_2 = Pipeline([
    (‘vect‘, HashingVectorizer(non_negative=True)),
    (‘clf‘, MultinomialNB()),
])
nbc_3 = Pipeline([
    (‘vect‘, TfidfVectorizer()),
    (‘clf‘, MultinomialNB()),
])

nbcs = [nbc_1, nbc_2, nbc_3]

交叉验证

我们下面设计一个对分类器的性能进行测试的交叉验证的函数:

from sklearn.cross_validation import cross_val_score, KFold
from scipy.stats import sem
import numpy as np

def evaluate_cross_validation(clf, X, y, K):
    # create a k-fold croos validation iterator of k=5 folds
    cv = KFold(len(y), K, shuffle=True, random_state=0)
    # by default the score used is the one returned by score method of the estimator (accuracy)
    scores = cross_val_score(clf, X, y, cv=cv)
    print scores
    print ("Mean score: {0:.3f} (+/-{1:.3f})").format(
        np.mean(scores), sem(scores))

将训练数据分成5份,输出验证的分数:

for nbc in nbcs:
    evaluate_cross_validation(nbc, X_train, Y_train, 5)

输出为:

[ 0.82589483 0.83473266 0.8272205 0.84136103 0.83377542]

Mean score: 0.833 (+/-0.003)

[ 0.76358816 0.72337605 0.72293416 0.74370305 0.74977896]

Mean score: 0.741 (+/-0.008)

[ 0.84975696 0.83517455 0.82545294 0.83870968 0.84615385]

Mean score: 0.839 (+/-0.004)

从上面的结果看出,CountVectorizer和TfidfVectorizer进行特征提取的方法要比HashingVectorizer的效果好。

优化特征提取提高分类的效果

接下来,我们通过正则表达式来解析文本得到标记词。

优化提取单词规则参数

TfidfVectorizer的一个参数token_pattern用于指定提取单词的规则。

默认的正则表达式是ur"\b\w\w+\b",这个正则表达式只匹配单词边界并考虑到了下划线,也可能考虑到了横杠和点。

新的正则表达式是ur"\b[a-z0-9_\-\.]+[a-z][a-z0-9_\-\.]+\b"

nbc_4 = Pipeline([
    (‘vect‘, TfidfVectorizer(
                token_pattern=ur"\b[a-z0-9_\-\.]+[a-z][a-z0-9_\-\.]+\b",
    )),
    (‘clf‘, MultinomialNB()),
])

evaluate_cross_validation(nbc_4, X_train, Y_train, 5)

[ 0.86478126 0.85461776 0.84489616 0.85505966 0.85234306]

Mean score: 0.854 (+/-0.003)

这个分数已经比之前的0.839提高了一些了。

优化省略词参数

TfidfVectorizer的一个参数stop_words这个参数指定的词将被省略不计入到标记词的列表中,比如一些出现频率很高的词,但是这些词对于特定的主题不能提供任何的先验支持。

def get_stop_words():
    result = set()
    for line in open(‘stopwords_en.txt‘, ‘r‘).readlines():
        result.add(line.strip())
    return result

stop_words = get_stop_words()
nbc_5 = Pipeline([
    (‘vect‘, TfidfVectorizer(
                stop_words=stop_words,
                token_pattern=ur"\b[a-z0-9_\-\.]+[a-z][a-z0-9_\-\.]+\b",
    )),
    (‘clf‘, MultinomialNB()),
])

evaluate_cross_validation(nbc_5, X_train, Y_train, 5)

[ 0.88731772 0.88731772 0.878038 0.88466637 0.88107869]

Mean score: 0.884 (+/-0.002)

分数又提升到了0.884。

优化贝叶斯分类器的alpha参数

MultinomialNB有一个alpha参数,该参数是一个平滑参数,默认是1.0,我们将其设为0.01。

nbc_6 = Pipeline([
    (‘vect‘, TfidfVectorizer(
                stop_words=stop_words,
                token_pattern=ur"\b[a-z0-9_\-\.]+[a-z][a-z0-9_\-\.]+\b",
    )),
    (‘clf‘, MultinomialNB(alpha=0.01)),
])

evaluate_cross_validation(nbc_6, X_train, Y_train, 5)

[ 0.91073796 0.92532037 0.91604065 0.91294741 0.91202476]

Mean score: 0.915 (+/-0.003)

这下分数已经优化的很好了。

评估分类器性能

我们通过交叉验证得到了效果比较好的分类器参数,下面我们可以用该分类器来测试我们的测试数据了。

from sklearn import metrics
nbc_6.fit(X_train, Y_train)
print "Accuracy on training set:"
print nbc_6.score(X_train, Y_train)
print "Accuracy on testing set:"
print nbc_6.score(X_test,Y_test)
y_predict = nbc_6.predict(X_test)
print "Classification Report:"
print metrics.classification_report(Y_test,y_predict)
print "Confusion Matrix:"
print metrics.confusion_matrix(Y_test,y_predict)

这里只输出准确率:

Accuracy on training set:

0.997701962171

Accuracy on testing set:

0.846919808816

参考资料

wiki:词袋模型

转载请注明作者Jason Ding及其出处

GitCafe博客主页(http://jasonding1354.gitcafe.io/)

Github博客主页(http://jasonding1354.github.io/)

CSDN博客(http://blog.csdn.net/jasonding1354)

简书主页(http://www.jianshu.com/users/2bd9b48f6ea8/latest_articles)

百度搜索jasonding1354进入我的博客主页

时间: 2024-10-14 16:22:13

【机器学习实验】使用朴素贝叶斯进行文本的分类的相关文章

机器学习基础——带你实战朴素贝叶斯模型文本分类

本文始发于个人公众号:TechFlow 上一篇文章当中我们介绍了朴素贝叶斯模型的基本原理. 朴素贝叶斯的核心本质是假设样本当中的变量服从某个分布,从而利用条件概率计算出样本属于某个类别的概率.一般来说一个样本往往会含有许多特征,这些特征之间很有可能是有相关性的.为了简化模型,朴素贝叶斯模型假设这些变量是独立的.这样我们就可以很简单地计算出样本的概率. 想要回顾其中细节的同学,可以点击链接回到之前的文章: 机器学习基础--让你一文学会朴素贝叶斯模型 在我们学习算法的过程中,如果只看模型的原理以及理

NLP系列(2)_用朴素贝叶斯进行文本分类(上)

作者:寒小阳 && 龙心尘 时间:2016年1月. 出处:http://blog.csdn.net/longxinchen_ml/article/details/50597149 http://blog.csdn.net/han_xiaoyang/article/details/50616559 声明:版权全部,转载请联系作者并注明出处 1. 引言 贝叶斯方法是一个历史悠久.有着坚实的理论基础的方法,同一时候处理非常多问题时直接而又高效.非常多高级自然语言处理模型也能够从它演化而来.因此,

机器学习之路--朴素贝叶斯

一,引言 前两章的KNN分类算法和决策树分类算法最终都是预测出实例的确定的分类结果,但是,有时候分类器会产生错误结果:本章要学的朴素贝叶斯分类算法则是给出一个最优的猜测结果,同时给出猜测的概率估计值. 1 准备知识:条件概率公式 相信学过概率论的同学对于概率论绝对不会陌生,如果一时觉得生疏,可以查阅相关资料,在这里主要是想贴出条件概率的计算公式: P(A|B)=P(A,B)/P(B)=P(B|A)*P(A)/P(B) 2 如何使用条件概率进行分类 假设这里要被分类的类别有两类,类c1和类c2,那

基于朴素贝叶斯的文本分类

前言:学习机器学习的过程意识到,数学是工科的基石.很多数学公式似懂非懂,因此有了这篇博客,想在学习每个模型的过程中搞懂其中的数学理论. 贝叶斯决策论 1.前置知识:先验概率与后验概率 先验概率P(B):根据以往经验和分析得到的概率 先验概率是一种常识性.经验性认知,比如抛硬币正反面的概率是1/2. 后验概率P(A|B):某个因素的到来影响了对某个结果发生可能性的判断 后验概率是基于已知,对结果发生的可能性一种推测. 比如:文本分类中,假设文章类别为3类,没有数据时,观测到类别c的概率是先验概率P

(数据挖掘-入门-8)基于朴素贝叶斯的文本分类器

主要内容: 1.动机 2.基于朴素贝叶斯的文本分类器 3.python实现 一.动机 之前介绍的朴素贝叶斯分类器所使用的都是结构化的数据集,即每行代表一个样本,每列代表一个特征属性. 但在实际中,尤其是网页中,爬虫所采集到的数据都是非结构化的,如新闻.微博.帖子等,如果要对对这一类数据进行分类,应该怎么办呢?例如,新闻分类,微博情感分析等. 本文就介绍一种基于朴素贝叶斯的文本分类器. 二.基于朴素贝叶斯的文本分类器 目标:对非结构化的文本进行分类 首先,回顾一下朴素贝叶斯公式: 特征.特征处理:

NLP系列(3)_用朴素贝叶斯进行文本分类(下)

作者: 龙心尘 && 寒小阳 时间:2016年2月. 出处: http://blog.csdn.net/longxinchen_ml/article/details/50629110 http://blog.csdn.net/han_xiaoyang/article/details/50629587 声明:版权所有,转载请联系作者并注明出处 1. 引言 上一篇文章我们主要从理论上梳理了朴素贝叶斯方法进行文本分类的基本思路.这篇文章我们主要从实践上探讨一些应用过程中的tricks,并进一步分

机器学习笔记:朴素贝叶斯方法(Naive Bayes)原理和实现

本文主要描述了朴素贝叶斯分类方法,包括模型导出和学习描述.实例部分总结了<machine learning in action>一书中展示的一个该方法用于句子感情色彩分类的程序. 方法概述 学习(参数估计) 实现:朴素贝叶斯下的文本分类 模型概述 朴素贝叶斯方法,是指 朴素:特征条件独立 贝叶斯:基于贝叶斯定理 根据贝叶斯定理,对一个分类问题,给定样本特征x,样本属于类别y的概率是 p(y|x)=p(x|y)p(y)p(x)......(1) 在这里,x是一个特征向量,将设x维度为M.因为朴素

吴裕雄--天生自然python机器学习:使用朴素贝叶斯过滤垃圾邮件

使用朴素贝叶斯解决一些现实生活中 的问题时,需要先从文本内容得到字符串列表,然后生成词向量. 准备数据:切分文本 测试算法:使用朴素贝叶斯进行交叉验证 文件解析及完整的垃圾邮件测试函数 def createVocabList(dataSet): vocabSet = set([]) #create empty set for document in dataSet: vocabSet = vocabSet | set(document) #union of the two sets return

机器学习笔记之朴素贝叶斯(Naive Bayes)原理

为什么叫朴素贝叶斯? 朴素是指特征条件独立:贝叶斯是指基于贝叶斯定理. $x=\frac{{-b}\pm\sqrt{b^2-4ac}}{2a}$ $p(y=c_k|x)=\dfrac{\prod_{i=1}^{M}p(x^i|y=c_k)p(y=c_k)}{\sum_kp(y=c_k)\prod_{i=1}^{M}P(x^i|y=c_k)}$....(2) $$ % MathType!MTEF!2!1!+-% feaagKart1ev2aaatCvAUfeBSjuyZL2yd9gzLbvyNv2