NumPy: как быстро нормализовать многие векторы?
Как можно упорядочить список векторов в NumPy?
Вот пример, который не работает:
from numpy import *
vectors = array([arange(10), arange(10)]) # All x's, then all y's
norms = apply_along_axis(linalg.norm, 0, vectors)
# Now, what I was expecting would work:
print vectors.T / norms # vectors.T has 10 elements, as does norms, but this does not work
Последняя операция дает "несоответствие формы: объекты не могут быть переданы в одну форму".
Как можно элегантно выполнить нормализацию 2D-векторов в vectors
, с NumPy?
Изменить. Почему это не работает при добавлении измерения в norms
работает (согласно моему ответу ниже)?
Ответы
Ответ 1
Хорошо, если я ничего не пропустил, это работает:
vectors / norms
Проблема в вашем предложении - правила вещания.
vectors # shape 2, 10
norms # shape 10
Форма не имеет одинаковой длины! Таким образом, правило состоит в том, чтобы сначала увеличить малую фигуру на один на влево:
norms # shape 1,10
Вы можете сделать это вручную, позвонив:
vectors / norms.reshape(1,-1) # same as vectors/norms
Если вы хотите вычислить vectors.T/norms
, вам нужно будет выполнить перестановку вручную, как показано ниже:
vectors.T / norms.reshape(-1,1) # this works
Ответ 2
Вычисление величины
Я столкнулся с этим вопросом и стал интересоваться вашим методом нормализации. Я использую другой метод для вычисления величин. Примечание. Я также обычно вычисляю нормы по последнему индексу (строки в этом случае, а не столбцы).
magnitudes = np.sqrt((vectors ** 2).sum(-1))[..., np.newaxis]
Как правило, однако, я просто нормализую так:
vectors /= np.sqrt((vectors ** 2).sum(-1))[..., np.newaxis]
Сравнение времени
Я проверил тест, чтобы сравнить время, и обнаружил, что мой метод выполняется быстрее, но предложение Freddie Witherdon еще быстрее.
import numpy as np
vectors = np.random.rand(100, 25)
# OP's
%timeit np.apply_along_axis(np.linalg.norm, 1, vectors)
# Output: 100 loops, best of 3: 2.39 ms per loop
# Mine
%timeit np.sqrt((vectors ** 2).sum(-1))[..., np.newaxis]
# Output: 10000 loops, best of 3: 13.8 us per loop
# Freddie (from comment below)
%timeit np.sqrt(np.einsum('...i,...i', vectors, vectors))
# Output: 10000 loops, best of 3: 6.45 us per loop
Остерегайтесь, так как это примечание fooobar.com/questions/89007/..., некоторые проверки безопасности не выполняются с einsum
, поэтому вы должны быть уверены, что dtype
of vectors
достаточно, чтобы точно хранить квадрат величин.
Ответ 3
В порядке: широковещательная передача формы NumPy добавляет размеры слева от формы массива, а не справа. Однако NumPy может быть проинструктирован добавить размер справа от массива norms
:
print vectors.T / norms[:, newaxis]
работает!
Ответ 4
в scikit уже есть функция:
import sklearn.preprocessing as preprocessing
norm =preprocessing.normalize(m, norm='l2')*
Дополнительная информация:
http://scikit-learn.org/stable/modules/preprocessing.html
Ответ 5
Мой предпочтительный способ нормализации векторов - использовать numpy inner1d для вычисления их величин. Здесь то, что было предложено до сих пор по сравнению с inner1d
import numpy as np
from numpy.core.umath_tests import inner1d
COUNT = 10**6 # 1 million points
points = np.random.random_sample((COUNT,3,))
A = np.sqrt(np.einsum('...i,...i', points, points))
B = np.apply_along_axis(np.linalg.norm, 1, points)
C = np.sqrt((points ** 2).sum(-1))
D = np.sqrt((points*points).sum(axis=1))
E = np.sqrt(inner1d(points,points))
print [np.allclose(E,x) for x in [A,B,C,D]] # [True, True, True, True]
Тестирование производительности с помощью cProfile:
import cProfile
cProfile.run("np.sqrt(np.einsum('...i,...i', points, points))**0.5") # 3 function calls in 0.013 seconds
cProfile.run('np.apply_along_axis(np.linalg.norm, 1, points)') # 9000018 function calls in 10.977 seconds
cProfile.run('np.sqrt((points ** 2).sum(-1))') # 5 function calls in 0.028 seconds
cProfile.run('np.sqrt((points*points).sum(axis=1))') # 5 function calls in 0.027 seconds
cProfile.run('np.sqrt(inner1d(points,points))') # 2 function calls in 0.009 seconds
inner1d вычислил величины волос быстрее, чем einsum. Поэтому, используя inner1d для нормализации:
n = points/np.sqrt(inner1d(points,points))[:,None]
cProfile.run('points/np.sqrt(inner1d(points,points))[:,None]') # 2 function calls in 0.026 seconds
Тестирование против scikit:
import sklearn.preprocessing as preprocessing
n_ = preprocessing.normalize(points, norm='l2')
cProfile.run("preprocessing.normalize(points, norm='l2')") # 47 function calls in 0.047 seconds
np.allclose(n,n_) # True
Заключение: использование inner1d представляется лучшим вариантом