听它爹说他孩儿:Keras 学习笔记 4.5

预测房价:回归的范例

机器学习问题的另一种类型是回归。回归预测的对象是连续值而非离散的标签。例如,用气象数据预测明天的气温;或者,根据详细计划书预测软件工程的完工日期。

波士顿房屋价格数据集

你将尝试预测房屋的中位价。数据点包括:1970 年代中期,在波士顿郊区,犯罪率,当地财产税率等。数据集只有 506 个数据点,并分成 404 个训练样本和 102 个测试样本。输入数据的特征,例如犯罪率,数值的变化范围大。例如,有的取值 0 至 1;有的取值 1 至 12 或 0 至 100,等等。

 装入波士顿房屋数据集 

from keras.datasets import boston_housing
(train_data, train_targets), (test_data, test_targets) = boston_housing.load_data()

# 看下数据:
>>> train_data.shape
(404, 13)
>>> test_data.shape
(102, 13)
# 13 个特征,如人均犯罪率、每处住宅的房间数、上公路的方便程度,等等

# 目标是房产的中位价(单位千美元):
>>> train_targets
[ 15.2, 42.3, 50. ... 19.4, 19.4, 29.1]
# 典型价格 10,000 至 50,000美元
数据预处理

现有数据值范围差别大,网络难以学习使用。对此,最好的处理办法是特征归一化:减去平均值再除以标准差。

 数据归一化 

mean = train_data.mean(axis=0)
train_data -= mean
std = train_data.std(axis=0)
train_data /= std
test_data -= mean
test_data /= std

注意,测试数据集做归一化时,用到的平均值和标准差是训练数据集计算所得。决不能用测试数据集做这种计算。

建立网络

由于样本很少,你将使用很小的网络,2 个隐藏层,每层 64 个单元。一般地说,训练数据越少,过度拟合越重。用小网络是减缓过度拟合的一个办法。

 定义模型 

from keras import models
from keras import layers
def build_model():
    model = models.Sequential()
    model.add(layers.Dense(64, activation='relu',
    input_shape=(train_data.shape[1],)))
    model.add(layers.Dense(64, activation='relu'))
    model.add(layers.Dense(1))
    model.compile(optimizer='rmsprop', loss='mse', metrics=['mae'])
    return model

网络最后一层只有一个单元,没有激活函数,形成线性层。这是典型的离散回归,用于预测单一连续值。若用激活函数,会限制输出值的范围。例如,在末层使用 S 型函数,网络将只学习预测 0 至 1 的值。现在,末层是纯线性的,网络可自由学习预测任意范围的值。

注意,编译这个网络时,你用的损失函数是 mse (mean squared error),预测与目标差值的平方。这个损失函数在回归问题上广泛使用。

在训练过程中,你监测了新的指标 MAE (mean absolute error),它是预测和目标的绝对差值。例如,若测到 MAE 为 0.5,则意味着你的预测偏离平均值 500 美元。

对你的训练进行 K 折验证

验证需要从训练用数据分出一部分供验证使用。但是,本例数据集很小,分割出的数据无法正常评估模型的表现。

这时最好的实用办法是 K 折交叉验证。它把数据分成 K 个部分(一般,K = 4 或 5),一个单独的子样本被保留作为验证模型的数据,其他K-1个样本用来训练。交叉验证重复K次,每个子样本验证一次,平均K次的结果或者使用其它结合方式,最终得到一个单一估测。

 K 折交叉验证 

import numpy as np
k=4
num_val_samples = len(train_data) // k
num_epochs = 100
all_scores = []
for i in range(k):
    print('processing fold #', i)
    val_data = train_data[i * num_val_samples: (i + 1) * num_val_samples]
    val_targets = train_targets[i * num_val_samples: (i + 1) * num_val_samples]
    partial_train_data = np.concatenate(
                                        [train_data[:i * num_val_samples],
                                         train_data[(i + 1) * num_val_samples:]],
                                        axis=0)
    partial_train_targets = np.concatenate(
                                       [train_targets[:i * num_val_samples],
                                        train_targets[(i + 1) * num_val_samples:]],
                                       axis=0)
    model = build_model()
    model.fit(partial_train_data, partial_train_targets,
    epochs=num_epochs, batch_size=1, verbose=0)
    val_mse, val_mae = model.evaluate(val_data, val_targets, verbose=0)
    all_scores.append(val_mae)

# 以 num_epochs = 100 运行,结果如下:
>>> all_scores
[2.588258957792037, 3.1289568449719116, 3.1856116051248984, 3.0763342615401386]
>>> np.mean(all_scores)
2.9947904173572462

各次运行成绩不同,从 2.6 至 3.2。平均值 3.0 比较靠谱,这意味着偏离均价 3,000 美元。

让我们增加训练达到 500 次迭代。为了记下每次迭代的成绩,你应增加代码保存验证成绩。

 保存每一 K 折的验证成绩 

num_epochs = 500
all_mae_histories = []
for i in range(k):
    print('processing fold #', i)
    val_data = train_data[i * num_val_samples: (i + 1) * num_val_samples]
    val_targets = train_targets[i * num_val_samples: (i + 1) * num_val_samples]
    partial_train_data = np.concatenate(
                                        [train_data[:i * num_val_samples],
                                         train_data[(i + 1) * num_val_samples:]],
                                        axis=0)
    partial_train_targets = np.concatenate(
                                           [train_targets[:i * num_val_samples],
                                            train_targets[(i + 1) * num_val_samples:]],
                                           axis=0)
    model = build_model()
    history = model.fit(partial_train_data, partial_train_targets,
                        validation_data=(val_data, val_targets),
                        epochs=num_epochs, batch_size=1, verbose=0)
    mae_history = history.history['val_mean_absolute_error']
    all_mae_histories.append(mae_history)

你可以计算每个 K 折每次迭代的 MAE 平均成绩,并用图展示。

average_mae_history = [np.mean([x[i] for x in all_mae_histories]) for i in range(num_epochs)]

import matplotlib.pyplot as plt
plt.plot(range(1, len(average_mae_history) + 1), average_mae_history)
plt.xlabel('Epochs')
plt.ylabel('Validation MAE')
plt.show()

由于伸缩的原因,图有点难看。让我们做以下修改:

  • 省略开头 10 个数据点,因其数值范围与众不同。
  • 对前面的相关点做指数平均移动,得到平滑曲线。
def smooth_curve(points, factor=0.9):
    smoothed_points = []
    for point in points:
        if smoothed_points:
            previous = smoothed_points[-1]
            smoothed_points.append(previous * factor + point * (1 - factor))
        else:
            smoothed_points.append(point)
    return smoothed_points

smooth_mae_history = smooth_curve(average_mae_history[10:])
plt.plot(range(1, len(smooth_mae_history) + 1), smooth_mae_history)
plt.xlabel('Epochs')
plt.ylabel('Validation MAE')
plt.show()

在你完成调整模型的其他参数后,或者增加迭代次数调整隐藏层的大小后,你可以用全部数据和最优参数,训练定型后的模型,并用测试数据查看它的性能表现。

 训练定型后的模型 

model = build_model()
model.fit(train_data, train_targets,epochs=80, batch_size=16, verbose=0)
test_mse_score, test_mae_score = model.evaluate(test_data, test_targets)

# 以下是最终结果:
>>> test_mae_score
2.5532484335057877
# 你还相差约 2,550 美元

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值