Python3:两个具有不同大小的 numpy 向量的字典消耗相同数量的 RAM
Python3: two dictionaries with numpy vectors of different size consume the same amount of RAM
我有两个 python 字典 {word: np.array(float)}
,在第一个字典中我使用 300 维的 numpy 向量,在第二个字典中(键是相同的)- 150 维。第一个文件大小为 4.3 GB,第二个文件大小为 2.2 GB。
当我使用 sys.getsizeof()
检查加载的对象时,我得到:
import sys
import pickle
import numpy as np
对于大字典:
with open("big.pickle", 'rb') as f:
source = pickle.load(f)
sys.getsizeof(source)
#201326688
all(val.size==300 for key, val in source.items())
#True
Linux top
命令显示 6.22GB:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
4669 hcl 20 0 6933232 6,224g 15620 S 0,0 19,9 0:11.74 python3
对于小词典:
with open("small.pickle", 'rb') as f:
source = pickle.load(f)
sys.getsizeof(source)
# 201326688 # Strange!
all(val.size==150 for key, val in source.items())
#True
但是当我使用 linux top
命令查看 python3 进程时,我看到 6.17GB:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
4515 hcl 20 0 6875596 6,170g 16296 S 0,0 19,7 0:08.77 python3
两个词典都是在Python3中使用pickle.HIGHEST_PROTOCOL
保存的,我不想使用json,因为可能存在编码错误和加载缓慢的问题。此外,使用 numpy 数组对我来说很重要,因为我为这些向量计算 np.dot
。
如何为其中包含较小向量的字典缩小 RAM?
更精确的内存测量:
#big:
sum(val.nbytes for key, val in source.items())
4456416000
#small:
sum(val.nbytes for key, val in source.items())
2228208000
编辑:感谢@etene 的提示,我已经设法使用 hdf5 保存和加载我的模型:
节省:
import pickle
import numpy as np
import h5py
with open("reduced_150_normalized.pickle", 'rb') as f:
source = pickle.load(f)
# list to save order
keys = []
values = []
for k, v in source.items():
keys.append(k)
values.append(v)
values = np.array(values)
print(values.shape)
with open('model150_keys.pickle',"wb") as f:
pickle.dump(keys, f,protocol=pickle.HIGHEST_PROTOCOL) # do not store stings in h5! Everything will hang
h5f = h5py.File('model150_values.h5', 'w')
h5f.create_dataset('model_values', data=values)
h5f.close()
生成长度为 3713680
的关键短语列表和形状为 (3713680, 150)
.
的向量数组
正在加载:
import pickle
import numpy as np
import h5py
with open('model150_keys.pickle',"rb") as f:
keys = pickle.load(f) # do not store stings in h5! Everything will hang
# we will construct model by reading h5 file line-by-line
h5f = h5py.File('model150_values.h5','r')
d=h5f['model_values']
print(len(keys))
print(d.shape)
model = {}
for i,key in enumerate(keys):
model[key]=np.array(d[i,:])
h5f.close()
现在我确实只消耗了 3GB 的 RAM:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
5012 hcl 20 0 3564672 2,974g 17800 S 0,0 9,5 4:25.27 python3
@etene,你可以写下你的评论作为答案,我会选择它。
剩下的唯一问题是加载现在需要相当长的时间(5 分钟),这可能是因为在 hdf5 文件中为 numpy 数组中的每个位置进行了查找。如果我可以通过第二个坐标以某种方式迭代 hdf5,而不加载到 RAM,那就太好了。
EDIT2:按照@hpaulj 的建议,我以块的形式加载文件,现在它和 pickle 一样快,甚至在使用 10k 块时更快(4 秒):
import pickle
import numpy as np
import h5py
with open('model150_keys.pickle',"rb") as f:
keys = pickle.load(f) # do not store stings in h5! Everything will hang
# we will construct model by reading h5 file line-by-line
h5f = h5py.File('model150_values.h5','r')
d=h5f['model_values']
print(len(keys))
print(d.shape)
model = {}
# we will load in chunks to speed up loading
for i,key in enumerate(keys):
if i%10000==0:
data = d[i:i+10000,:]
model[key]=data[i%10000,:]
h5f.close()
print(len(model))
谢谢大家!!!
总结我们在评论中发现的内容:
- sys.getsizeof 使用相同的键为两个
dict
返回相同的值是正常行为。来自文档:"Only the memory consumption directly attributed to the object is accounted for, not the memory consumption of objects it refers to."
- 一次反序列化所有数据会消耗大量 RAM; this Numpy discussion thread 提到 HDF5 文件格式是一种以小批量读取数据、减少内存使用的解决方案。
- 但是,由于磁盘 i/o,小批量读取也会对性能产生影响。感谢@hpaulj,@slowpoke 能够确定适合他的更大批量大小。
TL;DR 对于未来的读者:如果它真的很大,不要一次反序列化整个数据集,这会占用不可预测的 RAM 量。使用专用格式(例如 HDF5)并将数据分割成大小合理的批次,请记住较小的读取 = 更多的磁盘 i/o,较大的读取 = 更多的内存使用。
我有两个 python 字典 {word: np.array(float)}
,在第一个字典中我使用 300 维的 numpy 向量,在第二个字典中(键是相同的)- 150 维。第一个文件大小为 4.3 GB,第二个文件大小为 2.2 GB。
当我使用 sys.getsizeof()
检查加载的对象时,我得到:
import sys
import pickle
import numpy as np
对于大字典:
with open("big.pickle", 'rb') as f:
source = pickle.load(f)
sys.getsizeof(source)
#201326688
all(val.size==300 for key, val in source.items())
#True
Linux top
命令显示 6.22GB:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
4669 hcl 20 0 6933232 6,224g 15620 S 0,0 19,9 0:11.74 python3
对于小词典:
with open("small.pickle", 'rb') as f:
source = pickle.load(f)
sys.getsizeof(source)
# 201326688 # Strange!
all(val.size==150 for key, val in source.items())
#True
但是当我使用 linux top
命令查看 python3 进程时,我看到 6.17GB:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
4515 hcl 20 0 6875596 6,170g 16296 S 0,0 19,7 0:08.77 python3
两个词典都是在Python3中使用pickle.HIGHEST_PROTOCOL
保存的,我不想使用json,因为可能存在编码错误和加载缓慢的问题。此外,使用 numpy 数组对我来说很重要,因为我为这些向量计算 np.dot
。
如何为其中包含较小向量的字典缩小 RAM?
更精确的内存测量:
#big:
sum(val.nbytes for key, val in source.items())
4456416000
#small:
sum(val.nbytes for key, val in source.items())
2228208000
编辑:感谢@etene 的提示,我已经设法使用 hdf5 保存和加载我的模型:
节省:
import pickle
import numpy as np
import h5py
with open("reduced_150_normalized.pickle", 'rb') as f:
source = pickle.load(f)
# list to save order
keys = []
values = []
for k, v in source.items():
keys.append(k)
values.append(v)
values = np.array(values)
print(values.shape)
with open('model150_keys.pickle',"wb") as f:
pickle.dump(keys, f,protocol=pickle.HIGHEST_PROTOCOL) # do not store stings in h5! Everything will hang
h5f = h5py.File('model150_values.h5', 'w')
h5f.create_dataset('model_values', data=values)
h5f.close()
生成长度为 3713680
的关键短语列表和形状为 (3713680, 150)
.
正在加载:
import pickle
import numpy as np
import h5py
with open('model150_keys.pickle',"rb") as f:
keys = pickle.load(f) # do not store stings in h5! Everything will hang
# we will construct model by reading h5 file line-by-line
h5f = h5py.File('model150_values.h5','r')
d=h5f['model_values']
print(len(keys))
print(d.shape)
model = {}
for i,key in enumerate(keys):
model[key]=np.array(d[i,:])
h5f.close()
现在我确实只消耗了 3GB 的 RAM:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
5012 hcl 20 0 3564672 2,974g 17800 S 0,0 9,5 4:25.27 python3
@etene,你可以写下你的评论作为答案,我会选择它。
剩下的唯一问题是加载现在需要相当长的时间(5 分钟),这可能是因为在 hdf5 文件中为 numpy 数组中的每个位置进行了查找。如果我可以通过第二个坐标以某种方式迭代 hdf5,而不加载到 RAM,那就太好了。
EDIT2:按照@hpaulj 的建议,我以块的形式加载文件,现在它和 pickle 一样快,甚至在使用 10k 块时更快(4 秒):
import pickle
import numpy as np
import h5py
with open('model150_keys.pickle',"rb") as f:
keys = pickle.load(f) # do not store stings in h5! Everything will hang
# we will construct model by reading h5 file line-by-line
h5f = h5py.File('model150_values.h5','r')
d=h5f['model_values']
print(len(keys))
print(d.shape)
model = {}
# we will load in chunks to speed up loading
for i,key in enumerate(keys):
if i%10000==0:
data = d[i:i+10000,:]
model[key]=data[i%10000,:]
h5f.close()
print(len(model))
谢谢大家!!!
总结我们在评论中发现的内容:
- sys.getsizeof 使用相同的键为两个
dict
返回相同的值是正常行为。来自文档:"Only the memory consumption directly attributed to the object is accounted for, not the memory consumption of objects it refers to." - 一次反序列化所有数据会消耗大量 RAM; this Numpy discussion thread 提到 HDF5 文件格式是一种以小批量读取数据、减少内存使用的解决方案。
- 但是,由于磁盘 i/o,小批量读取也会对性能产生影响。感谢@hpaulj,@slowpoke 能够确定适合他的更大批量大小。
TL;DR 对于未来的读者:如果它真的很大,不要一次反序列化整个数据集,这会占用不可预测的 RAM 量。使用专用格式(例如 HDF5)并将数据分割成大小合理的批次,请记住较小的读取 = 更多的磁盘 i/o,较大的读取 = 更多的内存使用。