【问题标题】:Applying a function along a numpy array沿 numpy 数组应用函数
【发布时间】:2017-08-18 21:10:07
【问题描述】:

我有以下 numpy ndarray。

[ -0.54761371  17.04850603   4.86054302]

我想将此函数应用于数组的所有元素

def sigmoid(x):
  return 1 / (1 + math.exp(-x))

probabilities = np.apply_along_axis(sigmoid, -1, scores)

这是我得到的错误。

TypeError: only length-1 arrays can be converted to Python scalars

我做错了什么。

【问题讨论】:

  • math.exp 替换为np.exp 即可解决问题

标签: python numpy


【解决方案1】:

函数numpy.apply_along_axis 不适用于此目的。 尝试使用numpy.vectorize 向量化您的函数:https://docs.scipy.org/doc/numpy/reference/generated/numpy.vectorize.html 该函数定义了一个向量化函数,该函数将嵌套的对象序列或 numpy 数组作为输入,并返回单个或 numpy 数组的元组作为输出。

import numpy as np
import math

# custom function
def sigmoid(x):
  return 1 / (1 + math.exp(-x))

# define vectorized sigmoid
sigmoid_v = np.vectorize(sigmoid)

# test
scores = np.array([ -0.54761371,  17.04850603,   4.86054302])
print sigmoid_v(scores)

输出:[ 0.36641822 0.99999996 0.99231327]

性能测试表明scipy.special.expit 是计算逻辑函数的最佳解决方案,而矢量化变体则最差:

import numpy as np
import math
import timeit

def sigmoid_(x):
  return 1 / (1 + math.exp(-x))
sigmoidv = np.vectorize(sigmoid_)

def sigmoid(x):
   return 1 / (1 + np.exp(x))

print timeit.timeit("sigmoidv(scores)", "from __main__ import sigmoidv, np; scores = np.random.randn(100)", number=25),\
timeit.timeit("sigmoid(scores)", "from __main__ import sigmoid, np; scores = np.random.randn(100)",  number=25),\
timeit.timeit("expit(scores)", "from scipy.special import expit; import numpy as np;   scores = np.random.randn(100)",  number=25)

print timeit.timeit("sigmoidv(scores)", "from __main__ import sigmoidv, np; scores = np.random.randn(1000)", number=25),\
timeit.timeit("sigmoid(scores)", "from __main__ import sigmoid, np; scores = np.random.randn(1000)",  number=25),\
timeit.timeit("expit(scores)", "from scipy.special import expit; import numpy as np;   scores = np.random.randn(1000)",  number=25)

print timeit.timeit("sigmoidv(scores)", "from __main__ import sigmoidv, np; scores = np.random.randn(10000)", number=25),\
timeit.timeit("sigmoid(scores)", "from __main__ import sigmoid, np; scores = np.random.randn(10000)",  number=25),\
timeit.timeit("expit(scores)", "from scipy.special import expit; import numpy as np;   scores = np.random.randn(10000)",  number=25)

结果:

size        vectorized      numpy                 expit
N=100:   0.00179314613342 0.000460863113403 0.000132083892822
N=1000:  0.0122890472412  0.00084114074707  0.000464916229248
N=10000: 0.109477043152   0.00530695915222  0.00424313545227

【讨论】:

  • 值得注意的是:“提供vectorize函数主要是为了方便,而不是为了性能。实现本质上是一个for循环。”
  • numpy vectorize 的效率取决于数组的大小。
  • 当然可以,但它基本上是一个带有额外开销的 python for 循环。
  • 我想如果数组的大小变得重要,这些开销并不重要。即:stackoverflow.com/questions/35215161/…
  • 啊,我不知道scipy.special.expit,那肯定会更快!
【解决方案2】:

使用np.exp,它将以矢量化方式处理 numpy 数组:

>>> def sigmoid(x):
...     return 1 / (1 + np.exp(-x))
...
>>> sigmoid(scores)
array([  6.33581776e-01,   3.94391811e-08,   7.68673281e-03])
>>>

您可能不会比这更快。考虑:

>>> def sigmoid(x):
...     return 1 / (1 + np.exp(-x))
...

还有:

>>> def sigmoidv(x):
...   return 1 / (1 + math.exp(-x))
...
>>> vsigmoid = np.vectorize(sigmoidv)

现在,比较时间。使用小型(大小 100)数组:

>>> t = timeit.timeit("vsigmoid(arr)", "from __main__ import vsigmoid, np; arr = np.random.randn(100)", number=100)
>>> t
0.006894525984534994
>>> t = timeit.timeit("sigmoid(arr)", "from __main__ import sigmoid, np; arr = np.random.randn(100)", number=100)
>>> t
0.0007238480029627681

所以,小数组仍然存在数量级差异。对于 10,000 大小的数组,这种性能差异保持相对恒定:

>>> t = timeit.timeit("vsigmoid(arr)", "from __main__ import vsigmoid, np; arr = np.random.randn(10000)", number=100)
>>> t
0.3823414359940216
>>> t = timeit.timeit("sigmoid(arr)", "from __main__ import sigmoid, np; arr = np.random.randn(10000)", number=100)
>>> t
0.011259705002885312

最后是一个大小为 100,000 的数组:

>>> t = timeit.timeit("vsigmoid(arr)", "from __main__ import vsigmoid, np; arr = np.random.randn(100000)", number=100)
>>> t
3.7680041620042175
>>> t = timeit.timeit("sigmoid(arr)", "from __main__ import sigmoid, np; arr = np.random.randn(100000)", number=100)
>>> t
0.09544878199812956

【讨论】:

  • 谢谢,这应该是公认的答案,而不是上面的答案。
【解决方案3】:

只是为了澄清 apply_along_axis 正在做什么,或不做什么。

def sigmoid(x):
  print(x)    # show the argument
  return 1 / (1 + math.exp(-x))

In [313]: np.apply_along_axis(sigmoid, -1,np.array([ -0.54761371  ,17.04850603 ,4.86054302])) 
[ -0.54761371  17.04850603   4.86054302]   # the whole array
...
TypeError: only length-1 arrays can be converted to Python scalars

您收到错误的原因是apply_along_axis 将整个一维数组传递给您的函数。 IE。轴。对于您的一维数组,这与

sigmoid(np.array([ -0.54761371  ,17.04850603 ,4.86054302]))

apply_along_axis 对你没有任何作用。

正如其他人所指出的,切换到 np.exp 允许 sigmoid 使用数组(有或没有 apply_along_axis 包装器)。

【讨论】:

    【解决方案4】:

    scipy 已经实现了该功能 幸运的是,Python 允许我们在导入时重命名:

     from scipy.special import expit as sigmoid
    

    【讨论】:

      猜你喜欢
      • 2017-11-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-07-01
      • 2021-05-18
      • 2011-06-29
      • 2011-01-22
      相关资源
      最近更新 更多