NLP:Scikit-learn的Feature extraction文本特征提取的两种方式

本文参考来源:https://blog.csdn.net/pipisorry/article/details/419577631.文本特征提取的原理
(1)词袋(Bag of Words)表征
文本分析是机器学习算法的主要应用领域。但是,文本分析的原始数据无法直接丢给算法,这些原始数据是一组符号,因为大多数算法期望的输入是固定长度的数值特征向量而不是不同长度的文本文件。为了解决这个问题,scikit-learn提供了一些实用工具可以用最常见的方式从文本内容中抽取数值特征,比如说:

标记(tokenizing)文本以及为每一个可能的标记(token)分配的一个整型ID ,例如用白空格和标点符号作为标记的分割符(中文的话涉及到分词的问题)
计数(counting)标记在每个文本中的出现频率
正态化(nomalizating) 降低在大多数样本/文档中都出现的标记的权重

在这个方案中,特征和样本的定义如下:

将每个标记出现的频率(无论是否正态化)作为特征。给定文件中所有标记的出现频率所构成的向量作为多元样本。因此,语料文件可以用一个词文档矩阵代表,每行是一个文档,每列是一个标记(即词)。将文档文件转化为数值特征的一般过程被称为向量化。这个特殊的策略(标记,计数和正态化)被称为词袋或者Bag of n-grams表征。用词频描述文档,但是完全忽略词在文档中出现的相对位置信息。

(2)稀疏性
大多数文档通常只会使用语料库中所有词的一个子集,因而产生的矩阵将有许多特征值是0(通常99%以上都是0)。

例如,一组10,000个短文本(比如email)会使用100,000的词汇总量,而每个文档会使用100到1,000个唯一的词。

为了能够在内存中存储这个矩阵,同时也提供矩阵/向量代数运算的速度,通常会使用稀疏表征例如在scipy.sparse包中提供的表征。

(3)通用向量使用
CountVectorizer在一个类中实现了标记和计数:

from sklearn.feature_extraction.text import CountVectorizer
这个模型有许多参数,不过默认值已经非常合理(具体细节请见参考文档):

vectorizer = CountVectorizer(min_df=1)

结果: 
CountVectorizer(analyzer=...'word', binary=False, charset=None,
        charset_error=None, decode_error=...'strict',
        dtype=<... 'numpy.int64'>, encoding=...'utf-8', input=...'content',
        lowercase=True, max_df=1.0, max_features=None, min_df=1,
        ngram_range=(1, 1), preprocessor=None, stop_words=None,
        strip_accents=None, token_pattern=...'(?u)\\b\\w\\w+\\b',
        tokenizer=None, vocabulary=None)


用它来标记和计算一个简单语料的词频:

corpus = [
     'This is the first document.',
     'This is the second second document.',
     'And the third one.',
     'Is this the first document?',
 ]
X = vectorizer.fit_transform(corpus)


默认设置通过抽取2个字符以上的词标记字符。完成这个步骤的具体函数可以直接调用:

analyze = vectorizer.build_analyzer()
analyze("This is a text document to analyze.") == (
     ['this', 'is', 'text', 'document', 'to', 'analyze'])
 
结果:True


在拟合过程中,每一个分析器找到的词都会分配一个在结果矩阵中对应列的整型索引。列的含义可以用下面的方式获得:

vectorizer.get_feature_names() == (
     ['and', 'document', 'first', 'is', 'one',
      'second', 'the', 'third', 'this'])
 
结果:True
 
X.toarray()
结果:
array([[0, 1, 1, 1, 0, 0, 1, 0, 1],
       [0, 1, 0, 1, 0, 2, 1, 0, 1],
       [1, 0, 0, 0, 1, 0, 1, 1, 0],
       [0, 1, 1, 1, 0, 0, 1, 0, 1]]...)



特征名称与列索引的转化映射被存储在向量器(vectorizer)的vocabulary_属性中:

vectorizer.vocabulary_.get('document')
 
结果:1


因此,在训练语料中没有出现的词在后续调用转化方法时将被完全忽略:

vectorizer.transform(['Something completely new.']).toarray()
 
结果:array([[0, 0, 0, 0, 0, 0, 0, 0, 0]]...)


注意在前面的语料中,第一个和最后一个文档的词完全相同因此被编码为等价的向量。但是,我们丢失了最后一个文档是疑问形式的信息。为了保留一些局部顺序信息,我们可以在抽取词的1-grams(词本身)之外,再抽取2-grams:

bigram_vectorizer = CountVectorizer(ngram_range=(1, 2),
                                     token_pattern=r'\b\w+\b', min_df=1)
analyze = bigram_vectorizer.build_analyzer()
analyze('Bi-grams are cool!') == (
     ['bi', 'grams', 'are', 'cool', 'bi grams', 'grams are', 'are cool'])
 
结果:True


因此,由这个向量器抽取的词表非常大,现在可以解决由于局部位置模型编码的歧义问题:

X_2 = bigram_vectorizer.fit_transform(corpus).toarray()
X_2
结果:                          
array([[0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0],
       [0, 0, 1, 0, 0, 1, 1, 0, 0, 2, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0],
       [1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0],
       [0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1]]...)


特别是疑问形式“Is this”只出现在最后一个文档:

feature_index = bigram_vectorizer.vocabulary_.get('is this')
X_2[:, feature_index]     
结果:
array([0, 0, 0, 1]...)

二.TF-IDF(Term frequency * Inverse Doc Frequency)词权重


在较低的文本语料库中,一些词非常常见(例如,英文中的“the”,“a”,“is”),因此很少带有文档实际内容的有用信息。如果我们将单纯的计数数据直接喂给分类器,那些频繁出现的词会掩盖那些很少出现但是更有意义的词的频率。为了重新计算特征的计数权重,以便转化为适合分类器使用的浮点值,通常都会进行tf-idf转换。词重要性度量一般使用文本挖掘的启发式方法:TF-IDF。这是一个最初为信息检索(作为搜索引擎结果的排序功能)开发的词加权机制,在文档分类和聚类中也是非常有用的。

词频(term frequency,TF)指的是某一个给定的词语在该文件中出现的频率。这个数字是对词数(term count)的归一化,以防止它偏向长的文件(上面的TF计算公式就进行了这种处理;同一个词语在长文件里可能会比短文件有更高的词数,而不管该词语重要与否。)对于在某一特定文件里的词语来说,它的重要性可表示为:
逆向文件频率(inverse document frequency,IDF)是一个词语普遍重要性的度量(不同词重要性的度量)。某一特定词语的IDF,可以由总文件数目除以包含该词语之文件的数目,再将得到的商取对数得到。


sklearn实现TF-IDF有两种方法:

(1)
text.TfidfTransformer类实现了这种正态化:

from sklearn.feature_extraction.text import TfidfTransformer
transformer = TfidfTransformer()

结果:
TfidfTransformer(norm=...'l2', smooth_idf=True, sublinear_tf=False,
                 use_idf=True)


同样对于每个参数的详细解释,请参见参考文档。

让我们用下面的计数作为例子。第一个词出现每次100%出现因此不是携带的信息不多。另外两个特征只在不到50%的时间出现,因此,对文档内容的代表能力可能更强一些:

counts = [[3, 0, 1],
           [2, 0, 0],
           [3, 0, 0],
           [4, 0, 0],
           [3, 2, 0],
           [3, 0, 2]]
tfidf = transformer.fit_transform(counts)
tfidf.toarray()
                    
结果: 
array([[ 0.85...,  0.  ...,  0.52...],
       [ 1.  ...,  0.  ...,  0.  ...],
       [ 1.  ...,  0.  ...,  0.  ...],
       [ 1.  ...,  0.  ...,  0.  ...],
       [ 0.55...,  0.83...,  0.  ...],
       [ 0.63...,  0.  ...,  0.77...]])


每一行被正态化为单位的欧几里得范数。由fit方法计算的每个特征的权重存储在model属性中:

transformer.idf_                       

结果:
array([ 1. ...,  2.25...,  1.84...])

(2)
由于tf-idf经常用于文本特征,因此有另一个类称为TfidfVectorizer,将CountVectorizer和TfidfTransformer的所有选项合并在一个模型中:

from sklearn.feature_extraction.text import TfidfVectorizer
vectorizer = TfidfVectorizer(min_df=1)
vectorizer.fit_transform(corpus)

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值