赞
踩
周末各种事情,到了周日晚上,才想起来还要做作业,想起周一晚上还约了健身的课,没办法,只能硬着头皮写作业了。这一期的任务其实还是挺多的。
朴素贝叶斯分类器的基本思想是利用特征项和类别的联合概率来估计给定文档的类别概率。假设文本是基于词的一元模型,即文本中当前词的出现依赖于文本类型,但不依赖于其他词及文本的长度,即词与词之间是独立的,根据贝叶斯公式:
p
(
C
i
∣
D
o
c
)
=
p
(
C
i
)
p
(
D
o
c
∣
C
i
)
p
(
D
o
c
)
p(C_i | Doc) = \frac{p(C_i)p(Doc|C_i)}{p(Doc)}
p(Ci∣Doc)=p(Doc)p(Ci)p(Doc∣Ci), 朴素贝叶斯之所以叫做朴素,就是因为它假设构成文档的词是相互独立的, 即
p
(
D
o
c
∣
C
i
)
=
∏
w
i
∈
w
i
p
(
w
i
∣
C
i
)
p(Doc|C_i) = \prod_{w_i \in w_i} p(w_i | C_i)
p(Doc∣Ci)=∏wi∈wip(wi∣Ci)。
理论上来说,朴素贝叶斯模型假设属性之间相互独立,这个假设在实际应用中往往是不成立的,但其实在很多情况下,朴素贝叶斯已经可以取得较好的分类的效果,特别是垃圾邮件分类,在早期朴素贝叶斯,可以算是一个经典的算法了。
在深度学习大火之前,SVM几乎是最为热门的机器学习方法。犹记得我硕士的时候,室友熊哥就是搞SVM作文本挖掘的,当时很是羡慕他的方向。 SVM 本质上在线性分类的基础上,引入间隔这个概念,将分类问题从正确率问题转换成一个最小间隔的问题。 当然在此基础上,有很多的优化,比如软硬间隔,比如利用KKT条件和对偶将优化问题变成一个非常优美的仅有内积运算的形式,比如引入核函数,将线性不可分的情况再更高维的空间解决,比如利用坐标下降的方法的SMO。 SVM有着漂亮的数学形式,有着较好的分类结果,有着理论上无限维的求解能力,在一个很长的时间内是学术界和工业界的宠儿,直到深度学习时代的到来。
SVM的推导非常的美,当年在学习吴恩达老师的课以及找工作的时候看李航老师的《统计学习方法》[9]的时候,都曾经跟着推导过,这里时间有限,等什么时候兴致来了,专门写一个SVM的专题,再深入。其实除了基础的推导和SMO的算法,SVM其实在前些年被研究的很多,记得17年的NIPS上隔壁实验室的宋博还发表了一篇关于在类似的优化中,如果不在用高斯假设,即不用L2正则,而是稀疏假设,采用L1正则,改如何改进坐标下降的算法。记得当年没有看懂他用的关于从压缩感知中得到灵感的改进,以后写这个专题的时候,我会再次拜读一下大作。
引用文献[7]中的描述: 支持向量机(SVM)算法基于结构风险最小化原理,将数据集合压缩到支持向量集合,学习得到分类决策函数。这种技术解决了以往需要无穷大样本数量的问题,它只需要将一定数量的文本通过计算抽象成向量化的训练文本数据,提高了分类的精确率。支持向量机(SVM)算法是根据有限的样本信息,在模型的复杂性与学习能力之间寻求最佳折中,以求获得最好的推广能力支持向量机算法的主要优点有:
决策树是一种对实例进行分类的树形结构,树由结点和有向边组成。结点分为内结点和叶结点,前者表示一种属性或者特征,后者表示一个类别。决策树可以看成是if-then规则的集合,也可以看成是定义在特征空间和类空间上的条件概率分布。优点是可读性强,缺点是一般分类效果不如其他的分类器。 从分类的角度看,一般决策树算法,分为三个步骤:特征选择,决策树生成和裁剪。
常见的决策树算法有:
决策树算法:
1.CART
2. ID3:Iterative Dichotomiser 3
3. C4.5:ID3 算法的改进
4. CHAID:Chi-squared Automatic Interaction Detector
5. MARS:决策树的扩展式,以更好地解决数值型预测。
条件推断树
随机森林(RF)是一种集成算法(Ensemble Learning),它属于Bagging类型,通过组合多个弱分类器,最终结果通过投票或取均值,使得整体模型的结果具有较高的精确度和泛化性能。其可以取得不错成绩,主要归功于“随机”和“森林”,一个使它具有抗过拟合能力,一个使它更加精准。 RF相对于Bagging只是对其中一些细节做了自己的规定和设计。文献[10] 给出了比较精炼的总结
首先,RF使用了CART决策树作为弱学习器。换句话说,其实我们只是将使用CART决策树作为弱学习器的Bagging方法称为随机森林。
同时,在生成每棵树的时候,每个树选取的特征都仅仅是随机选出的少数特征,一般默认取特征总数m的开方。而一般的CART树则是会选取全部的特征进行建模。因此,不但特征是随机的,也保证了特征随机性。
相对于一般的Bagging算法,RF会选择采集和训练集样本数N一样个数的样本。
由于随机性,对于降低模型的方差很有作用,故随机森林一般不需要额外做剪枝,即可以取得较好的泛化能力和抗过拟合能力(Low Variance)。当然对于训练集的拟合程度就会差一些,也就是模型的偏倚会大一些(High Bias),仅仅是相对的。
说起Probability Latent Semantic Analysis (PLSA) ,先得从LSA说起。
LSA(latent semantic analysis)潜在语义分析,也被称为LSI(latent semantic index),是Scott Deerwester, Susan T. Dumais等人在1990年提出来的一种新的索引和检索方法。该方法和传统向量空间模型(vector space model)一样使用向量来表示词(terms)和文档(documents),并通过向量间的关系(如夹角)来判断词及文档间的关系;而不同的是,LSA将词和文档映射到潜在语义空间,从而去除了原始向量空间中的一些“噪音”,提高了信息检索的精确度。LSA的基本思想就是把高维的文档降到低维空间,那个空间被称为潜在语义空间。这个映射必须是严格线性的而且是基于共现表的奇异值分解。 LSA(隐性语义分析)的目的是要从文本中发现隐含的语义维度-即“Topic”或者“Concept”。LSA的目标就是要寻找到能够很好解决实体间词法和语义关系的数据映射。
原理:LSA潜在语义分析的目的,就是要找出词(terms)在文档和查询中真正的含义,也就是潜在语义,从而解决文档的空间向量模型(VSM)中的一词多义和一义多词问题。具体说来就是对一个大型的文档集合使用一个合理的维度建模,并将词和文档都表示到该空间,比如有2000个文档,包含7000个索引词,LSA使用一个维度为100的向量空间将文档和词表示到该空间,进而在该空间进行信息检索。而将文档表示到此空间的过程就是SVD奇异值分解和降维的过程。降维是LSA分析中最重要的一步,通过降维,去除了文档中的“噪音”,也就是无关信息(比如词的误用或不相关的词偶尔出现在一起),语义结构逐渐呈现。相比传统向量空间,潜在语义空间的维度更小,语义关系更明确。
pLSA 模型是有向图模型,将主题作为隐变量,构建了一个简单的贝叶斯网,采用EM算法估计模型参数。相比于 LSA 略显“随意”的SVD,pLSA 的统计基础更为牢固。
模型: 观测变量为文档 d m ∈ D d_m \in \mathbb{D} dm∈D(文档集共 M 篇文档)、词 w n ∈ W w_n \in \mathbb{W} wn∈W(设词汇表共有 V 个互不相同的词),隐变量为主题 z k ∈ Z z_k \in \mathbb{Z} zk∈Z(共 K 个主题)。在给定文档集后,我们可以得到一个词-文档共现矩阵,每个元素 n ( d m , w n ) n(d_m,w_n) n(dm,wn)表示的是词 w n w_n wn 在文档 d m d_m dm 中的词频。也就是说,pLSA 模型也是基于词-文档共现矩阵的,不考虑词序。
pLSA 模型通过以下过程来生成文档(记号里全部省去了对参数的依赖):
以概率 $P(d_m) $选择一篇文档 d m d_m dm
以概率 P ( z k ∣ d m ) P(z_k|d_m) P(zk∣dm)得到一个主题 z k z_k zk
以概率 P ( w n ∣ z k ) P(w_n|z_k) P(wn∣zk) 生成一个词 w n w_n wn
pLSA 模型的参数 是:K×M 个 P ( z k ∣ d m ) P(z_k|d_m) P(zk∣dm)、V×K 个 P ( w n ∣ z k ) P(w_n|z_k) P(wn∣zk) 。 P ( z k ∣ d m ) P(z_k|d_m) P(zk∣dm) 表征的是给定文档在各个主题下的分布情况,文档在全部主题上服从多项式分布(共 M 个); P ( w n ∣ z k ) P(w_n|z_k) P(wn∣zk) 则表征给定主题的词语分布情况,主题在全部词语上服从多项式分布(共 K 个)
具体参数求解的方法,采用是EM算法来进行迭代,这里可以参考文献 [12], 有非常详细清晰的过程,时间有限,我就不再这里重复去写。 EM算法,实际应用非常广泛,吴军先生的数学之美称其为上帝的算法。等后面腾出手来,我会仔细整理一下EM算法。 时间啊,都是时间。。。。
在贝叶斯统计中,如果后验分布与先验分布属于同类,则先验分布与后验分布被称为共轭分布,而先验分布被称为似然函数的共轭先验。比如,高斯分布家族在高斯似然函数下与其自身共轭 (自共轭)。这个概念,以及"共轭先验"这个说法,由 霍华德·拉法拉 和 罗伯特·施莱弗尔 在他们关于贝叶斯决策理论的工作中提出。
具体地说,就是给定贝叶斯公式 p ( θ ∣ x ) = p ( x ∣ θ ) p ( θ ) ∫ p ( x ∣ θ ′ ) p ( θ ′ ) d θ ′ p(\theta | x) = \frac{p(x | \theta) p(\theta)}{ \int p(x|\theta ')p(\theta ')d\theta '} p(θ∣x)=∫p(x∣θ′)p(θ′)dθ′p(x∣θ)p(θ) 假定似然函数 p ( x ∣ θ ) p(x|\theta ) p(x∣θ)是已知的,问题就是选取什么样的先验分布 p ( θ ) p(\theta) p(θ) 会让后验分布与先验分布具有相同的数学形式。
共轭先验的好处主要在于代数上的方便性,可以直接给出后验分布的封闭形式,否则的话只能数值计算。共轭先验也有助于获得关于似然函数如何更新先验分布的直观印象。
所有指数家族的分布都有共轭先验。
LDA 将模型参数视作随机变量,将多项式分布的共轭先验(也就是Dirichlet分布)作为参数的先验分布,并使用Gibbs sampling方法对主题进行采样。具体的流程参考了文献[5]。
对于语料库中的每篇文档,LDA定义了如下生成过程(generativeprocess):
文档集合D中每个文档d看作一个单词序列 < w 1 , w 2 , … … , w n > <w_1, w_2, …… ,w_n> <w1,w2,……,wn>, w i w_i wi表示第i个单词,设d有n个单词。
文档集合D中的所有单词组成一个集合VOC。
LDA以文档集合D作为输入,希望训练出两个结果向量(设聚成k个topic,VOC中共包含m个词)。
对每个D中的文档d,对应到不同Topic的概率 θ d < p t 1 , . . . , p t k > θ_d<p_{t_1},...,p_{t_k}> θd<pt1,...,ptk>,其中, p t i p_{t_i} pti表示d对应主题集合T中第i个topic的概率。计算方法是直观的, p t i = n t i / n p_{t_i}=n_{t_i}/n pti=nti/n,其中 n t i n_{t_i} nti表示d中对应第i个topic的词的数目,n是d中所有词的总数。
对每个T中的topic,生成不同单词的概率
φ
t
<
p
w
1
,
.
.
.
,
p
w
m
>
φ_t<p_{w_1},...,p_{w_m}>
φt<pw1,...,pwm>,其中,
p
w
i
p_{w_i}
pwi表示t生成VOC中第i个单词的概率。计算方法同样很直观,
p
w
i
=
N
w
i
/
N
p_{w_i}=N_{w_i}/N
pwi=Nwi/N,其中
N
w
i
N_{w_i}
Nwi表示对应到topic t的VOC中第i个单词的数目,N表示所有对应到topict的单词总数。
LDA的核心公式是:
p
(
w
∣
d
)
=
p
(
w
∣
t
)
∗
p
(
t
∣
d
)
p(w|d)=p(w|t)*p(t|d)
p(w∣d)=p(w∣t)∗p(t∣d)
直观的看这个公式,就是以Topic作为中间层,可以通过当前的 θ d θ_d θd和 φ t φ_t φt给出了文档d中出现单词w的概率。其中p(t|d)利用 θ d θ_d θd计算得到,p(w|t)利用 φ t φ_t φt计算得到。
实际上,利用当前的 θ d θ_d θd和 φ t φ_t φt,我们可以为一个文档中的一个单词计算它对应任意一个Topic时的p(w|d),然后根据这些结果来更新这个词应该对应的topic。然后,如果这个更新改变了这个单词所对应的Topic,就会反过来影响 θ d θ_d θd和 φ t φ_t φt。 这个过程其实同样是一个EM过程。
这里暂且偷懒,用了sklearn的库,且参考了[13], 其实各种分类器的处理大同小异,我写在了一起,最后的LDA,容我打卡之后,晚点再补,实在要去运动一下,身体吃不消了。
# -*- coding: utf-8 -*- # @Time : 2019/4/15 17:44 # @Author : Lei Wang # @Site : # @File : NB_classify.py # @Software: PyCharm import os import jieba from sklearn.datasets.base import Bunch import _pickle as pickle from sklearn.feature_extraction.text import TfidfVectorizer #feature_extraction‘特征提取’,Convert a collection of raw documents to a matrix of TF-IDF features. from sklearn.model_selection import train_test_split from sklearn.naive_bayes import MultinomialNB #朴素贝叶斯分类算法,Naive Bayes classifier for multinomial models from sklearn.metrics import classification_report#metrics度量,分类报告Build a text report showing the main classification metrics text_train_cnews='../cnews/cnews.train.txt' text_val_cnews='../cnews/cnews.val.txt' text_test_cnews='../cnews/cnews.test.txt' text_category_cnews='../cnews/cnews.category.txt' #category的内容为: ''' 体育 0 财经 1 房产 2 家居 3 教育 4 科技 5 时尚 6 时政 7 游戏 8 娱乐 9 ''' word_level='data/word_level' text_jieba_cnews='data/word_level/cnews.jieba.txt' text_cnews='data/word_level/cnews.word.jieba.dat' #创建文件夹,用来存放处理后的数据 if not os.path.exists(word_level): os.mkdir(word_level) class Categories: def __init__(self,filename):#初始化 self.category={}#将类别名和类别数值存储在字典里面 for line in open(filename,'r',encoding='utf-8'): c,label=line.strip('\r\n').split('\t')#体育 0;财经 1;房产 2; self.category[c]=label #根据类别名获取类别的数值 def get_category_label(self,name): return self.category[name] categories=Categories(text_category_cnews) # h=categories.get_category_label('体育')# # print(h) # def get_word(filenamelist,save_filename):#把所有的数据全部读取,并且输出存到同一个文本文件内 labels=[] count=0 for filename in filenamelist: count+=1 print('*****正在拆分第%s个输入文本的信息,请稍等*******'%count) with open(filename,'r',encoding='utf-8') as f: lines=f.readlines()#一开始写成了readline,然后找了半天都没有找出来哪里报错了,fuck!!!!! with open(save_filename,'a',encoding='utf-8') as f1:#用w的方式,每读取一个文件,输出的文本会将之前的数据覆盖了,用a的话就不会,追加 for line in lines: label,content=line.strip('\r\n').split('\t') labels.append(label) content_list=list(jieba.cut(content))#jieba分词 content_word='' #将list里面的元素(词),用‘’拼接成字符串 for word in content_list: word=word.strip() if word !='':#不等于空的数值,进行存储 content_word +=word+' '#使用空格进行区分 wordli='%s\t%s\n'%(label,content_word.strip())#存储分词后的新闻内容 f1.write(wordli) print(set(labels))#打印出labels中不重复的键值 filenamelist=[text_val_cnews,text_test_cnews,text_train_cnews] get_word(filenamelist,text_jieba_cnews) def save_bunch(input_file_name,out_file_name,category_file):#将输入的文本文件进行分词拆分、然后分类 categories=Categories(category_file) #实例化Bunch对象,包含targets、filenames、labels #contents bunch=Bunch(targets=[],filenames=[],labels=[],contents=[]) filename=0 lab=[] print('***********正在调用save_bunch函数,请稍等**********') with open(input_file_name,'r',encoding='utf-8') as f: lines=f.readlines() for line in lines: filename +=1 category,content=line.strip('\r\n').split('\t')#由于get_word 函数中最后的wordli又通过\t和\n将分类和对应的新闻又组合在了一起 bunch.contents.append(content) label=categories.get_category_label(category)#label的取值为0到9,代表体育、财经、房产。。。。。 bunch.labels.append(label) bunch.filenames.append(str(filename)) lab.append(label) bunch.targets=list(set(lab))#targets存的是大的分类,而labels是下属的每条新闻都对应一个分类,而这个label是和大的分类一致的。 #print(bunch) #{'targets': ['0'], 'filenames': ['1', '2', '3', '4', '5', '6'], 'labels': ['0', '0', '0', '0', '0', '0'], 'contents': ['黄蜂 vs 湖人 首发 : 科比 带伤 战 with open(out_file_name,'wb') as f:#将bunch存储到文件 pickle.dump(bunch,f) # save_bunch(text_jieba_cnews,text_cnews,text_category_cnews) text_tfdif_cnews='data/word_level/cnews.word.tfdif.jieba.dat' stop_word_file='../stoplist_baidu.txt' # def _read_bunch(filename): with open(filename,'rb') as f: bunch=pickle.load(f) return bunch def _write_bunch(bunch,filename): with open(filename,'wb') as f: pickle.dump(bunch,f) def get_stop_words(filename=stop_word_file): stop_word=[] for line in open(filename,'r',encoding='utf-8'): stop_word.append(line.strip()) return stop_word # # #权重策略TF——IDF # ''' # TF-IDF(Term frequency * Inverse Doc Frequency)词权重 # 在较低的文本语料库中,一些词非常常见(例如,英文中的“the”,“a”,“is”),因此很少带有文档实际内容的有用信息。 # 如果我们将单纯的计数数据直接喂给分类器,那些频繁出现的词会掩盖那些很少出现但是更有意义的词的频率。 # 为了重新计算特征的计数权重,以便转化为适合分类器使用的浮点值,通常都会进行tf-idf转换。 # 词重要性度量一般使用文本挖掘的启发式方法:TF-IDF。 # 这是一个最初为信息检索(作为搜索引擎结果的排序功能)开发的词加权机制,在文档分类和聚类中也是非常有用的 # 由于tf-idf经常用于文本特征,因此有另一个类称为TfidfVectorizer, # 将CountVectorizer和TfidfTransformer的所有选项合并在一个模型中 # ''' def tfidf_deal_cnews(input_file_name,out_file_name): bunch=_read_bunch(input_file_name)#读取数据 stop_words=get_stop_words()#得到停用词 #实例化bunch对象 #tmd(权重列表) #vocabulary(词典索引) space_bunch=Bunch(targets=bunch.targets,filename=bunch.filenames,labels=bunch.labels,tmd=[],vocabulary={}) #使用特征提取函数TfidfVectorizer初始化向量空间模型 vector=TfidfVectorizer(stop_words=stop_words,sublinear_tf=True,max_df=0.5)#提取函数的初始化,啥数据都没有处理。选择能代表新闻特征、独一无二的词汇,词频大于50%的就被过滤掉???如果过大、过小会如何? space_bunch.tmd=vector.fit_transform(bunch.contents)#contents只有新闻内容,没有分类。用df-idf训练转化,获得if-idf权值矩阵:fit_transform(raw_documents[, y]) Learn vocabulary and idf, return term-document matrix. ''' print(space_bunch.tmd)输出格式为以下: (0, 834) 0.2608786231499419 (0, 38) 0.2104752305319886 (0, 557) 0.29664039933480035 (0, 820) 0.2104752305319886 ''' space_bunch.vocabulary=vector.vocabulary_#词典索引,统计词频 ''' print(space_bunch.vocabulary)输出格式如下: {'黄蜂': 834, 'vs': 38, '湖人': 557, '首发': 820, '科比': 609, '带伤': 352, '保罗': 156, ''' _write_bunch(space_bunch,out_file_name)#写入文件 tfidf_deal_cnews(text_cnews,text_tfdif_cnews) bunch=_read_bunch(text_tfdif_cnews) # print(bunch.tmd.shape)#结果为(65000, 379716) #构建分类器 x_train,x_test,y_train,y_test=train_test_split(bunch.tmd,bunch.labels,test_size=0.2,random_state=100) #以上,提取新闻文档中的词频和对应的新闻分类代号 print('-----------------------NB--------------------------------------------') #先实例化模型,然后调用methods,如调用fit、predict nb=MultinomialNB(alpha=0.01)#实例化模型 alpha: Additive (Laplace/Lidstone) smoothing parameter (0 for no smoothing). nb.fit(x_train,y_train)#训练模型 Fit Naive Bayes classifier according to X, y y_pred=nb.predict(x_test)#预测测试集X; Perform classification(分类) on an array of test vectors X. print(classification_report(y_test,y_pred))#打印输出评分 print('-----------------------SVM---------------------------------------------') from sklearn.svm import SVC from sklearn.multiclass import OneVsRestClassifier #clf = SVC( probability=True) multiC = OneVsRestClassifier(estimator=SVC(random_state=0)) multiC.fit(x_train,y_train) y_pred_svm=multiC.predict(x_test) print(classification_report(y_test,y_pred_svm)) print('----------------------- Decision Tree---------------------------------------------') from sklearn.tree import DecisionTreeClassifier DT = DecisionTreeClassifier( ) DT.fit(x_train,y_train) y_pred_DT=clf.predict(x_test) print(classification_report(y_test,y_pred_DT)) print('----------------------- Random Forest---------------------------------------------') from sklearn.ensemble import RandomForestClassifier RF = RandomForestClassifier( ) RF.fit(x_train,y_train) y_pred_RF=clf.predict(x_test) print(classification_report(y_test,y_pred_RF))
结果图片:
SVM把我慢哭了,所以只训练了10000条数据
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。