np.dot 和 np.multiply 与 np.sum 在二进制交叉熵损失计算中的区别

Posted

技术标签:

【中文标题】np.dot 和 np.multiply 与 np.sum 在二进制交叉熵损失计算中的区别【英文标题】:Difference between np.dot and np.multiply with np.sum in binary cross-entropy loss calculation 【发布时间】:2018-06-20 11:13:46 【问题描述】:

我尝试了以下代码,但没有发现 np.dotnp.multiply 与 np.sum 之间的区别

这里是 np.dot 代码

logprobs = np.dot(Y, (np.log(A2)).T) + np.dot((1.0-Y),(np.log(1 - A2)).T)
print(logprobs.shape)
print(logprobs)
cost = (-1/m) * logprobs
print(cost.shape)
print(type(cost))
print(cost)

它的输出是

(1, 1)
[[-2.07917628]]
(1, 1)
<class 'numpy.ndarray'>
[[ 0.693058761039 ]]

这是 np.multiply 与 np.sum 的代码

logprobs = np.sum(np.multiply(np.log(A2), Y) + np.multiply((1 - Y), np.log(1 - A2)))
print(logprobs.shape)         
print(logprobs)
cost = - logprobs / m
print(cost.shape)
print(type(cost))
print(cost)

它的输出是

()
-2.07917628312
()
<class 'numpy.float64'>
0.693058761039

我无法理解类型和形状的差异,而两种情况下的结果值相同

即使在压缩前代码的情况下成本值与后相同但类型保持相同

cost = np.squeeze(cost)
print(type(cost))
print(cost)

输出是

<class 'numpy.ndarray'>
0.6930587610394646

【问题讨论】:

np.sum 返回一个标量,np.dot 不... 要获得具体答案,您可能应该提供输入数组的形状。通常,您看到的是 np.sum 默认情况下总是对整个输入求和并返回一个标量。 np.dot 仅在一个轴上求和(在您的情况下似乎是唯一一个)并保留尺寸。 您正在做的是计算 binary cross-entropy loss 并且您尝试的两种方法都是 等效 这就是您得到相同结果的原因。目前尚不清楚您的实际问题是什么。 实际上我正在寻找一个标量值来回答 np.dot 要获得标量值,您需要使用一维数组,而不是二维数组。 【参考方案1】:

np.dot 是两个矩阵的dot product。

|A B| . |E F| = |A*E+B*G A*F+B*H|
|C D|   |G H|   |C*E+D*G C*F+D*H|

np.multiply 执行两个矩阵的element-wise multiplication。

|A B| ⊙ |E F| = |A*E B*F|
|C D|   |G H|   |C*G D*H|

np.sum 一起使用时,结果相等只是巧合。

>>> np.dot([[1,2], [3,4]], [[1,2], [2,3]])
array([[ 5,  8],
       [11, 18]])
>>> np.multiply([[1,2], [3,4]], [[1,2], [2,3]])
array([[ 1,  4],
       [ 6, 12]])

>>> np.sum(np.dot([[1,2], [3,4]], [[1,2], [2,3]]))
42
>>> np.sum(np.multiply([[1,2], [3,4]], [[1,2], [2,3]]))
23

【讨论】:

只使用* 将两个矩阵相乘怎么样? 使用* 将矩阵相乘也可以进行元素乘法,如⊙ 和np.multiply 运算符。 嘿,你能解释一下 np.matmul() 的作用吗 @Avnishkumar 它执行矩阵乘法。请参阅我上面的答案以获得更多解释!【参考方案2】:

您所做的是计算binary cross-entropy loss,它衡量模型的预测(此处:A2)与真实输出(此处:Y)相比有多糟糕。

这是您案例的可重现示例,这应该可以解释为什么您在第二种情况下使用 np.sum 得到一个标量

In [88]: Y = np.array([[1, 0, 1, 1, 0, 1, 0, 0]])

In [89]: A2 = np.array([[0.8, 0.2, 0.95, 0.92, 0.01, 0.93, 0.1, 0.02]])

In [90]: logprobs = np.dot(Y, (np.log(A2)).T) + np.dot((1.0-Y),(np.log(1 - A2)).T)

# `np.dot` returns 2D array since its arguments are 2D arrays
In [91]: logprobs
Out[91]: array([[-0.78914626]])

In [92]: cost = (-1/m) * logprobs

In [93]: cost
Out[93]: array([[ 0.09864328]])

In [94]: logprobs = np.sum(np.multiply(np.log(A2), Y) + np.multiply((1 - Y), np.log(1 - A2)))

# np.sum returns scalar since it sums everything in the 2D array
In [95]: logprobs
Out[95]: -0.78914625761870361

请注意,np.dot 仅对与此处 (1x8) and (8x1) 匹配的内部尺寸求和。因此,8s 将在点积或矩阵乘法期间消失,产生的结果为 (1x1),这只是一个 标量,但返回为形状为 (1,1) 的二维数组。


另外,最重要的是注意这里np.dot is exactly same as doing np.matmul,因为输入是二维数组(即矩阵)

In [107]: logprobs = np.matmul(Y, (np.log(A2)).T) + np.matmul((1.0-Y),(np.log(1 - A2)).T)

In [108]: logprobs
Out[108]: array([[-0.78914626]])

In [109]: logprobs.shape
Out[109]: (1, 1)

标量值的形式返回结果

np.dotnp.matmul 根据输入数组返回任何结果数组形状。即使使用out= 参数,如果输入是二维数组,也无法返回标量。但是,如果结果数组的形状为 (1,1)(或更一般的 scalar 值包装在 nD 数组中),我们可以在结果上使用 np.asscalar() 将其转换为标量

In [123]: np.asscalar(logprobs)
Out[123]: -0.7891462576187036

In [124]: type(np.asscalar(logprobs))
Out[124]: float

ndarray 大小为 1 到 标量

In [127]: np.asscalar(np.array([[[23.2]]]))
Out[127]: 23.2

In [128]: np.asscalar(np.array([[[[23.2]]]]))
Out[128]: 23.2

【讨论】:

在这种情况下我们不能仅使用np.dot() 获得标量值吗?因为它给出的答案与np.multiply()np.sum() 相同。 @AsadShakeel 添加了将结果转换为标量的技巧:) 谢谢!如果可以的话,我会多次支持你:) 很好的解释。节省了大量时间。【参考方案3】:

如果 YA2 是 (1,N) 数组,则 np.dot(Y,A.T) 将产生 (1,1) 结果。它正在执行 (1,N) 与 (N,1) 的矩阵乘法。 N's 相加,留下 (1,1)。

multiply 的结果是 (1,N)。对所有值求和,结果是一个标量。

如果 YA2 是 (N,) 形(元素数量相同,但 1d),np.dot(Y,A2)(没有 .T)也会产生一个标量。来自np.dot 文档:

对于二维数组,它相当于矩阵乘法,对于一维数组,它相当于向量的内积

返回 a 和 b 的点积。如果 a 和 b 都是标量或都是一维数组,则返回标量;否则返回一个数组。

squeeze 减少所有大小为 1 的维度,但仍返回一个数组。在numpy 中,数组可以有任意数量的维度(从 0 到 32)。所以一个 0d 数组是可能的。比较np.array(3)np.array([3])np.array([[3]])的形状。

【讨论】:

【参考方案4】:
In this example it just not a coincidence. Lets take an example we have two (1,3) and (1,3) matrices. 
// Lets code 

import numpy as np

x1=np.array([1, 2, 3]) // first array
x2=np.array([3, 4, 3]) // second array

//Then 

X_Res=np.sum(np.multiply(x1,x2)) 
// will result 20 as it will be calculated as - (1*3)+(2*4)+(3*3) , i.e element wise
// multiplication followed by sum.

Y_Res=np.dot(x1,x2.T) 

// in order to get (1,1) matrix) from a dot of (1,3) matrix and //(1,3) matrix we need to //transpose second one. 
//Hence|1 2 3| * |3|
//               |4| = |1*3+2*4+3*3| = |20|
//               |3|
// will result 20 as it will be (1*3)+(2*4)+(3*3) , i.e. dot product of two matrices

print X_Res //20

print Y_Res //20

【讨论】:

以上是关于np.dot 和 np.multiply 与 np.sum 在二进制交叉熵损失计算中的区别的主要内容,如果未能解决你的问题,请参考以下文章

np.array()和np.mat()区别

为啥 np.dot 不精确? (n 维数组)

python中使用numpy包的向量矩阵相乘np.dot和np.matmul

python np.dot啥意思

多元回归没有得到与 sklearn 相同的系数

numpy使用np.dot函数或者@操作符计算两个numpy数组的点积数量积(dot productscalar product)