关于本实战案例相关的源码文件、数据文件,大家关注“AI自研社”公众号后,发送消息”贝叶斯”,即可获取网盘下载链接。
#1 基本情况概述
任务描述:使用贝叶斯算法对爬取的新闻数据进行自动标注分类。
我们在网上浏览新闻时,不同新闻有不同的板块归属,例如时尚类、政治类、经济类的新闻等等,我们的目的就是利用搜集到的新闻文本及手动标识的分类标签,通过算法对文本信息进行学习,最后实现对新的新闻文本进行自动分类。如下图为网易的新闻分类板块:
主要工作流程:
1、本项目我们已经预先从网络上爬取了新闻数据,并进行了必要性的处理(内容整理,手动打标签),因此我们只需要将新闻数据读取进来即可;
2、采用分词方法对新闻文章内容进行分词。因为,通常我们在处理文本时的基本对象都是词,而不是文章;
3、针对分词后的数据,我们需要去除掉停用词,因为停用词一般都没有实际的含义,对理解新闻文章的分类没有实际意义;
4、由于机器学习算法只能处理数值型数据,对文本数据无法直接处理,因此我们需要基于文本数据,通过某些方法来构建数值型的特征;
5、使用朴素贝叶斯算法来最终完成分类任务。
#2 代码详细解读
先导入我们要用到的库,其中jieba用于进行中文分词,如果没安装过,可以使用pip命令安装
import pandas as pdimport jieba#pip install jieba
1、基础数据分析
接下来,我们将数据导入,并查看数据的基本格式,我们将末尾五条数据显示出来,方便查看:
df_news = pd.read_table('./data/data.txt',names=['category','theme','URL','content'],encoding='utf-8')df_news = df_news.dropna()df_news.tail()
原始数据主要包含4列信息,具体解释如下:
Category:当前新闻所属的类别,一会我们要进行分别任务,这就是标签了。
Theme:新闻的主题,这个咱们先暂时用不上,大家在练习的时候也可以把它当作特征。
URL:爬虫爬取的新闻链接地址,方便检验爬取数据是不是完整的,这个咱们暂时也不需要。
Content:新闻的内容,这些就是一篇文章了,里面的内容还是很丰富的。
下面我们查看一下新闻主要有哪些分类
df_news['category'].drop_duplicates()
从返回结果可以看出,一共包含10个不同的分类。
接下来我们再查看一下样本的数据规模:
df_news.shape(5000, 4)
从返回结果来看,样本一共包含5000条数据,样本的规模并不算大。
2、进行中文分词
相比于过去的一些实战案例,本次文本数据比较特殊,之前的数据都是数值型的,直接传入算法中求解参数即可。
那么对于文本,我们如何将其转换成特征呢?
一般我们都是通过将文本转换成向量表示。但是如果将一整篇文章都转换成一个向量,显然内容过于庞大,况且文章中很多内容对于类别划分是无用的。所以通常我们的做法是先将文章进行分词,然后再提取关键词,最后我们再将文章的关键词进行向量化,最后利用算法进行分析和预测。
因此,我们先来进行中文分词,如果对分词不了解,可以查看我的上一篇文章。
先将df_news.content列转换成list数组,先存储在content变量中
content = df_news.content.values.tolist() #将每一篇文章转换成一个list#随便查看一下其中的一条数据print (content[1000])
使用结巴分词,这里面主要是调用jieba.lcut方法,并将分词后的结果存储在content_S变量中
content_S = []for line in content: current_segment = jieba.lcut(line) #对每一篇文章进行分词 if len(current_segment) > 1 and current_segment != 'rn': #换行符 content_S.append(current_segment) #保存分词的结果
然后我们查看一下分词后的结果:
df_content=pd.DataFrame({'content_S':content_S}) #专门展示分词后的结果df_content.head()
3、去除停用词
完成了分词任务之后,我们要处理的对象就是其中每一个词了,但是这里我们得先考虑一个问题,一篇文章是什么主题应该是由其内容中的一些关键词来决定的,比如这里的‘车展’,‘跑车’,‘发动机’等,这些词我们一看就知道跟汽车相关的。
但是另一类词,‘今天’,‘在’,‘3月份’等,这些词给我们的感觉好像既可以在汽车相关的文章中使用,也可以在其他类型的文章使用,就把它们称作停用词,也就是我们一会要过滤的目标。
首先需要选择一个合适的停用词库,这些网上有很多现成的,比如”哈工大停用词库”、”四川大学机器智能实验室停用词库”等等。
接下来,我们将停用词库读取进来
stopwords=pd.read_csv("stopwords.txt",index_col=False,sep="t",quoting=3,names=['stopword'], encoding='utf-8')
#随便查看一下内容:stopwords.iloc[20:30]
筛选过滤掉文章中存在的停用词,定义一个函数,专门用于过滤停用词
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(str(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_content=pd.DataFrame({'contents_clean':contents_clean})df_content.head()
就看这里最后一条数据吧,没过滤之前: [在, 今年, 3, 月, 的, 日内瓦, 车展, 上, ,, 我们, 见到, 了, 高尔夫... 过滤之后: [日内瓦, 车展, 见到, 高尔夫, 家族, 新, 成员, 高尔夫, 敞篷版, 款, 全新,…
4、数据集及数据标签制作
利用处理好的数据,重新构造一个数据集合,主要包含两列数据,一列是处理好的词列表,一列是其对应的标签。
df_train=pd.DataFrame({'contents_clean':contents_clean,'label':df_news['category']})df_train.tail()
接下来对标签进行编码,首先查看标签的唯一值
df_train.label.unique()
对标签进行编码
label_mapping = {"汽车": 1, "财经": 2, "科技": 3, "健康": 4, "体育":5, "教育": 6,"文化": 7,"军事": 8,"娱乐": 9,"时尚": 0}df_train['label'] = df_train['label'].map(label_mapping) #构建一个映射方法df_train.head()
5、划分训练集、测试集
from sklearn.model_selection import train_test_splitx_train, x_test, y_train, y_test = train_test_split(df_train['contents_clean'].values, df_train['label'].values, random_state=1) len(x_train)3750len(x_test)1250
6、TF-IDF关键词提取算法简介
简单来讲,TF-IDF的基本原理就是基于以下假设实现的:
如果一个词在整个语料库中(可以当作是在所有文章中)出现的次数都很高(这篇也有它,另一篇还有这个词),那么这个词的重要程度就不高,因为它更像一个通用词。
如果某一个词在整体的预料库中的词频很低,但是在这一篇文章中的词频却很高,我们就有理由认为它在这篇文章中就很重要了。TF-IDF就是基于这个假设来实现的。
为了便于理解,我们先举一个小例子:
#调用skearn下面的TfidfVectorizerfrom sklearn.feature_extraction.text import TfidfVectorizer #假设X_test中包含两篇文章,如下:X_test = ['家禽 飞翔 蓝色 天空','恐龙 飞翔 蓝色 天空'] #调用Tfidf进行分析tfidf=TfidfVectorizer()weight=tfidf.fit_transform(X_test).toarray()word=tfidf.get_feature_names()#查看提取的关键词及其特征权重for i in range(len(weight)): print (u"第", i, u"篇文章的tf-idf权重特征") for j in range(len(word)): print (word[j], weight[i][j])
从结果来看,第一篇文章中权重最高的是”家禽”这个词,因为他们仅在第一篇文章中出现了;同样,第二篇文章中权重最高的是”恐龙”这个词,因为它只在第二篇文章中出现了。所以要论区分程度的话,还是它俩更重要一些,其权重值自然就更大了。
TF-IDF算法计算出来的权重就是就是对应词的数值化特征,把它们组合在一起就形成我们的特征矩阵了。
7、使用TF-IDF构建特征
首先,我们需要先将所有词汇总在一起:
words = []for line_index in range(len(x_train)): try: words.append(' '.join(x_train[line_index])) except: print (line_index,word_index)
print (len(words))3750
使用TF-IDF构建特征
from sklearn.feature_extaction.text import TfidfVectorizervectorizer = TfidfVectorizer(analyzer='word', max_features=4000, lowercase = False)vectorizer.fit(words)
TfidfVectorizer()函数中我们还可以加入很多参数来控制特征,比如过滤停用词,最大特征个数,词频最大,最小比例限制等,这些都会对结果产生不同的影响,建议大家使用的时候还是先参考其API文档,价值还是蛮大的。
这里面我们仅设置了一个最大特征数量max_features=4000,表示我们的特征最大长度为4000,如果超出会自动去掉权重较小的词。
8、使用朴素贝叶斯算法进行训
from sklearn.naive_bayes import MultinomialNBclassifier = MultinomialNB()classifier.fit(vectorizer.transform(words), y_train)
9、在测试集上验证模型的最终效果
首先,我们同样需要将测试集中的所有词进行合并
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)
#直接调用score方法,计算模型预测的准确率。classifier.score(vectorizer.transform(test_words), y_test)0.8152
#3 总结
最后我们得到的预测准确率大概为81%左右,效果尚可,但肯定不是最优。
实际上朴素贝叶斯算法在小数据集上也能带来较好的效果,本次案例还有很多地方可以进一步优化和改进,比如词向量模型方面。
其实我们还有很多词向量模型,用于构建文本数据特征。
这里给大家再简单介绍一下word2vec词向量模型,它是基于神经网络来实现的,它的强大之处在于,不光对词进行了向量化,还会给其一个实际的含义,tf-idf是基于词频去做的,并没有实际的含义只是一个数字而已。
但是word2vec中每一个词都有实际的意义,只不过只有计算机能读懂它罢了,这样计算机能把词语进行理解,而不单单看其出现的位置和次数了。
如果大家对词向量模型感兴趣,可以参考gensim这个工具包,里面提供了非常简洁的函数帮助我们构建特征,后续,随着知识的积累及学习的深入,我们也会再陆续给大家进行讲解和介绍。
以上就是今天的案例分享,希望对大家有所帮助。



