使用朴素贝叶斯进行中文文本分类

1 应用场景

使用朴素贝叶斯对未知类型的小说(文本文档)进行类型分类。训练集有三种类型的小说,分别是玄幻、科幻和都市。在本文中,准备的数据从某小说网站下载.txt文件,采用GB2312编码。每种类型有三部小说。测试数据用同样的方法得到的,链接为http://www.55x.cn/html/dushi/txt39407.html

2 特征抽取

“词袋”

文本分析是机器学习算法的重要的应用领域。但是生数据————符号序列不能直接用于算法当中。这是因为许多算法是需要合适长度的的数值型的特征向量才能够进行必要的计算。

为了解决这个问题,scikit-learn提供了许多方法,能够用于绝大多数的从文本内容当中提取数值特征的场景:

  • 切分词并给予每一个可能的标记(词)一个整型ID,例如使用空格或者斜杠作为分隔符。

  • 在每一个文本中,记录每一个标记(词)的出现次数。

  • 标准化并计算权重

在这种模式下,特征和样本有如下定义:

  • 每一个独立的标记(词)的出现次数(不管是否标准化)作为一个特征。

  • 一个给定的文档的所有的标记(词)频次所组成的向量是一个多变量样本。

因此,一组文本可以由一个矩阵表示。这个矩阵的每一行表示一个文本,每一列表示一个标记(词)。

我们调用向量化过程来实现将一系列文本转换成数值型的特征向量。这些特定的方法(切分词、技术、标准化)被称为“词袋”或者“n-grams袋”表示。

从频次到频率

频次计数是一个好的开始,但是这里有一点问题:相较于较短的文档,较长的文档将会有更高的平均计数值,尽管它们可能谈论的是相同话题.

为了避免这些潜在的偏差,可以让它除以文档中所有词语在文档出现的次数的和.这个新特征被称为tf,即文档频率(Term Frequencies)。

在tf的上层再进行细化的一个办法是降低在语料库中多数文档出现的词语的权重,因为这些词语相较于那些只在较少语库文档中出现的词语所携带的类别指示信息要少.

这个降权方式被称为tf-idf,意即文档频率-逆文档频率(Term Frequency times Inverse Document Frequency)。TF-IDF是一种用于信息检索与数据挖掘的常用加权技术,是衡量词在某文本中重要性的一种度量。它比其他诸如简单地统计词频的方法好的一点是,它对那些在各处十分常见而又不具有太多实际意义的词处理地比较好,比如”a”, “the”等等,使得它们不能占据很多的权重。所以TF-IDF被广泛地应用在文本分类等多个领域,取得了不错的效果。

tf和tf-idf都可以像下面这样计算:

>>> from sklearn.feature_extraction.text import TfidfTransformer
>>> tf_transformer = TfidfTransformer(use_idf = False).fit(X_train_counts)
>>> x_train_tf = tf_transformer.transform(X_train_counts)

在上面的示例代码中,我们首先使用fit(...)方法将评估器应用数据上,接着transform(...)方法见我们的计数举证转换为tf-if形式.这两步完全可以结合起来,跳过冗余处理,以更快地获得相同的最终结果.者可以像下面这样使用fit_transform(...)方法来实现:

>>> tfidf_transformer = TfidfTransformer()
>>> x_train_tfidf = tfidf_transformer.fit_transform(X_train_counts)

为了让大家更直观的看到文本特征抽取的过程,我们可以先用一个简单的例子看一下:

from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfTransformer
integrated_train_data=[]
integrated_train_data.append("This is the first document")
integrated_train_data.append("This is the second document ! Yes ! It is")
integrated_train_data.append("Indeed! That's all!")
print(integrated_train_data)

count_vector = CountVectorizer()
matrix= count_vector.fit_transform(integrated_train_data)
print(matrix)

print(count_vector.get_feature_names())
print(matrix.toarray())

train_tf = TfidfTransformer(use_idf=False).fit_transform(matrix)
print(train_tf)

运行结果:

['This is the first document', 'This is the second document ! Yes ! It is', "Indeed! That's all!"]
  (0, 1)	1
  (0, 2)	1
  (0, 8)	1
  (0, 4)	1
  (0, 9)	1
  (1, 5)	1
  (1, 10)	1
  (1, 6)	1
  (1, 1)	1
  (1, 8)	1
  (1, 4)	2
  (1, 9)	1
  (2, 0)	1
  (2, 7)	1
  (2, 3)	1
['all', 'document', 'first', 'indeed', 'is', 'it', 'second', 'that', 'the', 'this', 'yes']
[[0 1 1 0 1 0 0 0 1 1 0]
 [0 1 0 0 2 1 1 0 1 1 1]
 [1 0 0 1 0 0 0 1 0 0 0]]
TfidfTransformer(norm='l2', smooth_idf=True, sublinear_tf=False, use_idf=False)
  (0, 1)	0.4472135955
  (0, 2)	0.4472135955
  (0, 8)	0.4472135955
  (0, 4)	0.4472135955
  (0, 9)	0.4472135955
  (1, 5)	0.316227766017
  (1, 10)	0.316227766017
  (1, 6)	0.316227766017
  (1, 1)	0.316227766017
  (1, 8)	0.316227766017
  (1, 4)	0.632455532034
  (1, 9)	0.316227766017
  (2, 0)	0.57735026919
  (2, 7)	0.57735026919
  (2, 3)	0.57735026919

通过输出结果,可以很轻易的看出整个转换的逻辑。词的排列是按照自然顺序(字母顺序)排列的。

		[[0 1 1 0 1 0 0 0 1 1 0]
		 [0 1 0 0 2 1 1 0 1 1 1]
		 [1 0 0 1 0 0 0 1 0 0 0]]

	  (0, 1)	1
	  (0, 2)	1
	  (0, 8)	1
	  (0, 4)	1
	  (0, 9)	1
	  (1, 5)	1
	  (1, 10)	1
	  (1, 6)	1
	  (1, 1)	1
	  (1, 8)	1
	  (1, 4)	2
	  (1, 9)	1
	  (2, 0)	1
	  (2, 7)	1
	  (2, 3)	1

其实是一个东西,就是词频的稀疏矩阵表示。后者的括号中的第一个元素表示的是行数,第二个元素表示的是列数。

TfidfTransformer(norm='l2', smooth_idf=True, sublinear_tf=False, use_idf=False)
  (0, 1)	0.4472135955
  (0, 2)	0.4472135955
  (0, 8)	0.4472135955
  (0, 4)	0.4472135955
  (0, 9)	0.4472135955
  (1, 5)	0.316227766017
  (1, 10)	0.316227766017
  (1, 6)	0.316227766017
  (1, 1)	0.316227766017
  (1, 8)	0.316227766017
  (1, 4)	0.632455532034
  (1, 9)	0.316227766017
  (2, 0)	0.57735026919
  (2, 7)	0.57735026919
  (2, 3)	0.57735026919

上面的这些输出是在转换为tf-idf度量的过程中的参数,和转换后的结果————权重矩阵。具体的权重计算公式可以参考:http://scikit-learn.org/stable/modules/feature_extraction.html

3 中文分词

中文分词不是本文的重点,这里我们直接使用第三方工具,除了使用jieba,还可以使用极易中文分词组件、MMSEG、中科院的ICTCLAS等等。

4 朴素贝叶斯

要注意的是,我们选用的朴素贝叶斯分类器类别:MultinomialNB,这个分类器以出现次数作为特征值,我们使用的TF-IDF也能符合这类分布。其他的朴素贝叶斯分类器如GaussianNB适用于高斯分布(正态分布)的特征,而BernoulliNB适用于伯努利分布(二值分布)的特征。

5 源码

#coding="gbk"
import os   #用于读取文件
import jieba #用于给中文分词
import pandas
import numpy
from sklearn.feature_extraction.text import TfidfTransformer
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.naive_bayes import MultinomialNB

#preprocess用于将一个文本文档进行切词,并以字符串形式输出切词结果
def preprocess(path_name):
    text_with_spaces=""
    textfile=open(path_name,"r",encoding="gbk").read()
    textcut=jieba.cut(textfile)
    for word in textcut:
        text_with_spaces+=word+" "
    return text_with_spaces


#loadtrainset用于将某一文件夹下的所有文本文档批量切词后,载入为训练数据集;返回训练集和每一个文本(元组)对应的类标号。
def loadtrainset(path,classtag):
    allfiles=os.listdir(path)
    processed_textset=[]
    allclasstags=[]
    for thisfile in allfiles:
        path_name=path+"/"+thisfile
        processed_textset.append(preprocess(path_name))
        allclasstags.append(classtag)
    return processed_textset,allclasstags


processed_textdata1,class1=loadtrainset("F:/Datasets/玄幻", "玄幻")
processed_textdata2,class2=loadtrainset("F:/Datasets/科幻", "科幻")
processed_textdata3,class3=loadtrainset("F:/Datasets/都市", "都市")
integrated_train_data=processed_textdata1+processed_textdata2+processed_textdata3
classtags_list=class1+class2+class3


count_vector = CountVectorizer()
#该类会将文本中的词语转换为词频矩阵,矩阵元素a[i][j] 表示j词在i类文本下的词频
vector_matrix = count_vector.fit_transform(integrated_train_data)

#tfidf度量模型
train_tfidf = TfidfTransformer(use_idf=False).fit_transform(vector_matrix)
#将词频矩阵转化为权重矩阵,每一个特征值就是一个单词的TF-IDF值


#调用MultinomialNB分类器进行训练
clf = MultinomialNB().fit(train_tfidf,classtags_list)#


#测试
testset=[]
testset.append(preprocess("F:/Datasets/testdata/testdata.txt"))
#testset.append("神仙 修炼 千年 上古 宫殿 功夫")
new_count_vector = count_vector.transform(testset)
new_tfidf= TfidfTransformer(use_idf=False).fit_transform(new_count_vector)
predict_result = clf.predict(new_tfidf)	#预测结果
print(predict_result)
原文地址:https://www.cnblogs.com/d0main/p/6914742.html