天天看点

朴素贝叶斯分类(下):如何对文档进行分类?朴素贝叶斯分类(下):如何对文档进行分类?text classification与代码同目录下

朴素贝叶斯分类(下):如何对文档进行分类?

朴素贝叶斯分类最适合的场景是文本分类、情感分析和垃圾邮件识别,其中情感分析和垃圾邮件识别都是通过文本来进行判断,三个场景基本都是文本分类,所以朴素贝叶斯常用于自然语言处理NLP的工具

使用朴素贝叶斯做文档分类:

sklearn机器学习包

sklearn提供了3个朴素贝叶斯分类算法,分别是高斯朴素贝叶斯、多项式朴素贝叶斯、伯努利朴素贝叶斯

根据特征变量的不同选择不同的算法:

  • 高斯朴素贝叶斯:特征变量是连续变量,符合高斯分布,比如人的身高、物体长度
  • 多项式朴素贝叶斯:特征变量是离散变量,符合多项分布,在文档分类中特征变量体现在一个单词出现的次数或者是单词的TF-IDF值等
  • 伯努利朴素贝叶斯:特征变量是布尔变量,符合0/1分布,在文档分类中特征是单词是否出现

伯努利朴素贝叶斯是以文件为粒度,如果该单词在某文件中出现了即为1,否则为0,而多项式朴素贝叶斯是以单词为粒度,会计算在某个文件中的具体次数,而高斯朴素贝叶斯适合处理特征变量是连续变量,且符合正态分布(高斯分布)的情况,比如身高、体重这种自然界的现象就比较适合用高斯朴素贝叶斯来处理,而文本分类是使用多项式朴素贝叶斯或者伯努利朴素贝叶斯。

TF-IDF值

TF-IDF是一个统计方法,用来评估某个词语对于一个文件集或文档库中的其中一份文件的重要程度

词频TF计算了一个单词在文档中出现的次数,认为一个单词的重要性和它在文档中出现的次数呈正比

逆向文档频率IDF,指一个单词在文档中的区分度,认为一个单词出现在的文档数越少,就越能通过这个单词把该文档和其他文档区分开,IDF越大就代表该单词的区分度越大

TF-IDF就是词频TF和逆向文档频率IDF的乘积,这样这个单词在一个文档中出现的次数多且很少出现在其他文档中,这样的单词适合用于分类

朴素贝叶斯分类(下):如何对文档进行分类?朴素贝叶斯分类(下):如何对文档进行分类?text classification与代码同目录下
朴素贝叶斯分类(下):如何对文档进行分类?朴素贝叶斯分类(下):如何对文档进行分类?text classification与代码同目录下

IDF中可能有些单词不会存在文档中,为了避免分母为0 所以统一给单词出现的文档数都+1

TF-IDF=

TF*IDF

更准确的对文档进行分类,比如“我”这种高频单词,所以TF词频高,但是IDF很低,整体的TF-IDF不高

例子:假设一个文件夹中一共有10篇文档,其中一篇文档有1000个单词,“this”单词出现了20次,“bayes”出现了5次,“this”在所有文档中均出现过,而“bayes”只在2篇文档中出现过,所以针对“this”,词频TF值为:20/1000=0.02,逆向文档频率IDF=log(10/10+1)=-0.0414,所以TF-IDF=-8.28e-4,针对“bayes”的词频TF=5/1000=0.005,IDF=log(10/2+1)=0.5229,TF-IDF=2.61e-3,所以“bayes”比“this”做区分要好

如何求TF-IDF?

在sklearn中直接使用TfidfVectorizer类,帮助计算单词TF-IDF向量的值,类中取sklearn计算的对数log时,底数为e,不是10

创建TfidfVectorizer类:
TfidfVectorizer(stop_words=stop_words, token_pattern=token_pattern)
           

创建的时候有两个构造参数,可以自定义停用词stop_words,和过滤规则token_pattern,注意传递的数据结构,停用词stop_words是一个列表List类型,而过滤规则token_pattern是正则表达式

停用词就是在分类中没有用的词,这些词的词频TF高,但是IDF很低,没有分类的作用,为了节省空间和计算空间,把这些词作为停用词stop_words,告诉机器这些词不需要计算

朴素贝叶斯分类(下):如何对文档进行分类?朴素贝叶斯分类(下):如何对文档进行分类?text classification与代码同目录下

创建好TF-IDF向量类型的时候,用fit_transform计算,返回文本矩阵,该矩阵表示了每个单词在每个文档中的TF-IDF值

朴素贝叶斯分类(下):如何对文档进行分类?朴素贝叶斯分类(下):如何对文档进行分类?text classification与代码同目录下

进行fit_transform拟合模型后,得到更多的TF-IDF向量属性,比如我们可以得到词汇的对应关系(字典类型)和向量的IDF值,也可以获取设置的停用词stop_words

例子:假如有4个文档:

文档1:this is the bayes document;

文档2:this is the second second document;

文档3:and the third one;

文档4: is this the document;

计算文档中都有哪些单词,这些单词在不同文档中的TF-IDF值都为多少

  1. 首先创建TfidfVectorizer类:
    from sklearn.feature_extraction.text import TfidfVectorizer
    tfidf_vec = TfidfVectorizer()
               
  2. 创建4个文档的列表documents,并让创建好的tfidf_vec 对documents进行拟合,得到TF-IDF矩阵
    documents = [
        'this is the bayes document',
        'this is the second second document',
        'and the third one',
        'is this the document'
    ]
    tfidf_matrix = tfidf_vec.fit_transform(documents)
               
    输出文档中所有不重复的词:
    print('不重复的词:', tfidf_vec.get_feature_names())
               

    运行结果:

    不重复的词: [‘and’, ‘bayes’, ‘document’, ‘is’, ‘one’, ‘second’, ‘the’, ‘third’, ‘this’]

输出每个单词对应的ID值:

print('每个单词的ID:', tfidf_vec.vocabulary_)
           

运行结果:

每个单词的ID: {‘this’: 8, ‘is’: 3, ‘the’: 6, ‘bayes’: 1, ‘document’: 2, ‘second’: 5, ‘and’: 0, ‘third’: 7, ‘one’: 4}

输出每个单词在每个文档中的TF-IDF值,向量里的顺序是按照词语的ID顺序来的:

print('每个单词的tfidf值:', tfidf_matrix.toarray())
           

如何对文档进行分类

对文档分类有两个重要阶段:

  1. 基于分词的数据准备:包括分词、单词权重计算、去掉停用词
  2. 应用朴素贝叶斯分类进行分类,首先通过训练集得到朴素贝叶斯分类器,然后将分类器应用于测试集,并与实际结果作对比,最终得到测试集的分类准确率

模块1:对文档进行分词

在准备阶段最重要的就是分词,英文文档和中文文档所使用的分词工具不同,英文文档中,最常用的是NTLK包,NTLK包中包含了英文的停用词stop_words、分词和标注方法

import nltk
word_list = nltk.word_tokenize(text) #分词
nltk.pos_tag(word_list) #标注单词的词性
           

中文文档中,最常用的是jieba包,jieba包中包含了中文的停用词和分词方法

import jieba
word_list = jieba.cut (text) #中文分词
           

模块2:加载停用词表

需要自己读取停用词表文件,从网上找到中文常用的 停用词保存在stop_words.txt,利用Python的文件读取函数读取文件,保存在stop_words数组中

stop_words = [line.strip().decode('utf-8') for line in io.open('stop_words.txt').readlines()]
           

模块3:计算单词的权重

直接创建TfidfVectorizer类,然后使用fit_transform方法进行拟合,得到TF-IDF特征空间features,理解为选出来的分词就是特征,计算这些特征在文档上的特征向量,得到特征空间features

tf = TfidfVectorizer(stop_words=stop_words, max_df=0.5)
features = tf.fit_transform(train_contents)
           

max_df参数用来描述单词在文档中的最高出现率,假设max_df=0.5,代表一个单词在50%的文档中都出现过,那么它只携带了非常少的信息因此不作为分词统计

不设置min_df,因为min_df很小

模块4:生成朴素贝叶斯分类器

将特征训练集的特征空间train_features,以及训练集对应的分类train_labels传递给贝叶斯分类器clf,可以自动生成一个符合特征空间和对应分类的分类器,采用多项式贝叶斯分类器,其中alpha为平滑参数。当alpha=1时,使用的是Laplace平滑,Laplace平滑就是采用+1的方式来统计没有出现过的单词概率,当0<alpha<1时,使用的是Lidstone 平滑,对于Lidstone平滑来说,alpha越小,迭代次数越多,精度越高,可以设置alpha为0.001

# 多项式贝叶斯分类器
from sklearn.naive_bayes import MultinomialNB  
clf = MultinomialNB(alpha=0.001).fit(train_features, train_labels)
           

模块5:使用生成的分类器做预测

首先需要得到测试集的特征矩阵,方法是用训练集的分词创建一个TfidfVectorizer类,使用同样的stop_words和max_df,然后用这个TfidfVectorizer类对测试集的内容进行fit_transform拟合,得到测试集的特征矩阵test_features

test_tf = TfidfVectorizer(stop_words=stop_words, max_df=0.5, vocabulary=train_vocabulary)
test_features=test_tf.fit_transform(test_contents)
           

用训练好的分类器对新数据做预测,方法使用predict函数,传入测试集的特征矩阵test_features,得到分类结果predicted_labels,predict函数的工作就是求解所有后验概率并找出最大的那个

predicted_labels=clf.predict(test_features)
           

模块6:计算准确率

就是对分类模型的评估,调用sklearn中的metrics包,在metrics中提供了accuracy_score函数,对实际结果和预测的结果作对比,给出模型的准确率

from sklearn import metrics
print metrics.accuracy_score(test_labels, predicted_labels)
           

数据挖掘的流程包括获取数据、数据清洗、模型训练、模型评估和模型部署几个过程

练习题:

对中文文档分类的练习题< https://github.com/cystanford/text_classification >

  1. 文档中有4中类型:女性、体育、文学、校园;
  2. 训练集放到train文件夹里,测试集放到test文件夹里,停用词放到stop文件夹里

请使用朴素贝叶斯分类对训练集进行训练,并对测试集进行验证,并给出测试集的准确率

假设我们要判断一个人的性别,是通过身高、体重、鞋码、外貌等属性进行判断的,如果我们用朴素贝叶斯做分类,适合使用哪种朴素贝叶斯分类器?停用词的作用是什么?

import os
import jieba
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn import metrics

LABEL_MAP = {'体育': 0, '女性': 1, '文学': 2, '校园': 3}
# 加载停用词
with open('./text classification/stop/stopword.txt', 'rb') as f:
    STOP_WORDS = [line.strip() for line in f.readlines()]


def load_data(base_path):
    """
    :param base_path: 基础路径
    :return: 分词列表,标签列表
    """
    documents = []
    labels = []

    for root, dirs, files in os.walk(base_path): # 循环所有文件并进行分词打标
        for file in files:
            label = root.split('\\')[-1] # 因为windows上路径符号自动转成\了,所以要转义下
            labels.append(label)
            filename = os.path.join(root, file)
            with open(filename, 'rb') as f: # 因为字符集问题因此直接用二进制方式读取
                content = f.read()
                word_list = list(jieba.cut(content))
                words = [wl for wl in word_list]
                documents.append(' '.join(words))
    return documents, labels


def train_fun(td, tl, testd, testl):
    """
    构造模型并计算测试集准确率,字数限制变量名简写
    :param td: 训练集数据
    :param tl: 训练集标签
    :param testd: 测试集数据
    :param testl: 测试集标签
    :return: 测试集准确率
    """
    # 计算矩阵
    tt = TfidfVectorizer(stop_words=STOP_WORDS, max_df=0.5)
    tf = tt.fit_transform(td)
    # 训练模型
    clf = MultinomialNB(alpha=0.001).fit(tf, tl)
    # 模型预测
    test_tf = TfidfVectorizer(stop_words=STOP_WORDS, max_df=0.5, vocabulary=tt.vocabulary_)
    test_features = test_tf.fit_transform(testd)
    predicted_labels = clf.predict(test_features)
    # 获取结果
    x = metrics.accuracy_score(testl, predicted_labels)
    return x


# text classification与代码同目录下
train_documents, train_labels = load_data('./text classification/train')
test_documents, test_labels = load_data('./text classification/test')
x = train_fun(train_documents, train_labels, test_documents, test_labels)
print(x)
           

eturn x

text classification与代码同目录下

train_documents, train_labels = load_data(’./text classification/train’)

test_documents, test_labels = load_data(’./text classification/test’)

x = train_fun(train_documents, train_labels, test_documents, test_labels)

print(x)

继续阅读