нулевая версия: 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.