scikit-learn:将数据拟合成块与一次拟合所有数据
scikit-learn: fitting data into chunks vs fitting it all at once
我正在使用 scikit-learn 构建一个分类器,它适用于(有点大的)文本文件。我现在需要一个简单的词袋特征,因此我尝试使用 TfidfVectorizer/HashingVectorizer/CountVectorizer 来获取特征向量。
但是,一次处理整个列车数据以获得特征向量会导致 numpy/scipy 中的内存错误(取决于我使用的向量化器)。所以我的问题是:
从原始文本中提取文本特征时:如果我将数据以块的形式拟合到矢量化器中,是否与一次拟合整个数据相同?
用代码来说明,如下:
vectoriser = CountVectorizer() # or TfidfVectorizer/HashingVectorizer
train_vectors = vectoriser.fit_transform(train_data)
不同于以下内容:
vectoriser = CountVectorizer() # or TfidfVectorizer/HashingVectorizer
start = 0
while start < len(train_data):
vectoriser.fit(train_data[start:(start+500)])
start += 500
train_vectors = vectoriser.transform(train_data)
提前致谢,如果这个问题完全迟钝,我们深表歉意。
我不是文本特征提取专家,但基于文档和我的其他分类器基础经验:
If I do several fits on chunks of the training data, will that be the
same as fitting the entire data at once?
您不能直接合并提取的特征,因为您将获得 不同的 重要性,即 weights
来自不同块的相同 token/word 不同 与块中其他单词的比例,用 不同 键表示。
你可以使用任何特征提取方法,我认为结果的有用性取决于任务。
但是如果你可以使用不同块的不同特征对相同数据进行分类。一旦你用相同的特征提取方法(或者你也可以使用不同的提取方法)获得的特征得到几个不同的输出,你就可以将它们用作"merging" 机制,如 bagging
、boosting
等。
实际上,在大多数情况下,在完成上述整个过程后,您将获得 更好的 最终输出,而不是将完整文件放入一个 "full-featured" 中,甚至是一个简单的分类器。
这取决于您使用的矢量化器。
CountVectorizer 计算文档中单词的出现次数。
它为每个文档输出一个 (n_words, 1)
向量,其中包含每个单词在文档中出现的次数。 n_words
是 文档中的单词总数 (也就是词汇量)。
它还适合一个词汇表,以便您可以反省模型(看看哪个词很重要,等等)。您可以使用 vectorizer.get_feature_names()
.
查看它
当您将它安装在您的前 500 个文档中时,词汇表将仅由这 500 个文档中的单词组成。假设有 30k 个,fit_transform
输出一个 500x30k
稀疏矩阵。
现在你 fit_transform
再次使用 500 个下一个文档,但它们只包含 29k 个单词,所以你得到一个 500x29k
矩阵...
现在,您如何对齐矩阵以确保所有文档具有一致的表示?
我现在想不出一个简单的方法来做到这一点。
使用 TfidfVectorizer 你还有另一个问题,那就是反向文档频率:为了能够计算文档频率,你需要一次查看所有文档。
然而,TfidfVectorizer
只是一个 CountVectorizer
后跟一个 TfIdfTransformer
,所以如果你设法获得 CountVectorizer
的正确输出,你就可以应用 TfIdfTransformer
关于数据。
使用 HashingVectorizer 情况不同:这里没有词汇。
In [51]: hvect = HashingVectorizer()
In [52]: hvect.fit_transform(X[:1000])
<1000x1048576 sparse matrix of type '<class 'numpy.float64'>'
with 156733 stored elements in Compressed Sparse Row format>
这里前1000个文档中没有1M+个不同的词,但是我们得到的矩阵有1M+列。
HashingVectorizer
不会将单词存储在内存中。这使它的内存效率更高,并确保矩阵 returns 始终具有相同的列数 。
因此,您不会遇到与此处 CountVectorizer
相同的问题。
这可能是您描述的批处理的最佳解决方案。有几个缺点,即您无法获得 idf 权重,并且您不知道单词与特征之间的映射。
HashingVectorizer documentation references an example that does out-of-core classification on text data。它可能有点乱,但它可以满足您的需求。
希望这对您有所帮助。
编辑:
如果你有太多数据,HashingVectorizer
是可行的方法。
如果您仍然想使用 CountVectorizer
,一个可能的解决方法是自己调整词汇表并将其传递给您的向量化器,这样您只需要调用 tranform
.
这是一个你可以改编的例子:
import re
from sklearn.datasets import fetch_20newsgroups
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfTransformer
news = fetch_20newsgroups()
X, y = news.data, news.target
现在行不通的方法:
# Fitting directly:
vect = CountVectorizer()
vect.fit_transform(X[:1000])
<1000x27953 sparse matrix of type '<class 'numpy.int64'>'
with 156751 stored elements in Compressed Sparse Row format>
注意我们得到的矩阵的大小。
拟合词汇 'manually':
def tokenizer(doc):
# Using default pattern from CountVectorizer
token_pattern = re.compile('(?u)\b\w\w+\b')
return [t for t in token_pattern.findall(doc)]
stop_words = set() # Whatever you want to have as stop words.
vocabulary = set([word for doc in X for word in tokenizer(doc) if word not in stop_words])
vectorizer = CountVectorizer(vocabulary=vocabulary)
X_counts = vectorizer.transform(X[:1000])
# Now X_counts is:
# <1000x155448 sparse matrix of type '<class 'numpy.int64'>'
# with 149624 stored elements in Compressed Sparse Row format>
#
X_tfidf = tfidf.transform(X_counts)
在您的示例中,您需要先构建 entire 矩阵 X_counts(对于所有文档),然后再应用 tfidf 转换。
我正在使用 scikit-learn 构建一个分类器,它适用于(有点大的)文本文件。我现在需要一个简单的词袋特征,因此我尝试使用 TfidfVectorizer/HashingVectorizer/CountVectorizer 来获取特征向量。
但是,一次处理整个列车数据以获得特征向量会导致 numpy/scipy 中的内存错误(取决于我使用的向量化器)。所以我的问题是:
从原始文本中提取文本特征时:如果我将数据以块的形式拟合到矢量化器中,是否与一次拟合整个数据相同?
用代码来说明,如下:
vectoriser = CountVectorizer() # or TfidfVectorizer/HashingVectorizer
train_vectors = vectoriser.fit_transform(train_data)
不同于以下内容:
vectoriser = CountVectorizer() # or TfidfVectorizer/HashingVectorizer
start = 0
while start < len(train_data):
vectoriser.fit(train_data[start:(start+500)])
start += 500
train_vectors = vectoriser.transform(train_data)
提前致谢,如果这个问题完全迟钝,我们深表歉意。
我不是文本特征提取专家,但基于文档和我的其他分类器基础经验:
If I do several fits on chunks of the training data, will that be the same as fitting the entire data at once?
您不能直接合并提取的特征,因为您将获得 不同的 重要性,即 weights
来自不同块的相同 token/word 不同 与块中其他单词的比例,用 不同 键表示。
你可以使用任何特征提取方法,我认为结果的有用性取决于任务。
但是如果你可以使用不同块的不同特征对相同数据进行分类。一旦你用相同的特征提取方法(或者你也可以使用不同的提取方法)获得的特征得到几个不同的输出,你就可以将它们用作"merging" 机制,如 bagging
、boosting
等。
实际上,在大多数情况下,在完成上述整个过程后,您将获得 更好的 最终输出,而不是将完整文件放入一个 "full-featured" 中,甚至是一个简单的分类器。
这取决于您使用的矢量化器。
CountVectorizer 计算文档中单词的出现次数。
它为每个文档输出一个 (n_words, 1)
向量,其中包含每个单词在文档中出现的次数。 n_words
是 文档中的单词总数 (也就是词汇量)。
它还适合一个词汇表,以便您可以反省模型(看看哪个词很重要,等等)。您可以使用 vectorizer.get_feature_names()
.
当您将它安装在您的前 500 个文档中时,词汇表将仅由这 500 个文档中的单词组成。假设有 30k 个,fit_transform
输出一个 500x30k
稀疏矩阵。
现在你 fit_transform
再次使用 500 个下一个文档,但它们只包含 29k 个单词,所以你得到一个 500x29k
矩阵...
现在,您如何对齐矩阵以确保所有文档具有一致的表示?
我现在想不出一个简单的方法来做到这一点。
使用 TfidfVectorizer 你还有另一个问题,那就是反向文档频率:为了能够计算文档频率,你需要一次查看所有文档。
然而,TfidfVectorizer
只是一个 CountVectorizer
后跟一个 TfIdfTransformer
,所以如果你设法获得 CountVectorizer
的正确输出,你就可以应用 TfIdfTransformer
关于数据。
使用 HashingVectorizer 情况不同:这里没有词汇。
In [51]: hvect = HashingVectorizer()
In [52]: hvect.fit_transform(X[:1000])
<1000x1048576 sparse matrix of type '<class 'numpy.float64'>'
with 156733 stored elements in Compressed Sparse Row format>
这里前1000个文档中没有1M+个不同的词,但是我们得到的矩阵有1M+列。
HashingVectorizer
不会将单词存储在内存中。这使它的内存效率更高,并确保矩阵 returns 始终具有相同的列数 。
因此,您不会遇到与此处 CountVectorizer
相同的问题。
这可能是您描述的批处理的最佳解决方案。有几个缺点,即您无法获得 idf 权重,并且您不知道单词与特征之间的映射。
HashingVectorizer documentation references an example that does out-of-core classification on text data。它可能有点乱,但它可以满足您的需求。
希望这对您有所帮助。
编辑:
如果你有太多数据,HashingVectorizer
是可行的方法。
如果您仍然想使用 CountVectorizer
,一个可能的解决方法是自己调整词汇表并将其传递给您的向量化器,这样您只需要调用 tranform
.
这是一个你可以改编的例子:
import re
from sklearn.datasets import fetch_20newsgroups
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfTransformer
news = fetch_20newsgroups()
X, y = news.data, news.target
现在行不通的方法:
# Fitting directly:
vect = CountVectorizer()
vect.fit_transform(X[:1000])
<1000x27953 sparse matrix of type '<class 'numpy.int64'>'
with 156751 stored elements in Compressed Sparse Row format>
注意我们得到的矩阵的大小。
拟合词汇 'manually':
def tokenizer(doc):
# Using default pattern from CountVectorizer
token_pattern = re.compile('(?u)\b\w\w+\b')
return [t for t in token_pattern.findall(doc)]
stop_words = set() # Whatever you want to have as stop words.
vocabulary = set([word for doc in X for word in tokenizer(doc) if word not in stop_words])
vectorizer = CountVectorizer(vocabulary=vocabulary)
X_counts = vectorizer.transform(X[:1000])
# Now X_counts is:
# <1000x155448 sparse matrix of type '<class 'numpy.int64'>'
# with 149624 stored elements in Compressed Sparse Row format>
#
X_tfidf = tfidf.transform(X_counts)
在您的示例中,您需要先构建 entire 矩阵 X_counts(对于所有文档),然后再应用 tfidf 转换。