程序员

jupyter_文本特征抽取_3TF-IDF处理

2018-01-28  本文已影响0人  thirsd
#本Notebook讲解向量归一化问题、停用词、词根
#作者:thirsd
#归一化。对于同一文章重复多次后,根据直观理解,相似度应该是相同的,但直接欧式距离,导致特征比原文章相似度降低,因为欧式距离增加。
#停用词。很多文章中of most等词汇特征缺乏明显性,去除这些无意义的词汇能减低维度,提升效率;并避免影响总体特征
#词根词。对于apple和apples,go\goes\went同一词根,对文本的语义是相近的

####################################归一化#####################################################

#1.1 问题描述
from sklearn.feature_extraction.text import CountVectorizer
vectorizer=CountVectorizer(min_df=1)
#列出所有的帖子
posts=["How to format my disk","hard disk format at","How to format my disk How to format my disk How to format my disk"]
#对于帖子进行词袋转化
x=vectorizer.fit_transform(posts)
print("feature_name:%s" % vectorizer.get_feature_names())

#获取样本数和特征个数
num_samples,num_features=x.shape


#针对新帖子的向量化
newpost="how to format my computer's disk"
new_post_vec=vectorizer.transform([newpost])

#定义文章向量相似度,采用词频向量的欧式距离
import scipy as sp
def dist_raw(v1,v2):
    delta=v1-v2
    return sp.linalg.norm(delta)

feature_name:[u'at', u'disk', u'format', u'hard', u'how', u'my', u'to']



#计算 new_post同所有帖子的欧式距离(dist_raw),记录最相近的一个
import sys
best_doc=None
best_dist=sys.maxint
best_i=None
for i in range(0,num_samples):
    post=posts[i]
    if post==newpost:
        continue
    post_vec=x.getrow(i)
    print("post_vec's shape:%s, new_post_vec's shape:%s" %(post_vec.shape,new_post_vec.shape))
    dist=dist_raw(post_vec.toarray(),new_post_vec.toarray())
    print "=== Post %i with dist=%.2f: %s" %(i,dist,post)
    if dist<best_dist:
        best_dist=dist
        best_i=i
print ("newpost :%s" % newpost)
print ("Best post is %i with dist=%.2f. Post Content:%s" %(best_i,best_dist,posts[best_i]))

post_vec's shape:(1, 7), new_post_vec's shape:(1, 7)
=== Post 0 with dist=0.00: How to format my disk
post_vec's shape:(1, 7), new_post_vec's shape:(1, 7)
=== Post 1 with dist=2.24: hard disk format at
post_vec's shape:(1, 7), new_post_vec's shape:(1, 7)
=== Post 2 with dist=4.47: How to format my disk How to format my disk How to format my disk
newpost :how to format my computer's disk
Best post is 0 with dist=0.00. Post Content:How to format my disk



#根据直接,第0片的帖子,同第2片相似度一样,但却发现欧式距离增大

print(x.getrow(0).toarray())
print(x.getrow(2).toarray())
print(new_post_vec.toarray())

[[0 1 1 0 1 1 1]]
[[0 3 3 0 3 3 3]]
[[0 1 1 0 1 1 1]]



#1.2 问题解决
#对帖子进行归一化处理,得到单位长度为1的向量
def dist_norm(v1,v2):
    v1_normed = v1/sp.linalg.norm(v1.toarray())
    v2_normed = v2/sp.linalg.norm(v2.toarray())
    delta = v1_normed - v2_normed
    return sp.linalg.norm(delta.toarray())

#差异仅为调用归一化后的距离计算公式
#计算 new_post同所有帖子的归一化后的欧式距离(dist_norm),记录最相近的一个
import sys
best_doc=None
best_dist=sys.maxint
best_i=None
for i in range(0,num_samples):
    post=posts[i]
    if post==newpost:
        continue
    post_vec=x.getrow(i)
    print("post_vec's shape:%s, new_post_vec's shape:%s" %(post_vec.shape,new_post_vec.shape))
    dist=dist_norm(post_vec,new_post_vec)
    print "=== Post %i with dist=%.2f: %s" %(i,dist,post)
    if dist<best_dist:
        best_dist=dist
        best_i=i
print ("newpost :%s" % newpost)
print ("Best post is %i with dist=%.2f. Post Content:%s" %(best_i,best_dist,posts[best_i]))

post_vec's shape:(1, 7), new_post_vec's shape:(1, 7)
=== Post 0 with dist=0.00: How to format my disk
post_vec's shape:(1, 7), new_post_vec's shape:(1, 7)
=== Post 1 with dist=1.05: hard disk format at
post_vec's shape:(1, 7), new_post_vec's shape:(1, 7)
=== Post 2 with dist=0.00: How to format my disk How to format my disk How to format my disk
newpost :how to format my computer's disk
Best post is 0 with dist=0.00. Post Content:How to format my disk



#归一化后,重复文本的欧式距离相同,相似度相同。

####################################停用词#####################################################

#2.1 问题描述
#类似of to at 这些词不是很重要,这些词语经常出现在各种不同的文本中,这种词叫停用词。
#最佳的选择是删除所偶这样的高频词语,因为它们对于区分文本并没有多大的帮助。
print("原始的feature_name:%s" % vectorizer.get_feature_names())
print("原始的samples: %d ,#features: %d" % (num_samples,num_features))

原始的feature_name:[u'at', u'disk', u'format', u'hard', u'how', u'my', u'to']
原始的samples: 3 ,#features: 7



# 其中at to 等词汇意义不大,对于处理相似度重要性较低,而且维度高时,消耗计算资源

# 2.2 问题解决
vectorizer_stopword=CountVectorizer(min_df=1,stop_words="english")
#对于帖子进行词袋转化
x_stopword=vectorizer_stopword.fit_transform(posts)
print("新的feature_name:%s" % vectorizer_stopword.get_feature_names())
#获取样本数和特征个数
num_samples_stopword,num_features_stopword=x_stopword.shape
print("新的samples: %d ,#features: %d" % (num_samples_stopword,num_features_stopword))

#停用词,可以自己维护一个停用词列表。如果不清楚,可以使用一个包含318个单词的英文停用词表。
print("english stop list sample:%s" % sorted(vectorizer_stopword.get_stop_words())[0:20])

新的feature_name:[u'disk', u'format', u'hard']
新的samples: 3 ,#features: 3
english stop list sample:['a', 'about', 'above', 'across', 'after', 'afterwards', 'again', 'against', 'all', 'almost', 'alone', 'along', 'already', 'also', 'although', 'always', 'am', 'among', 'amongst', 'amoungst']



#处理后,新的词袋列表减少指3个。当文本集海量时,尤其有效

new_post_vec_stopword=vectorizer_stopword.transform([newpost])

#差异仅为调用归一、使用停用词后的距离计算公式
for i in range(0,num_samples):
    post=posts[i]
    if post==newpost:
        continue
    post_vec=x_stopword.getrow(i)
    print("post_vec's shape:%s, new_post_vec's shape:%s" %(post_vec.shape,new_post_vec.shape))
    dist=dist_norm(post_vec,new_post_vec_stopword)
    print "=== Post %i with dist=%.2f: %s" %(i,dist,post)
    if dist<best_dist:
        best_dist=dist
        best_i=i
print ("newpost :%s" % newpost)
print ("Best post is %i with dist=%.2f. Post Content:%s" %(best_i,best_dist,posts[best_i]))

post_vec's shape:(1, 3), new_post_vec's shape:(1, 7)
=== Post 0 with dist=0.00: How to format my disk
post_vec's shape:(1, 3), new_post_vec's shape:(1, 7)
=== Post 1 with dist=0.61: hard disk format at
post_vec's shape:(1, 3), new_post_vec's shape:(1, 7)
=== Post 2 with dist=0.00: How to format my disk How to format my disk How to format my disk
newpost :how to format my computer's disk
Best post is 0 with dist=0.00. Post Content:How to format my disk



#可以明显看出效果并没有的改变(当日,效率是要增加的,尤其在海量数据才能体现处理)

####################################词根词#####################################################

#3.0 依赖的NLTK介绍
#scikit没有默认的词根处理器,可以通过NLTK(自然语言处理工具包)
#你可以在Python Text Processing with NLTK 2.0 Cookbook中找到一个很棒的NLTK教程
import nltk.stem
s=nltk.stem.SnowballStemmer('english')
print s.stem("go")
print s.stem("goes")
print s.stem("going")
print s.stem("went")

go
goe
go
went



#注意:词干处理的结果并不一定是有效的英文单词

#3.2 问题处理
#在把帖子传入CountVectorizer之前,需要对于词根进行处理。该类可以定制预处理和词语切分节点的操作。
#当另一种方法,可以避免我们需要亲自对词语进行切分和归一的问题,采用重写build_analyzer的方法实现
import nltk.stem
english_stemmer = nltk.stem.SnowballStemmer('english')
class StemmedCountVectorizer(CountVectorizer):
    def build_analyzer(self):
        analyzer = super(StemmedCountVectorizer,self).build_analyzer()
        return lambda doc:(english_stemmer.stem(word) for word in analyzer(doc))


#列出所有的帖子
posts_root=["How to format my disks","hard disk formating at","How to formated my disks"]

#采用没有词根的处理方式
vectorizer_stopword_noroot=CountVectorizer(min_df=1,stop_words="english")
#对于帖子进行词袋转化
x_stopword_noroot=vectorizer_stopword_noroot.fit_transform(posts_root)
print("新的feature_name_noroot:%s" % vectorizer_stopword_noroot.get_feature_names())
#获取样本数和特征个数
num_samples_stopword_noroot,num_features_stopword_noroot=x_stopword_noroot.shape
print("新的samples_noroot: %d ,#features_noroot: %d" % (num_samples_stopword_noroot,num_features_stopword_noroot))


#采用词根的处理方式
#对于帖子进行词袋转化
vectorizer_stopword_root=StemmedCountVectorizer(min_df=1,stop_words="english")
#对于帖子进行词袋转化
x_stopword_root=vectorizer_stopword_root.fit_transform(posts_root)
print("新的feature_name_root:%s" % vectorizer_stopword_root.get_feature_names())
#获取样本数和特征个数
num_samples_stopword_root,num_features_stopword_root=x_stopword_root.shape
print("新的samples_root: %d ,#features_root: %d" % (num_samples_stopword_root,num_features_stopword_root))

新的feature_name_noroot:[u'disk', u'disks', u'format', u'formated', u'formating', u'hard']
新的samples_noroot: 3 ,#features_noroot: 6
新的feature_name_root:[u'disk', u'format', u'hard']
新的samples_root: 3 ,#features_root: 3



#可以看到,通过词根处理,特征有效降低,泛化能力有明显增强

# 4 引入新的问题
#特征词词语在帖子中出现的次数,默认较大的特征意味这这个词语对帖子更重要,区分度更高。
#但例如邮件中的subject\sender\reciver,每个邮件中都出现的词语就不一样了
#我们可以通过max_df参数来删除,但设置90%,还是89%呢?即使设置了一个参数,但也有这样的问题:一些词语正好要比其他词语更具有区分性。

#这只能通过统计每个帖子的词频,并且对出现在多个帖子中的词语在权重上打折扣来解决。
#换而言之,就是某些词语经常出现在一些特定的帖子中,而其他地方很少出现的时候,我们会赋予该词语较高的权重。

#词频-反转文档频率(TF-IDF)就是处理该问题的。TF代表统计部分,而IDF把权重折扣考虑进去。
上一篇 下一篇

猜你喜欢

热点阅读