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)