3.3波士顿房价预测

加载波士顿房价数据

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

数据标准化

数据取值范围差异很大,需要对每个特征做标准化,即对于输入数据的每个特征(输入数据矩阵的列),减去特征平均值,再除以标准差,这样得到的特征平均值为0,标准差为1

mean = train_data.mean(axis=0)
train_data -= mean
std = train_data.std(axis=0) #求标准差
train_data /= std

test_data -= mean
test_data /= std

模型定义

一般来说,训练数据越少,过拟合会越严重,而较小的网络可以降低过拟合

MAE 平均绝对误差 预测值与目标值之差的绝对值 比如这个问题的MAE等于0.5,表示预测的房价与实际价格相差500美元

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']) #mse均方误差,预测值与目标值之差的平方 回归问题常用的损失函数
    return model

 网络的最后一层只有一个单元,没有激活,是一个线性层。这是标量回归(预测单一连续值的回归)的典型设置。添加激活函数将会限制输出范围。例如:如果向最后一层添加sigmoid激活函数,网络只能学会预测0~1范围内的值。这里最后一层是纯线性的,所以网络可以学会预测任意范围内的值。

利用K折验证

数据点少会导致验证集非常小,可以采用K折交叉验证。这个方法将可用数据划分为k个分区,实例化k个相同的模型,将每个模型在k-1个分区上训练,并在剩下的一个分区上进行评估。

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)
   
    #准备验证数据:第k个分区的数据
    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() #构建keras模型
model.fit(partial_train_data,partial_train_targets,
          epochs=num_epochs,batch_size=1,verbose=0)  #verbose为0就是不输出进度条,默认的是1,就是显示进度条
val_mse,val_mae = model.evaluate(val_data,val_targets,verbose=0) #在验证数据上评估模型
all_scores.append(val_mae)

#结果
print(all_scores)
print(np.mean(all_scores))

每轮运行模型得到的验证分数有很大差异,平均分数会更可靠(K折交叉验证的关键) 100个轮次获得的预测结果显示偏差较大,因此让训练的时间更长一点,达到500个轮次,为了记录模型在每轮的表现,需要修改训练循环,以保存每轮的验证分数记录

保存每折的验证结果

# K折验证
import numpy as np
k=4
num_val_samples = len(train_data)//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] #准备验证数据:第k个分区的数据
    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,
                    epochs=num_epochs,
                    batch_size=1,
                    verbose=0, #0表示不显示进度条
                    validation_data=(val_data,val_targets)) 
mae_history = history.history['val_mean_absolute_error']
all_mae_histories.append(mae_history)

#计算所有轮次中的K折验证分数平均值
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
#remove head 10 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)

#结果
print(test_mae_score)