代码之家  ›  专栏  ›  技术社区  ›  Eric O. Lebigot

如何快速规范化许多向量?

  •  20
  • Eric O. Lebigot  · 技术社区  · 15 年前

    如何在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 做得优雅,麻木?

    编辑 :向添加维度时,为什么上述操作不起作用? norms 是否有效(根据我下面的答案)?

    5 回复  |  直到 7 年前
        1
  •  13
  •   Eric O. Lebigot    11 年前

    好吧,除非我错过了什么,否则这确实有效:

    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
  •  25
  •   Community CDub    8 年前

    计算震级

    我遇到这个问题,对你的标准化方法很好奇。我用另一种方法来计算震级。 注意:我通常还计算最后一个索引的规范(在本例中是行,而不是列)。

    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'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 answer 注意,有些安全检查没有发生 einsum ,所以您应该确保 dtype 属于 vectors 足够精确地存储震级的平方。

        3
  •  13
  •   Eric O. Lebigot    12 年前

    好的:numpy的数组形状广播为 左边 数组的形状,而不是右边。但是,可以指示numpy在 norms 数组:

    print vectors.T / norms[:, newaxis]
    

    工作!

        4
  •  10
  •   SenhorSchaefers    11 年前

    SciKit学习中已经有一个功能:

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

    更多信息在:

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

        5
  •  2
  •   Fnord    9 年前

    我对向量规格化的首选方法是使用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似乎是最佳选择