朴素贝叶斯做文本分类代码_百万级新闻文本分类实践(朴素贝叶斯,SVM)
一个课程实践项目,当时感觉难点主要是一百万条新闻数据的获取,我一部分用了清华新闻文本数据集,一部分自己爬取,最后组合成了一个一百万条新闻文本的数据集,一共十个分类。清华大学中文文本分类数据集链接THUCTC: 一个高效的中文文本分类工具thuctc.thunlp.org这里只用了机器学习(朴素贝叶斯,SVM)做了文本分类,没有使用深度学习方法。文本分类流程:分词->去停用词->获取词
一个课程实践项目,当时感觉难点主要是一百万条新闻数据的获取,我一部分用了清华新闻文本数据集,一部分自己爬取,最后组合成了一个一百万条新闻文本的数据集,一共十个分类。清华大学中文文本分类数据集链接
THUCTC: 一个高效的中文文本分类工具thuctc.thunlp.org这里只用了机器学习(朴素贝叶斯,SVM)做了文本分类,没有使用深度学习方法。
文本分类流程:分词->去停用词->获取词典(降维)->求TF-IDF特征->分类
1.分词
采用jieba分词,示例:
import jieba.posseg as pseg
s = '北京邮电大学,简称北邮,是中华人民共和国教育部直属、工业和信息化部共建的全国重点大学'
seg_content = pseg.cut(s)
str_c = ''
for word, flag in seg_content:
str_c = str_c + word + '/' + flag + ' '
ptint(str_c)
选取其中所有的名词来代表一条文本(简单起见,因为名词在文本类别中起到主要作用)。去停用词,即去掉一些与类别无关的一些通用词。
2.获取词典(降维)
在一百万条文本数据集中,最后分词得到的名词有几百万个,它们之中有很多与本类别无关,最后的文本特征不需要这么高的维度。可以用卡方检验的方法找出每个类别的“关键词”,从而达到降维的目的。当然还有别的方法,比如互信息,我这里使用的卡方检验。
卡方检验:每个词对于每个类别,使用四表格的形式,计算该词对于该类是否有较大的影响,公式:
例如四表格:
A表示包含“算法”一词且属于计算机类的文本数,B表示包含“算法”一词且不属于计算机类的文本数。与计算机类别相关度高的词,所求的卡方值会偏高,反之卡方值很低。卡方值越高,代表该词对该类别的的影响越大。因此,我们求出词典中所有词相对于每一类的卡方值,就可以知道与每一类别相关度最高的词。
import json
import codecs
import re
from math import log2
import time
def chi_square(N_10, N_11, N_00, N_01):
"""
卡方计算
:param N_10:
:param N_11:
:param N_00:
:param N_01:
:return: 词项t卡方值
"""
fenzi = (N_11 + N_10 + N_01 + N_00)*(N_11*N_00-N_10*N_01)*(N_11*N_00-N_10*N_01)
fenmu = (N_11+N_01)*(N_11+N_10)*(N_10+N_00)*(N_01+N_00)
if fenmu == 0:
return 0
return fenzi*1.0/fenmu
def selectFeatures(documents, category_name, top_k, select_type="chi"):
"""
特征抽取
:param documents: 预处理后的文档集
:param category_name: 类目名称
:param top_k: 返回的最佳特征数量
:param select_type: 特征选择的方法,可取值chi,mi,freq,默认为chi
:return: 最佳特征词序列
"""
L = []
# 互信息和卡方特征抽取方法
if select_type == "chi" or select_type == "mi":
for t in vocabulary:
print("hans:",t)
start_time = time.time()
N_11 = 0
N_10 = 0
N_01 = 0
N_00 = 0
N = 0
for label, word_set in documents:
if (t in word_set) and (category_name == label):
N_11 += 1
elif (t in word_set) and (category_name != label):
N_10 += 1
elif (t not in word_set) and (category_name == label):
N_01 += 1
elif (t not in word_set) and (category_name != label):
N_00 += 1
else:
print("N error")
exit(1)
if N_00 == 0 or N_01 == 0 or N_10 == 0 or N_11 == 0:
continue
# 卡方计算
A_tc = chi_square(N_10, N_11, N_00, N_01)
L.append((t, A_tc))
end_time = time.time()
print("time:",end_time-start_time)
return sorted(L, key=lambda x:x[1], reverse=True)[:top_k]
vocabulary = set()
def main():
# 读取文档集(需要根据具体类目名称修改)
category_name_li = ["entertainment", "military", "sports",
"education", "finance", "politics","stock",
"energy","home","social","tech"]
# 获取文本(根目录需要根据具体类目名称修改)
all_text = getDocuments(category_name_li)
print("all_text len = ", len(all_text))
# 读取词汇表
getVocabulary(category_name_li)
print("vocabulary len = ", len(vocabulary))
# 获取特征词表
print("="*20, 'n', " 卡方特征选择 n", "="*20)
feature_select_type = "chi"
for category_name in category_name_li:
# 特征抽取,最后一个参数可选值 "chi"卡方
feature_li = selectFeatures(all_text, category_name, 1000, feature_select_type)
print(category_name)
f = open("count/"+category_name+".txt",'w')
for t, i_uc in feature_li:
print("%st%.3f" % (t, i_uc))
f.write(t+'t'+str(i_uc)+'n')
f.close()
if __name__ == "__main__":
main()
这里我选取了每一类的top1000卡方值。但是实际的特征并不是每一类1000个词,其实1000还挺多的,还可以再缩小范围。
3.求TF-IDF向量
接下来,根据我们卡方得到的词典,求出这个在这个词典上每条文本的TF-IDF表示。TF-IDF分为两部分:
TF: 词频。TF的形式化描述,对于在某一文本
IDF:逆文档率。IDF形式化描述为:
sklearn库里已经写好了TF-IDF的计算接口: train_list和test_list每个item的多个词表示一条文本;self.vocabulary是一个key为词,value为词下标的dict。
from sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer, TfidfTransformer
tfidf_vec = TfidfVectorizer(vocabulary=self.vocabulary)
tfidf_matrix_train = tfidf_vec.fit_transform(train_list) #train_list=['名词1 名词2 ...','名词1 名词2 ...',...]
tfidf_matrix_test = tfidf_vec.fit_transform(test_list) ##test_list=['名词1 名词2 ...','名词1 名词2 ...',...]
print(tfidf_matrix_train.shape)
print(tfidf_matrix_test.shape)
# output:
# (400000, 864)
# (600000, 864)
tfidf_matrix_train,tfidf_matrix_test 分别是训练集和测试集的TF-IDF向量,shape为(400000, 864),(600000,864),维度为864,训练集和测试集文本数分别为400000和600000
我们希望把这个tfidf向量保存下来,因为不想每次训练测试的时候都计算一次tfidf,这个过程有点耗时,不仅TfidfVectorizer计算慢(数据量太大),加载源数据也非常慢,需要好几分钟。
TfidfVectorizer返回的是一个稀疏矩阵,即tfidf_matrix_train和tfidf_matrix_test是两个稀疏矩阵,是scipy里实现的csr_matrix。下面的代码实现了csr_matrix的保存和加载,整个过程非非常快只要两三秒,保存的npz文件也只有几十兆。
from scipy import sparse
sparse.save_npz("train.npz",tfidf_matrix_train)
sparse.save_npz("test.npz",tfidf_matrix_test)
train_data = sparse.load_npz("train.npz")
test_data = sparse.load_npz("test.npz")
在处理数据的过程中,多次用到crs_matrix,比如在做交叉验证的时候,需要调换训练集和测试集的TF-IDF向量,所以这里简单总结一下crs_matrix用法
from scipy.sparse import csr_matrix
row = [0,0,1,3] # 行下标
col = [1,2,2,0] # 列下标
data = [1.0,2.0,3.0,4.0] # value
tfidf = csr_matrix((data, (row, col)), shape=(4,4)) # 构造函数
print(tfidf)
print(tfidf.toarray()) # 稀疏矩阵转m*n矩阵
''' output:
(0, 1) 1.0
(0, 2) 2.0
(1, 2) 3.0
(3, 0) 4.0
[[0. 1. 2. 0.]
[0. 0. 3. 0.]
[0. 0. 0. 0.]
[4. 0. 0. 0.]]
'''
d_coo = tfidf.getrow(0).tocoo() # 得到稀疏矩阵第0行并转换成COOrdinate格式
print(d_coo.col)
print(d_coo.data)
'''output:
[1 2]
[1. 2.]
'''
5.分类
朴素贝叶斯:
IDFfrom sklearn.naive_bayes import MultinomialNB
import numpy as np
from sklearn.metrics import confusion_matrix
class Eval:
def __init__(self,pred,gt,cfg):
self.pred = np.array(pred)
self.gt = np.array(gt)
assert len(self.pred.shape)==1, len(self.gt.shape)==1
'''
准确率(accuracy) = 预测对的/所有 = (TP+TN)/(TP+FN+FP+TN)
'''
def get_acc(self):
return len(np.where(self.pred==self.gt)[0])/len(self.gt)
'''
精确率是针对我们预测结果而言的,它表示的是预测为正的样本中有多少是真正的正样本。那么预测为正就有两种 可能了,一种就是把正类预测为正类(TP),另一种就是把负类预测为正类(FP)
精确率(precision) = TP/(TP+FP)
'''
def get_precision(self):
precision = []
u, indices = np.unique(self.pred, return_inverse=True)
for i in range(len(u)):
TP_FP = np.where(indices==i)[0]
TP = set(np.where(self.gt==u[i])[0]).intersection(set(TP_FP))
precision.append(len(TP)/len(TP_FP))
return precision
'''
召回率是针对我们原来的样本而言的,它表示的是样本中的正例有多少被预测正确了。那也有两种可能,一种是把 原来的正类预测成正类(TP),另一种就是把原来的正类预测为负类(FN)。
召回率(recall) = TP/(TP+FN)
'''
def get_recall(self):
recall = []
u, indices = np.unique(self.gt, return_inverse=True)
for i in range(len(u)):
TP_FN = np.where(indices==i)[0]
TP = set(np.where(self.pred==u[i])[0]).intersection(set(TP_FN))
recall.append(len(TP)/len(TP_FN))
return recall
'''
生成混淆矩阵
'''
def get_matrix(self):
cm = confusion_matrix(self.gt,self.pred)
return cm
clf = MultinomialNB()
print("training...")
clf.fit(X_train, y_train) # X_train:TF-IDF;y_train: label_list
print("predict start!")
y_predict = clf.predict(X_test)
evalution = Eval(y_predict, y_test, args.cfg)
print("Acc:",evalution.get_acc())
print("Precision:",evalution.get_precision())
print("Recall:",evalution.get_recall())
print("Confusion matrix:")
print(evalution.get_matrix())
SVM:
clf=LinearSVC()
print("SVM training")
clf.fit(X_train,y_train) # X_train:TF-IDF;y_train: label_list
print("predict start!")
y_predict = clf.predict(X_test)
evalution = Eval(y_predict, y_test, args.cfg)
print("Acc:", evalution.get_acc())
print("Precision:", evalution.get_precision())
print("Recall:", evalution.get_recall())
print("Confusion matrix:", evalution.get_matrix())
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐

所有评论(0)