自然语言处理4:文本分析

自然语言处理(4):文本表示

1.TF-IDF原理

  1. 词频TF(item frequency):某一给定词语在该文本中出现次数。该数字通常会被归一化(分子一般小于分母),以防止它偏向长的文件,因为不管该词语重要与否,它在长文件中出现的次数很可能比在段文件中出现的次数更大。 需要注意的是有一些通用词对文章主题没有太大作用,如“的”“是”等,而有一些频率出现少的词如一些专业词更能表现文章主题,所以为词语设置权重,权重的设计满足:一个词预测主题的能力越强,权重越大,反之,权重越小。也就是说,一些词只在很少几篇文章中出现,那么这样的词对文章主题的判断能力很大,这些词的权重应该设计的较大。IDF完成这样的工作。
  2. 逆向文件频率IDF(inverse document frequency):一个词语普遍重要性的度量。主要思想是:如果包含词条t的文档越少, IDF越大,则说明词条具有很好的类别区分能力。某一特定词语的IDF,可以由总文件数目除以包含该词语之文件的数目,再将得到的商取对数得到。
1
2
TF=(某个词在文章中出现的次数)/(文章的总词数)
IDF=log((语料库的文档总数)/(包含该词的文档数+1))

2.使用不同的方法来计算TF-IDF的值

  1. 词袋模型
    Bag of words,也叫做“词袋”,在信息检索中,Bag of words model假定对于一个文本,忽略其词序和语法,句法,将其仅仅看做是一个词集合,或者说是词的一个组合,文本中每个词的出现都是独立的,不依赖于其他词是否出现,或者说当这篇文章的作者在任意一个位置选择一个词汇都不受前面句子的影响而独立选择的。
1.使用gensim模块
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
corpus = [
'this is the first document',
'this is the second second document',
'and the third one',
'is this the first document'
]#语料库

#先做分词处理
word_list=[]
for i in range(len(corpus)):
word_list.append(corpus[i].split(' '))
print(word_list)

#得到每个词的id值和词频

from gensim import corpora
dic=corpora.Dictionary(word_list)#赋给语料库中每个不重复的词一个id
new_corpus=[dic.doc2bow(text) for text in word_list]#寻找整篇语料的词典、所有词,corpora.Dictionary
print(new_corpus)

#训练gensim模型
from gensim import models
tfidf=models.TfidfModel(new_corpus)
tfidf.save("my_model.tfidf")

#载入模型
tfidf=models.TfidfModel.load("my_model.tfidf")
#使用模型得到单词的tfidf值
tfidf_vec=[]
for i in range(len(corpus)):
string=corpus[i]
string_bow=dic.doc2bow(string.lower().split())
stringtfidf=tfidf[string_bow]
tfidf_vec.append(stringtfidf)
print(tfidf_vec)
  1. 结论
  • gensim训练出来的tf-idf值左边是词的id,右边是词的tfidf值
  • gensim有自动去除停用词的功能,比如the
  • gensim会自动去除单个字母,比如i
  • gensim会去除没有被训练到的词,比如name
    所以通过gensim并不能计算每个单词的tfidf值
2.使用sklearn提取文本tfidf特征
1
2
3
4
5
6
7
from sklearn.feature_extraction.text import TfidfVectorizer
tfidf_vec=TfidfVectorizer()#模型构建
tfidf_matrix=tfidf_vec.fit_transform(corpus)#传入句子组成的list

print(tfidf_vec.get_feature_names())#得到所有不重复的词
print(tfidf_vec.vocabulary_)#得到对应的id值
print(tfidf_matrix.toarray())
3.python提取文本特征tfidf值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#统计词频
from collections import Counter
countlist=[]
for i in range(len(word_list)):
count=Counter(word_list[i])#每个count是一个句子
countlist.append(count)
# word可以通过count得到,count可以通过countlist得到
# count[word]可以得到每个单词的词频, sum(count.values())得到整个句子的单词总数
def tf(word,count):
return count[word]/sum(count.values())
#统计含有该单词的句子数
def n_containing(word,countlist):
return sum(1 for count in countlist if word in count)
# len(count_list)是指句子的总数,n_containing(word, count_list)是指含有该单词的句子的总数,加1是为了防止分母为0
def idf(word, count_list):
return math.log(len(count_list) / (1 + n_containing(word, count_list)))

# 将tf和idf相乘
def tfidf(word, count, count_list):
return tf(word, count) * idf(word, count_list)

import math
for i,count in enumerate(countlist):
print("Top words in docment{}".format(i+1))
scores={word:tfidf(word,count,countlist) for word in count}
sorted_words=sorted(scores.items(),key=lambda x:x[1],reverse=True)
for word,score in sorted_words[:]:
print("\tWord: {}, TF-IDF: {}".format(word, round(score, 5)))

3.sklearn:点互信息和互信息

  1. 点互信息PMI
    机器学习相关文献里面,经常会用到点互信息PMI(Pointwise Mutual Information)这个指标来衡量两个事物之间的相关性(比如两个词)。
    在概率论中,我们知道,如果x跟y不相关,则p(x,y)=p(x)p(y)。二者相关性越大,则p(x, y)就相比于p(x)p(y)越大。用后面的式子可能更好理解,在y出现的情况下x出现的条件概率p(x|y)除以x本身出现的概率p(x),自然就表示x跟y的相关程度。举个自然语言处理中的例子来说,我们想衡量like这个词的极性(正向情感还是负向情感)。我们可以预先挑选一些正向情感的词,比如good。然后我们算like跟good的PMI。
1
PMI(x;y)=log(p(x,y)/(p(x)*p(y)))=log(p(y|x)/p(y))
  1. 互信息MI
    衡量的是两个随机变量之间的相关性,即一个随机变量中包含的关于另一个随机变量的信息量。所谓的随机变量,即随机试验结果的量的表示,可以简单理解为按照一个概率分布进行取值的变量,比如随机抽查的一个人的身高就是一个随机变量。可以看出,互信息其实就是对X和Y的所有可能的取值情况的点互信息PMI的加权和。因此,点互信息这个名字还是很形象的。

4.如何进行特征选择(理论篇)机器学习你会遇到的“坑”(来源于网络)

一个典型的机器学习任务,是通过样本的特征来预测样本所对应的值。如果样本的特征少了,我们会考虑增加特征,比如Polynomial Regression就是典型的增加特征的算法。在前一周的课程中,相信大家已经体会到,模型特征越多,模型的复杂度也就越高,越容易导致过拟合。事实上,如果我们的样本数少于特征数,那么过拟合就不可避免。

而现实中的情况,往往是特征太多了,需要减少一些特征。

首先是“无关特征”(irrelevant feature)。比如,通过空气的湿度,环境的温度,风力和当地人的男女比例来预测明天会不会下雨,其中男女比例就是典型的无关特征。

其次,要减少的另一类特征叫做“多余特征”(redundant feature),比如,通过房屋的面积,卧室的面积,车库的面积,所在城市的消费水平,所在城市的税收水平等特征来预测房价,那么消费水平(或税收水平)就是多余特征。证据表明,消费水平和税收水平存在相关性,我们只需要其中一个特征就够了,因为另一个能从其中一个推演出来。(如果是线性相关,那么我们在用线性模型做回归的时候,会出现严重的多重共线性问题,将会导致过拟合。)

减少特征有非常重要的现实意义,甚至有人说,这是工业界最重要的问题。因为除了降低过拟合,特征选择还可以使模型获得更好的解释性,加快模型的训练速度,一般的,还会获得更好的性能。

问题在于,在面对未知领域的时候,很难有足够的知识去判断特征与我们的目标是不是相关,特征与特征之间是不是相关。这时候,就需要一些数学和工程上的办法来帮助我们尽可能地把恰好需要的特征选择出来。

常见的方法包括过滤法(Filter)、包裹法(Warpper),嵌入法

1.过滤法

过滤法只用于检验特征向量和目标(响应变量)的相关度,不需要任何的机器学习的算法,不依赖于任何模型,只是应用统计量做筛选:我们根据统计量的大小,设置合适的阈值,将低于阈值的特征剔除.

2.包裹法

与过滤法不同的是,包裹法采用的是特征搜索的办法。它的基本思路是,从初始特征集合中不断的选择子集合,根据学习器的性能来对子集进行评价,直到选择出最佳的子集。在搜索过程中,我们会对每个子集做建模和训练。

图为包裹法的流程图,其中Estimated Accuracy是机器学习分类问题的典型的性能指标。

基于此,包裹法很大程度上变成了一个计算机问题:在特征子集的搜索问题(subset search)。我们有多种思路,最容易想到的办法是穷举(Brute-force search),遍历所有可能的子集,但这样的方法适用于特征数较少的情形,特征一旦增多,就会遇到组合爆炸,在计算上并不可行。(N个特征,则子集会有种可能)

另一个思路是随机化搜索,比如拉斯维加斯算法(Las Vegas algorithm),但这样的算法在特征数大的时候,计算开销仍然很大,而且有给不出任何解的风险。所以,我们常使用的是贪心算法:

  • 前向搜索(Forward search)

在开始时,按照特征数来划分子集,每个子集只有一个特征,对每个子集进行评价。然后在最优的子集上逐步增加特征,使模型性能提升最大,直到增加特征并不能使模型性能提升为止。

  • 后向搜索(Backward search)

在开始时,将特征集合分别减去一个特征作为子集,每个子集有N—1个特征,对每个子集进行评价。然后在最优的子集上逐步减少特征,使得模型性能提升最大,直到减少特征并不能使模型性能提升为止。

  • 双向搜索(Bidirectional search)

将Forward search 和Backward search结合起来。

  • 递归剔除(Recursive elimination )

反复的训练模型,并剔除每次的最优或者最差的特征,将剔除完毕的特征集进入下一轮训练,直到所有的特征被剔除,被剔除的顺序度量了特征的重要程度。