如何有效地使用 CountVectorizer 来组合目录中所有文件的 ngram 计数?
Posted
技术标签:
【中文标题】如何有效地使用 CountVectorizer 来组合目录中所有文件的 ngram 计数?【英文标题】:How to efficiently use CountVectorizer to get ngram counts for all files in a directory combined? 【发布时间】:2020-01-09 16:13:22 【问题描述】:我的目录中有大约 10k .bytes
文件,我想使用计数矢量化器来获取 n_gram 计数(即适合训练并转换测试集)。
在这 10k 个文件中,我有 8k 个文件作为训练文件,2k 个文件作为测试文件。
files =
['bfiles/GhHS0zL9cgNXFK6j1dIJ.bytes',
'bfiles/8qCPkhNr1KJaGtZ35pBc.bytes',
'bfiles/bLGq2tnA8CuxsF4Py9RO.bytes',
'bfiles/C0uidNjwV8lrPgzt1JSG.bytes',
'bfiles/IHiArX1xcBZgv69o4s0a.bytes',
...............................
...............................]
print(open(files[0]).read())
'A4 AC 4A 00 AC 4F 00 00 51 EC 48 00 57 7F 45 00 2D 4B 42 45 E9 77 51 4D 89 1D 19 40 30 01 89 45 E7 D9 F6 47 E7 59 75 49 1F ....'
我无法执行以下操作并将所有内容都传递给 CountVectorizer
。
file_content = []
for file in file:
file_content.append(open(file).read())
我不能将每个文件文本附加到一个大的嵌套文件列表中,然后使用CountVectorizer
,因为所有组合的文本文件大小超过 150gb。我没有资源来做这件事,因为CountVectorizer
使用大量内存。
我需要一种更有效的方法来解决这个问题,有没有其他方法可以实现我想要的,而无需一次将所有内容加载到内存中。非常感谢任何帮助。
我所能实现的只是读取一个文件,然后使用CountVectorizer
,但我不知道如何实现我想要的。
cv = CountVectorizer(ngram_range=(1, 4))
temp = cv.fit_transform([open(files[0]).read()])
temp
<1x451500 sparse matrix of type '<class 'numpy.int64'>'
with 335961 stored elements in Compressed Sparse Row format>
【问题讨论】:
很确定fit
接受任意可迭代,所以不要实现一个巨大的列表,只需使用生成器
@juanpa.arrivillaga 我不知道如何使用生成器来解决这个问题。如果不是太多,您可以提供一个我可以使用的示例答案吗?
试试 HashingVectorizer....
@qaiser HashingVectorizer 存在一个问题,即“不同的标记可以映射到相同的特征索引。但是在实践中,如果 n_features 足够大(例如 2 ** 18 用于文本分类),这很少会成为问题问题”我的 n_features 不够大。您可以在HashingVectorizer
的 scikit-docs 中阅读有关此问题的信息。
@user_12,由您来设置 n_features。只需将其设置为 2**18 就可以了。
【参考方案1】:
sklearn 文档指出.fit_transform
可以采用可生成 str、unicode 或文件对象的迭代。因此,您可以创建一个生成器,逐个生成文件并将其传递给 fit 方法。您可以通过将路径传递给文件来创建生成器,如下所示:
def gen(path):
A = os.listdir(path)
for i in A:
yield (i)
现在您可以创建生成器并将其传递给 CountVectorizer,如下所示:
q = gen("/path/to/your/file/")
from sklearn.feature_extraction.text import CountVectorizer
cv = CountVectorizer(ngram_range=(1, 4))
cv.fit_transform(q)
这对你有帮助!
【讨论】:
它是如何工作的?它会一个接一个地适应一个文件并最终返回输出吗?内存要求是什么?我的意思是我有大约 150GB 的数据?这不会需要巨大的内存吗?【参考方案2】:您可以使用以下流程构建解决方案:
1) 遍历您的文件并在您的文件中创建一组所有令牌。在下面的示例中,这是使用 Counter 完成的,但您可以使用 python 集来实现相同的结果。这里的好处是 Counter 还会为您提供每个术语的出现总数。
2) 使用标记集/列表拟合 CountVectorizer。您可以使用 ngram_range=(1, 4) 实例化 CountVectorizer。为了限制 df_new_data 中的特征数量,避免低于此值。
3) 像往常一样转换新数据。
以下示例适用于小数据。我希望您可以调整代码以满足您的需求。
import glob
import pandas as pd
import numpy as np
from collections import Counter
from sklearn.feature_extraction.text import CountVectorizer
# Create a list of file names
pattern = 'C:\\Bytes\\*.csv'
csv_files = glob.glob(pattern)
# Instantiate Counter and loop through the files chunk by chunk
# to create a dictionary of all tokens and their number of occurrence
counter = Counter()
c_size = 1000
for file in csv_files:
for chunk in pd.read_csv(file, chunksize=c_size, index_col=0, header=None):
counter.update(chunk[1])
# Fit the CountVectorizer to the counter keys
vectorizer = CountVectorizer(lowercase=False)
vectorizer.fit(list(counter.keys()))
# Loop through your files chunk by chunk and accummulate the counts
counts = np.zeros((1, len(vectorizer.get_feature_names())))
for file in csv_files:
for chunk in pd.read_csv(file, chunksize=c_size,
index_col=0, header=None):
new_counts = vectorizer.transform(chunk[1])
counts += new_counts.A.sum(axis=0)
# Generate a data frame with the total counts
df_new_data = pd.DataFrame(counts, columns=vectorizer.get_feature_names())
df_new_data
Out[266]:
00 01 0A 0B 10 11 1A 1B A0 A1 \
0 258.0 228.0 286.0 251.0 235.0 273.0 259.0 249.0 232.0 233.0
AA AB B0 B1 BA BB
0 248.0 227.0 251.0 254.0 255.0 261.0
数据生成代码:
import numpy as np
import pandas as pd
def gen_data(n):
numbers = list('01')
letters = list('AB')
numlet = numbers + letters
x = np.random.choice(numlet, size=n)
y = np.random.choice(numlet, size=n)
df = pd.DataFrame('X': x, 'Y': y)
return df.sum(axis=1)
n = 2000
df_1 = gen_data(n)
df_2 = gen_data(n)
df_1.to_csv('C:\\Bytes\\df_1.csv')
df_2.to_csv('C:\\Bytes\\df_2.csv')
df_1.head()
Out[218]:
0 10
1 01
2 A1
3 AB
4 1A
dtype: object
【讨论】:
这是最有效的方法吗?我所有的文件合并了 150gb 的数据?这些文件是 .bytes 文件(即文本数据)而不是 .csv 文件?另外,您不认为将数据加载到 pandas 数据帧中会浪费大量内存吗? 该示例仅旨在展示一般方法,因此使用 pandas 进行数据生成和读取。你如何阅读文件的细节是你应该能够解决的。如有必要,我很乐意提供帮助。稍后我将使用以下内容扩展答案 - 在第 3 步中,您可以迭代循环遍历文件、转换和累积单个矩阵/数据框中的计数,以获得一个矩阵中整个数据集的计数。 我已经修改了答案以演示您如何累积计数。如果不清楚,请告诉我。 问题是我的转换数据也是文件。我该怎么做? 查看修改后的答案。它适用于 csv 文件。您可能需要调整代码以使用您的特定文件格式。【参考方案3】:通过使用生成器而不是列表,您的代码不会将文件的值存储到内存中。相反,它将产生一个值并让它忘记它,然后产生下一个,依此类推。在这里,我将使用您的代码并进行简单的调整以将列表更改为生成器。您可以只使用()
而不是[]
。
cv = CountVectorizer(ngram_range=(1, 4))
temp = cv.fit_transform((open(file).read() for file in files))
【讨论】:
这种方法(或)上述建议(即使用计数器)在计算和内存方面哪个更有效?以上是关于如何有效地使用 CountVectorizer 来组合目录中所有文件的 ngram 计数?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 sklearn CountVectorizer and() 来获取包含任何标点符号作为单独标记的 ngram?
如何使用带有 countVectorizer.fit_transform() 的腌制分类器来标记数据