基于GNN Bundles构建的CNN在MNIST数据集上准确率偏低求助
MNIST CNN训练瓶颈排查(ECL + GNN Bundles)
我正在用ECL和GNN Bundles在MNIST数据集上训练CNN,模型包含3个带批量归一化和ReLU的卷积层(后接最大池化),以及3个全连接层。已完成数据归一化、维度重塑,采用分类交叉熵损失和Adam优化器,但训练准确率卡瓶颈,测试集准确率极低。调整学习率、批量大小、epoch数后无明显改善,期望达到标准CNN在MNIST上的准确率水平,求问题分析和优化建议。
附ECL代码
IMPORT Python3 AS Python; IMPORT GNN.Tensor; IMPORT GNN.Internal.Types AS iTypes; IMPORT GNN.Types; IMPORT GNN.GNNI; IMPORT GNN.Internal AS Int; IMPORT STD; kString := iTypes.kString; kStrType := iTypes.kStrType; t_Tensor := Tensor.R4.t_Tensor; TensData := Tensor.R4.TensData; // Test parameters batchSize := 32; numEpochs := 250; trainToLoss := .0001; bsr := .25; // BatchSizeReduction. 1 = no reduction. .25 = reduction to 25% of original. lrr := 0.01; // Learning Rate Reduction. 1 = no reduction. .1 = reduction to 10 percent of original. // Get training data SET OF REAL4 get_train_X() := EMBED(Python) import tensorflow as tf import numpy as np mnist = tf.keras.datasets.mnist (x_train, y_train),(x_test, y_test) = mnist.load_data() # Normalize the pixel values to be between 0 and 1 x_train = x_train.astype('float32') / 255.0 # Reshape to add the channel dimension x_train = x_train.reshape(-1, 28, 28, 1) return x_train.flatten().tolist() ENDEMBED; SET OF REAL4 get_train_Y() := EMBED(Python) import tensorflow as tf import numpy as np from tensorflow.keras.utils import to_categorical mnist = tf.keras.datasets.mnist (x_train, y_train), (x_test, y_test) = mnist.load_data() # Convert labels to one-hot encoding y_one_hot = to_categorical(y_train,10) return y_one_hot.flatten().tolist() ENDEMBED; train_X := get_train_X(); train_Y := get_train_Y(); t1Rec := RECORD REAL4 value; END; intpuRec := RECORD UNSIGNED8 id; REAL4 value; END; x1 := DATASET(train_X, t1Rec); y1 := DATASET(train_Y, t1Rec); x2 := PROJECT(x1, TRANSFORM(intpuRec, SELF.id := COUNTER - 1, SELF.value := LEFT.value)); y2 := PROJECT(y1, TRANSFORM(intpuRec, SELF.id := COUNTER - 1, SELF.value := LEFT.value)); x3 := PROJECT(x2, TRANSFORM(TensData, SELF.indexes := [TRUNCATE(LEFT.id/784) + 1, TRUNCATE(LEFT.id%784/28) + 1, LEFT.id%28 + 1], SELF.value := LEFT.value)); y3 := PROJECT(y2, TRANSFORM(TensData, SELF.indexes := [TRUNCATE(LEFT.id/10) + 1, LEFT.id%10 + 1], SELF.value := LEFT.value)); x := Tensor.R4.MakeTensor([0,28,28], x3); y := Tensor.R4.MakeTensor([0, 10], y3); // Define model ldef := [ '''layers.Conv2D(32, (3, 3), input_shape=(28, 28, 1))''', '''layers.BatchNormalization()''', '''layers.Activation('relu')''', '''layers.MaxPooling2D(pool_size=(2, 2))''', '''layers.Conv2D(64, (3, 3))''', '''layers.BatchNormalization()''', '''layers.Activation('relu')''', '''layers.MaxPooling2D(pool_size=(2, 2))''', '''layers.Conv2D(128, (3, 3))''', '''layers.BatchNormalization()''', '''layers.Activation('relu')''', '''layers.Flatten()''', '''layers.Dense(256)''', '''layers.BatchNormalization()''', '''layers.Activation('relu')''', '''layers.Dense(128)''', '''layers.BatchNormalization()''', '''layers.Activation('relu')''', '''layers.Dense(64)''', '''layers.BatchNormalization()''', '''layers.Activation('relu')''', '''layers.Dense(10)''', '''layers.Activation('softmax')''' ]; compileDef := '''compile(optimizer=tf.keras.optimizers.Adam(learning_rate=0.001), loss='categorical_crossentropy', metrics=[tf.keras.metrics.CategoricalAccuracy()]) '''; s := GNNI.GetSession(0); // Ensure this line uses the CPU mod := GNNI.DefineModel(s, ldef, compileDef); // Train model mod2 := GNNI.Fit(mod, x, y, batchSize := batchSize, numEpochs := numEpochs, trainToLoss := trainToLoss, learningRateReduction := lrr, batchSizeReduction := bsr); losses := GNNI.GetLoss(mod2); // Evaluate this model SET OF REAL4 get_test_X() := EMBED(Python) import tensorflow as tf import numpy as np mnist = tf.keras.datasets.mnist (x_train, y_train), (x_test, y_test) = mnist.load_data() # Normalize the pixel values to be between 0 and 1 x_test = x_test.astype('float32') / 255.0 # Reshape to add the channel dimension x_test = x_test.reshape(-1, 28, 28, 1) return x_test.flatten().tolist() ENDEMBED; SET OF REAL4 get_test_Y() := EMBED(Python) import tensorflow as tf import numpy as np mnist = tf.keras.datasets.mnist from tensorflow.keras.utils import to_categorical (x_train, y_train), (x_test, y_test) = mnist.load_data() # Convert labels to one-hot encoding y_one_hot = to_categorical(y_test,10) return y_one_hot.flatten().tolist() ENDEMBED; test_X := get_test_X(); test_Y := get_test_Y(); x1_test := DATASET(test_X, t1Rec); y1_test := DATASET(test_Y, t1Rec); x2_test := PROJECT(x1_test, TRANSFORM(intpuRec, SELF.id := COUNTER - 1, SELF.value := LEFT.value)); y2_test := PROJECT(y1_test, TRANSFORM(intpuRec, SELF.id := COUNTER - 1, SELF.value := LEFT.value)); x3_test := PROJECT(x2_test, TRANSFORM(TensData, SELF.indexes := [TRUNCATE(LEFT.id/784) + 1, TRUNCATE(LEFT.id%784/28) + 1, LEFT.id%28 + 1], SELF.value := LEFT.value)); y3_test := PROJECT(y2_test, TRANSFORM(TensData, SELF.indexes := [TRUNCATE(LEFT.id/10) + 1, LEFT.id%10 + 1], SELF.value := LEFT.value)); x_test := Tensor.R4.MakeTensor([0,28,28], x3_test); y_test := Tensor.R4.MakeTensor([0, 10], y3_test); metrics := GNNI.EvaluateMod(mod2, x_test, y_test); preds := GNNI.Predict(mod2, x_test); // GetWeights returns the initialized weights that have been synchronized across all nodes. wts := GNNI.GetWeights(mod); // OUTPUT results ORDERED([OUTPUT(STD.Date.CurrentTime(TRUE), NAMED('startTime')), OUTPUT(mod2, NAMED('mod2')), OUTPUT(STD.Date.CurrentTime(TRUE), NAMED('endTime')), OUTPUT(losses, NAMED('losses')), OUTPUT(metrics, NAMED('metrics')), OUTPUT(preds, NAMED('preds'))]); OUTPUT(wts, NAMED('InitWeights')); OUTPUT(IF(metrics[2].value>0.95, 'Pass', 'Fail'), NAMED('CategoricalAccuracy'));
问题原因分析
- 张量维度不匹配:输入张量
x定义为[0,28,28],但卷积层输入要求是(样本数,28,28,1),缺失通道维度会导致卷积计算逻辑错误,无法有效提取特征。 - 学习率衰减幅度过大:
lrr=0.01意味着每次衰减后学习率变为原有的1%,会导致模型快速进入参数冻结状态,无法继续拟合数据。 - 训练终止条件过于严苛:
trainToLoss=.0001远超MNIST数据集的合理损失范围,模型会因学习率过早衰减而停滞,无法达到最优精度。 - 全连接层冗余:3个全连接层加多层BN,在MNIST这种简单数据集上会导致参数过剩,容易引发过拟合或梯度消失。
- 数据转换索引风险:ECL张量索引从1开始,需确认
x3和y3的索引计算是否完全对应样本、像素、标签的正确位置,索引错位会导致模型学习错误关联。
优化建议
- 修正张量维度:将输入张量的维度改为
[0,28,28,1],匹配卷积层的输入形状:x := Tensor.R4.MakeTensor([0,28,28,1], x3); x_test := Tensor.R4.MakeTensor([0,28,28,1], x3_test); - 调整学习率衰减参数:将
lrr改为0.1,让学习率缓慢衰减,保留模型优化空间:lrr := 0.1; - 放宽训练终止条件:将
trainToLoss调整为0.001,避免模型过早停止优化:trainToLoss := 0.001; - 简化全连接层结构:减少全连接层数量,去掉冗余BN层,降低模型复杂度:
ldef := [ '''layers.Conv2D(32, (3, 3), input_shape=(28, 28, 1))''', '''layers.BatchNormalization()''', '''layers.Activation('relu')''', '''layers.MaxPooling2D(pool_size=(2, 2))''', '''layers.Conv2D(64, (3, 3))''', '''layers.BatchNormalization()''', '''layers.Activation('relu')''', '''layers.MaxPooling2D(pool_size=(2, 2))''', '''layers.Conv2D(128, (3, 3))''', '''layers.BatchNormalization()''', '''layers.Activation('relu')''', '''layers.Flatten()''', '''layers.Dense(128)''', '''layers.Activation('relu')''', '''layers.Dense(10)''', '''layers.Activation('softmax')''' ]; - 添加Dropout抑制过拟合:在全连接层后加入Dropout层,降低过拟合风险:
'''layers.Dense(128)''', '''layers.Activation('relu')''', '''layers.Dropout(0.2)''', - 禁用批量大小衰减:若不需要动态调整批量,设置
bsr=1,避免批量过小导致梯度不稳定:bsr := 1; - 验证数据转换逻辑:打印少量样本的
x3和y3索引与值,确认和原始MNIST数据的对应关系,排除索引错位问题。
内容的提问来源于stack exchange,提问作者Ananya S Kaligal
相关产品推荐
相关产品推荐

