Рубрики
Без рубрики

Наука о данных с помощью Python: Превратите условные циклы в векторы Numpy

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

Автор оригинала: Tirthajyoti Sarkar.

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

” Трюк векторизации довольно хорошо известен ученым, изучающим данные, и обычно используется в кодировании, чтобы ускорить общее преобразование данных, когда простые математические преобразования выполняются над повторяющимся объектом, например списком. Что менее ценно, так это то, что даже стоит векторизировать нетривиальные блоки кода, такие как условные циклы. “Разработан Harryarts/Freepik

изображение

Python быстро становится де-факто предпочтительным языком программирования для специалистов по обработке данных. Но в отличие от R или Julia, это язык общего назначения и не имеет функционального синтаксиса, чтобы начать анализировать и преобразовывать числовые данные прямо из коробки. Поэтому для этого нужна специализированная библиотека.

Numpy , сокращение от Numerical Python , является фундаментальным пакетом, необходимым для высокопроизводительных научных вычислений и анализа данных в экосистеме Python. Это основа, на которой построены почти все инструменты более высокого уровня, такие как Pandas и scikit-learn . TensorFlow использует массивы NumPy в качестве основного строительного блока, поверх которого они построили свои тензорные объекты и graphflow для задач глубокого обучения (что делает интенсивное использование операций линейной алгебры над длинным списком/вектором/матрицей чисел).

Многие операции Numpy реализованы в C, что позволяет избежать общих затрат на циклы в Python, косвенное обращение указателя и проверку динамического типа для каждого элемента. Увеличение скорости зависит от того, какие операции вы выполняете. Для науки о данных и современных задач машинного обучения это неоценимое преимущество.

Моя недавняя история о демонстрации преимущества Numpy векторизации на основе простой задачи преобразования данных привлекла некоторое внимание и была хорошо принята читателями . Была интересная дискуссия о полезности векторизации по сравнению с простотой кода и тому подобным.

Теперь математические преобразования, основанные на некотором предопределенном условии, довольно распространены в задачах науки о данных. И оказывается, что можно легко векторизовать простые блоки условных циклов, сначала превратив их в функции, а затем используя метод numpy.vectorize . В моей предыдущей статье Я показал увеличение скорости на порядок для векторизации numpy простого математического преобразования. В данном случае ускорение менее драматично, так как внутренний условный цикл все еще несколько неэффективен. Тем не менее, время выполнения по крайней мере на 20-50% улучшается по сравнению с другими простыми кодами ванильного Python.

Вот простой код, чтобы продемонстрировать это:

import numpy as np
from math import sin as sn
import matplotlib.pyplot as plt
import time

# Number of test points
N_point = 1000

# Define a custom function with some if-else loops
def myfunc(x,y): 
  if (x>0.5*y and y<0.3): return (sn(x-y)) 
  elif (x<0.5*y): return 0 
  elif (x>0.2*y): return (2*sn(x+2*y)) 
  else: return (sn(y+x))

# List of stored elements, generated from a Normal distribution
lst_x = np.random.randn(N_point)
lst_y = np.random.randn(N_point)
lst_result = []

# Optional plots of the data
plt.hist(lst_x,bins=20)
plt.show()
plt.hist(lst_y,bins=20)
plt.show()

# First, plain vanilla for-loop
t1=time.time()
First, plain vanilla for-loop
t1=time.time()
for i in range(len(lst_x)):
    x = lst_x[i]
    y= lst_y[i]
    if (x>0.5*y and y<0.3):
        lst_result.append(sn(x-y))
    elif (x<0.5*y):
        lst_result.append(0)
    elif (x>0.2*y):
        lst_result.append(2*sn(x+2*y))
    else:
        lst_result.append(sn(y+x))
t2=time.time()

print("\nTime taken by the plain vanilla for-loop\n----------------------------------------------\n{} us".format(1000000*(t2-t1)))

# List comprehension
print("\nTime taken by list comprehension and zip\n"+'-'*40)
%timeit lst_result = [myfunc(x,y) for x,y in zip(lst_x,lst_y)]

# Map() function
print("\nTime taken by map function\n"+'-'*40)
%timeit list(map(myfunc,lst_x,lst_y))

# Numpy.vectorize method
print("\nTime taken by numpy.vectorize method\n"+'-'*40)
vectfunc = np.vectorize(myfunc,otypes=[np.float],cache=False)
%timeit list(vectfunc(lst_x,lst_y))

# Results
Time taken by the plain vanilla for-loop
----------------------------------------------
2000.0934600830078 us

Time taken by list comprehension and zip
----------------------------------------
1000 loops, best of 3: 810 µs per loop

Time taken by map function
----------------------------------------
1000 loops, best of 3: 726 µs per loop

Time taken by numpy.vectorize method
----------------------------------------
1000 loops, best of 3: 516 µs per loop

Обратите внимание, что я использовал %time it |/Jupyter magic command везде, где я мог написать вычисленное выражение в одной строке. Таким образом, я эффективно выполняю не менее 1000 циклов одного и того же выражения и усредняю время выполнения, чтобы избежать любого случайного эффекта. Следовательно, если вы запустите весь этот сценарий в ноутбуке Jupyter, вы можете получить немного другой результат для первого случая, т. Е. простое выполнение цикла, но следующие три должны дать очень последовательную тенденцию (в зависимости от вашего компьютерного оборудования).

Мы видим доказательства того, что для этой задачи преобразования данных, основанной на серии условных проверок, подход векторизации с использованием numpy обычно дает около 20-50% ускорения по сравнению с общими методами Python.

Это может показаться не столь значительным улучшением, но каждый бит экономии времени складывается в конвейере науки о данных и окупается в долгосрочной перспективе! Если работа по обработке данных требует, чтобы это преобразование происходило миллион раз, это может привести к разнице между 2 днями и 8 часами.

Короче говоря, везде, где у вас есть длинный список данных и вам нужно выполнить над ними некоторое математическое преобразование, настоятельно рассмотрите возможность превращения этих структур данных python (список, кортежи или словари) в объекты numpy.ndarray и использования присущих им возможностей векторизации.

Numpy предоставляет C-API для еще более быстрого выполнения кода, но это лишает простоты программирования на Python. Эта лекционная заметка Scipy показывает все связанные с этим варианты, которые у вас есть в этом отношении.

Существует целая онлайн-книга с открытым исходным кодом на эту тему, написанная французским исследователем нейробиологии. Проверьте это здесь .

Если у вас есть какие-либо вопросы или идеи, которыми вы хотите поделиться, пожалуйста, свяжитесь с автором по адресу tirthajyoti[AT]gmail.com . Также вы можете проверить репозитории автора GitHub на наличие других интересных фрагментов кода в Python, R или MATLAB и ресурсах машинного обучения. Если вы, как и я, увлечены машинным обучением/наукой о данных/полупроводниками, пожалуйста, не стесняйтесь добавить меня в LinkedIn .