对新闻文本(10类)进行文本分类,通过准确率、召回率、 f1-score 等指标对分类结果进行分析。
python版本:python 3.6
分类方法:朴素贝叶斯
需导入的相关库
import os
import time
import numpy as np
import pandas as pd
import jieba
from jieba import analyse
from sklearn.utils import shuffle
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import CountVectorizer,TfidfVectorizer #词集转换成向量
from sklearn.naive_bayes import MultinomialNB #朴素贝叶斯多分类
from sklearn.metrics import classification_report
import gensim #自然语言处理库
from gensim import corpora,models,similarities
上述有些库源 在windows下好像与python3.7版本会出现版本不兼容的问题,我在linux下使用3.7正常,windows下出现“DLL load failed”,所以换用3.6
对数据源选取数据规模
大家可以选择网上现有的数据源,然后选出自己需要数量的数据。我的数据是我自行爬取的,所以在爬取的时候就做了相应的数据预处理。所以我选取的数据是已经去空行、去重去掉一些乱七八糟的信息之后的数据。
1、读取数据源
#读取数据源,注释掉的是拼接数据的部分,有需要的可以参考
def con_data():
# df1 = pd.read_csv('chinanews00.csv',names=['category','theme','URL','content'])
# df2 = pd.read_csv('chinanews11.csv',names=['category','theme','URL','content'])
# data = pd.concat([df1,df2],axis=0,ignore_index=True) #拼接表格
data = pd.read_csv('chinanews.csv',names=['category','theme','URL','content'])
df = data.groupby('category').count()#展示数据规模
print(df)
# print(data.shape)
return data
2、按类别,分别选出前n条
实验之前做的是100w条数据,但是在这里为了重复方便,我将每类数据缩减为1000条,这样比较快~
#分组选行
def group(data,amount,file_path):
df = data.groupby('category').head(amount)
df.to_csv(file_path,mode='a',header=None, index=False, encoding="utf-8-sig")
于是就有了目标数据 eg: test.csv
文本分类
1、读入待分类数据
def read_file():
data = pd.read_csv('test.csv',names=['category','theme','URL','content'])
df = data.groupby('category').count()#展示数据规模
print(df)
return data
数据规模:
样本长这样:
2、对数据进行切割
这一步可有可无,主要是因为有的新闻文本篇幅很长很长,有的又很短,后面会造成词向量的长短不一。所以理论上来讲,是需要去掉过长或过短的文本内容,使得新闻文本的字数大致保持在一个范围内。不剔除的话,会降低分类的准确率。
3、jieba分词并去停用词
def separate_words(data):
content = data.content.values.tolist() #将文本内容转换为list格式
#读入停用词表
stopwords = pd.read_csv("stopwords.txt",index_col=False,sep="\t",quoting=3,names=['stopword'], encoding='utf-8') #list
stopwords = stopwords.stopword.values.tolist()
print("正在分词,请耐心等候......")
contents_clean = []
all_words = []
for line in content:
current_segment = jieba.lcut(line) #jieba分词
current_segment = [x.strip() for x in current_segment if x.strip()!=''] #去掉分词后出现的大量空字符串
if len(current_segment) > 1 and current_segment != "\r\n":
line_clean = []
for word in current_segment:
if word in stopwords:
continue
line_clean.append(word)
all_words.append(str(word))
contents_clean.append(line_clean)
print('------------分词完成-----------')
return contents_clean, all_words
结果是这样的:
其中,all_words可以用来统计某些词出现的频率,依据这个来修改停用词表stopwords。下图是我按照自己的词频统计,去掉了一些不合理的高频词后出现的结果。
4、标签转换
中文标签改为数字标签,便于分类
转换之前的标签:
#标签转换
label_mappping = {'汽车':1,'财经':2, '法治':3, '社会':4, '体育':5, '国际':6, '文化':7, '军事':8, '娱乐':9, '台湾':0}
df_train["label"] = df_train["label"].map(label_mappping)
print(df_train.head())
print("--------------------------------------3------------------------------------------")
转换之后的结果:
5、切分数据集
按1:1切分
#切分数据集
x_train,x_test,y_train,y_test = train_test_split(df_train["contents_clean"].values,df_train["label"].values,test_size=0.5)
6、开始训练
因为现在的数据内容是以‘,’分隔开的,所以需要去掉将词与词之间用‘ ’连接起来。
def format_transform(x): #x是数据集(训练集或者测试集)
words =[]
for line_index in range(len(x)):
try:
words.append(" ".join(x[line_index]))
except:
print("数据格式有问题")
return words
#训练
words_train = format_transform(x_train)
vectorizer = TfidfVectorizer(analyzer='word', max_features=4000,ngram_range=(1, 3),lowercase = False)
vectorizer.fit(words_train)#转为向量格式
classifier = MultinomialNB()
classifier.fit(vectorizer.transform(words_train), y_train)
7、测试,并查看相关结果
words_test = format_transform(x_test)
score = classifier.score(vectorizer.transform(words_test), y_test)
print("----------------------------------分类结果报告-----------------------------------------")
print("分类准确率:" + str(score))
print("训练时间:" + str(round((end_1-start_1), 2)) + '秒')
print("测试时间:" + str(round((end_2-start_2), 2)) + '秒')
y_predict=classifier.predict(vectorizer.transform(words_test))
print(classification_report(y_test,y_predict))
出来的结果是这样的:
emmmm,这个分类结果不是很理想。。。可以通过调参提高分类结果准确率。