本人是零基础的小白,现在从零开始学习NLP,这是学习的一些简单的笔记,如有错误请指正。
编译环境:Jupyter Notebook
Windows x64
本文数据处理主要分为两个板块:
一 是数据预处理(Data Preparation)从而获得所需要的特征(feature),如将数据层层处理(分词、停用词过滤、向量化),本文向量化内容由于使用sklearn库,放置第二板块讲解。
二 是利用模型(Modeling)解决具体的问题,本文主要采用朴素贝叶斯经典机器学习方法对文本进行分类。
一、理论基础
下面简单回顾一下理论部分(可以直接跳过到实战部分)
1.1 词频(TF)
词频(term frequency) 指的是某一个给定的词语在该文件中出现的频率。对于在某一文件里的词语$t_i$来说,它的重要性可表示为:
$$ tf_{ij}=\frac{n_{i,j}}{\sum_kn_{k,j}} $$
其中,$n_{i,j}$是该词在文件$d_j$中出现次数,而分母是文件$d_j$中所有字词出现的次数总和。
1.2 逆向文本频率(IDF)
逆向文件频率(inverse document frequency) 是一个词语普遍重要性的度量。某一特定词语的idf,可以由总文件数目除以包含该词语之文件的数目,再将得到的商取以10为底的对数得到,个人理解为:对词频向量的改进,原因在于:词语出现的越多,并不能代表它就越重要,相反,文档中出现的越多,其实它的重要性是降低的,所以TFIDF考虑了单词的重要性而做的对词频的改进,可表示为:
$$ tfidf(w)=tf(d,w)\times{idf(w)}$$
(1)其中 $tf(d,w)$ 代表文档d中w的词频
(2)$idf(w)=\log\frac{N}{N(w)}$,${N}$代表语料库中的文档总数,${N(w)}$代表词语w出现在多少个文档中,出现在文档的次数越多,$\log$值越小,故称为逆向文本频率
1.3 朴素贝叶斯(Naive Bayesian Model,NBM)
朴素贝叶斯的中心思想,在于利用各类别在训练样本中的分布以及类别中各特征元素的分布,计算后验概率,使用极大似然法判断测试样本所属,一般用于简单分类。
贝叶斯公式:
$$P(B\mid{A})=\frac{P(A\mid{B})P(B)}{P(A)}$$
对应分类任务则为:
$$P(类别\mid{特征})=\frac{P(特征\mid{类别})P(类别)}{P(特征)}$$
垃圾邮件分类(判别模型)举例:
$P(特征\mid{类别})$ 相当于先验概率,也就是我们已知的概率,比如垃圾邮件分类里面,我们已有的数据中正常的类别邮件里面包含“购买”一词的概率,以及垃圾类别里面包含“购买”一次的概率等,$P(类别)$ 就是正常或者垃圾邮件在数据集中的概率,这些概率都已知。
那么要判断邮件为正常还是垃圾,则要判断:
$P(正常\mid内容)$ 与 $P(垃圾\mid内容)$ 的大小
$$P(正常\mid内容)=\frac{P(内容\mid正常)P(正常)}{P(内容)}$$
$$P(垃圾\mid内容)=\frac{P(内容\mid垃圾)P(垃圾)}{P(内容)}$$
$P(正常)$,$P(垃圾)$ 均已知,$P(内容)$消去,剩下就是要比较$P(内容\mid正常)$ 和$P(内容\mid垃圾)$
$P(内容\mid正常)=P(购买,物品,广告,产品\mid正常)\
=P(购买\mid正常)P(物品\mid正常)P(广告\mid正常)P(产品\mid正常)$,而这些先验概率前面都已算过,带入计算作比较大小即可。
二、数据预处理
数据预处理部分可谓是耗费了大部分的时间,参考了一些博客,但是感觉不是特别详细,其中也遇到了不少麻烦,下面一一讲解到位,非常适合小白参考。
2.1 数据下载及导入
首先下载搜狗实验室的文本数据(精简版347MB,tar.gz格式):
下载链接

解压后,得到如下128个txt文件
文件格式如下:
对于特定格式的文本,我们一般采用正则表达式来提取所需要的信息,代码如下:
1 | import re |
结果如下(有空值、内容也很乱),后面一步步处理:
下面我们再将URL内容再次正则一下,提取官方的分类label:
1 | labels=[] |
其中传统dataframe中dropna() 函数删空值的方法在这里并不适用,结果如下,待会会处理,我们先把label里面的格式调整一下,调整的原因:目前的label格式为list of list,为了方便后面筛选label来替换中文等后续操作,先脱去一层list:
1 | type(labels) |

1 | df=pd.DataFrame({'label':labels2,'URL':urls_total,'content':contents_total}) |

好了,到这里label格式已经调好了,接下来需要对label进行中文替换,所以我们需要先把各类label筛选出来,总共有以下label:
1 | print(df.label.unique()) #将所有不重复的label显示出来 |

将所需要的label对应的内容进行筛选查看(替换‘career’为各个label,查看相关内容),方便人为辨识类别
代码如下:
1 | df.loc[df['label']== 'career'].tail(20) |

接下来就是替换label,通过人为的观察上述各label所对应的分类,将中文替换到下列map映射之中,最后完成label替换:
1 | label_mapping={'sports':'体育', 'house':'房屋','it':'科技', '2008':'奥运', 'women':'女人',\ |

回到刚刚提到的空值问题,明明有很多空值,但isnull()查阅后仍然显示false,原因在于:pandas里空值是指NA,包括numpy的np.nan,python的None,pandas对空值进行操作可以用isnull/notnull/isna/notna/fillna/dropna等等,但是,这些操作对空字符串均无效(此处参考链接)。
空字符串即“ ”(一个或多个空格),但在excel表格里其实是看不出来,pandas也把它当成有值进行操作。
代码如下:
1 | df.content.replace(to_replace=r'^\s*$',value=np.nan,regex=True,inplace=True) |

这样一来,就将空值转换成了NaN,从而再可以使用dropna()。
1 | df2=df.dropna(axis=0, how='any') # 对任意含有NaN的行(axis=0)进行删除 |

再将索引重新排列一下:
1 | df3=df2.reset_index(drop=True) |

2.2 结巴分词及停用词过滤
此处我没有用前面的数据进行处理(毕竟有42w行数据,作为新手使用小数据集练手足够,后面可能还会发42w行的运行结果,这里采用了前辈整理好的5000行数据进行处理),格式和我之前处理得到的基本一致,不影响大家参考。
样例数据导入:
1 | import gensim |


2.2.1 结巴分词:
分词之前首先我们要将dataframe的格式转换为list才能适应jieba库,代码如下:
1 | content = df_news.content.values.tolist() #将datafrmae中content转化为list |

2.2.2 停用词过滤:
需要先下载好一份停用词表,网上有很多,此处提供前辈整理好的素材,很方便
1 | topwords=pd.read_csv("stopwords.txt",index_col=False,sep="\t",quoting=3,names=['stopword'], encoding='utf-8') |

1 | def drop_stopwords(contents,stopwords): |

三、模型(modeling)贝叶斯分类器
1 | df_train=pd.DataFrame({'contents_clean':contents_clean,'label':df_news['category']}) |

1 | df_train.label.unique() |

将数据切分为训练集(x_train,y_train)和测试集(x_test,y_test)
1 | from sklearn.model_selection import train_test_split |

3.1 文本数据向量化
数据向量化之前,我们先要将类型转换为list以适合CountVectorizer(词频)/TfidfVectorizer(逆向文本频率IDF)
1 | #将x_train(numpy.array型转换为list类型, |
3.1.1 基于词频向量化
导入sklearn机器学习库中的CountVectorizer词频向量化函数
1 | from sklearn.feature_extraction.text import CountVectorizer |
导入贝叶斯
1 | from sklearn.naive_bayes import MultinomialNB #导入贝叶斯 |

3.1.2 基于TFIDF向量化
1 | from sklearn.feature_extraction.text import TfidfVectorizer #基于TF-IDF向量 |

相比之下,TFIDF向量化的结果会偏高一点点,当然,这里采用的是很小的数据集(才5000行),精度很低,如果将42w的数据进行训练,精度应该会提升不少。到此为止,整个搜狗新闻文本分类任务就完成了。
本文到这里就全部结束了,如果有错误或者引用不当,还请指出,我会加以改进!欢迎大家评论留言,相互学习和进步!(前辈整理的数据集后面会上传到csdn上,如有需要可以联系)
参考文章:
https://blog.csdn.net/weixin_43269174/article/details/88634129
https://blog.csdn.net/sadfassd/article/details/80568321
https://www.jianshu.com/p/edad714110fb
https://blog.csdn.net/maotianyi941005/article/details/84315965
https://www.cnblogs.com/datou-swag/articles/10060532.html
