短文本分类小结

背景:文本分类技术汇总:
因为公司需求,开始探索文本挖掘技术在通信行业方面的应用,现将过程中常用的技术和原理更新如下。文本分类方法经过长期的研究在很多场景下已经得到了应用,但是短文本分类问题起步较晚,且一直也没有什么通用的,效果良好的方法,短文本分类的重点在特征处理环节和分类算法上面。因为其存在两个问题,1、短文本提供的词语少,提供的有效信息有限。2、根据分词结果构建的词频或者特征矩阵通常十分稀疏,大多数算法在处理稀疏矩阵问题时候,效果都不好。常用的处理短文本的方法基本可分为两类:一类是基于某种规则改进分类过程,优化改进模型;另一类是基于外部语义信息扩充短文本信息量,从而提高分类效果。下面讲述探索过程中的用到的技术及其原理:


文本挖掘传统方法讲述
常见流程:
一:分词
1、常用中文分词工具:jieba、SnowNLP(MIT)、pynlpir、thulac
在文本探索中主要用到了jieba库,所以下面主要对jieba库进行讲述,jieba分词是国内程序员开发的一款非常好用的开源中文分词的库。
原理讲述:
首先,基于词的前缀集合结构实现高效的词图扫描,生成句子汉字所有可能成词情况所构成的有向无环图
其次,采用了动态规划查找最大概率路径, 找出基于词频的最大切分组合
最后, 对于未登录词,采用了基于汉字成词能力的HMM模型,使用了Viterbi算法
用法试例:


# encoding=utf-8
import jieba
seg_list = jieba.cut("我来到北京清华大学", cut_all=True)
print("Full Mode: " + "/ ".join(seg_list))  # 全模式


seg_list = jieba.cut("我来到北京清华大学", cut_all=False)
print("Default Mode: " + "/ ".join(seg_list))  # 精确模式


seg_list = jieba.cut("他来到了网易杭研大厦")  # 默认是精确模式
print(", ".join(seg_list))


seg_list = jieba.cut_for_search("小明硕士毕业于中国科学院计算所,后在日本京都大学深造")  # 搜索引擎模式
print(", ".join(seg_list))
输出:


【全模式】: 我/ 来到/ 北京/ 清华/ 清华大学/ 华大/ 大学


【精确模式】: 我/ 来到/ 北京/ 清华大学


【新词识别】:他, 来到, 了, 网易, 杭研, 大厦    (此处,“杭研”并没有在词典中,但是也被Viterbi算法识别出来了)


【搜索引擎模式】: 小明, 硕士, 毕业, 于, 中国, 科学, 学院, 科学院, 中国科学院, 计算, 计算所, 后, 在, 日本, 京都, 大学, 日本京都大学, 深造


分词的话就根据具体场景来确定了,通常来说在分词的过程中,还需要不断的更新词库和停词库。构建词库的时候有一些小原则,就是在分词过程中有一些领域常见词可能不会被正确划分,这时候就需要对其进行一个整理,将一些奇怪的分词结果变成合理的词放进词库中,比如流量包,加餐包,分词结果经常为“流量,包”,“加餐,包”,但是其实将其放在一起才是一个领域的词,应该把他们加入到词库当中。


如果想了解结巴库的具体原理过程可以查看帖子:https://www.cnblogs.com/baiboy/p/jieba2.html


2、去除停用词
这个主要需要导入并构建停用词表,然后删除分词结果中停用词表中的词。简单说就是删除一些语气词了,这些词语并不能有效的代表句子的特征。


3、特征提取
中文分词和去除停用词后,需要对分词结果进行文本特征提取,常用的传统方法有:TFIDF,信息增益法,词频法,X^2统计法,互信息等等。
探索过程中,主要用到了TFIDF和信息增益的特征提取方法,所以主要讲述TFIDF,信息增益的原理的代码实现。 
TF-IDF技术采用一种统计方法,根据字词的在文本中出现的次数和在整个语料中出现的文档频率来计算一个字词在整个语料中的重要程度。简单说就是通过词频乘以逆文档频率的乘积来计算该词语在语料中的重要程度,更简单的说就是“我”(指一片文章)有很多这个词,“你们”(指其他文档)没有这个词,那么这个词在这篇文档里面的TFIDF值就很大。详见https://flystarhe.github.io/2015/05/19/tf-idf/
信息增益IG的是通过信息论的思想,如果该词语能在语料中能够带来更多的信息的话,该词语的信息增益就越大。那么相对来说就越重要。
from sklearn import feature_extraction  
from sklearn.feature_extraction.text import TfidfTransformer  
from sklearn.feature_extraction.text import CountVectorizer  
  
if __name__ == "__main__":  
    corpus=["我 来到 北京 清华大学",#第一类文本切词后的结果,词之间以空格隔开  
        "他 来到 了 网易 杭研 大厦",#第二类文本的切词结果  
        "小明 硕士 毕业 与 中国 科学院",#第三类文本的切词结果  
        "我 爱 北京 天安门"]#第四类文本的切词结果  
    vectorizer=CountVectorizer()#该类会将文本中的词语转换为词频矩阵,矩阵元素a[i][j] 表示j词在i类文本下的词频  
    transformer=TfidfTransformer()#该类会统计每个词语的tf-idf权值  
   tfidf=transformer.fit_transform(vectorizer.fit_transform(corpus))#第一个fit_transform是计算tf-idf,第二个




代码中有一个地方需要注意,此时变量tfidf是使用稀疏矩阵的存储方式来节省内存,如果需要得到矩阵形式的表示的话,需要使用tfidf.toarray()来获得,当数据量特别大的时候慎用,很容易内存就爆了。
IG的代码就不放上来了。见这个帖子就好了http://eip.teamshub.com/t/3313542。有一个小点说明下,帖子中附的论文使用TFIDFIG来改善效果,但是在短文本情况下效果不大。因为通常来说得到的特征矩阵过于稀疏,简单说是本来TFIDF矩阵中很多都是零。你再乘个IG(特征词的信息增益)的值,也还是零,对结果影响不大。代码的实现也可以使用gensim库实现,gensim是一个非常完备的文本相关的Python库。地址https://radimrehurek.com/gensim/


3、这时候样本特征矩阵就构建好了,可以使用一些传统的分类器进行分类预测了,比如LR,NB,SVM,RF等等。下面写一个logistics的例子,其他的类似,就是参数选择不同而已,可以查看sklearn官方文档的用法:
实例:
#导入数据集划分,LR模型以及评价指标模块
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report
划分数据集合,比例为8:2,随机数种子设置为10,X代表构建的训练样本,Y是标签
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size = 0.2,
                                                        random_state=10)
#训练LR模型,并进行预测
logreg = LogisticRegression()
logreg.fit(X_train, Y_train)
Y_pred = logreg.predict(X_test)


打印评价报告,因为这是多分类问题,所以评价指标使用不同类别的准确率,召回率和F指标。因为如果采用平均指标来评价的话可能会被误导,如果存在类别不平衡问题的时候,使用平均指标会影响对结果的判断。有些少量的样本类别,也许根据就不会被模型预测出来,如果真的训练样本不平衡的话,可以考虑将这部分少量样本直接作为异常检测,后面人工校对。
print(classification_report(Y_test, Y_pred))


3、其他的算法也类似,就是要注意在使用算法时候的参数的选择与设置,不同参数的物理意义,如何进行调优,这些参见官方文档,通常来说可以使用GridSearchCV()函数来网格搜索最优参数。


以上过程是使用传统方法的一个简单介绍,但是传统方法面临着一个问题,就是传统方法来进行短文本特征提取的时候经常会遇到矩阵十分稀疏的问题,针对这类问题,传统分类器的表现将受到一定的影响。所以可以考虑引入word embedding思想,将词嵌入到空间中,找到词与词之间的关系,而不是简单的将词作为一个个独立的样本来看待,来解决矩阵稀疏的问题,同时也打破了原一个个词独立的情况。


文本挖掘引入词嵌入思想方法讲述
1、主要概念:
新方法和传统方法的主要区别就是新方法引入了word embedding的思想。简单说就是打破了原版传统方法把一个个特征词作为独立特征的情况,原传统方法的处理,将一个个分词结果作为一个个特征词来处理,其实默认假设了词之间的独立。但其实现实中的语句中,词之间通常是存在一定的联系的。1986年HINTON第一次提出了词的分布式表示,将词作为一个个等长的向量来表示。
2003年Bengio《A Neural Probabilistic Language Model》的文章提出了神经网络语言模型,可以词进行分布式表示,将一个个词表示为等长的向量,用于后续的语义分析。但是由于其提出的网络结构存在隐层,且在处理多个输入词的投影层中采用了拼接的方式,导致向量长度过长,进行网络参数学习的BP时计算量偏大。所以在2013年Google 的 Tomas Mikolov 在《Efficient Estimation of Word Representation in Vector Space》提出了word2vec,对其进行了改进,提出了一个简单的三层神经网络,是一种语言模型的浅层神经网络表示方式。主要区别是在输入理多个层处词向量时采用的是相加的方式,这样使得向量的长度急剧减少,而且在输出层采用了根据词频构建的霍夫曼树作为输出,切高频词位于树的浅层,可以有限减少寻找次数,采用霍夫曼树+softmax层作为加速技巧,使得训练速度加快了很多。本地测试过程,在语料不大的情况下,训练时间都小于5分钟。当然其也存在一定的问题,就是越简单的模型就越需要更多的数据,才能表现的更好,而复杂的模型可以在小数据上表现不错。所以该方法,通常在大量预料下能表现不凡。之后更是开源了该项目,为文本分析提供了强大的工具。通过gensim也可以高效的调用word2vec方法,通过word2vec可以学习到词的表达方法,处理之后每一个词可以表示为一个个同维的向量。word2vec的学习方法主要有两种,分别是CBOW和Skip-Gram,简单说就是CBOW方法是通过上下文来推断出这个词的表达,Skip-Gram则相反,是通过这个的词来推断他的上下文。详见http://www.jmlr.org/papers/volume3/bengio03a/bengio03a.pdf和http://blog.csdn.net/wangyangzhizhou/article/details/77073023
https://zhuanlan.zhihu.com/p/26306795
https://www.zybuluo.com/Dounm/note/591752
实例:
​#导入word2vec模块
from gensim.models import Word2Vec
#训练word2vec模型
model = Word2Vec(LineSentence('word2vec.txt'),min_count=1,size=200,iter=10)
#获取每个词和其表征向量
w2v = dict(zip(model.wv.index2word, model.wv.syn0))
通常来说在该方法在大量语料的时候能获得更好的效果,word2vec.txt是每条记录的分词结果,分词结果用空格隔开,每条记录用回车区分,如:
踢 下线 国际社区支局
合同号 推送 电子发票 可否 天宫殿支局
回不了笼 普子支局
在训练之后,可以先把模型保存下来再导入,如:
model.save('word2vec')
model = Word2Vec.load('word2vec')
在获得了每个词的词向量表达之后主要就是要通过不同的方法来构建句向量了,各种不同方法的讨论可以见论文《基于机器学习的汉语短文本分类方法研究与实现 》,文章中引入了深度学习中pooling层的方法,将处理之后的句向量可以作为特征尝试使用传统的分类器进行学习,也可以导入LSTM等深度学习模型进行尝试。fasttext也是一个用于文本挖掘的库,作者和word2vec是同一个人。fasttext相当于是在word2vec的CBOW训练的词向量的基础上加了一个,softmax层来计算得出各类别的概率,最后得出所属类别。
fasttext可以参见资料:《Bag of Tricks for Efficient Text Classification》和一些CSDN上的相关博客


2、深度学习方法
使用深度学习的方法来做文本分类网上也很多资料了。CNN 和RNN都可以做,如果把一个一个词看成存在某种时序,序列的一种数据的输入。使用RNN来做是比较好的,其可以通过memory cell来捕获上一时刻和当前时刻的信息,大部分实验结果也表明在文本上面RNN表现更佳。而在RNN网络中可以考虑使用LSTM来进行模型的训练,当然具体还要看实际的业务场景。
—————————

你可能感兴趣的:(python,机器学习)