用scikit-learn的三种词袋(BoW)生成方法为机器学习任务准备文本数据
本文为翻译博客,详见: 原文
在使用文本数据建立预测模型之前,需要做特别的数据预处理工作。
文本必须先进行分词(tokenization)操作,然后进行特征提取,即向量化(vectorization)操作,将分词后的词编码成整形或浮点数据作为机器学习算法的输入。
scikit-learn库提供了易用的工具来对文本数据做分词和特征提取。
学完这个教程后,你将会知道:
- 如何使用
CountVectorizer
将文本转化成词统计向量 - 如何使用
TfidfVectorizer
将文本转化成词频向量 - 如何使用
HashingVectorizer
将文本转化成唯一数字向量
Bag-of-Words 模型
在进行模型训练之前,我们需要将文本转化成数字。如果我们对文档进行分类,那么每一个文档都是一个输入,并且有一个对应的输出类别。因为算法都是将数字向量作为输入,所以我们需要将文档转换成指定长度的数字向量。
一种简单有效的模型叫:Bag-of-Words(BoW)模型。这个模型之所以简单,是因为它将单词之间的顺序关系全部丢弃,只关注文档中单词出现的次数。该方法为每个单词分配一个唯一编号,这样一个文档就能够被编码成与已知词汇数量相同长度的向量。向量中每一个位置上的值就是其编号对应单词在文档中出现的次数。
BoW模型有许多优化版本,优化方向包括:1)对词义的更好阐述 2)对向量中每个词的编码方式
scikit-learn库中提供了三种可用的解决方案,下面作简要介绍。
用CountVectorizer统计词频
CountVectorier 不仅提供了一个简单的方法为一系列文本文件进行分词操作,从而用这些已有的词建立一个词汇表,同时还能用这个词汇表对新文档编码。
使用步骤如下:
- 创建
CountVectorizer
类的一个实例 - 调用
fit()
函数从一个或多个文件中学习一个词汇表 - 调用
transform()
函数,将一个文件编码成一个向量
返回一个与词汇表同长的向量,向量中每个位置对应词汇表该位置上的词在当前文件中出现的次数。因为向量中包含很多的0,所以很稀疏,Python在scipy.sparse 包中提供了一个有效的方法处理稀疏向量。
上述步骤3中的transform()
函数返回一个稀疏向量,为了直观理解,我们可以用toarray()
将稀疏向量转化成numpy数组。
下面是用CountVectorizer
进行分词、建立词汇表、编码一个文件的实例。
导入CountVectorizer
库和文件:
from sklearn.feature_extraction.text import CountVectorizer
text = ["The quick brown fox jumped over the lazy dog."]
建立词汇表:
# 创建transform
vectorizer = CountVectorizer()
# 分词并建立词汇表
vectorizer.fit(text)
# 结果输出
print vectorizer.vocabulary_
输出词汇表为:
{u'brown': 0, u'lazy': 4, u'jumped': 3, u'over': 5, u'fox': 2, u'dog': 1, u'quick': 6, u'the': 7}
按照词汇表,对文件进行编码:
vector = vectorizer.transform(text)
# 输出编码后的向量信息
print vector.shape
print type(vector)
print vector.toarray()
输出为:
(1, 8)
<class 'scipy.sparse.csr.csr_matrix'>
[[1 1 1 1 1 1 1 2]]
需要重点指出的是,相同的方法可以对包含词汇表之外词汇的文件进行编码,这些词汇表中不存在的词将在返回的向量中被忽略,举个例子:
# 对另一个文件进行编码
text2 = ["the puppy"]
vector = vectorizer.transform(text2)
print vector.toarray()
输出为:
[[0 0 0 0 0 0 0 1]]
只有词汇表中存在的第七个词”the”出现一次被编码了,词汇表中不存在的“puppy”直接被忽略。
用TfidfVectorizer进行词频统计
词数统计是一个好的着手点,但是很基础。简单的词数统计存在一个问题,比如:一些像“the”这样的单词可能出现很多次,虽然在编码的向量中他们对应的数值很大,但并没有什么意义。
一种解决方案是计算词频,TF-IDF 是眼下最流行的词频计算方法,它是“Term Frequency - Inverse Documentation Frequency” 首字母的缩写。
- Term Frequency :指在一个文件中一个指定单词出现的次数
- Inverse Document Frequency :它降低在多个文件中均出现的单词的重要性
本文不作数学计算解释,可参考weki 。TF-IDF是词频得分,它更偏重于某个文件中新颖的单词,比如:在一个文件中多次出现,但在其他文件很少出现的单词。
TfidfVectorizer 可以对文件进行分词、学习词汇表、计算词频得分,使你能够编码新文件。如果你已经学习了一个CountVectorizer
,你可以只需用TfidfTransformer
计算inverse document frequency ,然后编码新文件。
TfidfVectorizer的使用步骤和CountVectorizer一样:创建、fit操作、transform操作。
下面是用TfidfVectorizer对3个小文件学习词汇表和inverse document frequencies,然后编码其中一篇文件的例子:
先分词并建立词汇表,计算idf得分。
from sklearn.feature_extraction.text import TfidfVectorizer
# 文件列表
text = ["The quick brown fox jumped over the lazy dog.",
"The dog.",
"The fox"]
# 建立transform
vectorizer = TfidfVectorizer()
# 分词,建立词汇表
vectorizer.fit(text)
# 输出结果
print vectorizer.vocabulary_
print vectorizer.idf_
输出结果:
{u'brown': 0, u'lazy': 4, u'jumped': 3, u'over': 5, u'fox': 2, u'dog': 1, u'quick': 6, u'the': 7}
[ 1.69314718 1.28768207 1.28768207 1.69314718 1.69314718 1.69314718
1.69314718 1. ]
编码其中一个文件:
# 编码文件
vector = vectorizer.transform([text[0]])
# 输出编码结果
print vector.shape
print vector.toarray()
输出结果:
(1, 8)
[[ 0.36388646 0.27674503 0.27674503 0.36388646 0.36388646 0.36388646
0.36388646 0.42983441]]
这些得分向量被规范化到0-1之间,可以直接作为机器学习任务的输入。
用HashingVectorizer进行Hashing操作
词数和词频一般很有用,但这些方法有个限制——词汇表可能很大。
如果词汇表很大,那么就需要大的向量来编码文件,对存储造成压力,进而拖慢算法。
一个聪明的解决方案是对单词进行单向哈希操作,将它们转换成整数。聪明的地方在于不需要词汇表,你可以选择一个指定任意长度的向量。该方法的缺点是哈希是一个单向函数,所以不能将编码好的词变回原来的词(在大多数监督学习任务中没啥影响)。
HashingVectorizer 类步骤是先对词进行hash操作,然后对指定文件进行分词和编码。
下面的例子是使用HashVectorizer对单个文件进行操作。选择一个任意定长为20的向量,这对应了哈希函数的范围,选择的哈希长度太小(比如20)可能导致哈希冲突,这需要我们根据估计的词汇长度计算冲突的可能性,并选择合适的哈希长度。
注意这种向量化方法不需要在训练文件数据上调用fit函数,在实例化之后,它可以直接被用来编码文件。
from sklearn.feature_extraction.text import HashingVectorizer
# 文件列表
text = ["The quick brown fox jumped over the lazy dog."]
# 创建transform
vectorizer = HashingVectorizer(n_features=20)
# 编码文件
vector = vectorizer.transform(text)
# 输出编码后的结果
print vector.shape
print vector.toarray()
运行例子,将样本文件编码成一个有20个元素的稀疏数组。
文件编码向量中的值对应于将词数规范化到-1到1之间所得结果,也可以通过改变默认值修改范围。
上例所得结果如下:
(1, 20)
[[ 0. 0. 0. 0. 0. 0.33333333
0. -0.33333333 0.33333333 0. 0. 0.33333333
0. 0. 0. -0.33333333 0. 0.
-0.66666667 0. ]]
总结
这个教程讲述了如何用scikit-learn为机器学习任务准备文本数据(文件预处理),只讲了简单的实现方式,有待深挖。