какой из них быстрее np.vstack, np.append, np.concatenate или ручная функция, сделанная в cython?

Я написал программу, которая обновляет список numpy на каждой итерации и выполняет над ним некоторые операции. количество итераций зависит от времени. например, за 1 секунду может быть от 1000 до 2500 итераций. Это означает, что элементов в списке numpy не будет более 2500 для запуска программы в течение 1 секунды.

Я реализовал базовый алгоритм, и я не уверен, что это самый быстрый способ вычислить bonus:

import numpy as np

cdef int[:, :] pl_list
cdef list pl_length
cdef list bonus
pl_list = np.array([[8, 7]], dtype=np.int32)

def modify(pl_list, pl_length):
    cdef int k_const = 10
    mean = np.mean(pl_list, axis=0)
    mean = np.subtract(mean, pl_length)
    dev = np.std(pl_list, axis=0)
    mean[0] / dev[0] if dev[0] != 0 else 0
    mean[1] / dev[1] if dev[1] != 0 else 0

    bonus = -1 + (2 / (1 + np.exp(-k_const * mean)))
    return list(bonus)


for i in range(2499): # I just simplified the loop. the main loop works like startTime - time.clock() < seconds
    rand = np.random.randint(8, 64)
    pl_length = [rand, rand-1]

    pl_list = np.append(pl_list, [pl_length], axis=0)
    bonus = modify(pl_list, pl_length)

Я думал ускорить эту программу, используя следующие идеи:

  1. используя np.vstack, np.stack или, может быть, np.concatenate вместо np.append(pl_list, [pl_length]) (какой из них может быть быстрее?)
  2. Использование самодельных функций для вычисления np.std, np.mean следующим образом (поскольку в cython итерации в представлениях памяти выполняются так быстро):

    cdef int i,sm = 0
    for i in range(pl_list.shape[0]):
    sm += pl_list[i]
    mean = sm/pl_list.shape[0]

  3. Я также думал об определении статической длины (например, 2500) для просмотров памяти, поэтому мне не нужно было бы использовать np.append, и я мог бы построить структуру очереди в этом списке numpy. (Как насчет библиотеки очередей? Это быстрее, чем пустые списки в таких операциях?)

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


person Masoud Masoumi Moghadam    schedule 07.09.2017    source источник
comment
Я рекомендую профилировать возможные подходы, чтобы узнать, какой из них самый быстрый. en.wikipedia.org/wiki/Profiling_(computer_programming)   -  person Seth Difley    schedule 07.09.2017
comment
Хорошей практикой является задавать один вопрос на сообщение, но в отношении 2, как правило, Плохая идея повторно реализовать функциональность numpy.   -  person user2699    schedule 07.09.2017
comment
См. stackoverflow.com/questions/29280470/what-is- timeit-in-python, чтобы узнать больше о профилировании.   -  person user2699    schedule 07.09.2017


Ответы (1)


Игнорируя функцию modify, ядро ​​​​вашего цикла:

pl_list = np.array([[8, 7]], dtype=np.int32)
....

for i in range(2499):
    ....
    pl_list = np.append(pl_list, [pl_length], axis=0)
    ...

Как правило, мы не рекомендуем использовать np.concatenate и его производные в цикле. Быстрее добавлять к списку и выполнять конкатенацию один раз в конце. (об этом позже)

pl_list это список или массив? По имени это список, но созданный массив. Я не изучал modify, чтобы понять, требует ли он массива или списка.

Посмотрите исходный код таких функций, как np.append. Базовая функция — np.concatenate, которая берет список и объединяет его в новый массив по указанной оси. Другими словами, он хорошо работает с длинным списком массивов.

np.append заменяет этот ввод списка двумя аргументами. Поэтому его нужно применять идеативно. И это медленно. Каждое добавление создает новый массив.

np.hstack просто гарантирует, что элементы списка имеют размер не менее 1d, np.vstack делает их 2d, stack добавляет измерение и т. д. Так что в основном все они делают одно и то же, с небольшими изменениями во входных данных.

Другая модель заключается в выделении достаточно большого массива для начала, например. res = np.zeros((n,2)) и вставьте значения в res[i,:] = new_value. Скорости примерно такие же, как и при добавлении списка. Эту модель можно переместить в cython и typed memoryviews для (потенциально) значительного улучшения скорости.

person hpaulj    schedule 07.09.2017
comment
Хороший ответ ! Мой вывод - добавить массивы в список и вызвать np.concatenate в полном списке. В моем случае это значительно сократило время вычислений. - person Yohan Obadia; 19.03.2019