2010-05-17 35 views
19

Làm thế nào để danh sách các vectơ được chuẩn hóa một cách thanh lịch, trong NumPy?NumPy: làm thế nào để nhanh chóng chuẩn hóa nhiều vectơ?

Dưới đây là một ví dụ mà không không công việc:

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 

Lợi suất hoạt động cuối cùng "hình dạng không phù hợp: đối tượng không thể được phát sóng đến một hình dạng duy nhất".

Làm cách nào để chuẩn hóa các vectơ 2D trong vectors được thực hiện một cách tao nhã, với NumPy?

Chỉnh sửa: Tại sao điều trên không hoạt động khi thêm thứ nguyên vào norms hoạt động (theo câu trả lời của tôi bên dưới)?

+0

FYI, một commenter có thể có một phương pháp nhanh hơn, tôi thay đổi nội dung của tôi trả lời chi tiết hơn. – Geoff

Trả lời

12

Vâng, trừ khi tôi bị mất một cái gì đó, điều này không làm việc:

vectors/norms 

Vấn đề trong đề xuất của bạn là các quy tắc truyền thông.

vectors # shape 2, 10 
norms # shape 10 

Hình dạng không có cùng độ dài! Vì vậy, nguyên tắc là lần đầu tiên mở rộng hình dạng nhỏ bởi một trên trái:

norms # shape 1,10 

Bạn có thể làm điều đó bằng tay bằng cách gọi:

vectors/norms.reshape(1,-1) # same as vectors/norms 

Nếu bạn muốn tính toán vectors.T/norms, bạn sẽ phải để làm tái tạo hình bằng tay, như sau:

vectors.T/norms.reshape(-1,1) # this works 
+0

tại sao không chỉ làm (vectơ/định mức) .T nếu OP muốn điều này transposed. Có vẻ như đơn giản và thanh lịch với tôi. –

+0

Ah, ah! do đó, phần mở rộng thứ nguyên được thực hiện trên _left_: điều này thực sự giải thích hành vi được quan sát. Cảm ơn! – EOL

13

Được rồi: Phát sóng hình dạng mảng của NumPy thêm thứ nguyên vào bên trái của hình dạng mảng chứ không phải ở bên phải. Tuy nhiên, NumPy có thể được hướng dẫn thêm một thứ nguyên ở bên phải của mảng norms:

print vectors.T/norms[:, newaxis] 

không hoạt động!

+3

Chỉ cần một lưu ý, tôi sử dụng 'norms [..., np.newaxis]' trong trường hợp ma trận không chỉ là 2D. Nó sẽ làm việc với một 3D (hoặc nhiều hơn) tensor là tốt. – Geoff

23

Đang tính toán độ lớn

Tôi đã xem qua câu hỏi này và trở nên tò mò về phương pháp của bạn để bình thường hóa. Tôi sử dụng một phương pháp khác để tính toán độ lớn. Lưu ý: Tôi cũng thường tính toán các chỉ tiêu trên chỉ mục cuối cùng (các hàng trong trường hợp này, không phải cột).

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

Thông thường, tuy nhiên, tôi chỉ bình thường như vậy:

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

Một thời gian so sánh

Tôi chạy một thử nghiệm để so sánh thời gian, và thấy rằng phương pháp của tôi là nhanh hơn bằng cách khá một chút, nhưng đề xuất của Freddie Witherdon thậm chí còn nhanh hơn.

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 

Cẩn thận mặc dù, vì điều này StackOverflow answer ghi chú, có một số kiểm tra an toàn không xảy ra với einsum, vì vậy bạn nên chắc chắn rằng dtype của vectors là đủ để lưu trữ các bình phương của cường độ chính xác đủ.

+1

Kết quả thời gian thú vị (tôi nhận được tương ứng 0,8 s và 1,4 s, với chức năng timetime% mạnh mẽ hơn của IPython), cảm ơn! – EOL

+2

Tôi đã tìm thấy 'np.sqrt (np.einsum ('... i, ... i', vectơ, vectơ))' là ~ 4 lần nhanh hơn phương pháp 1 như đã nêu ở trên. –

+0

@FreddieWitherden - Cảm ơn nhận xét, tôi không biết về 'einsum'. Có một câu hỏi thú vị liên quan đến SO ở đây: http://stackoverflow.com/questions/18365073/why-is-numpys-einsum-faster-than-numpys-built-in-functions Nó thường sẽ nhanh hơn, nhưng có thể không an toàn (tùy thuộc vào 'dtype' của vectơ). – Geoff

9

đã có một chức năng trong scikit học:

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

Thông tin thêm tại địa chỉ:

http://scikit-learn.org/stable/modules/preprocessing.html

+0

Thông tin thú vị, nhưng câu hỏi là rõ ràng về NumPy. Nó sẽ là tốt hơn đặt nó trong một bình luận cho câu hỏi ban đầu. – EOL

2

cách ưa thích của tôi bình thường hóa vectơ được bằng cách sử dụng inner1d NumPy để tính toán độ lớn của họ. Dưới đây là những gì đang được đề xuất cho đến nay so với 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] 

hiệu suất kiểm tra với 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 tính độ lớn một sợi tóc nhanh hơn einsum. Vì vậy, sử dụng inner1d để bình thường hóa:

n = points/np.sqrt(inner1d(points,points))[:,None] 
cProfile.run('points/np.sqrt(inner1d(points,points))[:,None]') # 2 function calls in 0.026 seconds 

kiểm tra chống 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 

Kết luận: sử dụng inner1d dường như là lựa chọn tốt nhất

+0

Để tham khảo, câu hỏi thực sự gọi để tính toán định mức dọc theo thứ nguyên _first_, không phải là thứ hai (xem báo trước được thêm vào câu trả lời của Geoff). Điều này sẽ thay đổi kết quả như thế nào? Có thể có một tác động, vì cách truy cập bộ nhớ, đặc biệt nếu bạn có thứ nguyên thứ hai lớn hơn (thay vì 3 trong ví dụ của bạn). – EOL

Các vấn đề liên quan