基于Nielsen教材实现MNIST神经网络时遇元组解包值错误
报错根因定位
1. 核心触发原因:数据加载接口调用错误
你的神经网络代码要求传入的训练/测试数据,是单个样本为二元组(x, y)的可遍历集合,其中x是单个样本的输入向量,y是对应的标签值。
但你训练启动时调用的是mnist_loader.load_data(),这个接口返回的原始数据结构完全不符合要求:
- 整体返回是三元组
(训练集, 验证集, 测试集) - 每个子数据集本身又是两个独立的大数组:第一个元素是包含全部5万/1万张图像的高维数组,第二个元素是包含全部对应标签的一维数组,根本不是逐样本配对的二元组列表
当你把这个大数组转成列表传给SGD方法后,代码走到for x, y in mini_batch逐样本解包时,会拿到包含数千个元素的大数组试图拆成2个值,直接触发ValueError: too many values to unpack (expected 2)。
2. 附带的逻辑bug,就算修正数据源也会导致训练失效
你写的SGD方法存在缩进错误:
- 遍历mini batch更新参数的循环、以及epoch结束后的评估打印代码,全部没有缩进在
for j in range(epochs):的循环内部 - 现在的代码执行逻辑是:先循环30次打乱数据、切分batch,等循环完全结束后,才只跑1次batch更新、只打印1次结果,完全不符合逐epoch训练的设计逻辑
3. 代码书写错误,会触发后续连锁报错
- sigmoid函数存在两处书写错误:一是多写了一层
np(写成了np.np.exp(-z),会触发属性不存在的报错);二是公式符号写错(分母写成1 - np.exp(-z),正确应为1 + np.exp(-z),符号反了会导致激活值完全不符合设计,模型无法收敛) - Python 3环境下,
load_data_wrapper()里用zip()生成的是一次性迭代器,如果不转成列表,第一次epoch遍历完迭代器就会耗尽,后续循环拿不到任何数据
修复方案
- 修正训练启动代码,替换成正确的加载接口,同时适配Python3的迭代器特性:
import network import mnist_loader # 必须调用load_data_wrapper,该接口会把原始数据转成要求的逐样本二元组格式 training_data, validation_data, test_data = mnist_loader.load_data_wrapper() # 将zip生成的一次性迭代器转成列表,避免遍历一次就耗尽 training_data = list(training_data) test_data = list(test_data) net = network.Network([784, 30, 10]) # 原书推荐的初始学习率为3.0,你之前用的0.001过低会导致模型完全不收敛 net.SGD(training_data, 30, 10, 3.0, test_data=test_data)
- 修正SGD方法的缩进,把batch更新、评估打印的代码挪到epoch循环内部:
def SGD(self, training_data, epochs, mini_batch_size, eta, test_data=None): if test_data: n_test = len(test_data) n = len(training_data) for j in range(epochs): random.shuffle(training_data) mini_batches = [training_data[k:k+mini_batch_size] for k in range(0, n, mini_batch_size)] # 以下代码缩进进epoch循环内部 for mini_batch in mini_batches: self.update_mini_batch(mini_batch, eta) if test_data: print("Epoch {0}: {1} /{2}".format( j, self.evaluate(test_data), n_test)) else: print("Epoch {0} complete".format(j))
- 修正sigmoid函数的书写错误:
def sigmoid(z): """The sigmoid function.""" return 1.0/(1 + np.exp(-z))
内容的提问来源于stack exchange,提问作者Eyal Werber
相关产品推荐
相关产品推荐

