神经网络梯度计算:*与np.dot()的使用场景困惑
神经网络反向传播中元素级乘法(
*)与矩阵点积(np.dot())的适用场景 问题背景
在学习神经网络训练的过程中,我遇到了一个问题:针对如下代码,无法明确何时使用*进行元素级乘法,何时使用np.dot()进行矩阵点积,由于两者输出结果存在差异,明确其适用场景至关重要。
def loss_gradients(forward_info: Dict[str, ndarray], weights: Dict[str, ndarray]) -> Dict[str, ndarray]: ''' Compute the partial derivatives of the loss with respect to each of the parameters in the neural network. ''' dLdP = -(forward_info['y'] - forward_info['P']) dPdM2 = np.ones_like(forward_info['M2']) dLdM2 = dLdP * dPdM2 dPdB2 = np.ones_like(weights['B2']) dLdB2 = (dLdP * dPdB2).sum(axis=0) dM2dW2 = np.transpose(forward_info['O1'], (1, 0)) dLdW2 = np.dot(dM2dW2, dLdP) dM2dO1 = np.transpose(weights['W2'], (1, 0)) dLdO1 = np.dot(dLdM2, dM2dO1) dO1dN1 = sigmoid(forward_info['N1']) * (1- sigmoid(forward_info['N1'])) dLdN1 = dLdO1 * dO1dN1 dN1dB1 = np.ones_like(weights['B1']) dN1dM1 = np.ones_like(forward_info['M1']) dLdB1 = (dLdN1 * dN1dB1).sum(axis=0) dLdM1 = dLdN1 * dN1dM1 dM1dW1 = np.transpose(forward_info['X'], (1, 0)) dLdW1 = np.dot(dM1dW1, dLdM1) loss_gradients: Dict[str, ndarray] = {} loss_gradients['W2'] = dLdW2 loss_gradients['B2'] = dLdB2.sum(axis=0) loss_gradients['W1'] = dLdW1 loss_gradients['B1'] = dLdB1.sum(axis=0) return loss_gradients
一、元素级乘法(*,等价于np.multiply)
适用场景
当你需要对形状完全相同的两个张量,执行逐位置对应相乘的操作时使用,这对应链式法则里"逐元素传递梯度"的逻辑,常见于激活函数导数计算、损失对中间张量的直接梯度传递。
代码中的对应示例
激活函数导数计算:
dO1dN1 = sigmoid(forward_info['N1']) * (1- sigmoid(forward_info['N1']))sigmoid的导数公式是
σ'(x) = σ(x)*(1-σ(x)),需要对每个元素单独计算,因此用元素级乘法。梯度逐元素传递:
dLdM2 = dLdP * dPdM2 dLdN1 = dLdO1 * dO1dN1这里
dLdP与dPdM2、dLdO1与dO1dN1的形状完全一致,通过逐元素相乘,将损失梯度传递到前一个中间张量。偏置梯度的中间计算:
dLdB2 = (dLdP * dPdB2).sum(axis=0)dPdB2是和偏置B2同形状的全1矩阵,先和dLdP逐元素相乘,再沿着样本维度(axis=0)求和,得到所有样本对偏置的梯度总和(因为偏置在所有样本中共享)。
二、矩阵点积(np.dot())
适用场景
当你需要执行**线性代数中的矩阵乘法(或向量内积)**时使用,对应链式法则里"维度收缩、跨层传递梯度"的逻辑,主要用于计算权重参数的梯度——需要将前一层的激活张量与当前层的梯度张量进行矩阵乘法,以匹配权重的维度。
代码中的对应示例
输出层权重梯度计算:
dM2dW2 = np.transpose(forward_info['O1'], (1, 0)) dLdW2 = np.dot(dM2dW2, dLdP)O1的形状是[样本数, 隐藏层维度],转置后dM2dW2为[隐藏层维度, 样本数]dLdP的形状是[样本数, 输出层维度]- 两者点积后得到
[隐藏层维度, 输出层维度]的结果,正好匹配权重W2的形状。
隐藏层激活梯度传递:
dM2dO1 = np.transpose(weights['W2'], (1, 0)) dLdO1 = np.dot(dLdM2, dM2dO1)W2的形状是[隐藏层维度, 输出层维度],转置后dM2dO1为[输出层维度, 隐藏层维度]dLdM2的形状是[样本数, 输出层维度]- 点积后得到
[样本数, 隐藏层维度]的结果,匹配隐藏层激活O1的形状,实现梯度从输出层传递到隐藏层。
输入层权重梯度计算:
dM1dW1 = np.transpose(forward_info['X'], (1, 0)) dLdW1 = np.dot(dM1dW1, dLdM1)逻辑和输出层权重梯度一致,通过输入张量的转置与隐藏层梯度点积,得到输入层权重
W1的梯度。
关键区分总结
- 形状要求:
*要求两个张量形状完全一致;np.dot()要求第一个张量的最后一维长度,等于第二个张量的倒数第二维长度(符合矩阵乘法规则:(m,n) · (n,p) = (m,p))。
- 数学意义:
*是Hadamard积(逐元素相乘);np.dot()是线性代数中的矩阵乘法/内积。
内容的提问来源于stack exchange,提问作者Soham Ghodake
相关产品推荐
相关产品推荐

