Keras 2.x MNIST 数据预处理:3 种归一化与 One-Hot 编码方案详解
📅 2026/7/4 22:04:15
👁️ 阅读次数
📝 编程学习
Keras 2.x MNIST 数据预处理:3 种归一化与 One-Hot 编码方案详解
当你在Keras中加载MNIST数据集时,原始数据是以0-255的整数形式存储的28x28像素灰度图像。这些数据需要经过预处理才能输入神经网络进行训练。本文将深入探讨三种不同的数据归一化方法以及两种标签编码方案,帮助你理解每种方法的适用场景和实现细节。
1. MNIST数据集基础认知
MNIST数据集包含60,000张训练图像和10,000张测试图像,每张都是28x28像素的手写数字灰度图。原始数据的像素值范围是0-255,标签是0-9的整数。直接使用这些原始数据训练模型通常效果不佳,因此我们需要进行预处理。
from keras.datasets import mnist import numpy as np # 加载原始数据 (train_images, train_labels), (test_images, test_labels) = mnist.load_data() print(f"训练图像形状: {train_images.shape}") # (60000, 28, 28) print(f"训练标签形状: {train_labels.shape}") # (60000,) print(f"像素值范围: {np.min(train_images)}-{np.max(train_images)}") # 0-2552. 三种图像归一化方案对比
归一化是将数据缩放到固定范围的过程,有助于模型更快收敛。以下是三种常用的归一化方法:
2.1 简单除以255.0
这是最常见的方法,将像素值从0-255线性映射到0-1之间。
# 方法1:简单除以255.0 def normalize_divide_255(images): return images.astype('float32') / 255.0 # 实现示例 x_train_1 = normalize_divide_255(train_images) x_test_1 = normalize_divide_255(test_images) print(f"归一化后范围: {np.min(x_train_1):.2f}-{np.max(x_train_1):.2f}")特点:
- 实现简单,计算速度快
- 保持了原始数据的线性关系
- 适用于大多数情况
2.2 MinMaxScaler归一化
使用sklearn的MinMaxScaler可以更灵活地控制缩放范围。
from sklearn.preprocessing import MinMaxScaler # 方法2:MinMaxScaler def normalize_minmax(images): scaler = MinMaxScaler(feature_range=(0, 1)) # 需要先将图像展平为(样本数, 784)的形状 flattened = images.reshape(images.shape[0], -1) normalized = scaler.fit_transform(flattened) return normalized.reshape(images.shape) # 实现示例 x_train_2 = normalize_minmax(train_images) x_test_2 = normalize_minmax(test_images) print(f"MinMax归一化范围: {np.min(x_train_2):.2f}-{np.max(x_train_2):.2f}")特点:
- 可以自定义输出范围(如[-1,1])
- 需要额外的内存存储scaler对象
- 适合需要统一多个特征尺度的情况
2.3 Z-Score标准化
将数据转换为均值为0,标准差为1的分布。
# 方法3:Z-Score标准化 def normalize_zscore(images): mean = np.mean(images, axis=(0,1,2), keepdims=True) std = np.std(images, axis=(0,1,2), keepdims=True) return (images.astype('float32') - mean) / std # 实现示例 x_train_3 = normalize_zscore(train_images) x_test_3 = normalize_zscore(test_images) print(f"Z-Score均值: {np.mean(x_train_3):.2f}, 标准差: {np.std(x_train_3):.2f}")特点:
- 对异常值更鲁棒
- 输出范围不固定
- 适合数据分布不均匀的情况
归一化方法对比表
| 方法 | 公式 | 输出范围 | 优点 | 缺点 |
|---|---|---|---|---|
| /255.0 | x/255.0 | [0,1] | 简单快速 | 对异常值敏感 |
| MinMax | (x-min)/(max-min) | 可自定义 | 范围可控 | 需要存储参数 |
| Z-Score | (x-μ)/σ | 无固定 | 鲁棒性强 | 计算量较大 |
3. 两种标签编码方案
标签编码是将类别标签转换为模型可以处理的形式。MNIST有10个类别(0-9),我们需要将其转换为适合分类任务的格式。
3.1 One-Hot编码
将每个标签转换为长度为类别数的二进制向量。
from keras.utils import to_categorical # 方法1:One-Hot编码 def encode_onehot(labels, num_classes=10): return to_categorical(labels, num_classes) # 实现示例 y_train_1 = encode_onehot(train_labels) y_test_1 = encode_onehot(test_labels) print(f"原始标签: {train_labels[0]}") print(f"One-Hot编码后: {y_train_1[0]}")特点:
- 明确表示类别间的互斥关系
- 适合配合categorical_crossentropy损失函数
- 会稍微增加内存使用
3.2 整数标签
直接使用0-9的整数作为标签,配合稀疏分类交叉熵。
# 方法2:整数标签 def encode_integer(labels): return labels.astype('int32') # 实现示例 y_train_2 = encode_integer(train_labels) y_test_2 = encode_integer(test_labels) print(f"整数标签: {y_train_2[0]}")特点:
- 内存占用小
- 适合配合sparse_categorical_crossentropy
- 实现更简单
4. 完整预处理流程示例
下面展示一个结合了归一化和标签编码的完整预处理流程:
def preprocess_data(images, labels, normalize_method='divide_255', encode_method='onehot'): # 归一化处理 if normalize_method == 'divide_255': images = images.astype('float32') / 255.0 elif normalize_method == 'minmax': scaler = MinMaxScaler(feature_range=(0, 1)) images = scaler.fit_transform(images.reshape(images.shape[0], -1)) images = images.reshape(images.shape[0], 28, 28) elif normalize_method == 'zscore': mean = np.mean(images, axis=(0,1,2), keepdims=True) std = np.std(images, axis=(0,1,2), keepdims=True) images = (images.astype('float32') - mean) / std # 标签编码 if encode_method == 'onehot': labels = to_categorical(labels, 10) elif encode_method == 'integer': labels = labels.astype('int32') return images, labels # 使用示例 x_train, y_train = preprocess_data(train_images, train_labels, normalize_method='divide_255', encode_method='onehot')5. 不同预处理组合的性能影响
为了验证不同预处理方法的效果,我们构建一个简单的全连接网络进行测试:
from keras.models import Sequential from keras.layers import Dense, Flatten from keras.optimizers import Adam def build_model(input_shape, output_units): model = Sequential([ Flatten(input_shape=input_shape), Dense(128, activation='relu'), Dense(64, activation='relu'), Dense(output_units, activation='softmax') ]) model.compile(optimizer=Adam(), loss='categorical_crossentropy', metrics=['accuracy']) return model # 测试不同归一化方法 normalization_methods = ['divide_255', 'minmax', 'zscore'] results = {} for method in normalization_methods: x_train, y_train = preprocess_data(train_images, train_labels, normalize_method=method, encode_method='onehot') x_test, y_test = preprocess_data(test_images, test_labels, normalize_method=method, encode_method='onehot') model = build_model((28, 28), 10) history = model.fit(x_train, y_train, validation_data=(x_test, y_test), epochs=10, batch_size=128, verbose=0) results[method] = history.history['val_accuracy'][-1] print("不同归一化方法的测试准确率:") for method, acc in results.items(): print(f"{method}: {acc:.4f}")在我的测试中,三种归一化方法在MNIST上的表现差异不大,/255.0方法通常能达到98.5%以上的准确率,且实现最简单。选择哪种方法更多取决于具体应用场景和个人偏好。
编程学习
技术分享
实战经验