当前位置:   article > 正文

深度学习实战案例:预测房价_深度学习房价预测实验报告

深度学习房价预测实验报告

公众号:机器学习杂货店
作者:Peter
编辑:Peter

大家好,这里是机器学习杂货店 Machine Learning Grocery~

本文的案例讲解的是机器学习中一个重要问题:回归问题,它预测的是一个连续值而不是离散的标签。

注意:逻辑回归不是回归算法,而是分类算法

[外链图片转存中…(img-JnR0sik4-1648911615585)]

波士顿房价数据集

506个样本,其中404个训练样本,102个测试样本

In [1]:

import numpy as np

from keras.datasets import boston_housing
  • 1
  • 2
  • 3

划分数据集

In [2]:

# 限制前10000个最常见的单词

(train_data, train_targets), (test_data, test_targets) = boston_housing.load_data()
  • 1
  • 2
  • 3

In [3]:

train_data.shape  # 每个样本都是13个特征
  • 1

Out[3]:

(404, 13)
  • 1

In [4]:

test_data.shape
  • 1

Out[4]:

(102, 13)
  • 1

In [5]:

train_targets[:10]
  • 1

Out[5]:

array([15.2, 42.3, 50. , 21.1, 17.7, 18.5, 11.3, 15.6, 15.6, 14.4])
  • 1

数据标准化

神经网络中不能输入取值范围差异很大的数据集,需要进行标准化处理。

每个特征的标准化:(原数据 - 特征平均值) / 标准差。 得到的就是特征平均值为0,标准差为1

In [6]:

# numpy实现

mean = train_data.mean(axis=0)
train_data -= mean  # 等价于 train_data = train_data - mean
std = train_data.std(axis=0)
train_data / std

# 测试集
test_data -= mean # 训练集的均值和标准差
test_data /= std
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

注意的点:

  1. 测试集的标准化使用的是训练集的均值和方差
  2. 我们不能使用在测试上计算得到的任何结果

构建网络

样本量少,可构建2个隐藏层,每层64个单元。较小的网络能够降低过拟合

建模

In [7]:

import tensorflow as tf  # add
from keras import models
from keras import layers 


def build_model():
    model = models.Sequential()  # 需要将同一个模型多次实例化,使用同一个函数来构建模型
    model.add(tf.keras.layers.Dense(64,
                                    activation="relu",
                                    # 训练集的shape的第二个取值
                                    input_shape=(train_data.shape[1], )
                                   ))
    model.add(tf.keras.layers.Dense(64, 
                                    activation="relu"))
    model.add(tf.keras.layers.Dense(1))
    
    model.compile(optimizer="rmsprop",
                  loss="mse",
                  metrics=["mae"]
                 )
    
    return model
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

网络的特点:

  1. 最后一层:一个单元,没有激活,是一个线性层(标量回归的典型设置
  2. 损失函数mse-均方误差,(y_predict- y_true)^2;回归问题的常用损失函数
  3. 监控指标mae-平均绝对误差,|y_predict- y_true|;预测值和目标值之差的绝对值

K折交叉验证

原理

当样本数量很少的时候,验证集的划分方式可能会造成验证分数上有很大的方差,无法对模型进行可靠的评估。

最佳方法:使用K折交叉验证

  • 将数据划分为K个分区,通常是4或者5
  • 实例化K个模型,将模型在K-1个分区上训练,剩下的一个区上进行评估
  • 模型的验证分数等于K个验证分数的均值。

如何K折交叉验证:以3折交叉验证为例

[外链图片转存中…(img-Q2e9vpnQ-1648911615598)]

代码实现

In [8]:

import numpy as np


k = 4
num_val_samples = len(train_data) // k

for i in range(k):
    print("procesing 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]
    
    # 训练数据集的合并:axis=0方向
    # train_data合并
    partial_train_data = np.concatenate(
        [train_data[:i*num_val_samples],
        train_data[(i+1)*num_val_samples:]],
        axis=0
    )
    # train_targets合并
    partial_train_targets = np.concatenate(
        [train_targets[:i*num_val_samples],
        train_targets[(i+1)*num_val_samples:]],
        axis=0
    )
procesing fold ...... 0
procesing fold ...... 1
procesing fold ...... 2
procesing fold ...... 3
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

训练模型 + 验证集评估模型

In [9]:

import numpy as np

k = 4
num_val_samples = len(train_data) // k
number_epochs = 100
all_scores = []

for i in range(k):
    print("procesing 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]
    
    # 训练数据集的合并:axis=0方向
    # train_data合并
    partial_train_data = np.concatenate(
        [train_data[:i*num_val_samples],
        train_data[(i+1)*num_val_samples:]],
        axis=0
    )
    # train_targets合并
    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=number_epochs,
              batch_size=1,
              verbose=0  # 静默模式
             )
    
    # 验证数据上评估模型
    val_mse, val_mae = model.evaluate(val_data, val_targets, verbose=0)
    
    all_scores.append(val_mae)
procesing fold ...... 0
procesing fold ...... 1
procesing fold ...... 2
procesing fold ...... 3
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

In [10]:

all_scores
  • 1

Out[10]:

[2.6683619022369385,
 2.8356902599334717,
 2.8533785343170166,
 2.9509527683258057]
  • 1
  • 2
  • 3
  • 4

In [11]:

np.mean(all_scores)
  • 1

Out[11]:

2.827095866203308
  • 1

每次运行模型得到的数值还是有很大的差异,但是均值最终还是在2.94接近3,是一个比较可靠的结果。

增加模型训练轮次(修改)

  • 将模型训练500次
  • 保存每轮的验证分数记录
代码实现

In [14]:

import numpy as np

k = 4
num_val_samples = len(train_data) // k
number_epochs = 500
# 修改:统计mae得分
all_mae_histories = []

for i in range(k):
    print("procesing 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]
    
    # 训练数据集的合并:axis=0方向
    # train_data合并
    partial_train_data = np.concatenate(
        [train_data[:i*num_val_samples],
        train_data[(i+1)*num_val_samples:]],
        axis=0
    )
    # train_targets合并
    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=number_epochs,
        batch_size=1,
        verbose=0  # 静默模式
        )

    # 验证数据上评估模型:记录mae
    # 原文使用的是全称
    # mae_history = history.history["val_mean_absolute_error"]
    
    # 目前版本已经使用缩写形式
    mae_history = history.history["val_mae"]
    all_mae_histories.append(mae_history)
procesing fold ...... 0
procesing fold ...... 1
procesing fold ...... 2
procesing fold ...... 3
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50

计算每个轮次中所有折的MAE的平均值:

In [24]:

len(all_mae_histories)
  • 1

Out[24]:

4
  • 1

In [25]:

len(all_mae_histories[0])
  • 1

Out[25]:

500
  • 1

In [26]:

average_mae_history = [np.mean([x[i] for x in all_mae_histories]) for i in range(number_epochs)]
average_mae_history[:10]
  • 1
  • 2

Out[26]:

[6.998382568359375,
 5.590555667877197,
 6.159129858016968,
 5.215211391448975,
 5.865886926651001,
 5.083814740180969,
 5.314043760299683,
 4.525156497955322,
 4.432371437549591,
 5.098088502883911]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

In [27]:

len(average_mae_history)
  • 1

Out[27]:

500
  • 1
可视化:绘制验证分数

In [31]:

import matplotlib.pyplot as plt

plt.plot(range(1, len(average_mae_history) + 1), average_mae_history)

plt.xlabel("Epochs")
plt.ylabel("Validation MEA")

plt.show()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

[外链图片转存中…(img-hxH59OOL-1648911615600)]

可视化改进版

上面的图形纵轴范围大,且数据的方差大,很难看清图形,重新绘制:

  • 删除前面10个点,它们的取值和其他点的取值差异大
  • 将每个数据点替换为前面数据点的指数移动平均值,得到光滑的曲线

In [38]:

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:])


import matplotlib.pyplot as plt

plt.plot(range(1, len(smooth_mae_history) + 1), smooth_mae_history)

plt.xlabel("Epochs")
plt.ylabel("Validation MEA")

plt.show()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

[外链图片转存中…(img-S4DvOuIM-1648911615601)]

训练最终模型

使用最佳参数在整个训练集上进行训练,然后在测试集上进行测试:

In [40]:

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)
4/4 [==============================] - 0s 3ms/step - loss: 372.9089 - mae: 18.3248
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

In [41]:

test_mae_score
  • 1

Out[41]:

18.324810028076172
  • 1

可以看到预测的房价和真实的房价的相差约为1.8万美元

总结

  • 回归问题中,损失函数使用的是均方误差MSE
  • 回归问题中,评价指标使用的是平均绝对误差MAE
  • 如果数据的特征具有不同的取值范围,需要进行数据的归一化,进行数据缩放
  • 当数据少的时候,使用K折交叉验证来可靠的评估模型
  • 如果训练的数据少,最好使用隐藏层较少的小型网络,以避免严重的过拟合
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/549616
推荐阅读
相关标签
  

闽ICP备14008679号