源码 github 点击打开链接

报告点击打开链接

修改自 :http://blog.csdn.net/github_36326955/article/details/54891204

文本分类

摘要

文本分类指按照预先定义的主题类别,为文档集合中的每个文档确定一个类别。这样用户不但能够方便地浏览文档,而且可以通过限制搜索范围来使文档的查找更容易、快捷。目前,主要方法有朴素贝叶斯分类(Naive Bayesian Model),向量空间模型(Vector Space Model)以及线性最小二乘LLSF(Linear Least Square Fit)。本文中,使用一个已经分好类的数据集,通过对其中的文章进行分词,并存入Bunch数据结构,统计词频,然后再用TF-IDF的方法进行特征选取,最后使用利用朴素贝叶斯对测试数据集分类,并评估准确率。

1.文本分类过程

中文语言的文本分类技术和流程,主要包括下面几个步骤:
1. 预处理:去除文本噪声信息,例如HTML标签,文本格式转换,检测句子边界

2. 中文分词:使用中文分词器为文本分词,并去除停用词

3. 构建词向量空间:统计文本词频,生成文本的词向量空间

4. 权重策略——TF-IDF:使用TF-IDF发现特征词,并抽取为反映文档主题的特征

5. 分类:使用算法训练分类器,本文使用朴素贝叶斯

6. 评价分类结果

 

2.数据集

训练集train_small中包含10个分类,每个分类下有一些txt文本文件。

 

 

 

同样,在测试集test_small中也有10个分类的数据集,就是用这些数据来测试模型的好坏。

3.分词

分词是将连续的字序列按照一定的规范重新组合成词序列的过程,中文分词即将一个汉字序列(句子)切分成一个个独立的单词,中文分词很复杂,从某种程度上并不完全是一个算法问题,最终概率论解决了这个问题,算法是基于概率图模型的条件随机场(CRF)分词是自然语言处理中最基本,最底层的模块,分词精度对后续应用模块的影响很大,文本或句子的结构化表示是语言处理中最核心的任务,目前文本的结构化表示分为四大类:词向量空间、主体模型、依存句法的树表示、RDF的图表示。

由于数据已经相对规整,直接对训练集和测试集中所有文本中文章进行分词,并把分好的文章存入各自对应的分词文件夹。

# -*- coding: utf-8 -*-
import os
import jieba

def savefile(savepath, content):
    fp = open(savepath,"w",encoding='gb2312',errors='ignore')
    fp.write(content)
    fp.close()
def readfile(path):
    fp = open(path,"r",encoding='gb2312',errors='ignore')
    content = fp.read()
    fp.close()
    returncontent

corpus_path = "test_small/"  # 未分词分类预料库路径
seg_path = "test_seg/"  # 分词后分类语料库路径

catelist = os.listdir(corpus_path)  # 获取改目录下所有子目录
print(catelist)
for mydirin catelist:
    class_path = corpus_path + mydir + "/"  # 拼出分类子目录的路径
    
seg_dir = seg_path + mydir +"/"  # 拼出分词后预料分类目录
    
if notos.path.exists(seg_dir):  # 是否存在,不存在则创建
        
os.makedirs(seg_dir)
    file_list = os.listdir(class_path)
    forfile_path infile_list:
        fullname = class_path + file_path
        content = readfile(fullname).strip()  # 读取文件内容
        
content = content.replace("\r\n","").strip()  # 删除换行和多余的空格
        
content_seg = jieba.cut(content)
        savefile(seg_dir + file_path, " ".join(content_seg))
print("分词结束")

 

对于代码中corpus_path ="test_small/"seg_path ="test_seg/" 这两个变量,只要改为训练集的路径,就是对训练集进行分词。分词结果:

 

 

截止目前,我们已经得到了分词后的训练集语料库和测试集语料库,下面我们要把这两个数据集表示为变量,从而为下面程序调用提供服务。我们采用的是Scikit-Learn库中的Bunch数据结构来表示这两个数据集。用Bunch表示,就是:

from sklearn.datasets.base import Bunch
bunch = Bunch(target_name=[],label=[],filenames=[],contents=[]) 

我们在Bunch对象里面创建了有4个成员:
target_name:是一个list,存放的是整个数据集的类别集合。
label:是一个list,存放的是所有文本的标签。
filenames:是一个list,存放的是所有文本文件的名字。
contents:是一个list,分词后文本文件词向量形式

代码如下:

importos

import pickle
from sklearn.datasets.baseimport Bunch
#Bunch 类提供了一种key,value的对象形式
#target_name 所有分类集的名称列表
#label 每个文件的分类标签列表
#filenames 文件路径
#contents 分词后文件词向量形式
def readfile(path):
    fp = open(path,"r",encoding='gb2312',errors='ignore')
    content = fp.read()
    fp.close()
    returncontent

bunch=Bunch(target_name=[],label=[],filenames=[],contents=[])

# wordbag_path="train_word_bag/train_set.dat"
# seg_path="train_seg/"

wordbag_path="test_word_bag/test_set.dat"#test_word_bag文件夹已经自己创建
seg_path="test_seg/"

catelist=os.listdir(seg_path)
bunch.target_name.extend(catelist)#将类别信息保存到Bunch对象

for mydirin catelist:
    class_path=seg_path+mydir+"/"
    
file_list=os.listdir(class_path)
    forfile_path infile_list:
        fullname=class_path+file_path
        bunch.label.append(mydir)#保存当前文件的分类标签
        
bunch.filenames.append(fullname)#保存当前文件的文件路径
        
bunch.contents.append(readfile(fullname).strip())#保存文件词向量

#Bunch对象持久化
file_obj=open(wordbag_path,"wb")
pickle.dump(bunch,file_obj)
file_obj.close()
print("构建文本对象结束")

4.构建向量空间和权重策略

由于文本在储存向量空间是维度较高,为节省储存空间和提高搜索效率,在文本分类之前会自动过滤掉某些字词,这些字或词被称为停用词,本文使用网上的常用停用词。

3.1.TF-IDF方法:

如果某个词或短语在一篇文章中出现的频率高,并且在其他文章中很少出现,那么认为这个词或者短语具有很好的类别区分能力,适合用来分类。

 TF-IDF(Term Frequency-InversDocument Frequency)是一种常用于信息处理和数据挖掘的加权技术。该技术采用一种统计方法,根据字词的在文本中出现的次数和在整个语料中出现的文档频率来计算一个字词在整个语料中的重要程度。它的优点是能过滤掉一些常见的却无关紧要本的词语,同时保留影响整个文本的重要字词。计算方法如下面公式所示:

 

 其中,式中tfidfij表示词频tfi,j和倒文本词频idfi的乘积。TF-IDF值越大表示该特征词对这个文本的重要性越大。

词频(TF):指的是某一个给定的词语在该文件中出现的频率。这个数字是对词数的归一化,以防止它偏向长的文件,对于某一个特定文件里的词语来说,它的重要性可表示为:

 

分子是该词在文件中出现的次数,分母是在文件中所有字词的出现次数之和

逆向文件频率(IDF)是一个词语普遍重要性的度量,某一特定词语的IDF,可以由总文件数目除以包含该词语的文件的数目,再将得到的商取对数:

 

|D|是语料库中的文件总数,j是包含词语的文件数目,如果该词语不在语料库中,就会导致分母为零,因此一般情况下分母还要额外再加上1。

之后计算词频和逆向文件频率的乘积,某一特定文件内的高词语频率,以及该词语在整个文件集合中的低文件频率,可以产生出高权重的TF-IDF,因此TF-IDF倾向于过滤掉常见的词语,保留重要的词语。对训练数据的代码如下:

importos
from sklearn.datasets.baseimport Bunch
import pickle#持久化类
from sklearnimport feature_extraction
from sklearn.feature_extraction.textimport TfidfTransformer#TF-IDF向量转换类
from sklearn.feature_extraction.textimport TfidfVectorizer#TF-IDF向量生成类

def readbunchobj(path):
    file_obj=open(path,"rb")
    bunch=pickle.load(file_obj)
    file_obj.close()
    returnbunch
def writebunchobj(path,bunchobj):
    file_obj=open(path,"wb")
    pickle.dump(bunchobj,file_obj)
    file_obj.close()

def readfile(path):
    fp = open(path,"r",encoding='gb2312',errors='ignore')
    content = fp.read()
    fp.close()
    returncontent


path="train_word_bag/train_set.dat"
bunch=readbunchobj(path)

#停用词
stopword_path="stop_words.txt"
stpwrdlst=readfile(stopword_path).splitlines()
#构建TF-IDF词向量空间对象
tfidfspace=Bunch(target_name=bunch.target_name,label=bunch.label,filenames=bunch.filenames,tdm=[],vocabulary={})
#使用TfidVectorizer初始化向量空间模型
vectorizer=TfidfVectorizer(stop_words=stpwrdlst,sublinear_tf=True,max_df=0.5)
transfoemer=TfidfTransformer()#该类会统计每个词语的TF-IDF权值

#文本转为词频矩阵,单独保存字典文件
tfidfspace.tdm=vectorizer.fit_transform(bunch.contents)
tfidfspace.vocabulary=vectorizer.vocabulary_

#创建词袋的持久化
space_path="train_word_bag/tfidfspace.dat"
writebunchobj(space_path,tfidfspace)

结果是生成一个训练集的词袋。对于测试集生成向量空间,在训练词向量模型时需要加载训练集词袋,将测试集产生的词向量映射到训练集词袋的词典中,生成向量空间模型,测试集生成向量空间的代码:

importos
from sklearn.datasets.baseimport Bunch
import pickle#持久化类
from sklearnimport feature_extraction
from sklearn.feature_extraction.textimport TfidfTransformer#TF-IDF向量转换类
from sklearn.feature_extraction.textimport TfidfVectorizer#TF-IDF向量生成类
def readbunchobj(path):
    file_obj=open(path,"rb")
    bunch=pickle.load(file_obj)
    file_obj.close()
    returnbunch
def writebunchobj(path,bunchobj):
    file_obj=open(path,"wb")
    pickle.dump(bunchobj,file_obj)
    file_obj.close()
def readfile(path):
    fp = open(path,"r",encoding='gb2312',errors='ignore')
    content = fp.read()
    fp.close()
    returncontent

#导入分词后的词向量bunch对象
path="test_word_bag/test_set.dat"
bunch=readbunchobj(path)

#停用词
stopword_path="stop_words.txt"
stpwrdlst=readfile(stopword_path).splitlines()

#构建测试集TF-IDF向量空间
testspace=Bunch(target_name=bunch.target_name,label=bunch.label,filenames=bunch.filenames,tdm=[],vocabulary={})

#导入训练集的词袋
trainbunch=readbunchobj("train_word_bag/tfidfspace.dat")

#使用TfidfVectorizer初始化向量空间
vectorizer=TfidfVectorizer(stop_words=stpwrdlst,sublinear_tf=True,max_df=0.5,vocabulary=trainbunch.vocabulary)
transformer=TfidfTransformer();
testspace.tdm=vectorizer.fit_transform(bunch.contents)
testspace.vocabulary=trainbunch.vocabulary

#创建词袋的持久化
space_path="test_word_bag/testspace.dat"
writebunchobj(space_path,testspace)

5.分类与评估

5.1分类方法:

KNN算法原来最简单,分类精度尚可,但是速度最快支。

朴素贝叶斯算法对于短文本分类的效果最好,精度很高。

支持向量机算法的优势是支持线性不可分的情况,精度上取中。

本文中使用朴素贝叶斯算法,代码如下:

importpickle
from sklearn.naive_bayesimport MultinomialNB  # 导入多项式贝叶斯算法包

def readbunchobj(path):
    file_obj = open(path,"rb")
    bunch = pickle.load(file_obj)
    file_obj.close()
    returnbunch


# 导入训练集向量空间
trainpath = "train_word_bag/tfidfspace.dat"
train_set = readbunchobj(trainpath)
# d导入测试集向量空间
testpath = "test_word_bag/testspace.dat"
test_set = readbunchobj(testpath)
# 应用贝叶斯算法
# alpha:0.001 alpha 越小,迭代次数越多,精度越高
clf = MultinomialNB(alpha=0.001).fit(train_set.tdm, train_set.label)

# 预测分类结果
predicted = clf.predict(test_set.tdm)
total = len(predicted);
rate = 0
for flabel, file_name, expct_catein zip(test_set.label, test_set.filenames, predicted):
    ifflabel != expct_cate:
        rate += 1
        print(file_name,": 实际类别:", flabel,"-->预测分类:", expct_cate)
# 精度
print("error_rate:",float(rate) *100 /float(total),"%")

from sklearnimport metrics


def metrics_result(actual,predict):
    print("精度{0:.3f}".format(metrics.precision_score(actual,predict,average='weighted')))
    print("召回:{0:0.3f}".format(metrics.recall_score(actual,predict,average='weighted')))
    print("f1-score:{0:.3f}".format(metrics.f1_score(actual,predict,average='weighted')))
metrics_result(test_set.label, predicted)

5.2评估

预测结果和实际情况0-1状态图

 

实际情况1代表真实情况此类,0表示不属于

预测结果

1代表预测属于此类

0代表预测不属于此类

 

1

0

1

True positive

False positive

0

False negative

True negative

P = True positive/(True positive + False positive)

R = True positive/(True positive + False negative)

F1-Score = (2 * P * R)/(P + R)

在此实验中,我通过准确率、召回率和F1-Score这三个性能评估方法对最后的结果进行了详细的分析。结果显示:

准确率和召回率为0.92,F1-Score为0.918,准确率可以接受。

 

 

 

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐