В процессе освоения ML, СNN, NN на Python новичок почти всегда сталкивается с проблемой скорости вычислений, а иногда и нехватки оперативной памяти.

Особенно это заметно с большими базами, которые по размеру больше 50% свободной RAM. Мысли о покупке более достойного железа всего лишь одно из возможных решений.

Иной вариант использовать одну из возможностей в Python — итерации по самой функции.

Простой и наглядный пример. Допустим Вам необходимо возвести в 200 000 степень ряд чисел от 1 до 10 и сложить их сумму. В итоге вы должны получить число длинной 200 тыс знаков. это в 2 раза больше чем google)

Вот простой код такой функции:

from time import time # импорт модуля времени
# простая функция возведения в степень
def power_1(x):
    power_1 = 0 
    start = time()
    power_1 = x**200000
    end = time()
    total_1 = end - start
    return round(total_1,6)
# простые операции сложения, для того что бы посчитать отдельно время операции
power_1(1) + power_1(2) + power_1(3) + power_1(4) + power_1(5) + power_1(6) + power_1(7) + power_1(8) + power_1(9) + power_1(10)

В итоге мы получаем достаточно «затратную» операцию для вашего компа, и например у моего это занимает более 13 мс. А что если таких операций может быть много? Тогда все усложняется, а возможно Вам просто не хватит RAM.

Но можно быть хитрее и использовать туже самую функцию, прогнав с помощью итерации по вашим переменным.

Вот код такого простого решения, той же самой функции:

from time import time #получаем модуль времени 
# функция итерации
def power_2():
    for i in range(11):
        yield i
# функции возведения в степень 
for i in power_2():
    if i < 11:
        d = 0
        total_2 = 0
        start = time()
        d += i**200000
        end = time()
        total_2 += end - start
        print('Ineration {} lasts {}'.format(i, round(total_2, 6)))
        if i == 10:
            print('    \n End with total time {} \n'.format(round(total_2,3))) 

Временные затраты на моем компе



Разница 0,13 — 0,024 = 0,106 мс!


Если к этому огромному числу обычным способом просто добавить 1, то этой займет больше времени, чем само возведение в 200000 степень. То есть, преимущества такого способа очевидны.

def add_one(x):
    total_3 = 0
    start = time()
    s = x + 1
    end = time()
    total_3 +=  end - start
    print('Time to do add 1 to BIG number: {} \n'.format(total_3))
    return print('Difference in time {}\n'.format(round(total_2 - total_3 ,3)))
add_one(d)
print('The size of the number {}!!!'.format(len(str(d))))

Результат такой:



Почему так? Насколько, мне известно, при итерации по функции Python не создает временные объекты в памяти, что в свою очередь существенно ускоряет любой процесс вычислений. Поэтому, переписав таким образом функцию — вы экономите время и нервы.

Итого — для сложных вычислений с ограниченным размером RAM, лучше использовать итерацию по функции, чем просто сами функции.

Надеюсь, это поможет кому-то не потерять драгоценные минуты или не потратится на дополнительное, быстро устаревающее железо.

Навеяно по лекции

Комментарии (7)


  1. Tremere
    07.02.2019 12:25

    а нельзя просто заменить ??
    for i in power_2():

    на

    for i in range(11);


    1. Andy_U
      07.02.2019 12:31

      Это — да, перл. Столько написать, когда решение в одну строчку:

      powers = [i**200000 for i in range(11)]
      


  1. Andy_U
    07.02.2019 12:27

    У вас в «быстром» варианте очевидная ошибка — вы инициализируете переменную total_2 в теле цикла, а нужно — до него. Ну и про сам цикл я лучше промолчу.

    А как переместите инициализацию, так и получите одинаковые времена.


  1. Hardcoin
    07.02.2019 12:29

    просто добавить 1, то этой займет больше времени, чем само возведение в 200000 степень.

    Возведение в степень заняло 24 мс, прибавление единицы — 0.024 мс, судя по вашему скриншоту. Так почему вы утверждаете, что прибавление заняло больше времени?


    Быстрее, кстати, не стало. Total time во втором случае фальшивый, вы просто второй раз выводите время последней итерации.


  1. DjPhoeniX
    07.02.2019 13:10
    +1

    Наглядное пособие о том, как не надо писать бенчмарки. «Прогрева» нет, операции выполняются по одному разу (оценивать стоит по avg/med), ну и откровенные баги с подсчётом времени, которые уже указали выше.


  1. Deepwalker
    07.02.2019 13:37

    1. функция power_2 бесполезна и её результат аналогичен обычному range(11)
    2. if i < 11: бесполезен, range(11) последним числом вернет 10
      >>> list(range(11))
      [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    3. переменные в теле цикла не инициализируют, например d = 0 и d += i**200000 фактически просто прибавление i**200000 к нулю.

    Итого после исправления некоторых ошибок:


    >>>
    >>>
    >>> d = 0
    >>> total_2 = 0
    >>>
    >>> for i in range(1, 11):
    ...     start = time()
    ...     d += i**200000
    ...     end = time()
    ...     total_2 += end - start
    ...     # print('Iteration {} lasts {}'.format(i, round(total_2, 6)))
    ...
    >>> print('    \n End with total time {} \n'.format(round(total_2,3)))
    
     End with total time 0.077
    
    >>>
    >>>
    >>>
    >>> from time import time # импорт модуля времени
    >>>
    >>>
    >>> total_1 = 0
    >>>
    >>> def power_1(x):
    ...     global total_1
    ...     start = time()
    ...     power = x**200000
    ...     end = time()
    ...     total_1 += end - start
    ...     return round(total_1,6)
    ...
    >>> # простые операции сложения, для того что бы посчитать отдельно время операции
    ... power_1(1) + power_1(2) + power_1(3) + power_1(4) + power_1(5) + power_1(6) + power_1(7) + power_1(8) ... + power_1(9) + power_1(10)
    0.308704
    >>>
    >>> print('    \n End with total time {} \n'.format(round(total_1,3)))
    
     End with total time 0.078

    Тут бы еще поправить подсчет времени – сложение для больших чисел, как вы заметили, тоже требует времени, а оно тут не учитывается. Очевидно, что при исправлении ошибок код эквивалентен.


  1. resetme
    07.02.2019 23:16

    Стоит почитать про генераторы и итераторы в Python перед тем как писать подобные статьи.