programing

Numpy 어레이를 Disk에 보존하는 가장 좋은 방법

stoneblock 2023. 6. 8. 19:16

Numpy 어레이를 Disk에 보존하는 가장 좋은 방법

저는 큰 누피 배열을 빠르게 보존할 수 있는 방법을 찾고 있습니다.디스크에 바이너리 형식으로 저장한 다음 비교적 빠르게 메모리로 다시 읽어 들이고 싶습니다. cPickle은 아쉽게도 충분히 빠르지 않습니다.

numpy.saveznumpy.load를 찾았습니다.하지만 이상한 것은 numpy.load가 "메모리 맵"에 anpy 파일을 로드한다는 것입니다.이것은 배열을 정기적으로 조작하는 것이 매우 느리다는 것을 의미합니다.예를 들어, 이와 같은 것은 정말 느립니다.

#!/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 어레이 스토리지에 대한 벤치마크

Npy 및 이진 파일은 고밀도 데이터에 대해 매우 빠르고 작습니다.데이터가 희박하거나 매우 구조적인 경우 압축과 함께 npz를 사용하면 공간은 많이 절약되지만 로드 시간은 다소 소요될 수 있습니다.

휴대성이 문제라면 npy보다 이진수가 더 좋습니다.인간의 가독성이 중요한 경우 많은 성능을 희생해야 하지만 csv를 사용하면 상당히 잘 달성할 수 있습니다(물론 휴대성도 매우 높음).

자세한 내용과 코드는 github repo에서 확인할 수 있습니다.

저는 큰 numpy 배열을 저장하는 hdf5의 열렬한 팬입니다.파이썬에서 hdf5를 처리하기 위한 두 가지 옵션이 있습니다.

http://www.pytables.org/

http://www.h5py.org/

둘 다 numpy 어레이와 효율적으로 작동하도록 설계되었습니다.

으로 한 이제 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']
modules = dict(
    pickle=pickle, h5py=h5py, gzip=gzip, lzma=lzma, bz2=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:
        path = 'data.pkl.{}'.format(compression)

        if compression == 'pickle':
            time_start = time.time()
            pickle.dump(data[key], open(path, 'wb'))
            time_tot = time.time() - time_start
            sizes[key]['pickle'] = (
                os.path.getsize(path) * 10**-6, 
                time_tot.
            )
            os.remove(path)

        elif compression == 'h5py':
            time_start = time.time()
            with h5py.File(path, 'w') as h5f:
                h5f.create_dataset('data', data=data[key])
            time_tot = time.time() - time_start
            sizes[key][compression] = (os.path.getsize(path) * 10**-6, time_tot)
            os.remove(path)

        else:
            time_start = time.time()
            with modules[compression].open(path, 'wb') as fout:
                pickle.dump(data[key], fout)
            time_tot = time.time() - time_start
            sizes[key][labels[compressions.index(compression)]] = (
                os.path.getsize(path) * 10**-6, 
                time_tot,
            )
            os.remove(path)


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 어레이를 효율적으로 저장할 수 있는 또 다른 방법은 Blospack입니다.

#!/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 컴프레서를 사용하는 것이 기본입니다.동일한 스크립트이지만 '레벨' = 0(즉, 압축 사용 안 함)을 사용하는 경우:

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

Disk 성능으로 인해 병목 현상이 발생합니다.

사용할 때 때문에 조회 시간이 느립니다.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')

'최고'는 여러분의 목표에 달려 있습니다.다른 사람들이 말했듯이, 바이너리는 최대로 이동할 수 있지만, 문제는 데이터가 저장되는 방식에 대해 알아야 한다는 것입니다.

Darr은 플랫 이진 및 텍스트 파일을 기반으로 자체 문서화된 방식으로 Numpy 배열을 저장합니다.이를 통해 광범위한 가독성을 극대화할 수 있습니다.또한 Numpy 자체뿐만 아니라 R, Matlab, Julia 등과 같은 다양한 데이터 과학 언어로 배열을 읽는 방법에 대한 코드를 자동으로 포함합니다.

공개:나는 도서관을 썼습니다.

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