NumPy: как быстро нормализовать множество векторов? - PullRequest
20 голосов
/ 17 мая 2010

Как можно элегантно нормализовать список векторов в 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

Последняя операция выдает «несоответствие формы: объекты не могут быть переданы одной форме».

Как можно элегантно выполнить нормализацию двумерных векторов в vectors с помощью NumPy?

Редактировать : Почему вышеописанное не работает при добавлении измерения в norms (согласно моему ответу ниже)?

Ответы [ 5 ]

26 голосов
/ 03 октября 2012

Вычисление величины

Я наткнулся на этот вопрос и мне стало любопытно, какой у вас метод нормализации. Я использую другой метод для вычисления величин. Примечание: я также обычно вычисляю нормы по последнему индексу (в данном случае это строки, а не столбцы).

magnitudes = np.sqrt((vectors ** 2).sum(-1))[..., np.newaxis]

Обычно, однако, я просто нормализую примерно так:

vectors /= np.sqrt((vectors ** 2).sum(-1))[..., np.newaxis]

Сравнение времени

Я провел тест, чтобы сравнить время, и обнаружил, что мой метод немного быстрее, но предложение Фредди Уизертона еще быстрее.

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's (from comment below)
%timeit np.sqrt(np.einsum('...i,...i', vectors, vectors))
# Output: 10000 loops, best of 3: 6.45 us per loop

Остерегайтесь, хотя, как отмечается в StackOverflow , некоторые проверки безопасности не выполняются с einsum, поэтому вы должны быть уверены, что dtype из vectors достаточно для хранения квадрата достаточно точно.

14 голосов
/ 17 мая 2010

Ну, если я что-то пропустил, это работает:

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
13 голосов
/ 17 мая 2010

Хорошо: трансляция формы массива NumPy добавляет размеры к слева формы массива, а не к его праву. Тем не менее, NumPy можно поручить добавить измерение справа от массива norms:

print vectors.T / norms[:, newaxis]

работает!

11 голосов
/ 28 ноября 2013

в scikit уже есть функция обучения:

import sklearn.preprocessing as preprocessing
norm =preprocessing.normalize(m, norm='l2')*

Больше информации по адресу:

http://scikit -learn.org / стабильный / модули / preprocessing.html

3 голосов
/ 19 февраля 2016

Мой предпочтительный способ нормализации векторов - это использование параметра 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 представляется наилучшим вариантом

...