文本主题分类器
对文本进行分析、分类是贝叶斯最擅长的应用场景之一,对于不同主题的文本,我们可以用贝叶斯训练一个分类器,然后将其应用在新数据上,预测主题类型。本文主要介绍一下利用贝叶斯对文本进行分类。
需要用到的函数
主要用两个函数来实现文本特征的提取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)