큰 numpy 배열을 보존하는 빠른 방법을 찾고 있습니다. 바이너리 형식으로 디스크에 저장 한 다음 비교적 빠르게 메모리에 다시 읽어 들이고 싶습니다. cPickle은 불행히도 충분히 빠르지 않습니다.
numpy.savez 및 numpy.load를 찾았 습니다 . 하지만 이상한 점은 numpy.load가 npy 파일을 “memory-map”에로드한다는 것입니다. 이것은 배열의 규칙적인 조작이 정말 느리다는 것을 의미합니다. 예를 들어, 다음과 같은 것은 정말 느립니다.
#!/usr/bin/python
import numpy as np;
import time;
from tempfile import TemporaryFile
n = 10000000;
a = np.arange(n)
b = np.arange(n) * 10
c = np.arange(n) * -0.5
file = TemporaryFile()
np.savez(file,a = a, b = b, c = c);
file.seek(0)
t = time.time()
z = np.load(file)
print "loading time = ", time.time() - t
t = time.time()
aa = z['a']
bb = z['b']
cc = z['c']
print "assigning time = ", time.time() - t;
보다 정확하게는 첫 번째 줄은 정말 빠르지 만 배열을 할당하는 나머지 줄 obj
은 엄청나게 느립니다.
loading time = 0.000220775604248
assining time = 2.72940087318
numpy 배열을 보존하는 더 좋은 방법이 있습니까? 이상적으로는 하나의 파일에 여러 배열을 저장할 수 있기를 원합니다.
답변
나는 대용량 배열을 저장하는 hdf5의 열렬한 팬입니다. 파이썬에서 hdf5를 처리하는 데는 두 가지 옵션이 있습니다.
둘 다 numpy 배열과 효율적으로 작동하도록 설계되었습니다.
답변
numpy 배열을 저장하는 여러 방법에 대해 성능 (공간 및 시간)을 비교했습니다. 그들 중 일부는 파일 당 여러 배열을 지원하지만 어쨌든 유용 할 수도 있습니다.
Npy 및 바이너리 파일은 고밀도 데이터의 경우 정말 빠르고 작습니다. 데이터가 희박하거나 매우 구조화 된 경우 npz를 압축과 함께 사용하면 많은 공간이 절약되지만로드 시간이 약간 소요됩니다.
이식성이 문제라면 바이너리가 npy보다 낫습니다. 사람의 가독성이 중요하다면 많은 성능을 희생해야하지만 csv (물론 이식성이 매우 뛰어남)를 사용하면 상당히 잘 달성 할 수 있습니다.
자세한 내용과 코드는 github repo 에서 확인할 수 있습니다 .
답변
의 HDF5 기반 클론 지금이 pickle
라고는 hickle
!
https://github.com/telegraphic/hickle
import hickle as hkl
data = { 'name' : 'test', 'data_arr' : [1, 2, 3, 4] }
# Dump data to file
hkl.dump( data, 'new_data_file.hkl' )
# Load data from file
data2 = hkl.load( 'new_data_file.hkl' )
print( data == data2 )
편집하다:
다음을 수행하여 압축 된 아카이브로 직접 “피클”할 수도 있습니다.
import pickle, gzip, lzma, bz2
pickle.dump( data, gzip.open( 'data.pkl.gz', 'wb' ) )
pickle.dump( data, lzma.open( 'data.pkl.lzma', 'wb' ) )
pickle.dump( data, bz2.open( 'data.pkl.bz2', 'wb' ) )
부록
import numpy as np
import matplotlib.pyplot as plt
import pickle, os, time
import gzip, lzma, bz2, h5py
compressions = [ 'pickle', 'h5py', 'gzip', 'lzma', 'bz2' ]
labels = [ 'pickle', 'h5py', 'pickle+gzip', 'pickle+lzma', 'pickle+bz2' ]
size = 1000
data = {}
# Random data
data['random'] = np.random.random((size, size))
# Not that random data
data['semi-random'] = np.zeros((size, size))
for i in range(size):
for j in range(size):
data['semi-random'][i,j] = np.sum(data['random'][i,:]) + np.sum(data['random'][:,j])
# Not random data
data['not-random'] = np.arange( size*size, dtype=np.float64 ).reshape( (size, size) )
sizes = {}
for key in data:
sizes[key] = {}
for compression in compressions:
if compression == 'pickle':
time_start = time.time()
pickle.dump( data[key], open( 'data.pkl', 'wb' ) )
time_tot = time.time() - time_start
sizes[key]['pickle'] = ( os.path.getsize( 'data.pkl' ) * 10**(-6), time_tot )
os.remove( 'data.pkl' )
elif compression == 'h5py':
time_start = time.time()
with h5py.File( 'data.pkl.{}'.format(compression), 'w' ) as h5f:
h5f.create_dataset('data', data=data[key])
time_tot = time.time() - time_start
sizes[key][compression] = ( os.path.getsize( 'data.pkl.{}'.format(compression) ) * 10**(-6), time_tot)
os.remove( 'data.pkl.{}'.format(compression) )
else:
time_start = time.time()
pickle.dump( data[key], eval(compression).open( 'data.pkl.{}'.format(compression), 'wb' ) )
time_tot = time.time() - time_start
sizes[key][ labels[ compressions.index(compression) ] ] = ( os.path.getsize( 'data.pkl.{}'.format(compression) ) * 10**(-6), time_tot )
os.remove( 'data.pkl.{}'.format(compression) )
f, ax_size = plt.subplots()
ax_time = ax_size.twinx()
x_ticks = labels
x = np.arange( len(x_ticks) )
y_size = {}
y_time = {}
for key in data:
y_size[key] = [ sizes[key][ x_ticks[i] ][0] for i in x ]
y_time[key] = [ sizes[key][ x_ticks[i] ][1] for i in x ]
width = .2
viridis = plt.cm.viridis
p1 = ax_size.bar( x-width, y_size['random'] , width, color = viridis(0) )
p2 = ax_size.bar( x , y_size['semi-random'] , width, color = viridis(.45))
p3 = ax_size.bar( x+width, y_size['not-random'] , width, color = viridis(.9) )
p4 = ax_time.bar( x-width, y_time['random'] , .02, color = 'red')
ax_time.bar( x , y_time['semi-random'] , .02, color = 'red')
ax_time.bar( x+width, y_time['not-random'] , .02, color = 'red')
ax_size.legend( (p1, p2, p3, p4), ('random', 'semi-random', 'not-random', 'saving time'), loc='upper center',bbox_to_anchor=(.5, -.1), ncol=4 )
ax_size.set_xticks( x )
ax_size.set_xticklabels( x_ticks )
f.suptitle( 'Pickle Compression Comparison' )
ax_size.set_ylabel( 'Size [MB]' )
ax_time.set_ylabel( 'Time [s]' )
f.savefig( 'sizes.pdf', bbox_inches='tight' )
답변
savez () 데이터를 zip 파일로 저장합니다. 파일을 zip 및 압축 해제하는 데 시간이 걸릴 수 있습니다. save () 및 load () 함수를 사용할 수 있습니다.
f = file("tmp.bin","wb")
np.save(f,a)
np.save(f,b)
np.save(f,c)
f.close()
f = file("tmp.bin","rb")
aa = np.load(f)
bb = np.load(f)
cc = np.load(f)
f.close()
여러 배열을 하나의 파일에 저장하려면 먼저 파일을 연 다음 배열을 순서대로 저장하거나로드하면됩니다.
답변
numpy 배열을 효율적으로 저장할 수있는 또 다른 가능성은 Bloscpack입니다 .
#!/usr/bin/python
import numpy as np
import bloscpack as bp
import time
n = 10000000
a = np.arange(n)
b = np.arange(n) * 10
c = np.arange(n) * -0.5
tsizeMB = sum(i.size*i.itemsize for i in (a,b,c)) / 2**20.
blosc_args = bp.DEFAULT_BLOSC_ARGS
blosc_args['clevel'] = 6
t = time.time()
bp.pack_ndarray_file(a, 'a.blp', blosc_args=blosc_args)
bp.pack_ndarray_file(b, 'b.blp', blosc_args=blosc_args)
bp.pack_ndarray_file(c, 'c.blp', blosc_args=blosc_args)
t1 = time.time() - t
print "store time = %.2f (%.2f MB/s)" % (t1, tsizeMB / t1)
t = time.time()
a1 = bp.unpack_ndarray_file('a.blp')
b1 = bp.unpack_ndarray_file('b.blp')
c1 = bp.unpack_ndarray_file('c.blp')
t1 = time.time() - t
print "loading time = %.2f (%.2f MB/s)" % (t1, tsizeMB / t1)
내 노트북의 출력 (Core2 프로세서가 장착 된 비교적 오래된 MacBook Air) :
$ python store-blpk.py
store time = 0.19 (1216.45 MB/s)
loading time = 0.25 (898.08 MB/s)
즉, 매우 빠르게 저장할 수 있습니다. 즉, 병목 현상은 일반적으로 디스크입니다. 그러나 여기서 압축 비율이 꽤 좋기 때문에 유효 속도에 압축 비율을 곱합니다. 이러한 76MB 어레이의 크기는 다음과 같습니다.
$ ll -h *.blp
-rw-r--r-- 1 faltet staff 921K Mar 6 13:50 a.blp
-rw-r--r-- 1 faltet staff 2.2M Mar 6 13:50 b.blp
-rw-r--r-- 1 faltet staff 1.4M Mar 6 13:50 c.blp
Blosc 컴프레서 의 사용은 이를 달성하기위한 기본입니다. 동일한 스크립트이지만 ‘clevel’= 0 사용 (예 : 압축 해제) :
$ python bench/store-blpk.py
store time = 3.36 (68.04 MB/s)
loading time = 2.61 (87.80 MB/s)
디스크 성능으로 인해 병목 현상이 발생합니다.
답변
사용 mmap
하면 load
메서드 를 호출 할 때 배열의 내용을 메모리에로드하지 않기 때문에 조회 시간이 느립니다 . 특정 데이터가 필요할 때 데이터가 지연로드됩니다. 그리고 이것은 귀하의 경우 조회에서 발생합니다. 그러나 두 번째 조회는 그렇게 느리지 않습니다.
이것은 mmap
큰 배열을 가질 때 전체 데이터를 메모리에로드 할 필요가없는 좋은 기능입니다 .
joblib 사용을 해결하려면 joblib.dump
두 개 이상을 사용하여 원하는 객체를 덤프 할 수 있습니다 numpy arrays
. 예제를 참조하십시오.
firstArray = np.arange(100)
secondArray = np.arange(50)
# I will put two arrays in dictionary and save to one file
my_dict = {'first' : firstArray, 'second' : secondArray}
joblib.dump(my_dict, 'file_name.dat')