使用Apache MXNet(Gluon)训练CNN时程序崩溃且结果异常求助
看起来你遇到了两个挺闹心的问题:同一套代码跑同一批数据,结果波动大不说,还偶尔直接崩溃。我来帮你拆解下可能的原因,一步步解决:
1. 数据加载的致命bug(崩溃的大概率元凶)
你在创建ImageFolderDataset的时候传参错了!mx.gluon.data.vision.ImageFolderDataset的正确参数顺序是:根目录、图像转换函数、标签转换函数。但你写的代码里:
mx.gluon.data.vision.ImageFolderDataset("/somepath/train", 0, transform)
把整数0当成了图像转换函数传给了第二个参数,第三个参数才是你自定义的transform——这相当于让Dataset去调用0(data, label),不出错才怪!至于为什么有时能运行,可能是你实际代码里的写法有出入,但不管怎样,正确的写法应该是:
train_data = mx.gluon.data.DataLoader( mx.gluon.data.vision.ImageFolderDataset("/somepath/train", transform=transform), batch_size, shuffle=True ) test_data = mx.gluon.data.DataLoader( mx.gluon.data.vision.ImageFolderDataset("/somepath/test", transform=transform), batch_size, shuffle=False )
如果是想设置图像加载模式(比如转灰度图),应该在transform函数里处理,比如:
def transform(data, label): # 转灰度图 data = mx.image.rgb_to_grayscale(data) return nd.transpose(data.astype(np.float32), (2, 0, 1))/255, label
2. 随机种子没固定全(结果波动大的核心原因)
你已经设置了mx.random.seed(1),但训练中还有其他随机源会影响结果:
- numpy的随机数:数据处理或初始化可能用到它,得加
np.random.seed(1) - Python标准库的random:DataLoader的shuffle逻辑依赖它,得加
import random; random.seed(1)
把种子设置放在代码最开头,确保所有随机操作都基于同一个起点:
from __future__ import print_function import mxnet as mx import numpy as np import random from mxnet import nd, autograd, gluon import time # 固定所有随机源,保证每次训练的初始条件一致 random.seed(1) np.random.seed(1) mx.random.seed(1)
3. 其他小细节优化(让结果更稳定)
- 增加训练轮数:你原来只训练2个epoch,模型还没来得及收敛,结果自然波动大,建议加到10-50个epoch,同时在训练循环里监控损失和准确率,方便观察收敛情况。
- 统一变量命名:你评估测试集的时候用了
something这种模糊的变量名,改成data更清晰,也避免潜在的笔误。
修复后的完整代码
整合了所有修复点的代码如下,你可以直接测试:
from __future__ import print_function import mxnet as mx import numpy as np import random from mxnet import nd, autograd, gluon import time # 固定所有随机种子 random.seed(1) np.random.seed(1) mx.random.seed(1) # 设置计算上下文 ctx = mx.cpu() # 定义回调转换函数 def transform(data, label): return nd.transpose(data.astype(np.float32), (2, 0, 1))/255, label # 定义批量大小与输出层节点数 batch_size = 5 num_outputs = 2 # 修复后的数据集加载 train_data = mx.gluon.data.DataLoader( mx.gluon.data.vision.ImageFolderDataset("/somepath/train", transform=transform), batch_size, shuffle=True ) test_data = mx.gluon.data.DataLoader( mx.gluon.data.vision.ImageFolderDataset("/somepath/test", transform=transform), batch_size, shuffle=False ) # 使用gluon.nn定义CNN neural_net = gluon.nn.Sequential() num_fc = 512 with neural_net.name_scope(): neural_net.add(gluon.nn.Conv2D(channels=20, kernel_size=5, activation='relu')) neural_net.add(gluon.nn.MaxPool2D(pool_size=2, strides=2)) neural_net.add(gluon.nn.Conv2D(channels=50, kernel_size=5, activation='relu')) neural_net.add(gluon.nn.MaxPool2D(pool_size=2, strides=2)) neural_net.add(gluon.nn.Flatten()) neural_net.add(gluon.nn.Dense(num_fc, activation="relu")) neural_net.add(gluon.nn.Dense(num_outputs)) # 初始化参数、损失函数与训练器 neural_net.collect_params().initialize(mx.init.Xavier(magnitude=2.24), ctx=ctx) cross_entropy = gluon.loss.SoftmaxCrossEntropyLoss() trainer = gluon.Trainer(neural_net.collect_params(), 'adadelta') # 训练循环(增加到10个epoch,加入损失和准确率监控) total_time = 0 epochs = 10 for e in range(epochs): tick = time.time() train_loss = 0.0 train_acc = mx.metric.Accuracy() for idx, (dpoint, label) in enumerate(train_data): data = dpoint.as_in_context(ctx) label = label.as_in_context(ctx) with autograd.record(): output = neural_net(data) loss = cross_entropy(output, label) loss.backward() trainer.step(data.shape[0]) # 累计训练损失和准确率 train_loss += nd.mean(loss).asscalar() predictions = nd.argmax(output, axis=1) train_acc.update(predictions, label) tock = time.time() print(f"Epoch {e+1}/{epochs} | Train Loss: {train_loss/len(train_data):.4f} | Train Acc: {train_acc.get()[1]:.4f} | Time: {tock-tick:.2f}s") total_time += tock-tick print(f"\nTotal training time: {total_time:.2f} seconds") # 评估测试集准确率 test_acc = mx.metric.Accuracy() for idx, (data, label) in enumerate(test_data): data = data.as_in_context(ctx) label = label.as_in_context(ctx) output = neural_net(data) predictions = nd.argmax(output, axis=1) test_acc.update(predictions, label) print(f"Test Accuracy: {test_acc.get()[1]:.4f}")
内容的提问来源于stack exchange,提问作者dntk
相关产品推荐
相关产品推荐

