IT story

디스크에서 numpy 배열을 유지하는 가장 좋은 방법

hot-time 2020. 7. 30. 09:48
반응형

디스크에서 numpy 배열을 유지하는 가장 좋은 방법


큰 numpy 배열을 유지하는 빠른 방법을 찾고 있습니다. 디스크에 이진 형식으로 저장 한 다음 비교적 빠르게 메모리로 다시 읽습니다. cPickle은 불행히도 충분히 빠르지 않습니다.

numpy.saveznumpy.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 배열을 유지하는 더 좋은 방법이 있습니까? 이상적으로 하나의 파일에 여러 배열을 저장할 수 있기를 원합니다.


나는 큰 numpy 배열을 저장하는 hdf5의 큰 팬입니다. 파이썬에서 hdf5를 다루는 두 가지 옵션이 있습니다 :

http://www.pytables.org/

http://www.h5py.org/

둘 다 numpy 배열과 효율적으로 작동하도록 설계되었습니다.


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 파일로 데이터를 저장합니다. 파일을 압축 해제하려면 시간이 다소 걸릴 수 있습니다. 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)

that means that it can store really fast, i.e. the bottleneck is typically the disk. However, as the compression ratios are pretty good here, the effective speed is multiplied by the compression ratios. Here are the sizes for these 76 MB arrays:

$ 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

Please note that the use of the Blosc compressor is fundamental for achieving this. The same script but using 'clevel' = 0 (i.e. disabling compression):

$ python bench/store-blpk.py
store time = 3.36 (68.04 MB/s)
loading time = 2.61 (87.80 MB/s)

is clearly bottlenecked by the disk performance.


The lookup time is slow because when you use mmap to does not load content of array to memory when you invoke load method. Data is lazy loaded when particular data is needed. And this happens in lookup in your case. But second lookup won`t be so slow.

이것은 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')

참고 URL : https://stackoverflow.com/questions/9619199/best-way-to-preserve-numpy-arrays-on-disk

반응형