Keras - Regression Prediction using MPL
Keras - 使用 MLP 进行回归预测
Section titled “Keras - 使用 MLP 进行回归预测”虽然之前的示例着重于分类(预测离散类别),但 Keras 同样擅长回归任务,其目标是预测连续的数值。一个常见的示例是根据大小、位置和房间数量等特征预测房价。
在本章中,我们将使用 tf.keras 构建一个 Multi-Layer Perceptron (MLP) 来预测房价,使用 Boston Housing 数据集,这是一个经典的回归基准数据集,可在 tf.keras.datasets 中获取。
回归模型的关键区别:
- 输出层: 通常只有一个神经元(如果预测一个值),并且经常使用线性激活函数(或无激活函数),因为输出不像概率那样被限制在特定范围内。
- 损失函数: 常见选择包括均方误差(
'mse')或平均绝对误差('mae')。这些衡量预测连续值与实际连续值之间的平均差异。 - 指标: 评估通常使用 MAE(平均绝对差)或 RMSE(均方根误差)等指标,以了解目标变量原始单位(例如,房价的美元)中的典型误差大小。
回归 MLP 的概念结构:
输入特征 -> 全连接隐藏层(例如,ReLU 激活) -> 全连接输出层(1 个单元,线性激活) -> 预测值。
模型规格:
- 数据集: 波士顿房价数据集(13 个输入特征,1 个目标值:房屋中位价格)。
- 预处理: 特征缩放(归一化/标准化)对于具有不同尺度的特征的回归任务至关重要。
- 架构: 具有两个隐藏全连接层(例如,64 个单元,ReLU 激活)的 MLP。
- 输出层: 具有 1 个单元的全连接层(未指定激活函数,默认为线性)。
- 编译:
'mse'损失、'RMSprop'或'Adam'优化器、'mae'指标。 - 训练: 使用
EarlyStopping等回调函数防止过拟合。
第 1 步:导入必要模块
Section titled “第 1 步:导入必要模块”import tensorflow as tffrom tensorflow.keras.datasets import boston_housingfrom tensorflow.keras.models import Sequentialfrom tensorflow.keras.layers import Dense, Normalization # 添加了 Normalization 层from tensorflow.keras.optimizers import Adam # 使用 Adam 优化器from tensorflow.keras.callbacks import EarlyStoppingimport numpy as npimport matplotlib.pyplot as plt # 用于绘制结果
# 用于 sklearn 预处理(Normalization 层的替代方案)# from sklearn.preprocessing import StandardScaler第 2 步:加载数据
Section titled “第 2 步:加载数据”# 加载数据(x_train, y_train), (x_test, y_test) = boston_housing.load_data()
print(f"训练数据形状: {x_train.shape}")print(f"测试数据形状: {x_test.shape}")print(f"训练标签形状: {y_train.shape}")# 示例:查看第一个训练样本的特征和标签# print("第一个训练样本特征:", x_train[0])# print("第一个训练样本标签(价格,单位 $1000):", y_train[0])数据集每个样本有 13 个输入特征。
第 3 步:预处理数据(特征缩放)
Section titled “第 3 步:预处理数据(特征缩放)”Boston Housing 数据集中的特征范围各不相同。对它们进行缩放(例如,使其具有零均值和单位方差)有助于模型更有效地训练。我们可以使用 scikit-learn 的 StandardScaler 或 Keras 的 Normalization 层。
方法 1:使用 Keras Normalization 层(推荐)
Section titled “方法 1:使用 Keras Normalization 层(推荐)”此层可以直接包含在模型中。它会适应训练数据并在推理期间应用相同的转换。
# 初始化 Normalization 层normalizer = Normalization(axis=-1) # 独立归一化特征
# 使层适应训练数据(计算均值和方差)normalizer.adapt(x_train)
print('\n特征均值:', normalizer.mean.numpy())print('特征方差:', normalizer.variance.numpy())
# 注意:我们将把这个 'normalizer' 层作为模型的第一层添加。方法 2:使用 Scikit-learn StandardScaler(替代方案)
Section titled “方法 2:使用 Scikit-learn StandardScaler(替代方案)”这在模型外部执行缩放。
# scaler = StandardScaler()# x_train_scaled = scaler.fit_transform(x_train)# x_test_scaled = scaler.transform(x_test) # 对测试数据使用 transform,而不是 fit_transform!
# print("\n缩放后的形状 (sklearn):")# print("训练集:", x_train_scaled.shape)# print("测试集:", x_test_scaled.shape)
# 如果使用此方法,模型的 input_shape 仍将是 (13,)# 并且你会将 x_train_scaled/x_test_scaled 传递给 fit/evaluate 方法。第 4 步:构建回归模型
Section titled “第 4 步:构建回归模型”我们将构建一个简单的 Sequential 模型,包括 Normalization 层(如果使用方法 1)。
input_shape = (x_train.shape[1],) # 应为 (13,)
model = Sequential([ tf.keras.Input(shape=input_shape), # 显式定义输入层 normalizer, # 添加已适应的 normalization 层(方法 1) Dense(64, activation='relu'), Dense(64, activation='relu'), Dense(1) # 输出层:1 个单元,线性激活(默认)], name="boston_housing_mlp")
model.summary()如果使用 scikit-learn 缩放(方法 2),则从模型定义中省略 normalizer 层。
第 5 步:编译模型
Section titled “第 5 步:编译模型”使用合适的回归损失和指标进行编译。
model.compile(loss='mean_absolute_error', # 使用 MAE 作为损失 optimizer=Adam(learning_rate=0.001), metrics=['mae', 'mse']) # 同时跟踪 MAE 和 MSE第 6 步:训练模型
Section titled “第 6 步:训练模型”训练模型,使用 EarlyStopping 防止过拟合并找到一个好的停止点。
epochs = 500
# 定义 EarlyStopping 回调函数early_stopping = EarlyStopping( monitor='val_loss', # 监控验证损失 patience=20, # 在此数量的 epoch 中性能没有提升时停止训练 restore_best_weights=True # 恢复监控指标值最佳的那个 epoch 的模型权重)
print("\n训练模型...")history = model.fit( x_train, y_train, # 如果使用 Normalization 层,则传递原始 x_train # x_train_scaled, y_train, # 如果使用 sklearn scaler,则传递缩放后的数据 epochs=epochs, validation_split=0.2, # 使用 20% 的训练数据进行验证 verbose=0, # 设置 verbose=0 以减少训练期间的输出 callbacks=[early_stopping])
print("\n训练完成。")print(f"停止于 epoch: {early_stopping.stopped_epoch}")第 7 步:评估模型
Section titled “第 7 步:评估模型”在未见过的测试集上评估性能。
print("\n在测试数据上评估...")results = model.evaluate(x_test, y_test, verbose=0)# results = model.evaluate(x_test_scaled, y_test, verbose=0) # 如果使用 sklearn scaler
print(f"测试损失 (MAE): {results[0]:.2f}")print(f"测试 MAE: {results[1]:.2f}")print(f"测试 MSE: {results[2]:.2f}")‘测试 MAE’ 告诉你模型在测试集上预测的房价与实际房价之间的平均绝对差异(单位 $1000)。
第 8 步:进行预测和可视化
Section titled “第 8 步:进行预测和可视化”对测试集进行预测,并将预测值与实际值进行比较。
test_predictions = model.predict(x_test).flatten()# test_predictions = model.predict(x_test_scaled).flatten() # 如果使用 sklearn scaler
# 预测值与实际值的简单散点图plt.figure(figsize=(8, 8))plt.scatter(y_test, test_predictions)plt.xlabel('True Values [Price in $1000s]')plt.ylabel('Predictions [Price in $1000s]')plt.axis('equal')plt.axis('square')plt.xlim([0, plt.xlim()[1]])plt.ylim([0, plt.ylim()[1]])_ = plt.plot([-100, 100], [-100, 100]) # 对角线plt.title('回归预测 vs 实际值')plt.grid(True)plt.show()
# 绘制训练和验证损失曲线plt.figure(figsize=(10, 6))plt.plot(history.history['loss'], label='训练集 MAE 损失')plt.plot(history.history['val_loss'], label='验证集 MAE 损失')plt.title('训练和验证损失 (MAE)')plt.xlabel('Epoch')plt.ylabel('损失 (MAE)')plt.legend()plt.grid(True)plt.show()散点图有助于可视化模型的性能。靠近对角线的点表示预测准确。损失曲线显示了模型在 epoch 训练过程中的学习情况,有助于诊断过拟合或欠拟合问题。