нулевая версия: 1.22.4

Введение

При работе с очень большими массивами numpy могут возникнуть проблемы с ограничениями памяти. Чтобы справиться с этим, numpy предоставляет удобную функцию np.memmap(); memmap = memory mapped . Вместо того, чтобы читать все данные за один раз, он лениво считывает данные, поэтому потребляет мало оперативной памяти. Цитирование википедии

Файл с отображением памяти — это сегмент виртуальной памяти [1], которому назначена прямая побайтовая корреляция с некоторой частью файла или файлоподобного ресурса.

Подводные камни

При использовании np.memmap() я понял, что прочитанные данные отличаются от ожидаемых. Для справки, при чтении данных с помощью np.load() и проверке атрибутов данных:

data = np.load(data_path)
data.shape --> (53318, 3, 32, 543, 1)
np.max(data, axis = (0,2,3,4)) -->  [1718 2475 1489]
np.min(data, axis = (0,2,3,4)) --> [ -432 -2990 -2499]

Подводный камень 1. Явно укажите тип данных

Использование np.memmap:

data = np.memmap(data_path,                
                 mode="r",                
                 shape=(53318, 3, 32, 543, 1))
np.max(data, axis = (0,2,3,4)) --> [255 255 255]
np.min(data, axis = (0,2,3,4)) --> [0 0 0]

По умолчанию np.memmap предполагает, что тип данных (dtype) равен np.uint8. Глядя на приведенные выше результаты, становится очевидным, откуда возникла проблема, поскольку тип данных должен был быть np.int32 . Я думаю, что эту функцию можно улучшить, если она выдает предупреждение/ошибку о том, что в сохраненном файле больше байтов, чем требуется для данной формы и типа.

Подводный камень 2. Убедитесь, что размер данных соответствует ожидаемому

Несмотря на чтение как np.int32, атрибуты данных, которые я получил, были следующими

data = np.memmap(data_path,                
                 mode="r",                
                 shape=(53318, 3, 32, 543, 1),
                 dtype = np.int32)
data.shape --> (53318, 3, 32, 543, 1)
# Wrong values
np.max(data, axis = (0,2,3,4)) --> [1953656678 2475 2160]
np.min(data, axis = (0,2,3,4)) --> [-2073 -2990 -2990]

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

# Read without specifying the data shape
data = np.memmap(data_path,                
                 mode="r",         
                 dtype = np.int32)
# check the size
data.size --> (2779360736,)
# Expected size
53318*3*32*543 --> 2779360704
2779360736 - 2779360704 --> 32 # Extra 32 elements

Как показано выше, memmap возвращал 32 (отношение к 32 из np.int32 могло быть случайным, я не проверял) дополнительных элементов из того же файла по сравнению с np.load.

Ссылаясь на документацию на np.memmap

форма:кортеж, необязательно

Желаемая форма массива. Если mode == 'r' и количество оставшихся байтов после offset не кратно dtype в байтах, необходимо указать shape. По умолчанию возвращаемый массив будет одномерным с количеством элементов, определяемым размером файла и типом данных.

Однако, похоже, это не так.

Следующей задачей было определить расположение (начало/конец) дополнительных элементов. Протестировав оба варианта, я понял, что это было в начале.

# Read without specifying the data shape
data = np.memmap(data_path,                
                 mode="r",         
                 dtype = np.int32)
#! np.memmap reads extra data for some unknown reason
# Skip first 32 elements
data = data[32:].reshape((53318, 3, 32, 543, 1))
# Same results as from np.load()
np.max(data, axis = (0,2,3,4)) -->  [1718 2475 1489]
np.min(data, axis = (0,2,3,4)) --> [ -432 -2990 -2499]

Существует возможность указать смещение при использовании np.memmap. Однако он выдал ошибку, поэтому я приступил к своему специальному решению.

TypeError: __new__() получил неожиданный ключевой аргумент «offest»

Заключение

Обязательно проверяйте размер и тип данных при использовании np.memmap

Дополнительные сведения об использовании memmap() в машинном обучении см.



Я пишу о своем опыте работы над решениями для машинного обучения и о проблемах, с которыми я сталкиваюсь на пути. Если вы считаете подобные истории ценными и хотели бы поддержать меня как автора, рассмотрите возможность подписаться на меня или зарегистрироваться для членства в Medium.