You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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的索引计算是否完全对应样本、像素、标签的正确位置,索引错位会导致模型学习错误关联。

优化建议

  1. 修正张量维度:将输入张量的维度改为[0,28,28,1],匹配卷积层的输入形状:
    x := Tensor.R4.MakeTensor([0,28,28,1], x3);
    x_test := Tensor.R4.MakeTensor([0,28,28,1], x3_test);
    
  2. 调整学习率衰减参数:将lrr改为0.1,让学习率缓慢衰减,保留模型优化空间:
    lrr := 0.1;
    
  3. 放宽训练终止条件:将trainToLoss调整为0.001,避免模型过早停止优化:
    trainToLoss := 0.001;
    
  4. 简化全连接层结构:减少全连接层数量,去掉冗余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')'''
    ];
    
  5. 添加Dropout抑制过拟合:在全连接层后加入Dropout层,降低过拟合风险:
    '''layers.Dense(128)''',
    '''layers.Activation('relu')''',
    '''layers.Dropout(0.2)''',
    
  6. 禁用批量大小衰减:若不需要动态调整批量,设置bsr=1,避免批量过小导致梯度不稳定:
    bsr := 1;
    
  7. 验证数据转换逻辑:打印少量样本的x3和y3索引与值,确认和原始MNIST数据的对应关系,排除索引错位问题。

内容的提问来源于stack exchange,提问作者Ananya S Kaligal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 23:29:53