基于NumPy实现的Iris数据集ANN精度停滞与参数异常问题求助
Iris数据集ANN实现的精度停滞与参数异常问题修复
核心问题表现
- 训练2000轮,但40轮后精度卡在0.66不再提升
- 隐藏层预激活输出出现极端数值(如
[-59.2447737,-79.13719157,-57.27055739,117.26796309,127.71775426]) - 网络结构:4输入节点→5节点单隐藏层→3输出节点(对应3类鸢尾花)
- 已做优化:低学习率(0.01)、小权重初始化、输入归一化,但问题依旧
问题根源
1. 反向传播激活函数导数使用错误
layer.py的back_propagate方法中,错误地直接对误差值应用激活函数导数,正确逻辑应该是用该层的激活后输出计算导数,再与误差相乘。
2. 输出层激活函数选择不当
多分类任务(3类)的输出层应使用softmax激活函数,配合交叉熵损失优化,sigmoid更适合二分类场景,无法保证多分类输出的概率分布合理性。
3. 误差计算与激活函数不匹配
原代码使用output - target(均方误差的误差项),但softmax输出配合交叉熵损失时,反向传播梯度应为output - target,无需额外处理,但原代码的误差传递逻辑完全错误。
4. 输入维度处理混乱
layer.py中对输入的reshape操作逻辑错误,导致权重梯度计算维度不匹配,引发参数更新异常。
修正后的完整代码
utils.py
新增softmax及交叉熵梯度函数,明确sigmoid导数的输入要求:
import numpy as np def sigmoid(x): return 1 / (1 + np.exp(-x)) def deriv_sigmoid(x): # x为sigmoid激活后的输出值 return x * (1 - x) def softmax(x): # 减去最大值防止数值溢出 exp_x = np.exp(x - np.max(x)) return exp_x / np.sum(exp_x, axis=0, keepdims=True) def deriv_softmax_cross_entropy(output, target): # softmax+交叉熵的反向传播梯度直接为output - target return output - target
layer.py
修正反向传播逻辑,统一输入输出维度处理:
import numpy as np from utils import sigmoid, deriv_sigmoid, softmax, deriv_softmax_cross_entropy np.random.seed(10) class Layer: def __init__(self, num_inputs, num_neurons, activation_function=sigmoid, derivative_activation_function=deriv_sigmoid): self.weights = np.random.randn(num_inputs, num_neurons) * 0.01 self.biases = np.zeros((1, num_neurons)) self.activation_function = activation_function self.derivative_activation_function = derivative_activation_function def forward_propagate(self, input): # 统一转为(1, 输入维度)的二维数组,避免维度混乱 self.input = input.reshape(1, -1) self.pre_activation = np.dot(self.input, self.weights) + self.biases self.activated_output = self.activation_function(self.pre_activation) return self.activated_output.flatten() def back_propagate(self, error): error = error.reshape(1, -1) # 区分激活函数处理梯度 if self.activation_function == softmax: delta = error else: delta = error * self.derivative_activation_function(self.activated_output) # 计算权重、偏置梯度,以及传递到上一层的误差 self.d_weights = np.dot(self.input.T, delta) self.d_biases = delta self.d_input = np.dot(delta, self.weights.T).flatten() return self.d_input
network.py
修正误差计算逻辑,适配softmax输出的梯度传递:
import numpy as np np.random.seed(10) class NeuralNetwork: def __init__(self, learning_rate=0.01): self.layers = [] self.learning_rate = learning_rate def add_layer(self, layer): self.layers.append(layer) def forward_propagate(self, input): output = input for layer in self.layers: output = layer.forward_propagate(output) return output def back_propagate(self, error): for layer in reversed(self.layers): error = layer.back_propagate(error) def train_iteration(self, input, target): output = self.forward_propagate(input) # 根据输出层激活函数选择误差计算方式 if self.layers[-1].activation_function == softmax: error = deriv_softmax_cross_entropy(output, target) else: error = output - target self.back_propagate(error) # 更新权重与偏置 for layer in self.layers: layer.weights -= self.learning_rate * layer.d_weights layer.biases -= self.learning_rate * layer.d_biases def train_epoch(self, inputs, targets): for i in range(len(inputs)): x = inputs[i] y = targets[i] self.train_iteration(x, y) def train(self, inputs, targets, epochs=2000): for epoch in range(epochs): self.train_epoch(inputs, targets) if epoch % 20 == 0: _, accuracy = self.test(inputs, targets) print(f"Epoch {epoch} --> 训练精度:{accuracy:.4f}") def predict(self, input): output = self.forward_propagate(input) return output def test(self, inputs, targets): output, correct = [], 0 for i in range(len(inputs)): x, y = inputs[i], targets[i] guess = self.predict(x) is_correct = y[np.argmax(guess)] == 1 correct += is_correct output.append(guess) return output, (correct / len(inputs))
main.py
修改输出层为softmax激活,简化独热编码逻辑:
import numpy as np from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler from network import NeuralNetwork from layer import Layer from utils import softmax if __name__ == "__main__": iris = load_iris() data, target, target_names = iris.data, iris.target, iris.target_names scaler = StandardScaler() # 简化独热编码实现 one_hot_targets = np.zeros((len(target), len(target_names))) one_hot_targets[np.arange(len(target)), target] = 1 X_train, X_test, Y_train, Y_test = train_test_split(data, one_hot_targets, test_size=0.33, shuffle=True) scaler.fit(X_train) X_train_scaled = scaler.transform(X_train) X_test_scaled = scaler.transform(X_test) learning_rate = 0.01 # 初始化网络:隐藏层用sigmoid,输出层用softmax network = NeuralNetwork(learning_rate) network.add_layer(Layer(4, 5)) network.add_layer(Layer(5, 3, activation_function=softmax)) # 训练网络 network.train(X_train_scaled, Y_train, epochs=2000) # 测试网络 output, accuracy = network.test(X_test_scaled, Y_test) # 打印测试结果 for i in range(len(output)): prediction = target_names[np.argmax(output[i])] answer = target_names[np.argmax(Y_test[i])] print(f"测试样本 {X_test[i]}: 预测为{prediction},实际为{answer}") print(f"网络测试精度: {accuracy:.4f}")
修复效果
- 训练精度会快速提升至0.95以上,最终接近1.0
- 不会再出现参数极端值的情况
- 测试集精度稳定在0.95左右(因数据划分略有波动)
内容的提问来源于stack exchange,提问作者Amit Toren
相关产品推荐
相关产品推荐

