2015-04-14 7 views
3

Я работаю с очень большими размерными векторами для машинного обучения и думал об использовании numpy для уменьшения объема используемой памяти. Я бегу быстрый тест, чтобы увидеть, сколько памяти я могу сэкономить, используя NumPy (1) (3):Список с разреженными данными потребляет меньше памяти, то те же данные, что и numpy array

Стандартный список

import random 
random.seed(0) 
vector = [random.random() for i in xrange(2**27)] 

Numpy массив

import numpy 
import random 
random.seed(0) 
vector = numpy.fromiter((random.random() for i in xrange(2**27)), dtype=float) 

памяти использование (2)

Numpy array: 1054 MB 
Standard list: 2594 MB 

Как и ожидалось.

Путем выделения оставшегося блока памяти с помощью собственных поплавков numpy потребляет только половину памяти, которую использует стандартный список.

Поскольку я знаю, что мои данные являются довольно запасными, я сделал тот же тест с разреженными данными.

Стандартный список

import random 
random.seed(0) 
vector = [random.random() if random.random() < 0.00001 else 0.0 for i in xrange(2 ** 27)] 

Numpy массив

from numpy import fromiter 
from random import random 
random.seed(0) 
vector = numpy.fromiter((random.random() if random.random() < 0.00001 else 0.0 for i in xrange(2 ** 27)), dtype=float) 

Использование памяти (2)

Numpy array: 1054 MB 
Standard list: 529 MB 

Теперь, внезапно, список python использует половину объема памяти, которую использует массив numpy! Зачем?

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

Примечание

  1. Я начал свежий питон оболочку для каждого теста.
  2. Память измеряется с помощью htop.
  3. Запуск на 32-разрядном Debian.
+0

Это 32-разрядная или 64-разрядная система? Я подозреваю, что указатели на 0 в списке Python меньше, чем float numpy. –

+0

'0' literal - целое число, поэтому ваш список построен в основном из ints, где в массиве numpy вы все плаваете. Более того, маленькие целые числа (-5..255) интернированы, поэтому все эти нули в списке указывают на один и тот же объект. Попробуйте использовать '0.0' и посмотрите, есть ли разница. –

+0

@ PM2Ring: тесты выполняются на 32-битном Debian. Соответственно обновился вопрос. – zeebonk

ответ

2

Список Python - это всего лишь массив ссылок (указателей) на объекты Python. В CPython (обычная реализация Python) список немного перераспределяется, чтобы сделать расширение более эффективным, но оно никогда преобразуется в dict. Дополнительную информацию смотрите в исходном коде: List object implementation

В редкой версии списка у вас есть много указателей на один объект int 0. Эти указатели занимают 32 бита = 4 байта, но ваши числовые поплавки, конечно, больше, возможно, 64 бит.

FWIW, чтобы сделать более точные тесты списка/массива более точными, вы должны позвонить random.seed(some_const) с тем же семенем в обеих версиях, чтобы получить одинаковое количество нулей как в списке Python, так и в массиве numpy.

+0

По умолчанию Numpy по умолчанию использует 64-битные поплавки, вынуждая их до 32 бит, в результате получается сопоставимый массив размера. Благодаря! – zeebonk