反向传播实现报错:权重与权重增量维度不匹配问题排查
反向传播实现维度不匹配问题排查与修复
问题背景
我正在遵循《15 Steps to Implement a Neural Net》指南实现神经网络,目前卡在第12步的backpropagation(反向传播)实现环节。运行代码时触发ValueError,提示weights和weights_delta维度不匹配:weights形状为(5,3),weights_delta形状为(3,3),无法执行加法操作。
相关代码
import numpy as np # 假设activation_function、activation_function_derivative、evaluate_error、outputs_to_classes、PLOT_GRAPHS、plt已定义 def feed_forward(inputs, weights, biases): net = np.matmul(np.hstack((inputs, biases)), weights) output = activation_function(net) return (output, net) def initialize_weights(width, height, max_weight): return (np.random.random((width, height)) * np.random.randint(-max_weight, max_weight + 1, (width, height))) def backpropagation(inputs, weights, learning_rate, biases, number_of_samples_for_backpropagation, target_outputs): random_sample_index = np.random.randint(0, inputs.shape[0], size=number_of_samples_for_backpropagation) random_sample = inputs[random_sample_index, :] print("random_sample.shape:") print(random_sample.shape) target_outputs = target_outputs[random_sample_index, :] print("target_outputs.shape:") print(target_outputs.shape) biases = biases[random_sample_index, :] print("biases.shape:") print(biases.shape) outputs, net = feed_forward(random_sample, weights, biases) print("weights.shape:") print(weights.shape) print("outputs.shape:") print(outputs.shape) print("net.shape:") print(net.shape) error_vector = target_outputs - outputs print("error_vector.shape:") print(error_vector.shape) delta = np.multiply(error_vector, activation_function_derivative(net)) print("delta.shape:") print(delta.shape) weights_delta = learning_rate * np.kron(outputs.T, delta) print("outputs.T.shape:") print(outputs.T.shape) print("np.kron(outputs.T, delta).shape:") print(np.kron(outputs.T, delta).shape) weights = weights + weights_delta return weights def train(training_set_features, training_set_targets, number_of_samples_for_backpropagation, val_set_features, val_set_targets, test_set_features, test_set_targets): number_of_columns_train_features = training_set_features.shape[1] number_of_columns_train_targets = training_set_targets.shape[1] max_weight = 0.5 weights = initialize_weights(number_of_columns_train_features + 1, number_of_columns_train_targets, max_weight) number_of_rows_train = training_set_features.shape[0] biases_train = np.ones(shape=(number_of_rows_train, 1)) number_of_rows_val = val_set_features.shape[0] biases_val = np.ones(shape=(number_of_rows_val, 1)) number_of_rows_test = test_set_features.shape[0] biases_test = np.ones(shape=(number_of_rows_test, 1)) # training parameters number_of_epochs = 500 learning_rate = 0.1 train_errors = [] train_classification_errors = [] val_errors = [] val_classification_errors = [] test_errors = [] test_classification_errors = [] current_epoch_number = 0 while (current_epoch_number < number_of_epochs): weights = backpropagation(training_set_features, weights, learning_rate, biases_train, number_of_samples_for_backpropagation, training_set_targets) if (PLOT_GRAPHS == True): train_error, train_classification_error = evaluate_error(training_set_features, weights, training_set_targets, outputs_to_classes(training_set_targets), biases_train) val_error, val_classification_error = evaluate_error(val_set_features, weights, val_set_targets, outputs_to_classes(val_set_targets), biases_val) test_error, test_classification_error = evaluate_error(test_set_features, weights, test_set_targets, outputs_to_classes(test_set_targets), biases_test) train_errors.append(train_error) train_classification_errors.append(train_classification_error) val_errors.append(val_error) val_classification_errors.append(val_classification_error) test_errors.append(test_error) test_classification_errors.append(test_classification_error) current_epoch_number = current_epoch_number + 1 if (PLOT_GRAPHS == True): plt.plot(train_errors, label="Train errors") plt.plot(train_classification_errors, label="Train classification errors") plt.plot(val_errors, label="Validation errors") plt.plot(val_classification_errors, label="Validation classification errors") plt.plot(test_errors, label="Test errors") plt.plot(test_classification_errors, label="Test classification errors") plt.legend(loc="upper left") train_error, train_classification_error = evaluate_error(training_set_features, weights, training_set_targets, outputs_to_classes(training_set_targets), biases_train) val_error, val_classification_error = evaluate_error(val_set_features, weights, val_set_targets, outputs_to_classes(val_set_targets), biases_val) test_error, test_classification_error = evaluate_error(test_set_features, weights, test_set_targets, outputs_to_classes(test_set_targets), biases_test) # debug prints print("train_errors:") print(train_errors) print("train_classification_errors:") print(train_classification_errors) print("val_errors:") print(val_errors) print("val_classification_errors:") print(val_classification_errors) print("test_errors:") print(test_errors) print("test_classification_errors:") print(test_classification_errors) return (weights, train_error, train_classification_error, val_error, val_classification_error, test_error, test_classification_error) NUMBER_OF_SAMPLES_FOR_BACKPROPAGATION = 1 # 假设iris_train_features等已加载 # train(iris_train_features.values, iris_train_targets.values, NUMBER_OF_SAMPLES_FOR_BACKPROPAGATION, iris_val_features.values, iris_val_targets.values, iris_test_features.values, iris_test_targets.values)
报错信息
random_sample.shape: (1, 4) target_outputs.shape: (1, 3) biases.shape: (1, 1) weights.shape: (5, 3) outputs.shape: (1, 3) net.shape: (1, 3) error_vector.shape: (1, 3) delta.shape: (1, 3) outputs.T.shape: (3, 1) np.kron(outputs.T, delta).shape: (3, 3) --------------------------------------------------------------------------- ValueError Traceback (most recent call last) Cell In [105], line 1 ----> 1 train(iris_train_features.values, iris_train_targets.values, NUMBER_OF_SAMPLES_FOR_BACKPROPAGATION, iris_val_features.values, iris_val_targets.values, iris_test_features.values, iris_test_targets.values) Cell In [103], line 27, in train(training_set_features, training_set_targets, number_of_samples_for_backpropagation, val_set_features, val_set_targets, test_set_features, test_set_targets) 25 current_epoch_number = 0 26 while (current_epoch_number < number_of_epochs): ---> 27 weights = backpropagation(training_set_features, weights, learning_rate, biases_train, number_of_samples_for_backpropagation, training_set_targets) 28 if (PLOT_GRAPHS == True): 29 train_error, train_classification_error = evaluate_error(training_set_features, weights, training_set_targets, outputs_to_classes(training_set_targets), biases_train) Cell In [102], line 36, in backpropagation(inputs, weights, learning_rate, biases, number_of_samples_for_backpropagation, target_outputs) 33 print("np.kron(outputs.T, delta).shape:") 34 print(np.kron(outputs.T, delta).shape) ---> 36 weights = weights + weights_delta 38 return weights ValueError: operands could not be broadcast together with shapes (5,3) (3,3)
问题分析与修复
核心错误
误用np.kron(克罗内克积)计算权重更新量,不符合反向传播的权重梯度计算逻辑。正确的做法是使用包含偏置的输入层矩阵与delta的外积,因为权重矩阵的每一列对应输出节点的权重,梯度由输入和输出误差项(delta)共同决定。
修改后的backpropagation函数
def backpropagation(inputs, weights, learning_rate, biases, number_of_samples_for_backpropagation, target_outputs): random_sample_index = np.random.randint(0, inputs.shape[0], size=number_of_samples_for_backpropagation) random_sample = inputs[random_sample_index, :] target_outputs = target_outputs[random_sample_index, :] biases = biases[random_sample_index, :] # 获取拼接了偏置的输入矩阵(和feed_forward逻辑一致) input_with_bias = np.hstack((random_sample, biases)) outputs, net = feed_forward(random_sample, weights, biases) error_vector = target_outputs - outputs delta = np.multiply(error_vector, activation_function_derivative(net)) # 正确计算权重更新量:输入转置 × delta,再乘以学习率 # 单个样本时,input_with_bias是(1,5),delta是(1,3),转置后相乘得到(5,3),和weights形状匹配 weights_delta = learning_rate * np.matmul(input_with_bias.T, delta) weights = weights + weights_delta return weights
关键修改点说明
- 获取带偏置的输入矩阵:和
feed_forward中逻辑一致,用np.hstack((random_sample, biases))得到形状为(1,5)的输入矩阵(对应5个输入节点:4个特征+1个偏置)。 - 替换权重更新计算:用
np.matmul(input_with_bias.T, delta)代替np.kron,得到的weights_delta形状为(5,3),和原weights的形状完全匹配,可正常执行加法操作。 - 移除冗余调试打印(可选):若无需调试,可去掉所有shape打印语句,简化代码。
验证逻辑
对于单个样本:
input_with_bias.T形状为(5,1)delta形状为(1,3)- 矩阵乘法后得到(5,3)的
weights_delta,和weights的(5,3)形状一致,解决维度不匹配问题。
内容的提问来源于stack exchange,提问作者Pointer_to_void
相关产品推荐
相关产品推荐

