为何MNIST数字旋转检测模型预测精度远超预期?
问题:MNIST旋转角度检测模型为何对“0”“1”的旋转预测如此精准?
我在MNIST数据集上训练了一个模型,用于检测数字的旋转角度(0°、90°、180°、270°),训练代码如下:
# model / data parameters num_classes = 4 input_shape = (28, 28, 1) # load the data and split it between train and test sets (x_train_orig, _), (x_test_orig, _) = keras.datasets.mnist.load_data() # generate new data set with rotated numbers x_train = [] y_train = [] for img in x_train_orig: for i in range(0, 4): x_train.append(np.rot90(img, i)) y_train.append(i) x_train = np.array(x_train) y_train = np.array(y_train) x_test = [] y_test = [] for img in x_test_orig: for i in range(0, 4): x_test.append(np.rot90(img, i)) y_test.append(i) x_test = np.array(x_test) y_test = np.array(y_test) # scale images to the [0, 1] range x_train = x_train.astype("float32") / 255 x_test = x_test.astype("float32") / 255 # make sure images have shape (28, 28, 1) x_train = np.expand_dims(x_train, -1) x_test = np.expand_dims(x_test, -1) # convert class vectors to binary class matrices y_train = keras.utils.to_categorical(y_train, num_classes) y_test = keras.utils.to_categorical(y_test, num_classes) model = keras.Sequential([ keras.Input(shape=input_shape), layers.Conv2D(32, kernel_size=(3, 3), activation="relu"), layers.MaxPooling2D(pool_size=(2, 2)), layers.Conv2D(64, kernel_size=(3, 3), activation="relu"), layers.MaxPooling2D(pool_size=(2, 2)), layers.Flatten(), layers.Dropout(0.5), layers.Dense(num_classes, activation="softmax"), ]) batch_size = 128 epochs = 10 model.compile(loss="categorical_crossentropy", optimizer="adam", metrics=["accuracy"]) model.fit(x_train, y_train, batch_size=batch_size, epochs=epochs, validation_split=0.1) save_model(model, os.path.join('model', 'saved_numori_model'))
随后我用以下代码测试测试集中旋转270度的“1”:
model_ori = load_model(os.path.join('model', 'saved_numori_model'), compile=True) (_, _), (x_test_orig, y_test_orig) = keras.datasets.mnist.load_data() x_test_orig = x_test_orig.astype("float32") / 255 subimages = [] rot = 3 while len(subimages) < 8: rnd = np.random.randint(0, x_test_orig.shape[0]) if y_test_orig[rnd] != 1: continue subimage = x_test_orig[rnd] subimages.append(np.rot90(subimage, rot)) subimages = np.array(subimages) _, ax = plt.subplots(1, subimages.shape[0]) for i in range(subimages.shape[0]): ax[i].imshow(subimages[i]) ax[i].axis('off') plt.show() predictions_ori = model_ori.predict(np.expand_dims(subimages, -1), verbose=verbose) print("Probabilities for orientation:") for prediction in predictions_ori: for i, probability in enumerate(prediction): print(f"{i}: {probability:.02f} ", end="") print(f"Prediction: {np.argmax(prediction)}")
测试“1”的结果(270度旋转):
测试“0”的结果:
我原本预期“1”和“0”在旋转90°或270°时,模型会存在较高的预测不确定性,但实际模型预测异常精准。请问该模型为何能实现如此精准的预测?我是否操作有误?
解答
1. 操作无错误
从代码流程来看,数据生成、模型构建、训练环节均符合规范:
- 对每张原始MNIST图片生成4种旋转样本并标注对应标签,数据量充足且标签准确;
- 采用的CNN结构适合图像特征提取,训练参数设置合理。
2. 核心原因:MNIST的“0”“1”并非绝对旋转对称
你误以为“0”“1”旋转后完全一致,但MNIST手写数字存在细微不对称特征,模型精准捕捉到了这些细节:
- “1”的不对称性:MNIST里的“1”大多不是绝对垂直的,部分样本顶部带小横、底部有弯钩,或存在轻微倾斜。旋转后这些特征的位置会发生明显偏移,卷积层能捕捉到位置信息差异,从而区分旋转角度。
- “0”的不对称性:手写“0”很少是完美圆形,通常存在椭圆变形、笔画粗细不均(比如左侧粗右侧细)等特征。旋转后这些不对称特征的相对位置改变,模型可通过这些细节判断旋转角度。
3. 辅助因素:充足的训练数据强化特征学习
你用全量MNIST图片生成了4倍训练数据,模型在大量带标签的旋转样本上训练,足以吃透这些细微的不对称特征,甚至学到了MNIST数据集特定的笔画分布规律,最终实现高精度预测。
若要验证该结论,可尝试:
- 用完全对称的合成“0”“1”(如正圆形、纯直线)测试模型,此时模型会出现预测不确定性;
- 查看模型中间卷积层的激活图,直观观察模型关注的不对称区域。
内容的提问来源于stack exchange,提问作者user1195883
相关产品推荐
相关产品推荐

