基于贝叶斯算法的新闻分类器

文本主题分类器

对文本进行分析、分类是贝叶斯最擅长的应用场景之一,对于不同主题的文本,我们可以用贝叶斯训练一个分类器,然后将其应用在新数据上,预测主题类型。本文主要介绍一下利用贝叶斯对文本进行分类。

需要用到的函数

主要用两个函数来实现文本特征的提取CountVectorizer 和 TfidfVectorizer

  • CountVectorizer:
    只考虑词汇在文本中出现的频率
  • TfidfVectorizer:
    除了考量某词汇在文本出现的频率,还关注包含这个词汇的所有文本的数量。能够削减高频没有意义的词汇出现带来的影响, 挖掘更有意义的特征。
    举个例子:
    CountVectorizer的简单操作:它的输入要求是string串,主要用来形成词向量。例如:
from sklearn.feature_extraction.text import CountVectorizer
essay=["小明 长得 很帅","小明的朋友 长得 很帅","小明的 爸爸 很帅"]
cv=CountVectorizer()
cv_fit=cv.fit_transform(essay)
print(cv.get_feature_names())
print(cv_fit.toarray())

基于贝叶斯算法的新闻分类器

get_feature_names()可看到所有文本的关键字
toarray()可看到词频矩阵的结果

TfidfVectorizer的简单操作,与CountVectorizer大致相同

from sklearn.feature_extraction.text import  TfidfVectorizer
essay=["小明 长得 很帅","小明的朋友 长得 很帅","小明的 爸爸 很帅"]
cv= TfidfVectorizer()
cv_fit=cv.fit_transform(essay)
print(cv.get_feature_names())
print(cv_fit.toarray())

基于贝叶斯算法的新闻分类器

代码

首先对原数据进行处理,提取出标题,内容,URL等有用信息。

df_news=pd.read_table('路径/news.txt',names=['category','theme','URL','content'],encoding='gb18030')#由于的一些问题遇到了编码问题,这里用gb18030而不用utf-8
df_news=df_news.dropna()#直接把有缺失值的数据删除
df_news.head()

将content里面的内容转换成list的格式。

content=df_news.content.values.tolist()

使用jieba库分词器进行分词

content_S = []
for line in content:
    current_segment = jieba.lcut(line)
    if len(current_segment) > 1 and current_segment != '\r\n': #如果分词后的词大于1(也就是说可以分出词)而且不是换行符('\r\n),则加入到content_S中
        content_S.append(current_segment)

进行dataFrame处理

df_content=pd.DataFrame({'content_S':content_S})#为了方便下面的数据清理

接下来就是进行数据清洗,去掉停用词。读取停用词表。

stopwords=pd.read_csv('路径/Stop list.txt',index_col=False,sep="\t",quoting=3,names=['stopword'], encoding='gb18030')#停用词表可以去网上直接下载

去掉文本中所有的停用词

def drop_stopwords(contents,stopwords):
    contents_clean = []
    all_words = []
    for line in contents:
        line_clean = []
        for word in line:
            if word in stopwords:
                continue
            line_clean.append(word)
            all_words.append(word)
        contents_clean.append(line_clean)
    return contents_clean,all_words
contents = df_content.content_S.values.tolist()    
stopwords = stopwords.stopword.values.tolist()
contents_clean,all_words = drop_stopwords(contents,stopwords)

对清洗后的数据进行dataFrame处理

df_all_words=pd.DataFrame({'contents_clean':contents_clean})

可以将词组打印出来

df_all_words=pd.DataFrame({'all_words':all_words})
df_all_words.head()

统计所有词组的词频并按照次数高低排序

import numpy 
words_count=df_all_words.groupby(by=['all_words'])['all_words'].agg({"count":numpy.size}) #groupby分组与agg聚合。将词、字进行分组统计每个词、字出现的次数
words_count=words_count.reset_index().sort_values(by=["count"],ascending=False) #按统计的次数从高往低排序
words_count.head()

统计后的结果如下:
基于贝叶斯算法的新闻分类器
接下来是非常有趣的一步,就是创建词云(根据字母出现的次数生成各种形状的图,我这里是生成的心形词云),这里需要安装wordcloud库。

from wordcloud import WordCloud
from scipy.misc import imread
import matplotlib.pyplot as plt
%matplotlib inline
import matplotlib
mask=imread("心形图片路径/xin.jpg") #读入所要绘制词云的形状,这个图片是需要自己下载的,想生成什么形状的就下载什么形状的图片
matplotlib.rcParams['figure.figsize'] = (20.0, 10.0)#图片的长和宽
wordcloud=WordCloud(font_path="simhei.ttf",background_color="white",width=1000,mask=mask)#font_path="simhei.ttf"是字体的路径,mask就是所要生成词云的形状
word_frequence = {x[0]:x[1] for x in words_count.head(100).values}
wordcloud=wordcloud.fit_words(word_frequence)
plt.imshow(wordcloud)

效果如下:
基于贝叶斯算法的新闻分类器
下面我们就要用贝叶斯进行分类了。
首先加入label

df_train=pd.DataFrame({'contents_clean':contents_clean,'label':df_news['category']})
df_train.tail()

效果如下:
基于贝叶斯算法的新闻分类器
下面来看一看标签有多少类

df_train.label.unique()

结果中有9类。机器不能识别出汉字,所以我们要想办法转化为数字。

label_mapping = {"汽车": 1, "财经": 2, "科技": 3, "健康": 4, "教育": 5,"文化": 6,"军事": 7,"娱乐":8 ,"时尚": 9}
df_train['label'] = df_train['label'].map(label_mapping)  

进行测试集和训练集拆分

from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(df_train['contents_clean'].values, df_train['label'].values, random_state=1)

之后将每个新闻信息转换成字符串形式。因为CountVectorizer 和 TfidfVectorizer的输入为字符串形式。

words = []
for line_index in range(len(x_train)):
    try:
        words.append(' '.join(x_train[line_index]))
    except:
        print (line_index,word_index) #这里的try和except是用来处理异常的

基于词频,构造词组向量(具体方法上面已将提到)

from sklearn.feature_extraction.text import CountVectorizer
vec = CountVectorizer(analyzer='word', max_features=4000,  lowercase = False)
c=vec.fit(words)

之后构建贝叶斯分类器,要用到 MultinomialNB

from sklearn.naive_bayes import MultinomialNB
classifier = MultinomialNB()
classifier.fit(vec.transform(words), y_train)

然后进行测试,不过在测试之前,我们要保证测试集个训练集是一样的,测试集也需要进行字符串的转换

test_words = []
for line_index in range(len(x_test)):
    try:
        test_words.append(' '.join(x_test[line_index]))
    except:
         print (line_index,word_index)
test_words[0]

之后进行精确度的计算

classifier.score(vec.transform(test_words), y_test)

结果为:0.83018867924528306
为了比较一下CountVectorizer 和 TfidfVectorizer,又利用TfidfVectorizer构造向量

from sklearn.feature_extraction.text import TfidfVectorizer
vectorizer = TfidfVectorizer(analyzer='word', max_features=4000,  lowercase = False)
vectorizer.fit(words)
from sklearn.naive_bayes import MultinomialNB
classifier = MultinomialNB()
classifier.fit(vectorizer.transform(words), y_train)
classifier.score(vectorizer.transform(test_words), y_test)

结果为:0.79088050314465408
在这里CountVectorizer的效果要好一些,但是处理一些更大数据的时候,一般使用TF-IDF。

完整代码

import pandas as pd
import jieba
import numpy 
import matplotlib.pyplot as plt
from wordcloud import WordCloud
from scipy.misc import imread
from sklearn.model_selection import train_test_split
df_news=pd.read_table('news.txt',names=['category','theme','URL','content'],encoding='gb18030')#由于的一些问题我的数据遇到了编码问题,这里用gb18030而不用utf-8
df_news=df_news.dropna()#直接把有缺失值的数据删除
#df_news.head()
content=df_news.content.values.tolist()#转化为list格式,方便下面的遍历
#print(content[0])
#接下来进行分词
content_S = []
for line in content:
    current_segment = jieba.lcut(line)
    if len(current_segment) > 1 and current_segment != '\r\n':
        content_S.append(current_segment)
#content_S[1000]
df_content=pd.DataFrame({'content_S':content_S})#为了方便下面的数据清理
#df_content.head()
stopwords=pd.read_csv('Stop list.txt',index_col=False,sep="\t",quoting=3,names=['stopword'], encoding='gb18030')#读入停用词表
#接下来去掉停用词
def drop_stopwords(contents,stopwords):
    contents_clean = []
    all_words = []
    for line in contents:
        line_clean = []
        for word in line:
            if word in stopwords:
                continue
            line_clean.append(word)
            all_words.append(word)
        contents_clean.append(line_clean)
    return contents_clean,all_words
contents = df_content.content_S.values.tolist()    
stopwords = stopwords.stopword.values.tolist()
contents_clean,all_words = drop_stopwords(contents,stopwords)
df_all_words=pd.DataFrame({'contents_clean':contents_clean})
#df_all_words.head()
df_all_words=pd.DataFrame({'all_words':all_words})
#df_all_words.head()
words_count=df_all_words.groupby(by=['all_words'])['all_words'].agg({"count":numpy.size}) #groupby分组与agg聚合。将词、字进行分组统计每个词、字出现的次数
words_count=words_count.reset_index().sort_values(by=["count"],ascending=False) #按统计的次数从高往低排序
#words_count.head()
words_count=df_all_words.groupby(by=['all_words'])['all_words'].agg({"count":numpy.size}) #groupby分组与agg聚合。将词、字进行分组统计每个词、字出现的次数
words_count=words_count.reset_index().sort_values(by=["count"],ascending=False) #按统计的次数从高往低排序
#words_count.head()
#接下来绘制词云
%matplotlib inline
import matplotlib
mask=imread("xin.jpg")
matplotlib.rcParams['figure.figsize'] = (20.0, 10.0)
wordcloud=WordCloud(font_path="simhei.ttf",background_color="white",width=1000,mask=mask)
word_frequence = {x[0]:x[1] for x in words_count.head(100).values}
wordcloud=wordcloud.fit_words(word_frequence)
plt.imshow(wordcloud)
df_train=pd.DataFrame({'contents_clean':contents_clean,'label':df_news['category']})
label_mapping = {"汽车": 1, "财经": 2, "科技": 3, "健康": 4, "教育": 5,"文化": 6,"军事": 7,"娱乐":8 ,"时尚": 9}
df_train['label'] = df_train['label'].map(label_mapping)  #因为机器只能识别数字,所以将标签转化为数字
x_train, x_test, y_train, y_test = train_test_split(df_train['contents_clean'].values, df_train['label'].values, random_state=1)
#接下来转化为字符串
words = []
for line_index in range(len(x_train)):
    try:
        words.append(' '.join(x_train[line_index]))
    except:
        print (line_index,word_index) #这里的try和except是用来处理异常的
from sklearn.feature_extraction.text import CountVectorizer
vec = CountVectorizer(analyzer='word', max_features=4000,  lowercase = False)
c=vec.fit(words)
#接下来进行测试
test_words = []
for line_index in range(len(x_test)):
    try:
        test_words.append(' '.join(x_test[line_index]))
    except:
         print (line_index,word_index)
 classifier.score(vec.transform(test_words), y_test)

输出:0.83018867924528306
最后的向量构造也可以用TfidfVectorizer

from sklearn.feature_extraction.text import TfidfVectorizer
vectorizer = TfidfVectorizer(analyzer='word', max_features=4000,  lowercase = False)
vectorizer.fit(words)
from sklearn.naive_bayes import MultinomialNB
classifier = MultinomialNB()
classifier.fit(vectorizer.transform(words), y_train)
classifier.score(vectorizer.transform(test_words), y_test)