эффективность Адамара в списке массивов - PullRequest
0 голосов
/ 30 декабря 2018

Numpy, продукт Hadmard в вертикальном стеке 1D-массивов, значительно быстрее, чем циклический просмотр списка 1D-массивов и выполнение продукта Hadamard (поэлементно) для каждого (что имеет смысл, и я проверял его в любом случае),

У меня есть ситуация, в которой мне нужно выполнить произведение Адамара между одним набором массивов numpy и другим следующим образом:

stacked_arrays = np.vstack([1D-arrays...])
stacked_arrays *= np.power(factor, np.arange(1, num_arrays))

Однако Мне нужна эта операция, чтобы изменитькаждый компонент 1D массива в списке , и эта операция должна происходить много раз.Я знаю, что это звучит как странная особенность, но есть ли способ сделать это без цикла вроде:

factors = factor ** np.arange(1, num_arrays)
for array, f in zip([1D..arrays], factors):
     array *= f

или без снятия с учета результата операции?

Также mapне может использоваться, поскольку map создает копии массивов numpy следующим образом:

result = map(lambda x, y: x * y, zip([1D..arrays], factors))

, поскольку вы не можете сделать *= с lambda, возвращается список массивов numpy, оставляя оригиналы

Есть ли способ заставить np.vstack по-прежнему ссылаться на старые массивы компонентов каким-либо образом, или альтернативный способ достижения скорости продукта Адамара между массивами, которые равны stacked, при мутировании непакетированных массивов??Так как некоторое время может быть сэкономлено, если не нужно выполнять unstacking (np.split).

Результаты TimeIt:

m = []
for _ in range(100):
    m.append(np.array([1, 2, 4, 5], dtype=np.float64))
factors = np.expand_dims(np.power(2, np.arange(100, dtype=np.float64)), axis=1)

def split_and_unstack():
    l = np.vstack(m)
    l *= factors
    result = np.split(l, 100)

def split_only():
    l = np.vstack(m)
    l *= factors

print(timeit.timeit(split_and_unstack, number=10000))
# 1.8569015570101328

print(timeit.timeit(split_only, number=10000))
# 0.9328480050317012

# makes sense that with unstacking it is about double the time

Уточнение: список [1D массивов], упомянутых выше,подсписок большего списка 1D массивов.Этот большой список collections.deque.И это deque должно быть перетасовано перед извлечением подсписка (то есть это буфер воспроизведения опыта для стохастического градиентного спуска).

Буфер pop и append скорость:

times = int(1e4)
tgt = np.array([1, 2, 3, 4])

queue = collections.deque([tgt] * times, maxlen=times)
reg_list = [tgt] * times
numpy_list = np.array([tgt] * times)

def pop():
    queue.pop()
def pop_list():
    reg_list.pop()
def pop_np():
    global numpy_list
    numpy_list = numpy_list[1:]

print(timeit.timeit(pop, number=times))
# 0.0008135469979606569
print(timeit.timeit(pop_list, number=times))
# 0.000994370027910918
print(timeit.timeit(pop_np, number=times))
# 0.0016436030273325741

def push():
    queue.append(tgt)
def push_list():
    reg_list.append(tgt)
numpy_list = np.array([tgt] * 1)
def push_np():
    numpy_list[0] = tgt

print(timeit.timeit(push, number=times))
# 0.0008797429618425667
print(timeit.timeit(push_list, number=times))
# 0.00097957398975268
print(timeit.timeit(push_np, number=times))
# 0.003331452957354486

1 Ответ

0 голосов
/ 30 декабря 2018

Давайте разберем проблему.Вы хотите иметь список ссылок на массивы, которые все изменяемы, но вы хотите иметь возможность выполнять операции с ними как с блоком.

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

Замените текущий цикл

m = [np.array([1, 2, 4, 5], dtype=np.float64) for _ in range(100)]

одним буфером и просмотрите в каждой строке:

buf = np.vstack([np.array([1, 2, 4, 5], dtype=np.float64) for _ in range(100)])
m = list(buf)  # check that m[0].base is b

Теперь у вас есть список массивов m, каждый из которых вы можете изменять индивидуально.Пока вы сохраняете изменения на месте и не переназначаете элементы списка, все изменения будут отображаться непосредственно в buf.В то же время вы можете выполнять свои пакетные вычисления на buf, и, пока вы делаете их на месте, m будет отражать все изменения.

В действительности вам может даже не понадобиться m.Обратите внимание, как list(buf) создает представления в каждой строке фрагмента.Вы можете также легко индексировать прямо в буфер.Например, m[3][8] обычно записывается в терминах buf как buf[3, 8], но вы также можете использовать тот факт, что buf является последовательностью и писать buf[3][8].Это менее эффективно, поскольку каждый раз будет создаваться новое представление (buf[3]), но не намного.

Чтобы извлечь перемешанное подмножество строк, вы также можете использовать индексирование последовательности.Допустим, в вашем буфере сохраняются последние M строк, которые вы хотите перетасовать и извлекать последовательность из N строк.Вы можете сделать это, создав массив индексов и перетасовывая их снова и снова:

 indices = np.arange(M)

 # inside the loop:

 np.random.shuffle(indices)
 chunk = buf[indices[:N]]
 # do your math on `chunk`

Вам не нужно перераспределять или повторно сортировать indices, пока M этого не делаетизменить, и вы считаете, что случайное перемешивание достаточно случайно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...