English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Универсальные функции ufunc в NumPy

NumPy 提供了两种基本的对象,即 ndarray 和 ufunc 对象。ufunc 是 universal function的缩写,意思是“通用函数”,它是一种能对数组的每个元素进行操作的函数。
许多 ufunc 函数都是用C语言级别实现的,因此它们的计算速度非常快。
此外,ufun 比 math 模块中的函数更灵活。math 模块的输入一般是标量,但 NumPy 中的函数可以是向量或矩阵,而利用向量或矩阵可以避免使用循环语句,这点在机器学习、深度学习中非常重要。

为什么要使用 ufuncs?

ufunc 用于在 NumPy 中实现矢量化,这比迭代元素要快得多。
它们还提供广播和其他方法,例如减少、累加等,它们对计算非常有帮助。
ufuncs 还接受其他参数,比如:
where 布尔值数组或条件,用于定义应在何处进行操作。
dtype 定义元素的返回类型。
out 返回值应被复制到的输出数组。

NumPy中的几个常用通用函数
函数使用方法
sqrt()计算序列化数据的平方根
sin()、cos()三角函数
abs()计算序列化数据的绝对值
dot()矩阵运算
log()、logl()、log2()对数函数
exp()指数函数
cumsum()、cumproduct()累计求和、求积
sum()Суммирование последовательности данных
mean()Вычисление среднего
median()Вычисление медианы
std()Вычисление стандартного отклонения
var()Вычисление дисперсии
corrcoef()Вычисление корреляционного коэффициента

Сравнение производительности функций math и numpy

import time
 import math
 import numpy as np
 x = [i * 0.001 for i in np.arange(1000000)]
 start = time.clock()
 for i, t in enumerate(x):
 x[i] = math.sin(t)
 print("math.sin:", time.clock() - start)
 x = [i * 0.001 for i in np.arange(1000000)]
 x = np.array(x)
 start = time.clock()
 np.sin(x)
 print("numpy.sin:", time.clock() - start)

Результат выполнения:

math.sin: 0.5169950000000005
 numpy.sin: 0.05381199999999886

Таким образом, numpy.sin в 10 раз быстрее math.sin.

Векторизация

Преобразование инструкций итерации в векторные операции называется векторизацией.
Благодаря оптимизации современных CPU для таких операций, они выполняются быстрее.
Сложение элементов двух списков:
Список 1: [1, 2, 3, 4]
Список 2: [4, 5, 6, 7]
Одним из методов является итерация по двум спискам и сложение каждого элемента.

Если нет ufunc, мы можем использовать встроенный метод zip() в Python:

x = [1, 2, 3, 4]
 y = [4, 5, 6, 7]
 z = []
 for i, j in zip(x, y):
   z.append(i + j)
 print(z)

Результат выполнения:

[5, 7, 9, 11]

Для этого у NumPy есть ufunc, называемая add(x, y), которая выдает одинаковый результат, и с помощью ufunc мы можем использовать функцию add():

import numpy as np
 x = [1, 2, 3, 4]
 y = [4, 5, 6, 7]
 z = np.add(x, y)
 print(z)

Результат выполнения:

[5, 7, 9, 11]

Сравнение циклов с векторными вычислениями

Полное использование встроенных функций библиотеки NumPy для реализации векторизации вычислений может значительно повысить производительность выполнения. Внутренние функции библиотеки NumPy используют SIMD инструкции. Векторизация, как показано ниже, значительно быстрее, чем вычисления с использованием циклов. Если использовать GPU, производительность будет еще выше, но NumPy не поддерживает GPU.
Смотрите следующий код:

import time
 import numpy as np
 x1 = np.random.rand(1000000)
 x2 = np.random.rand(1000000)
 ## Вычисление точного произведения векторами с помощью цикла
 tic = time.process_time()
 dot = 0
 for i in range(len(x1)):
 dot += x1[i]*x2[i]
 toc = time.process_time()
 print("dot = " + str(dot) + "\nfor-цикл ---- Время расчета = " + str(1000*(toc - tic)) + "ms")
 ## Использование функции np.dot для вычисления точного произведения
 tic = time.process_time()
 dot = 0
 dot = np.dot(x1,x2)
 toc = time.process_time()
 print("dot = " + str(dot) + "\nВерсия Verctor ---- Время расчета = " + str(1000*(toc - tic)) + "ms")

Результат выполнения:

 dot = 250215.601995
 for-цикл ---- Время расчета = 798.3389819999998ms
 dot = 250215.601995
 Версия Verctor ---- Время расчета = 1.885051999999554ms