一个numpy数组的softmax函数逐行
Posted
技术标签:
【中文标题】一个numpy数组的softmax函数逐行【英文标题】:Softmax function of a numpy array by row 【发布时间】:2017-09-03 13:08:32 【问题描述】:我正在尝试将 softmax 函数应用于 numpy 数组。但我没有得到想要的结果。这是我尝试过的代码:
import numpy as np
x = np.array([[1001,1002],[3,4]])
softmax = np.exp(x - np.max(x))/(np.sum(np.exp(x - np.max(x)))
print softmax
我认为x - np.max(x)
代码没有减去每行的最大值。需要从 x 中减去最大值以防止出现非常大的数字。
这应该是输出
np.array([
[0.26894142, 0.73105858],
[0.26894142, 0.73105858]])
但我得到了:
np.array([
[0.26894142, 0.73105858],
[0, 0]])
【问题讨论】:
【参考方案1】:编辑。从 1.2.0 版本开始,scipy 包含了 softmax 作为一个特殊功能:
https://scipy.github.io/devdocs/generated/scipy.special.softmax.html
我写了一个在任意轴上操作的非常通用的 softmax 函数,包括棘手的最大减法位。函数如下,我写了blog post about it here。
def softmax(X, theta = 1.0, axis = None):
"""
Compute the softmax of each element along an axis of X.
Parameters
----------
X: ND-Array. Probably should be floats.
theta (optional): float parameter, used as a multiplier
prior to exponentiation. Default = 1.0
axis (optional): axis to compute values along. Default is the
first non-singleton axis.
Returns an array the same size as X. The result will sum to 1
along the specified axis.
"""
# make X at least 2d
y = np.atleast_2d(X)
# find axis
if axis is None:
axis = next(j[0] for j in enumerate(y.shape) if j[1] > 1)
# multiply y against the theta parameter,
y = y * float(theta)
# subtract the max for numerical stability
y = y - np.expand_dims(np.max(y, axis = axis), axis)
# exponentiate y
y = np.exp(y)
# take the sum along the specified axis
ax_sum = np.expand_dims(np.sum(y, axis = axis), axis)
# finally: divide elementwise
p = y / ax_sum
# flatten if X was 1D
if len(X.shape) == 1: p = p.flatten()
return p
【讨论】:
【参考方案2】:我的 5-liner(它使用 scipy logsumexp 来处理棘手的问题):
def softmax(a, axis=None):
"""
Computes exp(a)/sumexp(a); relies on scipy logsumexp implementation.
:param a: ndarray/tensor
:param axis: axis to sum over; default (None) sums over everything
"""
from scipy.special import logsumexp
lse = logsumexp(a, axis=axis) # this reduces along axis
if axis is not None:
lse = np.expand_dims(lse, axis) # restore that axis for subtraction
return np.exp(a - lse)
如果你有旧的 scipy 版本,你可能必须使用 from scipy.misc import logsumexp
。
【讨论】:
很漂亮。【参考方案3】:这个怎么样?
要沿行获取max
,只需将参数指定为axis=1
,然后使用np.newaxis/None
将结果转换为列向量(但实际上是二维数组)。
In [40]: x
Out[40]:
array([[1001, 1002],
[ 3, 4]])
In [41]: z = x - np.max(x, axis=1)[:, np.newaxis]
In [42]: z
Out[42]:
array([[-1, 0],
[-1, 0]])
In [44]: softmax = np.exp(z) / np.sum(np.exp(z), axis=1)[:, np.newaxis]
In [45]: softmax
Out[45]:
array([[ 0.26894142, 0.73105858],
[ 0.26894142, 0.73105858]])
在最后一步中,再次进行求和时,只需指定参数axis=1
即可沿行求和。
【讨论】:
您也必须在softmax
行 (44) 中执行 [:, np.newaxis]
的操作。对于给定的示例,您碰巧得到了正确的结果,但这本质上是巧合。 (它之所以有效,是因为两行总和恰好具有相同的值,因此它们以哪种方式广播并不重要。)尝试例如x = [[1001, 1002], [1, 4]]
而不是得到错误的结果。或x = [[1001, 1002, 1003], [2, 3, 4]]
得到一个彻底的错误。
@PaulPanzer Danke Schön!发现此类错误的最佳方法是什么?我对 NumPy 的理解是如此微妙
不要在你的玩具示例中使用方形数组 ;-] 说真的,这对我来说至少抓住了其中的一半。【参考方案4】:
keepdims
关键字是保留“减少”操作(例如 max
或 sum
)所消耗的轴的便捷方法:
mx = np.max(x, axis=-1, keepdims=True)
mx
# array([[1002],
# [ 4]])
x - mx
# array([[-1, 0],
# [-1, 0]])
numerator = np.exp(x - mx)
denominator = np.sum(numerator, axis=-1, keepdims=True)
denominator
# array([[ 1.36787944],
# [ 1.36787944]])
numerator/denominator
# array([[ 0.26894142, 0.73105858],
[ 0.26894142, 0.73105858]])
【讨论】:
【参考方案5】:x - np.max(x)
代码没有进行逐行减法。
让我们逐步进行。首先,我们将通过平铺或复制列来创建一个“maxes”数组:
maxes = np.tile(np.max(x,1), (2,1)).T
这将创建一个 2X2 矩阵,该矩阵将通过复制列(平铺)对应于每行的最大值。之后你可以这样做:
x = np.exp(x - maxes)/(np.sum(np.exp(x - maxes), axis = 1))
你应该得到你的结果。 axis = 1
用于您在答案标题中提到的逐行 softmax。希望这会有所帮助。
【讨论】:
以上是关于一个numpy数组的softmax函数逐行的主要内容,如果未能解决你的问题,请参考以下文章
在 tensorflow 2.4 中使用 sampled_softmax 时无法将符号 Keras 输入/输出转换为 numpy 数组 TypeError