贝叶斯新闻分类实战项目详解

手动反爬虫:原博地址

 知识梳理不易,请尊重劳动成果,文章仅发布在CSDN网站上,在其他网站看到该博文均属于未经作者授权的恶意爬取信息

如若转载,请标明出处,谢谢!

前言

关于贝叶斯要解决的问题(贝叶斯方法源于他生前为解决“逆概”问题写的一篇文章):

正向概率:假设袋里面有N个白球,M个黑球,伸手进去摸一把,摸出黑球的概率有多
逆向概率:如果我们事先并不知道袋子里面黑白球的比例,而是闭着眼睛摸出一个(或者好几个球),观察这些取出来球的颜色后,那么我们可以就此对袋子里面的黑白球的比例作出什么样的推测

还是举个具体的例子进行理解逆向概率,如下

假设学校男生占60%,女生占40%,男生总是喜欢穿长裤,而女生则是一半穿长裤一半穿裙子

在这里插入图片描述

正向概率:随机选取一个学生,其穿长裤的概率和穿裙子的概率有多大?
逆向概率:迎面走来了一个穿长裤的学生,你只看见其穿的是长裤,而无法确定其性别,那么可以推断其是女生的概率有多大?

贝叶斯方法要解决的问题就是上面举例中的逆向概率的求解

一、贝叶斯公式推导

首先是假设条件:
① 学校里面的总人数是U个;
② 穿长裤的(男生): U ∗ P ( B o y ) ∗ P ( P a n t s ∣ B o y ) U * P(Boy) * P(Pants|Boy) UP(Boy)P(PantsBoy)

  • P ( B o y ) P(Boy) P(Boy)是男生的概率 = 60%
  • P ( P a n t s ∣ B o y ) P(Pants|Boy) P(PantsBoy)是条件概率,即在Boy这个条件下穿长裤的概率有多大,这里是100%

③穿长裤的(女生): U ∗ P ( G i r l ) ∗ P ( P a n t s ∣ G i r l ) U * P(Girl) * P(Pants|Girl) UP(Girl)P(PantsGirl)

  • P ( G i r l ) P(Girl) P(Girl)是女生的概率 = 40%
  • P ( P a n t s ∣ G i r l ) P(Pants|Girl) P(PantsGirl)是条件概率,即在Girl这个条件下穿长裤的概率有多大,这里就是50%

求解问题:穿长裤的人里面有多少女生的概率?(分子为穿长裤的女生,分母就是穿长裤的总数)

  • 穿长裤的总数: U ∗ P ( B o y ) ∗ P ( P a n t s ∣ B o y ) + U ∗ P ( G i r l ) ∗ P ( P a n t s ∣ G i r l ) U * P(Boy) * P(Pants|Boy) + U * P(Girl) * P(Pants|Girl) UP(Boy)P(PantsBoy)+UP(Girl)P(PantsGirl)
  • 穿长裤的女生概率: P ( G i r l ∣ P a n t s ) = U ∗ P ( G i r l ) ∗ P ( P a n t s ∣ G i r l ) 穿 长 裤 的 总 数 P(Girl|Pants) = \frac{U * P(Girl) * P(Pants|Girl) }{穿长裤的总数} P(GirlPants)=穿UP(Girl)P(PantsGirl)

即完整的式子如下:
P ( G i r l ∣ P a n t s ) = U ∗ P ( G i r l ) ∗ P ( P a n t s ∣ G i r l ) U ∗ P ( B o y ) ∗ P ( P a n t s ∣ B o y ) + U ∗ P ( G i r l ) ∗ P ( P a n t s ∣ G i r l ) P(Girl∣Pants) = \frac{U * P(Girl) * P(Pants|Girl) }{U * P(Boy) * P(Pants|Boy) + U * P(Girl) * P(Pants|Girl)} P(GirlPants)=UP(Boy)P(PantsBoy)+UP(Girl)P(PantsGirl)UP(Girl)P(PantsGirl)

看到这里有点高等数学的感觉,不要慌,都是很容易理解几个基础概念

在这里插入图片描述

接着对式子进行化简操作,发现等式右边分子和分母都含有学校学生总数 U U U,因此可以进行消去,也就是说是和总人数无关
P ( G i r l ∣ P a n t s ) = P ( G i r l ) ∗ P ( P a n t s ∣ G i r l ) P ( B o y ) ∗ P ( P a n t s ∣ B o y ) + P ( G i r l ) ∗ P ( P a n t s ∣ G i r l ) P(Girl∣Pants) =\frac{P(Girl) * P(Pants|Girl)}{ P(Boy) * P(Pants|Boy) + P(Girl) * P(Pants|Girl)} P(GirlPants)=P(Boy)P(PantsBoy)+P(Girl)P(PantsGirl)P(Girl)P(PantsGirl)可以仔细看一下分母,其实上就是男生穿长裤的概率与女生穿长裤的概率,由于没有第三种性别,所以两者之和就是总的概率,也就是穿长裤的概率 P ( P a n t s ) P(Pants) P(Pants),而分子就是女生穿长裤的概率,这里写作 P ( P a n t s , G i r l ) P(Pants,Girl) P(Pants,Girl)

最终化简为: P ( G i r l ∣ P a n t s ) = P ( P a n t s , G i r l ) P ( P a n t s ) P(Girl∣Pants) = \frac{P(Pants,Girl)}{P(Pants)} P(GirlPants)=P(Pants)P(Pants,Girl)

进行推广,就是假定女生为事件A,所有穿长裤的为事件B,于是就有了穿长裤的人里面有多少女生的概率:
贝 叶 斯 公 式 : P ( A ∣ B ) = P ( A ) ∗ P ( B ∣ A ) P ( B ) 贝叶斯公式: P(A|B) = \frac{P(A)*P(B|A)}{P(B)} P(AB)=P(B)P(A)P(BA)

下面就介绍一下贝叶斯公式应用的几个小实例,方便进行理解

二、拼写纠正实例

问题:当我们看到用户输入了一个不在字典中的单词,我们需要猜测一下“这个家伙到底真正想输入的单词是什么?”

相当于计算 P(我们猜测他想输入的单词 | 他实际输入的单词),比如用户输入的tha这个词,当然是在字典中是不存在的,根据其输入实际结果的可能性,可以是the,也可以是than,还有其他很多的可能性,因此就相当于是计算 P ( t h e ∣ t h a ) , P ( t h a n ∣ t h a ) , P ( t h a n k s ∣ t h a ) . . . . . . P(the|tha),P(than|tha),P(thanks|tha)...... P(thetha),P(thantha),P(thankstha)......

那么将上面的例子进行扩展,假定用户实际输入的单词记为D(D代表data,用户输入的数据)

  • 对应的猜测1: P ( h 1 ∣ D ) P(h_{1}|D) P(h1D), 猜测2: P ( h 2 ∣ D ) P(h_{2}|D) P(h2D), 猜测3: P ( h 3 ∣ D ) , . . . . . . P(h_{3}|D), ...... P(h3D),......, 猜测n: P ( h n ∣ D ) P(h_{n}|D) P(hnD)
  • 最后就可以统一称为: P ( h i ∣ D )        i = 1 , 2 , 3 , . . . , n P(h_{i}|D) \space \space\space\space\space\space i=1,2,3,...,n P(hiD)      i=1,2,3,...,n

根据贝叶斯公式: P ( h i ∣ D ) = P ( h i ) ∗ P ( D ∣ h i ) P ( D ) P(h_{i}|D) = \frac{P(h_{i}) * P(D|h_{i})}{P(D)} P(hiD)=P(D)P(hi)P(Dhi)

结合我们日常使用的输入法在进行单词(或者汉字拼音)的输入后,会返回的是不同优先级的单词(汉字)排序供用户选择,也就是说后台就会按照 P ( h i ∣ D ) P(h_{i}|D) P(hiD)的大小进行相对应的 h i h_{i} hi输出,最终就回到了对比不同值的大小了,而对于不同的猜测 h i h_{i} hi,按照贝叶斯公司计算的时候 P ( D ) P(D) P(D)都是一样的,因此可以统一忽略这个常数,只需要比较分子的大小即可
P ( h i ∣ D ) ∝ P ( h i ) ∗ P ( D ∣ h i ) P(h_{i}|D) \propto P(h_{i}) * P(D|h_{i}) P(hiD)P(hi)P(Dhi)那么对于给定的观测数据,猜测可能性的大小,就取决于 P ( h i ) P(h_{i}) P(hi)这个猜测本身独立的可能性大小(也称作先验概率,Pior)和“ P ( D ∣ h i ) P(D|h_{i}) P(Dhi)这个猜测生成我们观测到的数据的可能性大小。

首先“先验概率”的理解,简单的理解就是之前的数据得出的经验概率,就比如去赌场赌博,看到同一桌面上有一哥们都已经连续赢了十把了,这时候你要是按照之前逻辑回归的思想考虑的话就是,我来赌的话那接下来赢得可能性就非常非常大,但是根据贝叶斯公式,其考虑到先验概率,也就是咱们常说的十赌九输,那么即使你面前的这个老哥赢了十把了,对于你来说还是有90%的概率会输的。
在这里插入图片描述

更容易理解的例子就是,在抛硬币的时候,前面已经是100次为正面了,那么接下来一次就一定是正面吗?可以参考一下先验概率就知道每次抛硬币正反面的概率都是二分之一,这样接下来的抛硬币仍然不可能是百分百的为正面(当然,看过《八佰》电影的可以除外,抛硬币决定谁出去的时候这个场景中的硬币是做了手脚的)
在这里插入图片描述
那么如何知道 P ( h i ) P(h_{i}) P(hi)这个先验概率呢?在不同的任务下计算的方式是不同的,这里指的是单词,那就相当于可以找一个很大的容器,计算一下这个单词在该容器中出现的词频,然后在对比总单词数(好比有人已经统计过键盘中哪个键使用的频率最高,就是空格,所以在进行键盘输入的时候将空格键设置为最长,方便输出)

接着就是式子的第二个计算概率 P ( D ∣ h i ) P(D|h_{i}) P(Dhi),代表着 h i h_{i} hi被写成 D D D情况下的概率(比如:the被写成tha的概率),这个就需要我们自己指定相关的规则了,比如 P ( t h e ∣ t h a ) , P ( t h a n ∣ t h a ) , P ( t h a n k s ∣ t h a ) P(the|tha),P(than|tha),P(thanks|tha) P(thetha),P(thantha),P(thankstha)这三个概率,那么规则可以指定经过一次增改的概率值是大于二次增改概率值,大于三次的增改的概率值。

举个例子当用户输入tlp时候,判断是想输入的是top还是tip,显然这个增改的次数是一致的,那么就比先验概率,top的见面的次数要远远高于tip,所以最终在输入法中,top要出现在tip前面,认为用户是想打的是top这个单词

综上可知,在进行单词纠正的实际操作中,判断用户真正想输入的单词的概率不仅是和目标单词的先验概率有关,还与实际输入单词与目标单词经过增改的次数有关。

三、垃圾邮件过滤实例

问题:给定一封邮件,判断它是否属于垃圾邮件, D D D用来表示这封邮件,邮件中共有 N N N个单词组成,用 h − h- h表示正常邮件, h + h+ h+表示垃圾邮件

那么根据贝叶斯公式:
P ( h + ∣ D ) = P ( h + ) ∗ P ( D ∣ h + ) P ( D )             P ( h − ∣ D ) = P ( h − ) ∗ P ( D ∣ h − ) P ( D ) P(h+|D) = \frac{P(h+) * P(D|h+)}{P(D)} \space \space \space \space \space \space \space \space \space \space \space P(h-|D) = \frac{P(h-) * P(D|h-)}{P(D)} P(h+D)=P(D)P(h+)P(Dh+)           P(hD)=P(D)P(h)P(Dh)

关于邮件是否为垃圾邮件还是正常邮件的先验概率,在大数据情况下,这两个数值是很容易计算出来的(一个邮件库中的正常邮件和垃圾邮件的比例),那么需要解决的就是 P ( D ∣ h + ) P(D|h+) P(Dh+) P ( D ∣ h − ) P(D|h-) P(Dh)的问题了

前面假设中, D D D中是有 N N N个单词,用 d 1 d_{1} d1 d 2 d_{2} d2 d 3 d_{3} d3,…, d n d_{n} dn表示, P ( D ∣ h + ) = P ( d 1 , d 2 , d 3 , . . . , d n ∣ h + ) P(D|h+)=P(d_{1},d_{2},d_{3},...,d_{n}|h+) P(Dh+)=P(d1,d2,d3,...,dnh+)就是说在垃圾邮件当中出现跟我们目前这封邮件一模一样的一封邮件的概率有多大

既然要求的是一模一样,也就是说邮件中的单词顺序是不能以进行位置的随意变换,指定的什么位置的就是什么位置的,因此就可以把上面的式子进行拆开
P ( d 1 , d 2 , d 3 , . . . , d n ∣ h + ) = P ( d 1 ∣ h + ) ∗ P ( d 2 ∣ d 1 , h + ) ∗ P ( d 3 ∣ d 1 , d 2 , h + ) ∗ . . . ∗ P ( d n ∣ d 1 , d 2 , . . . , d n − 1 , h + ) P(d_{1},d_{2},d_{3},...,d_{n}|h+) = P(d_{1}|h+)*P(d_{2}|d_{1},h+)*P(d_{3}|d_{1},d_{2},h+)*...*P(d_{n}|d_{1},d_{2},...,d_{n-1},h+) P(d1,d2,d3,...,dnh+)=P(d1h+)P(d2d1,h+)P(d3d1,d2,h+)...P(dnd1,d2,...,dn1,h+)观察这个式子,如果数据量较少的情况下,前几项还是很好算的,如果数据量一上去,那么计算起来就麻烦多了,甚至还没法解决。那么有没有方法求解呢?这时候 朴素贝叶斯 就出来了(这里的朴素不是说贝叶斯不被人认可,生活上就很穷,而是可以理解为数据之间都很朴实无华,之间相互不勾搭,不受彼此的控制),显示在数据上就是 d i d_{i} di d i − 1 d_{i-1} di1之间是完全条件无关的,相互独立,互补影响。于是上面的式子就可以简化为:
P ( d 1 , d 2 , d 3 , . . . , d n ∣ h + ) = P ( d 1 ∣ h + ) ∗ P ( d 2 ∣ h + ) ∗ P ( d 3 ∣ h + ) ∗ . . . ∗ P ( d n ∣ h + ) P(d_{1},d_{2},d_{3},...,d_{n}|h+) = P(d_{1}|h+)*P(d_{2}|h+)*P(d_{3}|h+)*...*P(d_{n}|h+) P(d1,d2,d3,...,dnh+)=P(d1h+)P(d2h+)P(d3h+)...P(dnh+)因此最终只要统计 d i d_{i} di这个单词在垃圾邮件库中的频率即可,同理计算 d i d_{i} di这个单词在正常邮件库中的频率,最后比较一下这两个概率,作为判断是否为垃圾邮件的依据,如果这封信中的单词在垃圾邮件库中出现的频率大就认为是垃圾邮件,反之认为是正常邮件

四、新闻分类实战

1. 文本分词

就是将获取到的新闻数据,转化成有一个个词语组成的样式。新闻的数据是由搜狗实验室提供,可以根据链接进行获取,这里使用的数据是已经经过清洗后的数据,直接进行加载(关于文章中使用的数据,包括处理后的新闻数据和停用词数据,已经上传至资源,可以自行下载)

1.1 数据加载

#既然涉及到分词,最常用,也是最成熟的一个包就是jieba了
import pandas as pd
import jieba

import os
os.chdir(r'C:\Users\86177\Desktop\机器学习案例\新闻分类')
df_news = pd.read_table('./data/data.txt',names=['category','theme','URL','content'],encoding='utf-8')
df_news = df_news.dropna()
df_news.tail()

输出的结果为:(Category:当前新闻所属的类别,一会我们要进行分别任务,这就是标签了。Theme:新闻的主题,这个咱们先暂时不用,大家在练习的时候也可以把它当作特征。URL:爬取的界面的链接,方便检验爬取数据是不是完整的,这个咱们暂时也不需要。Content:新闻的内容,这些就是一篇文章了,里面的内容还是很丰富的。)
在这里插入图片描述
既然数据导入之后,就看一下数据的规模

df_news.shape
#(5000, 4)
#数据量这里就只导入了5000条,特征的话就是4个

接下来任务已经很明确了,根据文章的内容来进行类别的划分。那如何做这个事呢?咱们之前看到的数据都是数值的,直接传入到算法中求解参数就可以了。这份数据就有些特别了,都是文本,计算机可不认识这些文字的,所以我们需要把这些文字转换成特征,比如说将一篇文章转换成一个向量,这样就可以玩起来了。 对于一篇文章来说,里面的内容还是有点太多了,如果直接把它转换成向量,有一串数字来表示这篇文章,一方面来说难度有些大,另一方面这些表示的效果也不尽如人意。通常的做法都是先把文章进行分词,然后在词的层面上去做文章。先拿到一篇文章,然后我们再看看其分词的结果

content = df_news.content.values.tolist() #将每一篇文章转换成一个list
print (content[4999]) #随便选择其中一个看看,就是最后一篇新闻

输出的结果为:(在进行分词前要将多篇新闻数据放在一个列表中,方便后序的遍历)
在这里插入图片描述

1.2 jieba分词基本步骤

使用jieba分词筛选结果的模板(标准方式),共五步:

  • 在遍历数据之前创建一个空列表,用于存放数据,
  • 然后在依次遍历存放新闻数据的列表,获取每一篇新闻数据,
  • 使用jieba.lcut()方法对文本进行分词,
  • 接着就是剔除掉字符个数小于等于1个的词语,还有顺带去掉换行符(也可以按照自己的格式要求进行剔除),
  • 最后就是将满足的结果添加到最初创建的空列表中
content_S = []
for page in content:
    current_segment = jieba.lcut(page) #对每一篇文章进行分词
    if len(current_segment) > 1 and current_segment != '\r\n': #换行符
        content_S.append(current_segment) #保存分词的结果

分词之后的结果可以进行查看,也可以将其转换为DataFrame数据

content_S[4999]

#['全球','最美','女人','合成图',':',':', '国','整形外科','教授' '李承哲',',','在','国际', '学术','杂志','美容','整形外科',
#'学会','学报','发表','了','考虑','种族','和','民族','脸型','多样性','的','黑人','、','白人','、','中国',
#'人','、','日本','人','女性','“','最具','魅力','的','脸型','”','的','论文','。','李承哲','合成','这些','脸型','时','采用',
#'的','演艺人','脸型','有',',','黑人','1','3','名','、','白','1','6','名','、','中国','人','2','0','名','、','日本',
#'人','1','4','名','等','共','6','3','名','的','脸型','。','M','贾凶','宰笙','蛴','乙来','挝',':','黑人','美女','、',
#'白人','美女','、','中国','美女','、','日本','美女','和','韩国','美女','。']

因为分词后的内容是列表,可以直接使用字典套列表的形式创建DataFrame,而字典的键就是分词的结果的列名

df_content=pd.DataFrame({'content_S':content_S}) #专门展示分词后的结果
df_content.head()

输出结果为:
在这里插入图片描述
创建的DataFrame数据可以用于绘制词云图,从分词后的结果可以看出,虽然剔除了部分数据,但是还是有很多不是我们想看到的内容,比如冒号空格,句号空格等。因此绘制词云图之前需要先剔除一些常见的不是很重要的词语,前面的是初加工筛选,这里就要精加工筛选了,为了避免一个个手动剔除常见的一些不重要的词汇,所以就有了停用词字典这个概念(就是把日常的词汇收集在一起,这个字典不是指python的字典数据类型,而是指新华字典,用于存放收集的数据的地方),凡是分词结果后的词语在这个停用词范围中的,都将被舍弃

1.3 加载停用词字典数据进行数据清洗

stopwords=pd.read_csv("stopwords.txt",index_col=False,sep="\t",quoting=3,names=['stopword'], encoding='utf-8')
stopwords.head(10)

输出结果为:(前面的一些停用词都是标点符号,后面的都是常见的汉字词语,文件已经上传到我的资源)
在这里插入图片描述
为了方便日后的使用,这里直接封装函数,只需要传入相对应格式的数据即可(数据就是新闻数据的列表,和停用词的列表)

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.tail()

输出结果为:(这里的数据是将每篇新闻分词后清洗的结果重新放置在列表中)
在这里插入图片描述

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

输出结果为:(这里的数据是将所有分词的结果以单个数据的形式都保存起来了,)
在这里插入图片描述

1.4 词云图绘制

那么接下来就可以使用数据绘制词云图,既可以选择按照每篇新闻分词的数据,也可以使用全部词语的数据,下面就使用所有词语的数据进行绘制,基本步骤就是四步:

  • 首先截取要呈现的目标数据(一般20,50或者100,根据需要选定)
  • 词云图对象初始化(这里可以对词云图的参数进行设置)
  • 将选取的数据传入到fit.words()方法中
  • 最后就是将词云图展示出来
from wordcloud import WordCloud
import matplotlib.pyplot as plt
%matplotlib inline
import matplotlib
matplotlib.rcParams['figure.figsize'] = (10.0, 5.0)

woordcloud_data = df_all_words.all_words.value_counts()[:100]
wordcloud=WordCloud(font_path="./data/simhei.ttf",background_color="white",max_font_size=80)
wordcloud=wordcloud.fit_words(woordcloud_data)
plt.imshow(wordcloud)

输出的结果为:(可以发现所有数据的词云中,一个个单词还是很多的)
在这里插入图片描述

2. TF-IDF:提取关键词

解释一下TF-IDF是一个什么东西,还是进行举例子,假设一篇文章为《中国的蜜蜂养殖》

当我进行词频统计的时候,发现这篇文章中,‘中国’,‘蜜蜂’,‘养殖’这三个词出现的次数(TF就代表这出现的词频)是一样的,比如都是10次,那这个时候如果判断其重要性呢?这一篇文章应该讲述的是都跟蜜蜂和养殖相关的技术,所以这俩词应当是重点了。而中国这个词,我们既可以说中国的蜜蜂,还可以说中国的篮球,中国的大熊猫,能派上用场的地方简直太多了,没有那么专一,所以在这篇文章中它应当不是那么重要的。 这里我们就可以给出一个合理的定义了,如果一个词在整个语料库中(可以当作是在所有文章中)出现的次数都很高(这篇也有它,另一片还有这个词),那么这个词的重要程度就不高,因为它更像一个通用词。如果另一个词在整体的语料库中的词频很低,但是在这一篇文章中的词频却很高(这个就是IDF逆文本频率),我们就有理由认为它在这篇文章中就很重要了。比如蜜蜂这个词,在篮球,大熊猫相关的文章中基本不可能出现,这里却大量出现了。因此就可以使用TF-IDF进行文本关键词的提取

import jieba.analyse #工具包
index = 2400 #随便找一篇文章就行
content_S_str = "".join(content_S[index]) #把分词的结果组合在一起,形成一个句子
print (content_S_str) #打印这个句子
print ("  ".join(jieba.analyse.extract_tags(content_S_str, topK=5, withWeight=False)))#选出来5个核心词

输出的结果为:(根据汇总出的五个关键词可以发现,基本上可以概括出这篇新闻的内容,主要围绕着赞助的问题在比赛球衣上进行宣传)
在这里插入图片描述

3. 词袋模型

由上面的分类看出,通过jieba可以直接提取文本的特征,但是要进行文本的分类,对于计算机来讲的话,它并不认识这些汉字,它只认识数值,有没有可能性将汉字都转化为数字,用数字进行表示呢?那么词袋模型就可以解决这个问题

既然是进行文本分类,首先要做的就是进行标签制作,这里就按照常见的是个新闻标签进行对应新闻的匹配

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.tail()

输出结果为:(这样就实现标签的映射了)
在这里插入图片描述
标签处理好了之后就是进行数据特征的处理,那么就有个问题,每篇的新闻的字数是不一样的,有的是200词,有的是300词,有的甚至更多,但是在机器学习中要求输入的数据的维度是统一的,因此就得把数据的维度化为统一,那么就需要找一个可以进行衡量的支点,而这个支点就是语料库,即使每篇文章的数据的长短不一样,但是由所有文章组成的语料库的容量是一定了,所以判断每篇文章中数据是否在语料库中既可以对应上,也就转化为同一维度了,为了方便理解还是进行举例说明

from sklearn.feature_extraction.text import CountVectorizer
texts=["dog cat fish","dog cat cat","fish bird", 'bird'] #为了简单期间,这里4句话我们就当做4篇文章了
cv = CountVectorizer() #词频统计
cv_fit=cv.fit_transform(texts) #转换数据

print(cv.get_feature_names())
print(cv_fit.toarray())
print(cv_fit.toarray().sum(axis=0))

输出的结果为:(语料库就是有四个单词组成的,可以通过cv.get_feature_names()获取所有文章组成的语料库,这个语料库中包含所有的词汇,cv_fit.toarray()就可以将每篇的新闻数据转化为array数组,实现字母/汉字与数值的对应,当出现相同的内容时候,数值会加1,最后也可以进行多篇新闻数据的单词频数汇总)

['bird', 'cat', 'dog', 'fish']
[[0 1 1 1]
 [0 2 1 0]
 [1 0 0 1]
 [1 0 0 0]]
[2 3 2 2]

经过结果的分析,可以归纳出词袋模型的特征:说白了它就是看每一个词出现几次,来统计词频就可以了,再把所有出现的词组成特征的名字,依次统计其个数就可以得到文本特征了。这里给我们的感觉有点过于简单了,只考虑词频而不考虑词出现的位置以及先后顺序,那能不能稍微再改进一些呢?这里我们还可以通过设置ngram_range来控制特征的复杂度,比如我们不光可以考虑单单一个词,还可以考虑两个词连在一起,甚至更多的词连在一起。

from sklearn.feature_extraction.text import CountVectorizer
texts=["dog cat fish","dog cat cat","fish bird", 'bird']
cv = CountVectorizer(ngram_range=(1,4)) #设置ngram参数,让结果不光包含一个词,还有2个,3个的组合
cv_fit=cv.fit_transform(texts)

print(cv.get_feature_names())
print(cv_fit.toarray())

输出的结果为:(可以发现,设置了参数保留1-4个词汇特征的时候,原来语料库中只有四个单词的,现在直接扩充2.5倍,这种方式是可以标记对于文本中出现的词汇顺序,但是如果一篇文章就有很多单词,统计5000篇就可能超过10w,那么再设置ngram_range参数很大的话,就可能导致语料库达到几千万的容量,因此这里可以考虑单词出现的顺序,设置为2就可以了,然后在创建语料库的时候加一些限制,过滤掉词频较小的词汇)

['bird', 'cat', 'cat cat', 'cat fish', 'dog', 'dog cat', 'dog cat cat', 'dog cat fish', 'fish', 'fish bird']
[[0 1 0 1 1 1 0 1 1 0]
 [0 2 1 0 1 1 1 0 0 0]
 [1 0 0 0 0 0 0 0 1 1]
 [1 0 0 0 0 0 0 0 0 0]]

那么就开始拿新闻数据进行实践,首先就是划分训练集和测试集

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)

接着进行把新闻数据的类型由列表全部转化为指定的格式(列表中嵌套着字符串数据)

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)
print(type(test_words))
test_words[0]

#<class 'list'>
#3750

如果不进行词频较小的特征过滤,看一下生成的语料库的,然后再对比一下进行过滤后的

from sklearn.feature_extraction.text import CountVectorizer

vec = CountVectorizer(analyzer='word',lowercase = False)
feature = vec.fit_transform(words)
print(feature.shape)

vec = CountVectorizer(analyzer='word', max_features=4000,  lowercase = False)
feature = vec.fit_transform(words)
print(feature.shape)

输出结果为:(可以发现如果不进行限制的话,3750篇文章共创建的语料库的容量为8w多,而限制保留指定的特征后,就会选取词汇频数最大的4000个进行保留。可以想象一下加入一篇文章有一千个字,那么对应8w,生成数组的结果肯定会有很多很多0,导致特征过于稀疏,所以有必要对创建的语料库容器进行限制)

(3750, 85093)
(3750, 4000)

最后一步就是实现词袋模型的特征建模,导入贝叶斯模型

from sklearn.naive_bayes import MultinomialNB #贝叶斯模型
classifier = MultinomialNB() 
classifier.fit(feature, 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[-1]

输出结果为:(至此,模型也训练好了,测试数据也化为统一格式了,那么接下来就可以进行模型得分的评测)
在这里插入图片描述

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

输出结果为:0.8032 (还可以的)

那么就可以用这个模型进行预测一下,看看文本分类的结果

classifier.predict(vec.transform(test_words)[-1])

输出结果为:array([7], dtype=int64)
而直接查看一下对应的标签,并对应上最初的映射结果

print(y_test[-1])
print(['{}:{}'.format(key,value) for key,value in filter(lambda x:7 == x[1], label_mapping.items())])

输出的结果为:(根据上面的新闻数据的输出可知,是进行中国京剧的介绍的,归类为文化是没有问题的)

7
['文化:7']

4. TF-IDF制作特征

前面已经讲解到了TF-IDF,不过是直接用来提取特征,那么接下来介绍一下它怎么制作特征,还是进行举例,这次就简单的两句话

from sklearn.feature_extraction.text import TfidfVectorizer

X_test = ['卡尔 敌法师 蓝胖子 小小','卡尔 敌法师 蓝胖子 痛苦女王']

tfidf=TfidfVectorizer()
weight=tfidf.fit_transform(X_test).toarray()
word=tfidf.get_feature_names()
print (weight)
for i in range(len(weight)):  
    print (u"第", i, u"篇文章的tf-idf权重特征")
    for j in range(len(word)):
        print (word[j], weight[i][j])

输出的结果为:(和之前的 CountVectorizer类似,之前计算的是出现的频次,这里除了考虑词频TF,还有IDF逆文本频率,最后按照权重进行赋值)

[[0.44832087 0.63009934 0.44832087 0.         0.44832087]
 [0.44832087 0.         0.44832087 0.63009934 0.44832087]]0 篇文章的tf-idf权重特征
卡尔 0.44832087319911734
小小 0.6300993445179441
敌法师 0.44832087319911734
痛苦女王 0.0
蓝胖子 0.448320873199117341 篇文章的tf-idf权重特征
卡尔 0.44832087319911734
小小 0.0
敌法师 0.44832087319911734
痛苦女王 0.6300993445179441
蓝胖子 0.44832087319911734

接下来就可以使用TF-IDF特征建模来观察结果,和之前的词袋模型的代码很类似,就是换了几个单词

vectorizer = TfidfVectorizer(analyzer='word', max_features=4000,  lowercase = False)
vectorizer.fit(words)

classifier = MultinomialNB()
classifier.fit(vectorizer.transform(words), y_train)

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

输出结果为:0.8136 (比之前的模型得分要好一点)

至此关于贝叶斯新闻分类实战项目梳理就到此完结了,撒花撒花✿✿ヽ(°▽°)ノ✿

总结

本文是对贝叶斯新闻分类实战项目进行梳理,从贝叶斯公式的推导,到列举几个日常应用的小实例,最后再进行贝叶斯新闻分类深度讲解。重点在于一下的几个知识点

评论 18
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lys_828

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值