Как обработать большой тензор numpy?

Имеется тензор, сохранённый в файле .npy, который читаю так:

x = np.load('x.npy')

Задача состоит в том, чтобы выполнить с ним математические преобразования и сохранить в другой файл.

x = process(x)
np.save('x_processed.npy', x)

Исходный тензор помещается в оперативную память, возвращаемое функцией process значение - нет.

Можно преобразовывать данные по частям: применять функцию process к срезу тензора x, но формат .npy не поддерживает добавление (append).

Я попробовал применить numpy.memmap таким образом:

x = np.load('x.npy', mmap_mode='r+')

Ожидаемое поведение - тензор не будет загружен в оперативную память, вместо этого будет создано отображение файла в оперативную память. Если в дальнейшем редактировать данные в тензоре x, то они будут редактироваться именно в файле.

Но функция process построена именно так, что она возвращает вновь созданный тензор, поэтому он создаётся в оперативной памяти и полностью её съедает.

Всё-таки есть какой-то способ обрабатывать исходный тензор по частям (срезам)?


Пусть имеются два файла, полученные путём обработки среза функцией process, которые затем загружаются посредством mmap:

x1 = np.load('x1.npy', mmap_mode='r')
x2 = np.load('x2.npy', mmap_mode='r')

И файл, куда надо записать результат:

x_targ = np.memmap(filename, dtype='float32', mode='w+', shape=full_tensor_shape)

тогда можно ли делать так:

x_targ[0:x1.shape[0], :, :, :] = x1
x_targ[0:x2.shape[0], :, :, :] = x2

Или как их объединить в один файл?


Ответы (1 шт):

Автор решения: maestro

Поскольку размер выходного тензора известен, можно создать сразу весь целевой файл, содержащий новый тензор, и открыть его как отображение в память. Затем читать исходный файл по частям, обрабатывать его и записывать результат в целевой.

x = np.load('x.npy', mmap_mode='r')
x_out_shape = (x.shape[0], A, B, C)
x_out = np.memmap('x_out.npy', dtype='uint8', mode='w+', shape=x_out_shape)
particle = 1000

filePos = 0
while True:
    # Если filePos + particle больше размера тензора, 
    # то автоматически подставится верхняя граница
    x_out[filePos:filePos + particle, :, :, :] = process(x[filePos:filePos + particle, :, :, :])
    filePos += particle
    if filePos >= x_out_shape[0]:
        break
x_out.flush()
→ Ссылка