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

为何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度旋转):
测试“1”的结果图

测试“0”的结果:
测试“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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 05:33:23