1. 词袋模型 (Bag of Words, BOW)

文本分析是机器学习算法的一个主要应用领域。然而,原始数据的这些符号序列不能直接提供给算法进行训练,因为大多数算法期望的是固定大小的数字特征向量,而不是可变长度的原始文本。

为了解决这个问题,scikit-learn提供了从文本内容中提取数字特征的常见方法,即:

  1. tokenizing: 标记字符串并为每个可能的token提供整数id,例如使用空白和标点作为token分隔符;(分词标记)
  2. counting: 统计每个文档中出现的token次数;(统计词频)
  3. normalizing: 通过减少大多数样本/文档中都会出现的一般性标记来进行标准化和加权。(标准化/归一化)

在此方案中,特征和样本定义如下:

每个独立token出现的频率(已标准化或未标准化)作为特征。

给定文档的所有token频率的向量作为多元样本。

因此,文本语料库可以由矩阵表示,每一行代表一个文本,每一列代表一个token(例如一个单词)。

向量化:将文本集合转换为数字特征向量的一般过程。

这种方法(tokenizing,counting和normalizing)称为“词袋”或“n-gram”模型。 即只通过单词频率来描述文档,而完全忽略文档中单词的相对位置信息。

2. 稀疏表示

由于大多数文本通常只使用语料库中的很小一部分单词,因此生成的矩阵将具有许多为零的特征值(通常超过99%)。

例如,有一个文本集合,包含一万个文本(邮件等),它使用的词汇表大约为十万个词,而其中每个文档单独使用的词只有100到1000个。

为了能够将这样的矩阵存储在内存中并且加快矩阵/向量的代数运算,实现上通常会使用稀疏表示,在scipy.sparse包中有实现方法。

3. 常用的Vectorizer的用法

CountVectorizer在单个类中同时实现tokenizing和counting:

from sklearn.feature_extraction.text import CountVectorizer

该模型具有许多参数,但是默认值是相当合理的:

vectorizer = CountVectorizer()
vectorizer

CountVectorizer(

analyzer='word', binary=False, decode_error='strict',

dtype=<class '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)
X

<4x9 sparse matrix of type '<class 'numpy.int64'>'

with 19 stored elements in Compressed Sparse Row format>

其默认参数配置是通过提取单词(至少2个字母)来标记字符串。也可以通过显式请求来查看这一步骤:

analyze = vectorizer.build_analyzer()
analyze("This is a text document to analyze.")

['this', 'is', 'text', 'document', 'to', 'analyze']

在拟合过程中,将由分析器找到的每一项分配一个唯一的整数索引,该索引对应于所得矩阵中的一列。 可以按以下方式检索这些列:

vectorizer.get_feature_names()

['and', 'document', 'first', 'is', 'one', 'second', 'the', 'third', 'this']

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('first')

2

因此,在之后对transform方法的调用中,训练语料库中未出现的单词将被完全忽略:

vectorizer.transform(['Something completely new.']).toarray()

array([[0, 0, 0, 0, 0, 0, 0, 0, 0]])

注意,在前一语料库中,第一个文档和最后一个文档具有完全相同的词,因此被编码为同样的向量。 但这就失去了最后一个文档是疑问句的信息。

为了保留一些局部信息,我们可以提取单词的1-gram(单个单词)以外的2-gram信息:

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'])

因此,这个Vectorizer提取的词汇量要更大,而且现在可以解决局部定位模式中的编码歧义:

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])

4. 停用词

停用词是指诸如“and”,“the”,“him”之类的词,它们被认为在表示文本内容方面没有提供任何信息,可以将其删除以避免其影响预测效果。

但是,有时候,类似的单词对于预测很有用,例如在对写作风格或语言个性进行分类时。

请谨慎选择停用词列表。 通用的停用词列表也可能包含对某些特定任务(例如计算机领域)非常有用的词。

此外,还应该确保停用词列表的预处理和标记化与Vectorizer中使用的预处理和标记化相同。

CountVectorizer的默认标记器将单词"we've"分为we和ve,因此,如果“we've”在stop_words中,而ve则没有,则在转换后的文本中会保留ve。

我们的Vectorizer将尝试识别并警告某些不一致之处。

TF-IDF模型

在大型文本语料库中,会经常出现一些单词(例如英语中的“ the”,“ a”,“ is”),而这些单词几乎不包含关于文档实际内容的有意义的信息。

如果我们将直接计数数据不加处理地提供给分类器,那么那些高频词会影响低频但更有意义的词的出现概率。

为了将计数特征重新加权为适合分类器使用的浮点值,通常使用tf–idf变换。

tf表示词频,而tf–idf表示词频乘以逆文档频率:

\(\text{tf-idf(t,d)}=\text{tf(t,d)} \times \text{idf(t)}\)

TfidfTransformer的默认参数为,TfidfTransformer(norm='l2', use_idf=True, smooth_idf=True, sublinear_tf=False)。

词频,即一个单词在文档中出现的频率,乘以idf:

\(\text{idf}(t) = \log{\frac{1 + n}{1+\text{df}(t)}} + 1\)

n是文本集中文本总数,df(t)是包含t词的文本数,然后将所得的tf-idf向量通过欧几里得范数归一化:

\(v_{norm} = \frac{v}{||v||_2} = \frac{v}{\sqrt{v{_1}^2 + v{_2}^2 + \dots + v{_n}^2}}\)

这最初是信息检索的词加权方案,作为搜索引擎结果的排名方法,目前也在文档分类和聚类中广泛应用。

以下各节包含进一步的说明和示例,这些示例说明了如何精确计算tf-idf,以及在scikit-learn的TfidfTransformer和TfidfVectorizer中怎样计算的。

与标准教科书的符号稍微不同,idf定义为:

\(\text{idf}(t) = \log{\frac{n}{1+\text{df}(t)}}.\)

在TfidfTransformer和TfidfVectorizer中设置smooth_idf=False,将“ 1”计数添加到IDF中,而不是IDF的分母中:

\(\text{idf}(t) = \log{\frac{n}{\text{df}(t)}} + 1\)

这一规范化由TfidfTransformer类实现:

from sklearn.feature_extraction.text import TfidfTransformer

transformer = TfidfTransformer(smooth_idf=False)
transformer

TfidfTransformer(norm='l2', smooth_idf=False, 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

<6x3 sparse matrix of type '<class 'numpy.float64'>'

with 9 stored elements in Compressed Sparse Row format>

tfidf.toarray()

array([[0.81940995, 0. , 0.57320793],

[1. , 0. , 0. ],

[1. , 0. , 0. ],

[1. , 0. , 0. ],

[0.47330339, 0.88089948, 0. ],

[0.58149261, 0. , 0.81355169]])

每行均经过单位欧几里得范数计算以进行标准化:

\(v_{norm} = \frac{v}{||v||_2} = \frac{v}{\sqrt{v{_1}^2 + v{_2}^2 + \dots + v{_n}^2}}\)

例如,我们可以如下计算counts数组中第一个文档中第一项的tf-idf:

\(n = 6\)

\(\text{df}(t)_{\text{term1}} = 6\)

\(\text{idf}(t)_{\text{term1}} = \log \frac{n}{\text{df}(t)} + 1 = \log(1)+1 = 1\)

\(\text{tf-idf}_{\text{term1}} = \text{tf} \times \text{idf} = 3 \times 1 = 3\)

现在,如果我们对文档中剩余的2个词重复此计算,我们将得到:

\(\text{tf-idf}_{\text{term2}} = 0 \times (\log(6/1)+1) = 0\)

\(\text{tf-idf}_{\text{term3}} = 1 \times (\log(6/2)+1) \approx 2.0986\)

原始的tf-idf向量:

\(\text{tf-idf}_{\text{raw}} = [3, 0, 2.0986].\)

然后,应用欧几里得(L2)范数,我们为文本1获得以下tf-idfs:

\(\frac{[3, 0, 2.0986]}{\sqrt{\big(3^2 + 0^2 + 2.0986^2\big)}} = [ 0.819, 0, 0.573].\)

此外,默认参数smooth_idf = True将“ 1”添加到分子和分母,就好像看到一个额外的文档恰好包含一次集合中的每个术语一次,从而避免了分母为零的问题:

\(\text{idf}(t) = \log{\frac{1 + n}{1+\text{df}(t)}} + 1\)

使用此修改,文档1中第3项的tf-idf更改为1.8473:

\(\text{tf-idf}_{\text{term3}} = 1 \times \log(7/3)+1 \approx 1.8473\)

并且L2归一化的tf-idf变为:

\(\frac{[3, 0, 1.8473]}{\sqrt{\big(3^2 + 0^2 + 1.8473^2\big)}} = [0.8515, 0, 0.5243]\)

transformer = TfidfTransformer()
transformer.fit_transform(counts).toarray()

array([[0.85151335, 0. , 0.52433293],

[1. , 0. , 0. ],

[1. , 0. , 0. ],

[1. , 0. , 0. ],

[0.55422893, 0.83236428, 0. ],

[0.63035731, 0. , 0.77630514]])

由fit方法调用计算出的每个特征的权重存储在model属性中:

transformer.idf_

array([1. , 2.25276297, 1.84729786])

由于tf–idf通常用于文本特征,因此还有另一个名为TfidfVectorizer的类,它将CountVectorizer和TfidfTransformer的所有选项组合在一个模型中:

from sklearn.feature_extraction.text import TfidfVectorizer

vectorizer = TfidfVectorizer()
vectorizer.fit_transform(corpus)

<4x9 sparse matrix of type '<class 'numpy.float64'>'

with 19 stored elements in Compressed Sparse Row format>

尽管tf–idf归一化通常非常有用,但是在某些情况下,二进制频率标记法可能会提供更好的特性。 这可以通过使用CountVectorizer的二进制参数来实现。

特别是,某些估计量(例如Bernoulli Naive Bayes)明确地对离散的布尔型随机变量建模。 同样,很短的文本可能带有tf–idf值的噪声,而二进制出现信息则更稳定。

通常,调整特征提取参数的最佳方法是使用交叉验证的网格搜索,例如用分类器将特征提取器进行流水线化。

参考资料

sklearn.feature_extraction.text文档

【sklearn文本特征提取】词袋模型/稀疏表示/停用词/TF-IDF模型的更多相关文章

  1. sklearn文本特征提取

    http://cloga.info/2014/01/19/sklearn_text_feature_extraction/ 文本特征提取 词袋(Bag of Words)表征 文本分析是机器学习算法的 ...

  2. Feature extraction - sklearn文本特征提取

    http://blog.csdn.net/pipisorry/article/details/41957763 文本特征提取 词袋(Bag of Words)表征 文本分析是机器学习算法的主要应用领域 ...

  3. sklearn文本特征提取——TfidfVectorizer

    什么是TF-IDF IF-IDF(term frequency-inverse document frequency)词频-逆向文件频率.在处理文本时,如何将文字转化为模型可以处理的向量呢?IF-ID ...

  4. 文本分类学习(三) 特征权重(TF/IDF)和特征提取

    上一篇中,主要说的就是词袋模型.回顾一下,在进行文本分类之前,我们需要把待分类文本先用词袋模型进行文本表示.首先是将训练集中的所有单词经过去停用词之后组合成一个词袋,或者叫做字典,实际上一个维度很大的 ...

  5. 机器学习入门-文本数据-构造词频词袋模型 1.re.sub(进行字符串的替换) 2.nltk.corpus.stopwords.words(获得停用词表) 3.nltk.WordPunctTokenizer(对字符串进行分词操作) 4.np.vectorize(对函数进行向量化) 5. CountVectorizer(构建词频的词袋模型)

    函数说明: 1. re.sub(r'[^a-zA-Z0-9\s]', repl='', sting=string)  用于进行字符串的替换,这里我们用来去除标点符号 参数说明:r'[^a-zA-Z0- ...

  6. 机器学习入门-文本数据-构造Tf-idf词袋模型(词频和逆文档频率) 1.TfidfVectorizer(构造tf-idf词袋模型)

    TF-idf模型:TF表示的是词频:即这个词在一篇文档中出现的频率 idf表示的是逆文档频率, 即log(文档的个数/1+出现该词的文档个数)  可以看出出现该词的文档个数越小,表示这个词越稀有,在这 ...

  7. 使用Python中的NLTK和spaCy删除停用词与文本标准化

    概述 了解如何在Python中删除停用词与文本标准化,这些是自然语言处理的基本技术 探索不同的方法来删除停用词,以及讨论文本标准化技术,如词干化(stemming)和词形还原(lemmatizatio ...

  8. 机器学习之路:python 文本特征提取 CountVectorizer, TfidfVectorizer

    本特征提取: 将文本数据转化成特征向量的过程 比较常用的文本特征表示法为词袋法词袋法: 不考虑词语出现的顺序,每个出现过的词汇单独作为一列特征 这些不重复的特征词汇集合为词表 每一个文本都可以在很长的 ...

  9. ElasticSearch 2 (24) - 语言处理系列之停用词:性能与精度

    ElasticSearch 2 (24) - 语言处理系列之停用词:性能与精度 摘要 在信息检索早期,磁盘和内存相较我们今天的使用只是很小的一部分.将索引空间保持在一个较小的水平是至关重要的,节省每个 ...

随机推荐

  1. 实验20:IPv6

    实验17-1: IPv6 静态路由 Ø    实验目的通过本实验可以掌握(1)启用IPv6 流量转发(2)配置IPv6 地址(3)IPv6 静态路由配置和调试(4)IPv6 默认路由配置和调试 Ø   ...

  2. 实验8:路由器IOS升级2

    IOS 升级 在介绍CISCO路由器IOS升级方法前,有必要对Cisco路由器的存储器的相关知识作以简单介绍.路由器与计算机相似,它也有内存和操作系统.在Cisco路由器中,其操作系统叫做互连网操作系 ...

  3. 自学Java第二章——《Java的基础语法》

    2.1 标识符 简单的说,凡是程序员自己命名的部分都可以称为标识符. 即给类.变量.方法.包等命名的字符序列,称为标识符. 1.标识符的命名规则 (1)Java的标识符只能使用26个英文字母大小写,0 ...

  4. 测试用例设计:PICT的安装及使用

    一.下载与安装 打开百度网页,搜索PICT,即可找到许多下载链接,点击这里,下载到桌面,点击安装.一直NEXT,安装路径保存在C盘: 二.PICT 使用 1.找到安装目录,即可看到以下内容 2.创建t ...

  5. Codeforces_462_B

    http://codeforces.com/problemset/problem/462/B 简单的贪心,排序即可看出来. #include<cstdio> #include<ios ...

  6. 《Python学习手册 第五版》 -第10章 Python语句简介

    前面在开始讲解数据类型的时候,有说过Python的知识结构,在此重温一下 Python知识结构: 程序由模块组成 模块包含语句 语句包含表达式 表达式创建并处理对象 关于知识结构,前面已经说过我自己的 ...

  7. java中list的sort()功能如何使用?

    排序时正序/倒序处理起来可能会混淆,可以用更简单的方法.可以使用java.util自带的比较器来做 Comparator.comparingInt(Integer::intValue).reverse ...

  8. Generator - Python 生成器

    Generator, python 生成器, 先熟悉一下儿相关定义, generator function 生成器函数, 生成器函数是一个在定义体中存有 'yield' 关键字的函数. 当生成器函数被 ...

  9. [redis读书笔记] 第二部分 单机数据库 RDB持久化

    内存中的rdb是会存为文件以做到RDB持久化的.RDB文件时一个二进制文件. 一 载入与存储 文件的载入是在server启动时进行的(rdbload()),因为AOF的更新频率比RDB高,所以如果AO ...

  10. Apache开启GZIP 压缩网页

    首先我们先了解Apache Gzip的相关资料. 一.gzip介绍 Gzip是一种流行的文件压缩算法,现在的应用十分广泛,尤其是在Linux平台.当应用Gzip压缩到一个纯文本文件时,效果是非常明显的 ...