这是《Python深度学习》第一个回归问题,它的预测值是一个数值。常用的损失函数是均方误差MSE,常用的回归指标是平方绝对误差MAE

1.数据集介绍

波士顿房价是1970s波士顿郊区房屋价格的中位数,包含506个数据,分为404个训练样本和102个测试样本。每个样本有13个数值特征。

2.思路

与先前的手写数字和电影评论分类的例子不同,房价预测中的特征的重要性不一致,而且由于单位不尽相同,需要先对数据进行标准化处理。其次设定网络结构,然后确定网络参数,沿用电影分类中方式,先预训练一波,最后训练目标模型
数据处理->设定网络结构->调节参数->训练目标模型

3.数据预处理

对于这种取值范围差异较大的数据,一般采用标准化处理。
即对于数据的每个特征,减去特征平均值,再除以标准差,这样得到的特征的平均值为0,标准差为1。

4.网络结构

本题样本较小,网络结构不复杂,采用三层全连接层。在项目2中介绍过输入数据为向量(数组组成的数组),标签为标量(单个数组),对于这种类型的问题,带有relu激活的全连接层的简单堆叠表现较好。因此前两层的激活函数为‘relu’。最后一层使用纯线性(不加激活层),这样可以预测任意范围内的值。
第一层:全连接层,设置64个单元,激活函数为‘relu’
第二层:全连接层,设置64个单元,激活函数为‘relu’
第三层:全连接层,设置1个单元

5.参数调节

与路透社电影评论分类的参数选择一致,这里在调参的时候也先进行了预训练。
对于本项目中样本较少的情况,采用k折交叉验证是一个比较好的选择,将数据划分为k分,实例化k个相同的模型,每个模型在k-1个分区上训练,并在剩下的一个分区上进行苹果。最终的验证分数等于k个验证分数的平均。

6.代码详解

from keras.datasets import boston_housing
from keras import models
from keras import layers
import numpy as np
import matplotlib.pyplot as plt

#载入数据
(train_data, train_targets), (test_data, test_targets) = boston_housing.load_data()

mean = train_data.mean(axis = 0) #计算训练集特征的平均值
std = train_data.std(axis = 0) #计算训练集特征的标准差

#训练集特征标准化
train_data  -= mean #减去特征平均值
train_data /= std #除以标准差

#测试集特征标准化
test_data -= mean #减去训练集特征平均值
test_data /= std #处理训练集特征的标准差
#在工作流程中,不能使用在测试数据上计算得到的任何结果

#搭建网络
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 #返回模型

'''
#K折交叉验证
k = 4 #设定k的值
num_val_samples = len(train_data) // k #计算验证集(每一份)的数据个数
num_epochs = 100 #epochs_size的值
all_scores = [] #存放验证分数
all_mae_history = [] #存放验证分数

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] #验证集结果
    #使用concatenate函数将剩下的几份合并成一个训练集,axis表示在哪个轴上合并,为0表示在最外层的轴
    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() #搭建网络
    #verbose表示是否查看每次训练的结果,为0表示不看,为2表示每次迭代输出loss等相关值
    history = model.fit(partial_train_data, partial_train_targets, epochs = num_epochs, batch_size=1, verbose=2) #训练网络
    mae_history = history.history['mean_absolute_error'] #返回模型的mae
    all_mae_history.append(mae_history) #记录mae
    #val_mse, val_mae = model.evaluate(val_data, val_targets, verbose=0) 
    #评估模型
    #all_scores.append(val_mae)
    #记录mae

#计算所有模型的mae的平均值
average_mae_history = [np.mean([x[i] for x in all_mae_history]) for i in range(num_epochs)]

#平滑曲线,将每个值变成上一个值乘以权重和该值乘以权重的和,将曲线的变化率降低。
def smooth_curve(points, factor = 0.9):
    smooth_points = []
    for point in points:
        if smooth_points:
            previous = smooth_points[-1]
            smooth_points.append(previous * factor + point * (1-factor))
        else:
            smooth_points.append(point)
    return smooth_points

smooth_mae_history = smooth_curve(average_mae_history[10 :]) #前10个值偏差过大,去掉。将其他值平滑处理

plt.plot(range(1, len(smooth_mae_history) + 1), smooth_mae_history) #绘制曲线
plt.xlabel('Epochs')
plt.ylabel('Validation MAE')
plt.show()
'''
#得到结果发现在第80轮后mae不再显著降低,epochs设为80

model = build_model() #搭建模型
model.fit(train_data, train_targets, epochs = 80, batch_size = 16, verbose = 2) #训练模型
test_mse_score, test_mae_score = model.evaluate(test_data, test_targets) #评估模型
print('mse = ',test_mse_score, 'mae = ', test_mae_score)

训练结果

Python中麦粒 python mse_全连接